# Glacier 公司管理系统 — 技术架构设计(草案 v0.4) > 状态:设计阶段(已确认关键技术决策,见 §0),仅设计,不写代码。 > 适用范围:Glacier 公司内部管理系统(首页 + 业务功能子空间 + 数据采集)。 --- ## 0. 已确认决策(v0.2 落实) | # | 决策点 | 结论 | 对架构的影响 | |---|---|---|---| | 1 | 模板引擎 | **Twig**(composer 引入) | §3.3:模板/脚本分离由 Twig 承担,自带转义 | | 2 | 依赖策略 | **尽可能减少依赖**;无法绕过的才用 composer 第三方 | 核心(路由/DB/会话/中间件)全部自研;**唯一默认第三方 = Twig** | | 3 | 子空间管理 | **同一仓库**(glacier 单仓多模块) | §2:模块收拢在 `app/modules/` 下 | | 4 | 数据库 | **单库多表**(库名 `glacier`) | §3.5:统一表前缀约定,避免表名冲突 | | 5 | 上传文件 | **本地磁盘**,但**规避直接访问** | §3.9:存 `storage/uploads/`(Web 根之外),经控制器鉴权下发 | | 6 | 语言 / 时区 | **中文界面**,**UTC+8** | §3.4:全链路统一时区与字符集 | | 7 | 数据来源 | **Python 爬虫**为可能的数据来源 | §3.8:采集入库供系统展示 | | 8 | 数据采集实现 | **采用现成框架**(Scrapy 等),**不自研** | §3.8:P6 暂缓;接入时遵循单写者原则 | --- ## 1. 总体架构 ``` ┌──────────────────────────────────────────────────┐ 浏览器 (HTTP) │ │ │ │ 生产: Debian Caddy(自动HTTPS) → PHP-FPM │ ▼ │ 开发: Windows WAMP Apache → mod_php │ ┌──────────┐ │ │ │ public/ │ │ ┌──────────────────────┐ │ │ index.php│───▶│ │ 应用核心 (app/core) │ │ └──────────┘ │ │ Router · Twig │ │ │ │ │ Database · Session │ │ │ │ └──────────┬───────────┘ │ │ │ │ │ │ │ ┌────────────┼────────────┐ │ │ │ ▼ ▼ ▼ │ │ │ modules/ controllers/ models/ │ │ │ (业务子空间) (业务逻辑) (数据层) │ │ │ │ │ │ │ ▼ │ │ │ Go 服务 (独立进程, 按需引入) │ │ └───────────────┬──────────────────────────────────┘ │ ▼ │ MariaDB (生产) / MySQL (开发) │ 库: glacier, 单库多表, 统一前缀 ▼ ▲ 静态资源: /assets │ 写入 (只写库+私有存储, 不碰 Web 层) 上传: storage/uploads (私有) │ │ ┌────────────┴────────────┐ │ Python 数据采集 (crawlers/) │ │ 现成框架(Scrapy 等,不自研) │ │ 采集 → 清洗/去重 → 入库 │ └─────────────────────────┘ ``` ### 1.1 分层职责 | 层 | 职责 | 不许做的事 | |---|---|---| | Web 服务器 | TLS、静态资源、URL rewrite 到入口 | 不执行业务逻辑 | | 入口 (public/index.php) | 引导、加载配置、调用 Router | 不包含业务逻辑 | | Router(URL 模式引擎) | 将 URL 映射到控制器/模块 | 不解析物理路径 | | Controller | 接收请求、调模型、产出数据 | 不直接输出 HTML(交给 Twig) | | Model | 数据访问(PDO 预处理)、业务规则 | 不处理 URL | | View(Twig 模板) | 只渲染控制器给的数据 | 不写业务逻辑、不做数据库访问 | | Go 服务 | 复杂/长任务/高并发能力(按需) | 不处理 Web 会话 | | Python 爬虫 | 外部数据采集、清洗、入库 | 不直接提供 Web 页面、不碰会话 | ### 1.2 技术选型速查 | 项 | 选型 | 理由 / 备注 | |---|---|---| | PHP | 8.1+(生产 PHP-FPM,开发 mod_php) | 生态成熟,WAMP 开箱即用 | | 模板引擎 | **Twig**(composer,唯一默认第三方) | 自带转义、布局继承;稳定成熟 | | 路由 | 自研 URL Pattern Engine | 需求明确、可控、零依赖 | | DB | PDO + prepared statement | 防注入、跨 MySQL/MariaDB 一致 | | 会话 | 原生 Session 封装(可选 DB 存储) | 后期扩展权限体系 | | 复杂后端 | Go 独立二进制(按需) | 编译型、并发强、部署简单 | | 数据采集 | **Python + 现成框架**(Scrapy 等,不自研) | 成熟框架自带调度/去重/并发/robots 支持;P6 暂缓,有明确目标时引入 | | 前端 | 原生 HTML/CSS + 少量 JS;无前端框架 | 管理系统以表单/表格为主 | | 生产反代 | Caddy | 自动 HTTPS、配置简单 | | 开发 Web | WAMP Apache | 本机开发一致预览 | --- ## 2. 目录结构设计(仓库蓝图) ``` glacier/ ├── public/ # ★ 唯一对外 Web 根目录 │ ├── index.php # 单一入口(前端控制器) │ ├── .htaccess # 开发环境 rewrite(Apache/WAMP) │ └── assets/ # css / js / img(前端静态资源,可直接访问) ├── app/ │ ├── core/ # 核心基础设施(自研,零依赖) │ │ ├── Router.php # URL 模式引擎 │ │ ├── Database.php # PDO 封装 + 迁移 │ │ ├── Session.php # 会话管理封装 │ │ ├── Middleware.php # 中间件管道(本期预留 auth/权限/CSRF) │ │ ├── Response.php # 统一响应(HTML/JSON/重定向) │ │ ├── File.php # 上传/下载控制器支撑(私有存储鉴权下发) │ │ └── helpers.php # 转义等公共函数 │ ├── config/ # 配置(环境分层) │ │ ├── env.dev.php # 开发环境配置(含 DB、时区、BASE_URL) │ │ ├── env.prod.php # 生产环境配置 │ │ └── routes.php # ★ URL 模式路由表(集中声明) │ ├── controllers/ # 各页面控制器 │ ├── models/ # 数据模型 │ ├── views/ # Twig 模板(与脚本分离) │ │ ├── layouts/ # 布局模板(页头/页脚/导航) │ │ └── partials/ # 可复用片段 │ ├── middleware/ # 中间件实现(后期) │ └── modules/ # ★ 业务子空间(见 §4) │ └── / # 每个 / 一个模块 │ ├── routes.php # 模块内路由(前缀自动挂载) │ ├── controllers/ │ ├── models/ │ └── views/ ├── storage/ # ★ 私有存储(Web 根之外,禁止直接访问) │ ├── uploads/ # 上传文件(本地磁盘,经控制器鉴权下发) │ ├── cache/ # 模板/路由缓存 │ └── logs/ # 应用日志 ├── crawlers/ # ★ 数据采集(见 §3.8,采用现成框架如 Scrapy;P6 暂缓) │ └── (待引入框架项目结构:scrapy.cfg / pyproject.toml 等) ├── go/ # Go 复杂后端服务(独立 module,按需) │ └── services/ ├── database/ # SQL 迁移脚本(版本化,单库多表) ├── docs/ # 文档(本文件) ├── deploy/ # 部署资产 │ ├── Caddyfile # 生产 rewrite + TLS 配置 │ ├── systemd/ # PHP-FPM / Go / Python 任务单元 │ └── deploy.sh # 发布脚本 ├── vendor/ # composer 第三方(Twig 等)— .gitignore ├── .gitignore # 需重写:PHP + Python + 上传/密钥/缓存 ├── composer.json # 仅声明 Twig(+ 后续按需评估) ├── README.md # 项目说明(指向本文档) └── LICENSE ``` **关键原则:** 1. 只暴露 `public/`;上传在 `storage/`,物理路径不可能出现在 URL,也不可能被直接访问。 2. 所有动态请求进 `index.php`,由路由表声明式分发;`assets/` 是唯一直接服务的静态目录。 3. 依赖最小化:核心全自研,composer 只装 Twig;Python 侧只装采集所需轻量库。 --- ## 3. 核心机制设计 ### 3.1 URL 模式引擎(Router)— 自研,不依赖物理路径 - **单一入口**:`public/index.php` 接收所有请求(rewrite 见 §3.2),把 URL 路径交给 Router。 - **路由表(声明式)**:`app/config/routes.php`,形如 `模式 → [处理器, 中间件]`。 - **模式语法(极简)**:静态段 `/`、`/about`;参数段 `/user/:id`;可选段 `/news[/:page]`;模块前缀 `/` 自动挂载到 `app/modules//routes.php`。 - **匹配规则**:按注册顺序;区分 GET/POST;未命中 → 渲染 404 模板页。 - **物理路径隔离**:Router 只消费 URL 模式与模块名,从不把 URL 段拼进文件系统路径;模块名白名单(`[a-z0-9_-]` + 目录存在性校验),杜绝路径穿越。 **示例映射(概念,非代码):** | URL | 处理器 | |---|---| | `/` | Home 控制器(本期 → "开发中"占位页) | | `/assets/*` | Web 服务器直接服务(不经过 PHP) | | `/file/download/:id` | 私有文件鉴权下发(§3.9) | | `/project` | 模块 project 入口(子空间) | | `/project/xxx` | 模块 project 内部路由 | ### 3.2 URL rewrite:开发 / 生产语义对齐 - 开发(WAMP/Apache):`public/.htaccess` — 非静态文件请求 rewrite 到 `index.php`。 - 生产(Caddy):`deploy/Caddyfile` — 真实文件(assets)直接服务,否则交 PHP-FPM 入口。 - 两份规则同一语义:`真实文件 → 直接返回;否则 → 入口`。上传目录不在 Web 根,天然不参与。 ### 3.3 模板与脚本分离 — Twig - **分工**:Controller 产出数据;Twig 只渲染。 - **Twig 使用约定**: - 模板目录:`app/views/`(布局/片段),模块模板在 `app/modules//views/`; - 布局继承:`{% extends 'layouts/base.html.twig' %}` + `{% block %}`; - **默认自动转义**(Twig 原生);富文本用 `|raw` 时先经净化器(后期统一白名单净化); - 模板编译缓存写入 `storage/cache/`(开发可关,生产开)。 - **依赖策略**:Twig 是**唯一默认 composer 依赖**;后续如需第三方,先评估能否自研或替代,无法绕过才引入(§9 持续跟踪)。 ### 3.4 配置与环境隔离 - `APP_ENV` 切换 `env.dev.php` / `env.prod.php`。 - 统一配置项:DB 连接、**时区 Asia/Shanghai(UTC+8)**、**语言 zh-CN**、字符集 utf8mb4、`BASE_URL`(模板生成链接用,杜绝物理路径)、Session 名/有效期、上传/存储目录、爬虫调度开关等。 - 密钥不落库不入库:生产经环境变量/独立文件注入(.gitignore 排除)。 - 时区在 **PHP(date_default_timezone_set)、DB 连接(time_zone)、Python(Asia/Shanghai)** 三层统一为 UTC+8。 ### 3.5 数据库 — 单库多表 - 库名:`glacier`(开发 MySQL / 生产 MariaDB 同名)。 - **表前缀约定**(避免模块间冲突、便于迁移与权限): - 系统核心表:`g_`(如 `g_users`、`g_sessions`、`g_settings`); - 模块表:`_`(如 `news_articles`、`project_tasks`); - 采集表:`crawl_`(如 `crawl_sources`、`crawl_items`)。 - 统一 `Database.php`(PDO):DSN、utf8mb4、异常模式;**一律 prepared statement**。 - 迁移:`database/` 下 `0001_xxx.sql` 顺序编号(up/down),开发/生产共用同一套。 - 时间字段统一 TIMESTAMP/DATETIME + UTC+8 语义;ID 统一约定(自增或雪花,按模块需要)。 ### 3.6 Session 与访问控制(本期只预留接口) - `Session.php` 封装:HttpOnly、SameSite=Lax、Secure(生产开)、session 名唯一化、CSRF Token(表单统一中间件校验)。 - **中间件管道**:本期实现 CSRF、请求日志;**预留** `auth` / `permission` 空实现。 - 后期权限模型(设计预留):用户 + 角色 + 权限(RBAC 简化版);Session 在域名根共享,跨子空间统一登录;权限判断集中在中间件。 ### 3.7 Go 复杂后端集成(设计预留) - 适用场景(出现才引入):定时任务、队列/异步、报表聚合、外部 API 网关、并发同步、性能敏感接口。 - 形态:独立二进制 + systemd,监听内部端口/unix socket;PHP 经 cURL 调用(JSON、错误码、超时重试统一)。 ### 3.8 数据采集(Python 爬虫 — 采用现成框架,不自研) **定位**:外部数据来源(如行业新闻、政策、竞品信息等)→ 采集 → 清洗/去重 → 入库 → 系统各模块展示。**爬虫只写数据库与私有存储,不提供 Web 页面、不碰会话**。 **实现方式(v0.4 决策)**:**采用现成爬虫框架(如 Scrapy 等),不自研采集管道**。自研收益低、维护成本高;成熟框架自带调度、去重、并发、中间件与 robots 支持。 **框架接入约定**: - 框架项目位于 `crawlers/`(独立子项目,含 scrapy.cfg / pyproject.toml 等自身结构); - **单写者原则不变**:写库统一经采集项目的数据管道;表结构仍以 `database/` 迁移为唯一来源; - 采集表约定保留:`crawl_` 前缀(如 `crawl_sources` / `crawl_items`),字段含 `source_url`(唯一约束去重)、`content_hash`、`fetched_at`、`status`; - 运行环境:开发 Windows(venv)/ 生产 Debian(systemd timer 或 cron 触发框架命令); - **安全不变**:内容入库前净化(防存储型 XSS,展示层 Twig 转义兜底);凭据不入库不入仓库;遵守目标站点 robots 与频率限制; - **本阶段(P6)暂缓**:待有明确采集目标时再引入框架实施,不影响 PHP 侧开发。 ### 3.9 上传与私有文件(本地磁盘,规避直接访问) - 上传落盘:`storage/uploads/`(Web 根之外)→ **任何 URL 都访问不到物理文件**。 - 下发:`/file/download/:id` 控制器 → 校验(登录/权限/归属,后期)→ 流式输出(Content-Disposition 可控)。 - 存储命名:随机文件名 + 扩展名白名单 + MIME 校验 + 大小限制;元数据入库(`g_files` 或模块表)。 - 前端静态资源(css/js/img)仍走 `public/assets/` 直接访问,两者严格区分。 --- ## 4. 子空间(/project)体系设计 - `domain.com/` 每个 `` 是独立业务子空间(模块),收拢在 `app/modules//`: - `routes.php`(模块内路由,前缀自动挂载)、`controllers/`、`models/`、`views/`(Twig)、`module.php`(声明:名称、访问要求、导航注册)。 - **单库多表**:模块表用 `_` 前缀,无独立数据库。 - **开源程序改造接入**:放入模块内,不直接暴露其入口文件;URL 经模块路由收口;Session 统一;登录/权限由系统中间件决定。 - **访问控制(后期)**:系统级认证 + 模块级角色/权限声明,集中在中间件。 --- ## 5. 首页 - 本期:`/` → Home 控制器 → Twig 渲染"开发中"占位页(品牌 + 占位说明,中文)。 - 后期:公司新闻(news 模块,可消费爬虫采集的 `crawl_*_items` 数据)+ 功能导航(从 `module.php` 聚合菜单)。 --- ## 6. 安全基线(贯穿全程) | 项 | 措施 | |---|---| | SQL 注入 | PDO prepared statement 唯一路径(PHP);Python 写库同样参数化 | | XSS | Twig 默认转义;富文本显式净化 | | CSRF | 表单统一 token 校验(中间件) | | 路径穿越 | 模块名白名单 + 不拼接物理路径 | | 文件上传/下载 | 私有目录存储 + 控制器鉴权下发(§3.9) | | 会话 | HttpOnly + SameSite;登录后重置 session id | | 采集安全 | robots/限速;内容净化;凭据不落库不入库 | | 敏感信息 | 密钥不落库不入库;生产注入 | | 错误处理 | 开发显示详情,生产只记日志 | --- ## 7. 开发 / 生产一致性对照 | 维度 | 开发(WAMP/Windows) | 生产(Debian) | 处理 | |---|---|---|---| | Web 服务器 | Apache | Caddy | rewrite 语义对齐;以 Caddy 为准 | | PHP | mod_php | PHP-FPM | 版本一致(8.1+);php.ini 关键项对齐 | | 数据库 | MySQL | MariaDB | 兼容语法;迁移两端验证 | | Python | Windows venv (3.10+) | python3-venv + systemd timer/cron | 版本一致;requirements 固定版本 | | 路径 | 反斜杠 | 斜杠 | 代码内一律相对路径/配置基址 | | 大小写 | 不敏感 | 敏感 | 一律小写命名 | | 时区/字符集 | Asia/Shanghai + utf8mb4 | 同左 | 三层统一(PHP/DB/Python) | --- ## 8. 实施步骤(分阶段) | 阶段 | 内容 | 验收点 | |---|---|---| | **P0 环境** | WAMP(PHP 8.1+)与 Python 3.10+ venv 就绪;初始化目录骨架;重写 .gitignore(PHP+Python+上传/密钥/缓存);composer 引入 Twig | 环境可跑;仓库结构就位 | | **P1 骨架** | 单一入口 + Router(URL 模式引擎)+ 错误处理 + 日志 | 任意 URL 进入口,不出现物理路径 | | **P2 模板** | Twig 接入 + 布局 + 首页"开发中"占位页 + assets | 首页渲染正常;模板/脚本分离生效 | | **P3 数据层** | Database 封装 + 迁移机制 + 配置分层(dev/prod,UTC+8) | 迁移可在开发库执行;配置切换生效 | | **P4 模块机制** | modules/ 约定 + 模块路由挂载 + 模块声明 | 示例模块经 `/` 可访问 | | **P5 会话与中间件** | Session 封装 + CSRF + auth/permission 空实现 | 中间件可挂载 | | **P6 数据采集**(**暂缓**) | **采用现成框架**(Scrapy 等,不自研);待有明确采集目标时引入 | 有目标后:框架采集 → 清洗入库 crawl_* 表(迁移先行) | | **P7 首个真实功能**(**暂缓**) | 待用户明确业务需求后规划 | 数据展示、模板渲染、权限挂载(框架已就绪) | | **P8 部署** | Caddyfile + systemd(PHP-FPM/Go/Python 任务)+ 部署脚本;生产试部署 | 生产 https 访问;rewrite 一致 | | **P9 Go 集成(按需)** | 出现复杂需求再引入 Go | 内部接口连通 | > 建议 P0→P8 顺序执行,P9 按需触发;每阶段一次 git 提交。P6(数据采集)、P7(首个真实功能)暂缓,待需求明确后继续;基础设施(P0–P5)已就绪。 --- ## 9. 决策状态与待确认事项 ### 已确认(v0.2) - ✅ 模板:Twig(composer) - ✅ 依赖:最小化;核心自研;无法绕过的第三方按需引入(当前仅 Twig) - ✅ 子空间:同一仓库单仓多模块 - ✅ 数据库:单库多表(`g_` / `_` / `crawl_` 前缀) - ✅ 上传:本地磁盘 + 私有存储 + 控制器鉴权下发 - ✅ 语言/时区:中文 + UTC+8(三层统一) - ✅ 数据来源:Python 爬虫(可能的数据来源) - ✅ 数据采集实现:**采用现成框架**(Scrapy 等),不自研;P6 暂缓,有明确目标时引入 ### 待确认事项处理(v0.3 更新) 1. **采集目标**:**留空(待定)** — 第一个爬虫的数据源与频率在 P6 实施前再定,不影响前期阶段。 2. **调度方式**:✅ **确认** — 初期 cron / 手动触发够用;不做调度 UI。 3. **Go 引入时机**:**占位留空(挂起)** — P9 保持按需触发,无明确需求不启动。 4. **登录体系范围**:**占位留空** — P5 只做接口预留,登录 UI 归属在 P7 前后再定。 5. **开源程序改造清单**:**留空(待定)** — 后续确定接入哪些开源 PHP 程序时再补充。 6. **composer 第三方补充**:✅ **否** — 除 Twig 外无其他必装库;维持最小依赖(核心自研),后续按需评估。 --- ## 10. 附录:核心概念速记 - **单一入口**:所有动态请求进 `public/index.php`。 - **URL ≠ 物理路径**:路由表是 URL 的唯一解释者;上传文件在 Web 根之外。 - **模块 = 子空间**:`/` 即一个模块,收拢在 `app/modules//`。 - **模板只做展示**:Twig 渲染,默认转义。 - **单库多表**:`glacier` 库 + 统一前缀(系统 `g_` / 模块 `_` / 采集 `crawl_`)。 - **数据采集用现成框架**(Scrapy 等,不自研);爬虫只写数据不提供页面;P6 暂缓。 - **Go 按需引入**:不提前造复杂后端。 - **Session/权限后期做**:本期只留中间件接口。