chore: 重写初始提交(清空历史,整理后全量提交)

This commit is contained in:
ywxapp
2026-08-16 16:54:14 +08:00
commit 6c1a106bc1
1808 changed files with 238144 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
# 目录结构
YwxApp 采用 ThinkPHP 多应用模式,目录职责划分清晰。
## 顶层目录
```
ywxapp_dev/
├── app/ 应用目录(多应用模式)
├── addon/ 插件目录,每个子目录是一个独立插件
├── config/ 全局配置
├── docs/ 开发文档(本文档站的数据源)
├── extend/ 扩展类库(PSR-0
├── public/ Web 根目录,入口文件与静态资源
├── route/ 全局路由定义
├── runtime/ 运行时缓存、日志、临时文件
├── templates/ 模板皮肤包
├── vendor/ Composer 依赖
└── ywxapp/ 框架核心库(PSR-4 命名空间 ywxapp\
```
## app 应用目录
```
app/
├── api/ 对外 API 接口应用
├── backend/ 后台管理应用
├── member/ 会员中心应用
├── frontend/ 前台应用
├── site/ 展示官网 / 文档站应用
├── common.php 全局公共函数
├── event.php 全局事件定义与监听器注册
├── middleware.php 全局中间件
├── provider.php 容器绑定
└── service.php 服务注册(含 AppService
```
> **注意**:多应用模式下 `base_path()` 返回的是 `app/` 目录而非项目根目录。
> 若需要扫描 `addon/`、`app/` 等顶层目录,必须使用 `root_path()`。
## addon 插件目录
单个插件的标准结构:
```
addon/<插件名>/
├── info.php 插件清单:name/title/intro/version/state
├── Addon.php 插件主类,继承 ywxapp\addon
├── config.php 插件配置项定义
├── menu.json 后台菜单定义
├── install.sql 安装 SQL
├── controller/
│ ├── backend/ 后台控制器(继承 AddonBackend
│ ├── api/ 接口控制器
│ └── frontend/ 前台控制器
├── model/ 数据模型
├── service/ 业务服务
├── subscribe/ 事件订阅器(钩子实现)
├── route/app.php 插件路由(写相对组名,框架自动补前缀)
├── view/ 视图模板
└── docs/ 插件文档
```
## ywxapp 核心库
```
ywxapp/
├── command/ 自定义命令行工具
├── controller/ 控制器基类(Frontend / Backend / AddonBackend / Api
├── library/ 工具类库(SchemaGuard / SkinOverlay / Markdown 等)
├── middleware/ 框架中间件(MultiApp / AddonPerformance 等)
├── service/ 核心服务(AppService / AddonService
├── addon.php 插件基类
└── helper.php 全局辅助函数
```
## 路由约定
由于 `MultiApp` 中间件会将 URL 首段解析为应用名,因此:
- 应用级路由文件 `app/<app>/route/route.php` **不会自动加载**
- 全局路由统一写在 `route/app.php`,并携带应用名前缀
```php
// route/app.php
Route::get('site/doc', 'site/doc/index');
```
插件路由则写在 `addon/<插件名>/route/app.php`,使用相对组名,由
`AppService::loadAddonRoutes()` 自动包裹 `Route::group('<插件名>')`
+44
View File
@@ -0,0 +1,44 @@
# 框架介绍
YwxApp 是一套基于 **ThinkPHP 8** 构建的多应用业务框架,核心目标是让「业务代码」与「扩展能力」彻底解耦。
## 设计理念
传统 PHP 项目做二次开发时,往往需要直接改动核心代码,导致后续升级困难。YwxApp 通过**事件驱动的插件机制**解决这个问题:
- 核心框架只负责触发事件(钩子)
- 插件通过订阅器监听事件并注入自己的逻辑
- 卸载插件后核心代码完全不受影响
## 核心特性
| 特性 | 说明 |
| --- | --- |
| 插件机制 | 基于事件订阅器的热插拔插件,钩子自动注册 |
| 多应用架构 | api / backend / member / frontend / site 应用隔离 |
| 插件市场 | 中心站分发、客户机在线安装与升级 |
| 会员与支付 | 免签支付 + 会员开通事件链,扫码到账自动开通 |
| 模板皮肤 | 运行时视图覆盖,插件视图与皮肤合并 |
| 命令行工具 | addon:make / addon:manage / addon:health 等脚手架 |
## 技术栈
- PHP >= 8.0
- ThinkPHP 8.x + think-orm 4.x
- think-multi-app 多应用扩展
- MySQL 5.7+ / MariaDB
- 前端:Layui + 原生模板引擎
## 与同类框架对比
相比 FastAdmin 的钩子机制,YwxApp 的差异在于:
1. **基于原生事件系统**:不需要额外的 hook 表,订阅器由 `AppService` 在启动阶段自动扫描注册
2. **插件自带路由**:插件可在 `route/app.php` 中注册自己的路由,由框架自动加入分组
3. **数据库自愈**:通过 `SchemaGuard` 统一管理表结构,插件升级时自动补齐缺失的列
## 下一步
- 阅读 [目录结构](guide/directory) 了解项目组织方式
- 阅读 [快速开始](guide/quickstart) 搭建本地开发环境
- 阅读 [插件开发指南](addon/development) 编写第一个插件
+93
View File
@@ -0,0 +1,93 @@
# 模型自愈机制(Schema Self-Heal
YwxApp 的数据库结构自愈引擎收敛在 `ywxapp/model/BaseModel.php` 的静态方法中。
它的定位是 **「缺表 / 缺列兜底」**,不是迁移工具——旧表改名、跨前缀重整等问题仍需手动执行迁移 SQL(见 `docs/migrations/`)。
## 一、自愈引擎能力(BaseModel 静态方法)
| 方法 | 作用 | 说明 |
| --- | --- | --- |
| `ensureTable($table, $sql)` | 表不存在则 CREATE | `$table` 为已带前缀的完整表名;内部先 `SHOW TABLES LIKE` 探测 |
| `ensureTableFromInstall($prefix, $table)` | 从 `public/install/install.sql` 正则提取建表 DDL 执行 | **install.sql 是唯一事实源**`$table` 为不含前缀的表名 |
| `ensureColumn($table, $col, $def)` | 列不存在则 ALTER ADD | MySQL 不支持 `ADD COLUMN IF NOT EXISTS`,故先探测 |
| `ensureAutoIncrementPk($table, $col='id')` | 修复 id 非自增主键 | 老库 `id` 定义成 NOT NULL 但无 AUTO_INCREMENT 时,create() 会报 1364 |
| `tableExists($table)` | 探测表是否存在 | `SHOW TABLES LIKE`,异常时返回 false |
| `currentPrefix()` | 取运行时表前缀 | 优先 `config/database.php`,回退 `Db::getConfig('prefix')` |
所有方法内部都包了 `try/catch`:自愈失败(如数据库账号无 `CREATE/ALTER` 权限、`NO_BACKSLASH_ESCAPES` 导致 SQL 出错)会**被静默吞掉并记录日志**,不会阻断业务——但意味着「自愈没起作用」时不会立刻报错,需要查日志才能发现。
## 二、模型壳约定
每个需要自愈的模型都自带一个静态方法:
```php
public static function ensureSchema(): void
{
try {
$prefix = \think\facade\Db::getConfig('prefix') ?: '';
$table = $prefix . 'notice';
// 缺表建表(优先 ensureTableFromInstall,或从 install.sql 同款 DDL 兜底)
self::ensureTable($table, "CREATE TABLE IF NOT EXISTS `{$prefix}notice` (...)");
// 老库扩展列兜底(幂等)
self::ensureColumn($table, 'type', "tinyint(1) NOT NULL DEFAULT 1");
} catch (\Throwable $e) {
// 忽略自愈失败,由业务暴露
}
}
```
**幂等性**:所有自愈都先探测(SHOW TABLES / SHOW COLUMNS)再操作,已存在则跳过,重复调用安全。
## 三、触发时机(谁在什么时候调用 ensureSchema
自愈**不会定时 / 不会自动**,全部是「业务请求时按需触发」。
### 1. 核心库(每次相关请求都跑一次,开销极小)
| 入口 | 调用 | 触发场景 |
| --- | --- | --- |
| `ywxapp/library/AdminAuth.php` (`login`/`register`) | `BackendAdmin::ensureSchema()` | 后台登录 / 注册 |
| `ywxapp/controller/MemberBase.php` (`_initialize`) | `MemberUser::ensureSchema()` | 访问任意会员中心控制器 |
### 2. 业务方法内触发
| 模型 / 类 | 触发方法 | 触发场景 |
| --- | --- | --- |
| `ywxapp/model/Notice.php` | `getActiveTopNotices()` / `getList()` | 前台公告条渲染、公告列表页 |
| `ywxapp/model/Ad.php` | `getByPosition()` / `getSlots()` | 广告位渲染 |
### 3. 中间件 / 蜘蛛统计
| 入口 | 调用 | 触发场景 |
| --- | --- | --- |
| `SpiderStat` 中间件 | `ensureTables()` | 每次请求(蜘蛛日志 / 统计表) |
### 4. 插件安装 / 升级
| 插件 | 触发入口 | 触发场景 |
| --- | --- | --- |
| blog | `BlogSchema::ensure*` | 插件业务方法 |
| forum | `ForumSchema::ensure*` | 插件业务方法 |
| appmall(中心站) | `AppmallSchema::ensure*` | 开发者中心业务方法 |
| haonav / seo | `Addon.php::upgrade()` / `ensureTablesFromInstallSql()` | 插件升级 / 安装 |
| wxchat / mqttbroker / mqpay 等 | 各自 Schema 类 | 插件初始化 |
## 四、线上老库迁移不在自愈范围
自愈只负责「缺表 / 缺列兜底」,**不会做 RENAME、不会跨前缀重整**。旧库改名必须手动执行迁移 SQL:
- `docs/migrations/migrate_admin_user_to_backend_member_wxapp.sql` — admin/user → backend/member
- `docs/migrations/rename_table_prefix.sql` — 批量改前缀
- `docs/migrations/fix_missing_wxapp_notice.sql` — 自愈因权限被吞时手动补表
## 五、排错:遇到 1146 Table doesn't exist
1. 确认表是否在 `install.sql` 中有建表语句(事实源)。
2. 若是核心 / 插件表,确认对应 `ensureSchema()` 是否被调用(见第三节入口)。
3. ⚠️ **高频根因:运行时没取到表前缀**`ensureSchema()` 若用 `Db::getConfig('prefix')` 取前缀,在部分线上环境(多应用路由 / CLI / Db 配置未就绪)会返回空字符串,导致自愈建出**无前缀的表**(如 `notice`),而模型 `name` 拼前缀后查的是 `wxapp_notice` → 1146。
- **修复**:所有模型壳 / 控制器一律改用 `BaseModel::currentPrefix()` 取前缀(优先 `config/database.php`,回退 `Db::getConfig`),并已统一修复 `Notice` / `Ad``appmall` / `appmall` 控制器。
- 自查:`grep "Db::getConfig('prefix')"` 应只剩 0 处(全部换成 `currentPrefix`)。
4. 若前缀正确、`ensureSchema()` 已调用仍 1146 → **多半是线上 DB 账号无 CREATE/ALTER 权限**,自愈被静默吞掉。
- 根治:给 DB 用户授予建表 / 改表权限,自愈会自动补齐;
- 兜底:手动执行 `docs/migrations/` 下对应修复 SQL(如 `fix_missing_wxapp_notice.sql`)。
5. 若是改名类(admin_* / user_*)→ 必须手动跑迁移 SQL,自愈无法处理。
+114
View File
@@ -0,0 +1,114 @@
# 快速开始
本文介绍如何在本地搭建 YwxApp 开发环境并运行第一个页面。
## 环境要求
| 项目 | 要求 |
| --- | --- |
| PHP | >= 8.0(推荐 8.3 |
| 数据库 | MySQL 5.7+ / MariaDB 10.3+ |
| Composer | 2.x |
| 扩展 | pdo_mysql、mbstring、openssl、fileinfo、zip |
## 安装步骤
### 1. 获取代码并安装依赖
```bash
git clone <仓库地址> ywxapp
cd ywxapp
composer install
```
### 2. 配置环境变量
复制示例配置并按实际情况修改:
```bash
cp .env.example .env
```
关键配置项:
```ini
APP_DEBUG = true
[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = ywxapp
USERNAME = root
PASSWORD =
HOSTPORT = 3306
PREFIX = wxapp_
```
### 3. 导入数据库
`install.sql` 导入到你创建的数据库中,或访问安装向导按提示完成。
### 4. 配置 Web 服务器
将站点根目录指向 `public/`,并配置伪静态。
**Nginx**
```nginx
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=$1 last;
}
}
```
**Apache**`public/.htaccess` 已内置规则,确保开启 `mod_rewrite`
### 5. 本地快速启动
若暂时不配置 Web 服务器,可用 PHP 内置服务器:
```bash
php think run -p 8000
```
访问 `http://127.0.0.1:8000` 即可看到展示官网首页。
## 目录权限
以下目录需要可写权限:
```
runtime/
public/uploads/
addon/
config/
```
Linux 下执行:
```bash
chmod -R 755 runtime public/uploads addon config
```
## 常用命令
```bash
# 创建插件脚手架
php think addon:make demo -a build
# 安装 / 开发模式安装插件
php think addon:manage demo -a develop
# 刷新插件后台菜单
php think addon:manage demo -a refresh-menu
# 插件健康体检
php think addon:health --detail
```
## 下一步
- [插件开发指南](addon/development):编写你的第一个插件
- [支付钩子机制](pay/hooks):了解事件驱动的扩展方式
- [自定义命令清单](cli/commands):查看全部 CLI 工具