状态:设计阶段(已确认关键技术决策,见 §0),仅设计,不写代码。 适用范围:Glacier 公司内部管理系统(首页 + 业务功能子空间 + 数据采集)。
| # | 决策点 | 结论 | 对架构的影响 |
|---|---|---|---|
| 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 暂缓;接入时遵循单写者原则 |
┌──────────────────────────────────────────────────┐
浏览器 (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 等,不自研) │
│ 采集 → 清洗/去重 → 入库 │
└─────────────────────────┘
| 层 | 职责 | 不许做的事 |
|---|---|---|
| Web 服务器 | TLS、静态资源、URL rewrite 到入口 | 不执行业务逻辑 |
| 入口 (public/index.php) | 引导、加载配置、调用 Router | 不包含业务逻辑 |
| Router(URL 模式引擎) | 将 URL 映射到控制器/模块 | 不解析物理路径 |
| Controller | 接收请求、调模型、产出数据 | 不直接输出 HTML(交给 Twig) |
| Model | 数据访问(PDO 预处理)、业务规则 | 不处理 URL |
| View(Twig 模板) | 只渲染控制器给的数据 | 不写业务逻辑、不做数据库访问 |
| Go 服务 | 复杂/长任务/高并发能力(按需) | 不处理 Web 会话 |
| Python 爬虫 | 外部数据采集、清洗、入库 | 不直接提供 Web 页面、不碰会话 |
| 项 | 选型 | 理由 / 备注 |
|---|---|---|
| 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 | 本机开发一致预览 |
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)
│ └── <name>/ # 每个 /<name> 一个模块
│ ├── 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
关键原则:
public/;上传在 storage/,物理路径不可能出现在 URL,也不可能被直接访问。index.php,由路由表声明式分发;assets/ 是唯一直接服务的静态目录。public/index.php 接收所有请求(rewrite 见 §3.2),把 URL 路径交给 Router。app/config/routes.php,形如 模式 → [处理器, 中间件]。/、/about;参数段 /user/:id;可选段 /news[/:page];模块前缀 /<module> 自动挂载到 app/modules/<module>/routes.php。[a-z0-9_-] + 目录存在性校验),杜绝路径穿越。示例映射(概念,非代码):
| URL | 处理器 |
|---|---|
/ |
Home 控制器(本期 → "开发中"占位页) |
/assets/* |
Web 服务器直接服务(不经过 PHP) |
/file/download/:id |
私有文件鉴权下发(§3.9) |
/project |
模块 project 入口(子空间) |
/project/xxx |
模块 project 内部路由 |
public/.htaccess — 非静态文件请求 rewrite 到 index.php。deploy/Caddyfile — 真实文件(assets)直接服务,否则交 PHP-FPM 入口。真实文件 → 直接返回;否则 → 入口。上传目录不在 Web 根,天然不参与。app/views/(布局/片段),模块模板在 app/modules/<name>/views/;{% extends 'layouts/base.html.twig' %} + {% block %};|raw 时先经净化器(后期统一白名单净化);storage/cache/(开发可关,生产开)。APP_ENV 切换 env.dev.php / env.prod.php。BASE_URL(模板生成链接用,杜绝物理路径)、Session 名/有效期、上传/存储目录、爬虫调度开关等。glacier(开发 MySQL / 生产 MariaDB 同名)。g_(如 g_users、g_sessions、g_settings);<module>_(如 news_articles、project_tasks);crawl_(如 crawl_sources、crawl_items)。Database.php(PDO):DSN、utf8mb4、异常模式;一律 prepared statement。database/ 下 0001_xxx.sql 顺序编号(up/down),开发/生产共用同一套。Session.php 封装:HttpOnly、SameSite=Lax、Secure(生产开)、session 名唯一化、CSRF Token(表单统一中间件校验)。auth / permission 空实现。定位:外部数据来源(如行业新闻、政策、竞品信息等)→ 采集 → 清洗/去重 → 入库 → 系统各模块展示。爬虫只写数据库与私有存储,不提供 Web 页面、不碰会话。
实现方式(v0.4 决策):采用现成爬虫框架(如 Scrapy 等),不自研采集管道。自研收益低、维护成本高;成熟框架自带调度、去重、并发、中间件与 robots 支持。
框架接入约定:
crawlers/(独立子项目,含 scrapy.cfg / pyproject.toml 等自身结构);database/ 迁移为唯一来源;crawl_ 前缀(如 crawl_sources / crawl_items),字段含 source_url(唯一约束去重)、content_hash、fetched_at、status;storage/uploads/(Web 根之外)→ 任何 URL 都访问不到物理文件。/file/download/:id 控制器 → 校验(登录/权限/归属,后期)→ 流式输出(Content-Disposition 可控)。g_files 或模块表)。public/assets/ 直接访问,两者严格区分。domain.com/<name> 每个 <name> 是独立业务子空间(模块),收拢在 app/modules/<name>/:
routes.php(模块内路由,前缀自动挂载)、controllers/、models/、views/(Twig)、module.php(声明:名称、访问要求、导航注册)。<module>_ 前缀,无独立数据库。/ → Home 控制器 → Twig 渲染"开发中"占位页(品牌 + 占位说明,中文)。crawl_*_items 数据)+ 功能导航(从 module.php 聚合菜单)。| 项 | 措施 |
|---|---|
| SQL 注入 | PDO prepared statement 唯一路径(PHP);Python 写库同样参数化 |
| XSS | Twig 默认转义;富文本显式净化 |
| CSRF | 表单统一 token 校验(中间件) |
| 路径穿越 | 模块名白名单 + 不拼接物理路径 |
| 文件上传/下载 | 私有目录存储 + 控制器鉴权下发(§3.9) |
| 会话 | HttpOnly + SameSite;登录后重置 session id |
| 采集安全 | robots/限速;内容净化;凭据不落库不入库 |
| 敏感信息 | 密钥不落库不入库;生产注入 |
| 错误处理 | 开发显示详情,生产只记日志 |
| 维度 | 开发(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) |
| 阶段 | 内容 | 验收点 |
|---|---|---|
| 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/ 约定 + 模块路由挂载 + 模块声明 | 示例模块经 /<name> 可访问 |
| 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)已就绪。
g_ / <module>_ / crawl_ 前缀)public/index.php。/<name> 即一个模块,收拢在 app/modules/<name>/。glacier 库 + 统一前缀(系统 g_ / 模块 <module>_ / 采集 crawl_)。