feat: use xpier media library for uploads and markdown embeds
Build and Deploy / verify (push) Successful in 35s
Build and Deploy / deploy (push) Successful in 33s

Store media:ID in article bodies instead of CDN URLs, and keep sablog attach tokens as a fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
gouki
2026-09-16 19:03:52 +00:00
co-authored by Cursor
parent 0b0110a681
commit 64655ced1e
49 changed files with 5615 additions and 2132 deletions
+18 -12
View File
@@ -1,9 +1,11 @@
# LaraBlog 架构与开发思想
## 目标
把 sablog 内容与 SEO 权重迁到现代 Laravel 栈:**不克隆过时能力**(Trackback/WAP/旧 Admin),用主题/插件/对象存储/AI 队列替换。
## 分层
```
HTTP (legacy .shtml + /admin Filament + /api/v1)
→ Controllers / Filament Pages
@@ -13,32 +15,36 @@ HTTP (legacy .shtml + /admin Filament + /api/v1)
```
## 关键约定
| 区域 | 路径 |
|---|---|
| Legacy 前台 URL | `routes/legacy.php` |
| Web 辅助 | `routes/web.php` |
| JSON API(小程序/其他端) | `routes/api.php` → `/api/v1/*` |
| 领域逻辑 | `app/Domain/*` |
| 主题 | `themes/{slug}/` |
| 插件 | `plugins/{vendor}/{name}/` |
| 规格 | `docs/specs/larablog-platform/` |
| 安装 / 导入 / 部署 | `docs/ops/install.md`、`import.md`、`deploy.md` |
| OpenAPI | `docs/api/openapi.yaml` |
| 区域 | 路径 |
| ------------------------- | ----------------------------------------------- |
| Legacy 前台 URL | `routes/legacy.php` |
| Web 辅助 | `routes/web.php` |
| JSON API(小程序/其他端) | `routes/api.php` → `/api/v1/*` |
| 领域逻辑 | `app/Domain/*` |
| 主题 | `themes/{slug}/` |
| 插件 | `plugins/{vendor}/{name}/` |
| 规格 | `docs/specs/larablog-platform/` |
| 安装 / 导入 / 部署 | `docs/ops/install.md`、`import.md`、`deploy.md` |
| OpenAPI | `docs/api/openapi.yaml` |
## 开发模式
1. **规格先行**:非琐碎功能写 SPEC / CHECKLIST / TESTPLAN
2. **兼容优先**:文章/分类 ID、`.shtml` 内容 URL 保持可达
3. **引用不写死 CDN**:正文存 `[attach=id]` / `attach:id`,渲染期转 `/attachment.php?id=`
3. **引用不写死 CDN**:新内容正文存 `media:ID` / `[media=ID]`;老内容仍用 `[attach=id]` / `attach:id`。渲染期再换成当前公开地址。见 [media.md](media.md)。
4. **异步 AI**:Web 不阻塞 LLM;队列 / Workerman 消费
5. **默认中文**:`APP_LOCALE=zh_CN`,文案走 `lang/zh_CN/*`
6. **测试**:PHPUnit Feature sqlite `:memory:`(见 `phpunit.xml`);CI 同配置
## PHP / 代码风格
- 目标运行时:**PHP 8.2+**(`composer.json`)
- 应用代码逐步统一 `declare(strict_types=1);`
- 类型提示、返回类型、枚举优先;避免软依赖全局状态
## 一期 vs 二期
- **一期已做**:核心博客、主题引擎、插件框架、SEO/GEO、导入、AI stub 管道、前台皮肤、后台 Filament
- **一期骨架**:支付 / 会员 / 双商城(非真实结算)
- **二期**:自动配图、真实支付会员、小程序完整写接口与鉴权深化
@@ -0,0 +1,565 @@
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `activity_log`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `activity_log` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`log_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`description` text COLLATE utf8mb4_unicode_ci NOT NULL,
`subject_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`event` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`subject_id` bigint unsigned DEFAULT NULL,
`causer_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`causer_id` bigint unsigned DEFAULT NULL,
`properties` json DEFAULT NULL,
`batch_uuid` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `subject` (`subject_type`,`subject_id`),
KEY `causer` (`causer_type`,`causer_id`),
KEY `activity_log_log_name_index` (`log_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `article_membership`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `article_membership` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`article_id` bigint unsigned NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '0',
`required_plan_id` bigint unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `article_membership_article_id_unique` (`article_id`),
KEY `article_membership_required_plan_id_foreign` (`required_plan_id`),
CONSTRAINT `article_membership_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE CASCADE,
CONSTRAINT `article_membership_required_plan_id_foreign` FOREIGN KEY (`required_plan_id`) REFERENCES `membership_plans` (`id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `article_products`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `article_products` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`article_id` bigint unsigned NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '0',
`price` decimal(10,2) NOT NULL,
`currency` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY',
`trial_mode` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'chars',
`trial_value` int unsigned NOT NULL DEFAULT '200',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `article_products_article_id_unique` (`article_id`),
CONSTRAINT `article_products_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `article_tag`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `article_tag` (
`article_id` bigint unsigned NOT NULL,
`tag_id` bigint unsigned NOT NULL,
UNIQUE KEY `article_tag_article_id_tag_id_unique` (`article_id`,`tag_id`),
KEY `article_tag_tag_id_foreign` (`tag_id`),
CONSTRAINT `article_tag_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE CASCADE,
CONSTRAINT `article_tag_tag_id_foreign` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `articles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `articles` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`category_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`content` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`content_format` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'html',
`description` text COLLATE utf8mb4_unicode_ci,
`keywords` text COLLATE utf8mb4_unicode_ci,
`published_at` timestamp NULL DEFAULT NULL,
`views` bigint unsigned NOT NULL DEFAULT '0',
`comments_count` int unsigned NOT NULL DEFAULT '0',
`slug` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`stick` tinyint(1) NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1',
`close_comment` tinyint(1) NOT NULL DEFAULT '0',
`read_password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`ai_summary` text COLLATE utf8mb4_unicode_ci,
`ai_suggestions` json DEFAULT NULL,
`ai_polished_content` longtext COLLATE utf8mb4_unicode_ci,
`legacy_attachments` json DEFAULT NULL,
`cover_disk` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`cover_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`cover_source` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'none',
`cover_status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'none',
`cover_generated_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `articles_slug_unique` (`slug`),
KEY `articles_user_id_foreign` (`user_id`),
KEY `articles_visible_published_at_index` (`visible`,`published_at`),
KEY `articles_category_id_index` (`category_id`),
CONSTRAINT `articles_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
CONSTRAINT `articles_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `attachments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `attachments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`article_id` bigint unsigned DEFAULT NULL,
`disk` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'attachments',
`path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`thumb_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`filename` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`mime` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`size` bigint unsigned NOT NULL DEFAULT '0',
`checksum` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`visibility` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'public',
`legacy_filepath` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`synced_at` timestamp NULL DEFAULT NULL,
`downloads` bigint unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `attachments_article_id_foreign` (`article_id`),
KEY `attachments_legacy_filepath_index` (`legacy_filepath`),
CONSTRAINT `attachments_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `cache`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `cache_locks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache_locks` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`owner` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_locks_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `categories`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `categories` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`intro` text COLLATE utf8mb4_unicode_ci,
`keywords` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`cover_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`display_order` int NOT NULL DEFAULT '0',
`articles_count` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `comments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`article_id` bigint unsigned NOT NULL,
`author` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`content` text COLLATE utf8mb4_unicode_ci NOT NULL,
`ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`moderation_status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`published_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `comments_article_id_foreign` (`article_id`),
CONSTRAINT `comments_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `entitlements`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `entitlements` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`product_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`product_id` bigint unsigned NOT NULL,
`source_order_id` bigint unsigned DEFAULT NULL,
`granted_at` timestamp NOT NULL,
`revoked_at` timestamp NULL DEFAULT NULL,
`expires_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `entitlements_user_id_product_type_product_id_unique` (`user_id`,`product_type`,`product_id`),
CONSTRAINT `entitlements_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `failed_jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `failed_jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `job_batches`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `job_batches` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`total_jobs` int NOT NULL,
`pending_jobs` int NOT NULL,
`failed_jobs` int NOT NULL,
`failed_job_ids` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`options` mediumtext COLLATE utf8mb4_unicode_ci,
`cancelled_at` int DEFAULT NULL,
`created_at` int NOT NULL,
`finished_at` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`attempts` tinyint unsigned NOT NULL,
`reserved_at` int unsigned DEFAULT NULL,
`available_at` int unsigned NOT NULL,
`created_at` int unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `jobs_queue_index` (`queue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `links`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `links` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`note` text COLLATE utf8mb4_unicode_ci,
`display_order` int NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `membership_plans`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `membership_plans` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` text COLLATE utf8mb4_unicode_ci,
`price` decimal(10,2) NOT NULL,
`currency` varchar(8) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY',
`duration_days` int unsigned DEFAULT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`sort_order` int NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `membership_plans_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `migrations` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`batch` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`model_id`,`model_type`),
KEY `model_has_permissions_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_roles` (
`role_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`role_id`,`model_id`,`model_type`),
KEY `model_has_roles_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `order_items`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `order_items` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` bigint unsigned NOT NULL,
`product_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`product_id` bigint unsigned NOT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`amount` decimal(10,2) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `order_items_order_id_foreign` (`order_id`),
CONSTRAINT `order_items_order_id_foreign` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `orders`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `orders` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`amount` decimal(10,2) NOT NULL,
`currency` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY',
`gateway` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`paid_at` timestamp NULL DEFAULT NULL,
`meta` json DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `orders_user_id_foreign` (`user_id`),
CONSTRAINT `orders_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `password_reset_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `password_reset_tokens` (
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `payment_transactions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `payment_transactions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` bigint unsigned NOT NULL,
`gateway` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`external_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`payload` json DEFAULT NULL,
`status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `payment_transactions_order_id_foreign` (`order_id`),
CONSTRAINT `payment_transactions_order_id_foreign` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `permissions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `permissions_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `plugins`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `plugins` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`version` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1.0.0',
`enabled` tinyint(1) NOT NULL DEFAULT '0',
`path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`config` json DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `plugins_name_unique` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `role_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `role_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`role_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`role_id`),
KEY `role_has_permissions_role_id_foreign` (`role_id`),
CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE,
CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `roles` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `roles_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `sessions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sessions` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`user_agent` text COLLATE utf8mb4_unicode_ci,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`last_activity` int NOT NULL,
PRIMARY KEY (`id`),
KEY `sessions_user_id_index` (`user_id`),
KEY `sessions_last_activity_index` (`last_activity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `settings`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `settings` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`group` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`locked` tinyint(1) NOT NULL DEFAULT '0',
`payload` json NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `settings_group_name_unique` (`group`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `stylevars`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `stylevars` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` text COLLATE utf8mb4_unicode_ci,
`visible` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tags` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`use_count` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `tags_name_unique` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`password_legacy` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`login_count` int unsigned NOT NULL DEFAULT '0',
`login_ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`login_at` timestamp NULL DEFAULT NULL,
`reg_ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`last_post_at` timestamp NULL DEFAULT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`),
UNIQUE KEY `users_username_unique` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (1,'0001_01_01_000000_create_users_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (2,'0001_01_01_000001_create_cache_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (3,'0001_01_01_000002_create_jobs_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (4,'2022_12_14_083707_create_settings_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (5,'2026_08_11_092526_create_permission_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (6,'2026_08_11_092528_create_activity_log_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (7,'2026_08_11_092529_add_event_column_to_activity_log_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (8,'2026_08_11_092530_add_batch_uuid_column_to_activity_log_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (9,'2026_08_11_100000_create_blog_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (10,'2026_08_11_100001_create_larablog_settings',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (11,'2026_08_11_100002_add_attachments_url_prefix_setting',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (12,'2026_08_11_100003_drop_trackback_legacy',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (13,'2026_08_11_100003_remove_trackback_setting',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (14,'2026_08_11_100004_add_article_content_format',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (15,'2026_08_11_100004_add_content_format_settings',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (16,'2026_08_11_100005_add_article_cover_placeholders',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (17,'2026_08_11_220000_expand_blog_comment_settings',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (18,'2026_08_11_230000_create_snippet_settings',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (19,'2026_08_12_001000_create_orders_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (20,'2026_08_12_001001_create_order_items_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (21,'2026_08_12_001002_create_entitlements_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (22,'2026_08_12_001003_create_payment_transactions_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (23,'2026_08_12_002000_create_article_products_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (24,'2026_08_12_013201_add_article_ai_polished_content',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (25,'2026_08_12_034500_add_expires_at_to_entitlements_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (26,'2026_08_12_034600_create_membership_plans_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (27,'2026_08_12_034601_create_article_membership_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (28,'2026_08_13_015800_add_category_seo_fields',1);
@@ -0,0 +1,619 @@
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `activity_log`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `activity_log` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`log_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`description` text COLLATE utf8mb4_unicode_ci NOT NULL,
`subject_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`event` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`subject_id` bigint unsigned DEFAULT NULL,
`causer_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`causer_id` bigint unsigned DEFAULT NULL,
`properties` json DEFAULT NULL,
`batch_uuid` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `subject` (`subject_type`,`subject_id`),
KEY `causer` (`causer_type`,`causer_id`),
KEY `activity_log_log_name_index` (`log_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `article_membership`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `article_membership` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`article_id` bigint unsigned NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '0',
`required_plan_id` bigint unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `article_membership_article_id_unique` (`article_id`),
KEY `article_membership_required_plan_id_foreign` (`required_plan_id`),
CONSTRAINT `article_membership_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE CASCADE,
CONSTRAINT `article_membership_required_plan_id_foreign` FOREIGN KEY (`required_plan_id`) REFERENCES `membership_plans` (`id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `article_products`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `article_products` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`article_id` bigint unsigned NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '0',
`price` decimal(10,2) NOT NULL,
`currency` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY',
`trial_mode` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'chars',
`trial_value` int unsigned NOT NULL DEFAULT '200',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `article_products_article_id_unique` (`article_id`),
CONSTRAINT `article_products_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `article_tag`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `article_tag` (
`article_id` bigint unsigned NOT NULL,
`tag_id` bigint unsigned NOT NULL,
UNIQUE KEY `article_tag_article_id_tag_id_unique` (`article_id`,`tag_id`),
KEY `article_tag_tag_id_foreign` (`tag_id`),
CONSTRAINT `article_tag_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE CASCADE,
CONSTRAINT `article_tag_tag_id_foreign` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `articles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `articles` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`category_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`content` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`content_format` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'html',
`description` text COLLATE utf8mb4_unicode_ci,
`keywords` text COLLATE utf8mb4_unicode_ci,
`published_at` timestamp NULL DEFAULT NULL,
`views` bigint unsigned NOT NULL DEFAULT '0',
`comments_count` int unsigned NOT NULL DEFAULT '0',
`slug` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`stick` tinyint(1) NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1',
`close_comment` tinyint(1) NOT NULL DEFAULT '0',
`read_password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`ai_summary` text COLLATE utf8mb4_unicode_ci,
`ai_suggestions` json DEFAULT NULL,
`ai_polished_content` longtext COLLATE utf8mb4_unicode_ci,
`legacy_attachments` json DEFAULT NULL,
`cover_disk` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`cover_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`cover_source` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'none',
`cover_status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'none',
`cover_media_id` bigint unsigned DEFAULT NULL,
`cover_generated_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `articles_slug_unique` (`slug`),
KEY `articles_user_id_foreign` (`user_id`),
KEY `articles_visible_published_at_index` (`visible`,`published_at`),
KEY `articles_category_id_index` (`category_id`),
CONSTRAINT `articles_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
CONSTRAINT `articles_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `attachments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `attachments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`article_id` bigint unsigned DEFAULT NULL,
`disk` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'attachments',
`path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`thumb_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`filename` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`mime` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`size` bigint unsigned NOT NULL DEFAULT '0',
`checksum` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`visibility` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'public',
`legacy_filepath` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`synced_at` timestamp NULL DEFAULT NULL,
`downloads` bigint unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `attachments_article_id_foreign` (`article_id`),
KEY `attachments_legacy_filepath_index` (`legacy_filepath`),
CONSTRAINT `attachments_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `cache`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `cache_locks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `cache_locks` (
`key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`owner` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_locks_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `categories`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `categories` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`intro` text COLLATE utf8mb4_unicode_ci,
`keywords` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`cover_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`display_order` int NOT NULL DEFAULT '0',
`articles_count` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `comments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`article_id` bigint unsigned NOT NULL,
`author` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`content` text COLLATE utf8mb4_unicode_ci NOT NULL,
`ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`moderation_status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',
`published_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `comments_article_id_foreign` (`article_id`),
CONSTRAINT `comments_article_id_foreign` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `entitlements`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `entitlements` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`product_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`product_id` bigint unsigned NOT NULL,
`source_order_id` bigint unsigned DEFAULT NULL,
`granted_at` timestamp NOT NULL,
`revoked_at` timestamp NULL DEFAULT NULL,
`expires_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `entitlements_user_id_product_type_product_id_unique` (`user_id`,`product_type`,`product_id`),
CONSTRAINT `entitlements_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `failed_jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `failed_jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `job_batches`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `job_batches` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`total_jobs` int NOT NULL,
`pending_jobs` int NOT NULL,
`failed_jobs` int NOT NULL,
`failed_job_ids` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`options` mediumtext COLLATE utf8mb4_unicode_ci,
`cancelled_at` int DEFAULT NULL,
`created_at` int NOT NULL,
`finished_at` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`attempts` tinyint unsigned NOT NULL,
`reserved_at` int unsigned DEFAULT NULL,
`available_at` int unsigned NOT NULL,
`created_at` int unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `jobs_queue_index` (`queue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `links`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `links` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`note` text COLLATE utf8mb4_unicode_ci,
`display_order` int NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `media_folders`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `media_folders` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`parent_id` bigint unsigned DEFAULT NULL,
`code` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`sort` int NOT NULL DEFAULT '0',
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `media_folders_code_unique` (`code`),
KEY `media_folders_parent_id_foreign` (`parent_id`),
CONSTRAINT `media_folders_parent_id_foreign` FOREIGN KEY (`parent_id`) REFERENCES `media_folders` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `media_library`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `media_library` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned DEFAULT NULL,
`disk` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'public',
`path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`original_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`extension` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`mime_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`size` bigint unsigned NOT NULL DEFAULT '0',
`type` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'image',
`source` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'admin',
`folder` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`alt_text` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`custom_properties` json DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `media_library_path_unique` (`path`),
KEY `media_library_user_id_foreign` (`user_id`),
KEY `media_library_type_index` (`type`),
KEY `media_library_folder_index` (`folder`),
KEY `media_library_source_index` (`source`),
CONSTRAINT `media_library_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `membership_plans`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `membership_plans` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`description` text COLLATE utf8mb4_unicode_ci,
`price` decimal(10,2) NOT NULL,
`currency` varchar(8) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY',
`duration_days` int unsigned DEFAULT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`sort_order` int NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `membership_plans_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `migrations` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`batch` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`model_id`,`model_type`),
KEY `model_has_permissions_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `model_has_roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `model_has_roles` (
`role_id` bigint unsigned NOT NULL,
`model_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`role_id`,`model_id`,`model_type`),
KEY `model_has_roles_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `order_items`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `order_items` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` bigint unsigned NOT NULL,
`product_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`product_id` bigint unsigned NOT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`amount` decimal(10,2) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `order_items_order_id_foreign` (`order_id`),
CONSTRAINT `order_items_order_id_foreign` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `orders`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `orders` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`amount` decimal(10,2) NOT NULL,
`currency` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY',
`gateway` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`paid_at` timestamp NULL DEFAULT NULL,
`meta` json DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `orders_user_id_foreign` (`user_id`),
CONSTRAINT `orders_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `password_reset_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `password_reset_tokens` (
`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `payment_transactions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `payment_transactions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` bigint unsigned NOT NULL,
`gateway` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`external_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`payload` json DEFAULT NULL,
`status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `payment_transactions_order_id_foreign` (`order_id`),
CONSTRAINT `payment_transactions_order_id_foreign` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `permissions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `permissions_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `plugins`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `plugins` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`version` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1.0.0',
`enabled` tinyint(1) NOT NULL DEFAULT '0',
`path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`config` json DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `plugins_name_unique` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `role_has_permissions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `role_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`role_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`role_id`),
KEY `role_has_permissions_role_id_foreign` (`role_id`),
CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE,
CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `roles` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `roles_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `sessions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sessions` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`user_agent` text COLLATE utf8mb4_unicode_ci,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`last_activity` int NOT NULL,
PRIMARY KEY (`id`),
KEY `sessions_user_id_index` (`user_id`),
KEY `sessions_last_activity_index` (`last_activity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `settings`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `settings` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`group` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`locked` tinyint(1) NOT NULL DEFAULT '0',
`payload` json NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `settings_group_name_unique` (`group`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `stylevars`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `stylevars` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` text COLLATE utf8mb4_unicode_ci,
`visible` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tags` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`use_count` int unsigned NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `tags_name_unique` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`password_legacy` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`login_count` int unsigned NOT NULL DEFAULT '0',
`login_ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`login_at` timestamp NULL DEFAULT NULL,
`reg_ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`last_post_at` timestamp NULL DEFAULT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`),
UNIQUE KEY `users_username_unique` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (1,'0001_01_01_000000_create_users_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (2,'0001_01_01_000001_create_cache_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (3,'0001_01_01_000002_create_jobs_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (4,'2022_12_14_083707_create_settings_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (5,'2026_08_11_092526_create_permission_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (6,'2026_08_11_092528_create_activity_log_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (7,'2026_08_11_092529_add_event_column_to_activity_log_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (8,'2026_08_11_092530_add_batch_uuid_column_to_activity_log_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (9,'2026_08_11_100000_create_blog_tables',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (10,'2026_08_11_100001_create_larablog_settings',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (11,'2026_08_11_100002_add_attachments_url_prefix_setting',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (12,'2026_08_11_100003_drop_trackback_legacy',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (13,'2026_08_11_100003_remove_trackback_setting',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (14,'2026_08_11_100004_add_article_content_format',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (15,'2026_08_11_100004_add_content_format_settings',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (16,'2026_08_11_100005_add_article_cover_placeholders',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (17,'2026_08_11_220000_expand_blog_comment_settings',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (18,'2026_08_11_230000_create_snippet_settings',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (19,'2026_08_12_001000_create_orders_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (20,'2026_08_12_001001_create_order_items_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (21,'2026_08_12_001002_create_entitlements_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (22,'2026_08_12_001003_create_payment_transactions_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (23,'2026_08_12_002000_create_article_products_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (24,'2026_08_12_013201_add_article_ai_polished_content',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (25,'2026_08_12_034500_add_expires_at_to_entitlements_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (26,'2026_08_12_034600_create_membership_plans_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (27,'2026_08_12_034601_create_article_membership_table',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (28,'2026_08_13_015800_add_category_seo_fields',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (29,'2026_08_15_000001_create_media_library_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (30,'2026_08_15_000002_create_media_folders_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (31,'2026_08_15_000003_add_soft_deletes_to_media_library_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (32,'2026_08_15_000004_add_custom_properties_to_media_library_table',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (33,'2026_09_16_000001_add_cover_media_id_to_articles',2);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (34,'2026_09_16_000002_seed_default_media_folders',2);
+32
View File
@@ -0,0 +1,32 @@
# Media library (xpier)
New uploads, covers, and in-body images/files use `xpier/filament-media-library`. The sablog `attachments` table and `attach:` protocol stay for import/legacy content.
## Markdown body (do not paste CDN URLs)
Store a **library id**, never a bucket/CDN URL. Rendering expands it to the current public URL.
| Format | Image | File |
| -------- | ------------------ | ----------------------- |
| Markdown | `![alt](media:12)` | `[notes.pdf](media:12)` |
| HTML | `[media=12]` | `[media=12]` |
Admin flow: pick a file, then **Insert into body**. Markdown keeps `media:ID`; HTML keeps `[media=ID]`.
Legacy tokens stay valid: `![alt](attach:ID)`, `[attach=ID]`, `/attachment.php?id=ID`.
## Admin
- Library: `/admin/media-library`
- Folders: `/admin/media-folders` (defaults: `general`, `articles`, `banners`)
- Cover: `cover_media_id` via `MediaPicker` (`coverMedia` relation). `cover_path` remains for older auto-covers.
## Config
```env
MEDIA_DISK=public
MEDIA_DIRECTORY=media
MEDIA_VISIBILITY=public
```
Run `php artisan storage:link` when using the `public` disk locally.
+15 -14
View File
@@ -4,14 +4,14 @@
## 环境
| 项 | 要求 |
|---|---|
| PHP | **8.2+**(`composer.json`) |
| 扩展 | `mbstring`、`openssl`、`pdo`、`tokenizer`、`xml`、`ctype`、`json`、`fileinfo`、`gd`(封面/图片)、`zip` |
| Composer | 2.x |
| 数据库 | 开发可用 SQLite;也可用 MySQL / MariaDB |
| 可选 | Redis(队列/缓存);Node.js 仅在改前端资源时需要 |
| 可选 | `pcntl` + `posix`(本机跑 `workerman:ai`;没有就用 `queue:ai`) |
| 项 | 要求 |
| -------- | ------------------------------------------------------------------------------------------------------- |
| PHP | **8.2+**(`composer.json`) |
| 扩展 | `mbstring`、`openssl`、`pdo`、`tokenizer`、`xml`、`ctype`、`json`、`fileinfo`、`gd`(封面/图片)、`zip` |
| Composer | 2.x |
| 数据库 | 开发可用 SQLite;也可用 MySQL / MariaDB |
| 可选 | Redis(队列/缓存);Node.js 仅在改前端资源时需要 |
| 可选 | `pcntl` + `posix`(本机跑 `workerman:ai`;没有就用 `queue:ai`) |
macOS 可用 Laravel Herd / Valet,指向仓库的 `public/`。也可用 `php artisan serve`。
@@ -41,6 +41,7 @@ DB_CONNECTION=sqlite
# 开发附件落本地,不必配 S3
ATTACHMENTS_DRIVER=local
MEDIA_DISK=public
QUEUE_CONNECTION=database
CACHE_STORE=database
@@ -107,12 +108,12 @@ php artisan queue:ai
## 5. 入口与账号
| 入口 | 地址 |
|---|---|
| 前台 | `/`、`/show-1.shtml`、`/login.shtml` |
| 后台 | `/admin` |
| API | `/api/v1/meta`、`/api/v1/articles` |
| OpenAPI | `/docs/api/openapi.yaml` |
| 入口 | 地址 |
| ------- | ------------------------------------ |
| 前台 | `/`、`/show-1.shtml`、`/login.shtml` |
| 后台 | `/admin` |
| API | `/api/v1/meta`、`/api/v1/articles` |
| OpenAPI | `/docs/api/openapi.yaml` |
演示管理员(`DemoBlogSeeder`):
+57 -37
View File
@@ -1,16 +1,19 @@
# LaraBlog 平台一期
## 状态
- 状态:已定稿
- 创建:2026-08-11 17:14
- 最近更新:2026-08-11 17:53
## 背景
SaBlog-X 1.6(约二十年前设计)需迁移到现代 Laravel 栈。迁移目标不是「功能克隆」,而是:**保留有价值的内容、SEO 权重与核心写作/阅读体验**,用当代博客实践替换过时、产垃圾或维护成本不合理的设计。
参考源:https://github.com/neatstudio/sablog-archive
## 现代化取舍原则
1. **内容与 SEO 优先**:文章/分类/标签 ID 与关键旧 URL 保留,避免权重归零
2. **不迁垃圾协议与死技术**:Trackback/Pingback、WAP、旧 searchindex、旧 PHP Admin、Feedsky 等一律不实现
3. **用当代能力替换**:AI 评论审核代替巨型敏感词表为主力;对象存储代替本地附件目录;Workerman 承载 LLM;Filament 后台
@@ -18,6 +21,7 @@ SaBlog-X 1.6(约二十年前设计)需迁移到现代 Laravel 栈。迁移
5. **旧入口若被爬虫打到**:对明确废弃路径返回 **410 Gone**(不实现业务)
## 目标
- 可运行的现代博客:前台阅读/评论/搜索/归档 + Filament 5 后台
- 严格兼容 sablog **内容向**伪静态与关键 query URL;导入后文章/分类 ID 一致
- 多主题、插件框架、SEO/GEO、S3 兼容附件、Workerman AI 运行时
@@ -26,6 +30,7 @@ SaBlog-X 1.6(约二十年前设计)需迁移到现代 Laravel 栈。迁移
## 范围
### In scope
1. Laravel 12 + Livewire 4 + Filament 5 + Spatie(permission、settings、feed、sitemap、activitylog)
2. 博客核心:文章、分类、标签、评论、附件、友情链接、用户/角色、站点片段(stylevars)
3. Legacy **内容 URL** + 新 canonical(见 URL 矩阵)
@@ -40,6 +45,7 @@ SaBlog-X 1.6(约二十年前设计)需迁移到现代 Laravel 栈。迁移
12. **正文格式双轨**:`content_format = html|markdown`;站点默认新作为 Markdown;导入默认保留 HTML;可选 HTML→Markdown 转换开关
### Out of scope(明确不迁移 / 不复刻)
- **Trackback / Pingback**(含表、后台、前台、开启开关);`/tburl.php`、`/trackback.php` 仅 410
- WAP、旧 `searchindex` 搜索缓存表、自建 sessions 表、Feedsky/旧 JS 挂件
- 旧 `/admin/*` PHP 后台原样兼容(新后台 Filament `/admin`;旧路径 410)
@@ -49,10 +55,12 @@ SaBlog-X 1.6(约二十年前设计)需迁移到现代 Laravel 栈。迁移
- 以巨型政治/广告敏感词表为唯一防垃圾手段(可保留极简词表作预过滤,主力为 AI + rate limit)
### 延期(二期+,本期只预留)
**自动配图 / 由正文生成封面**(老 sablog 无现代「特色图」概念):
- 能力意向:
1. **自动配图**:从正文首图 / 附件图 / OG 候选中选封面
2. **生成配图**:用标题+摘要(或 LLM 提示)调用文生图 / 模板渲染,产出封面,写入对象存储
1. **自动配图**:从正文首图 / 附件图 / OG 候选中选封面
2. **生成配图**:用标题+摘要(或 LLM 提示)调用文生图 / 模板渲染,产出封面,写入对象存储
- 执行方式:Workerman / 队列 Job + 可重跑 Artisan 脚本,**批量慢生成**,不堵发布主路径
- 本期只做:表字段与状态枚举预留、`GenerateArticleCoverJob` 空壳、SPEC/清单占位;**不做**真实选图/生图对接
- 封面文件同样只存对象存储,规则同附件
@@ -60,31 +68,34 @@ SaBlog-X 1.6(约二十年前设计)需迁移到现代 Laravel 栈。迁移
## 方案要点
### 技术栈
| 层 | 选型 |
|---|---|
| 框架 | Laravel 12、PHP 8.2+、Livewire 4 |
| 后台 | Filament 5 |
| Spatie | permission、settings、feed、sitemap、activitylog |
| 附件 | 自建 `attachments` + S3 disk(不用 medialibrary) |
| AI | Workerman + Redis;`LlmProvider`(OpenAI 兼容 + Stub) |
| 层 | 选型 |
| --------- | --------------------------------------------------------- |
| 框架 | Laravel 12、PHP 8.2+、Livewire 4 |
| 后台 | Filament 5 |
| Spatie | permission、settings、feed、sitemap、activitylog |
| 附件 | 自建 `attachments` + S3 disk(不用 medialibrary) |
| AI | Workerman + Redis;`LlmProvider`(OpenAI 兼容 + Stub) |
| 净化 / 图 | mews/purifier;Intervention Image(临时处理后退对象存储) |
### 数据模型
- `articles`:id、category_id、user_id、title、content、**content_format(`html`|`markdown`)**、description、keywords、slug(nullable)、published_at、views、comments_count、stick、visible、close_comment、read_password(nullable,兼容旧私密文)、ai_summary、ai_suggestions、legacy_attachments、**cover_***(二期配图预留,见下)
- 封面预留字段:`cover_disk`、`cover_path`、`cover_source`(`none`|`manual`|`attachment`|`content_image`|`generated`)、`cover_status`(`none`|`pending`|`ready`|`failed`)、`cover_generated_at`
- **无** `trackbacks` 表;**无** `close_trackback` / `trackbacks_count`
### 正文格式(HTML / Markdown)
- **原则**:旧 sablog 正文是 HTML;**新写作默认 Markdown**(未来主路径)
- **文章级开关**:`articles.content_format`;后台编辑器按格式切换(Markdown 文本域 / HTML 富文本)
- **导入双模式**(两份能力,同一命令开关):
1. **`raw`(默认)**:正文保持 HTML,`content_format=html`,保留 sablog `[attach=id]`
2. **`markdown`**:HTML→Markdown,`content_format=markdown`;因 `[]` 是 Markdown 保留字符,**导入时把 `[attach=id]` 改写成 Markdown 安全引用**,不把对象存储 URL 写进库
1. **`raw`(默认)**:正文保持 HTML,`content_format=html`,保留 sablog `[attach=id]`
2. **`markdown`**:HTML→Markdown,`content_format=markdown`;因 `[]` 是 Markdown 保留字符,**导入时把 `[attach=id]` 改写成 Markdown 安全引用**,不把对象存储 URL 写进库
- **附件引用策略(已定)**:
| 场景 | 数据库保存 | 预览/前台输出 |
|---|---|---|
| raw/HTML | `[attach=123]`(原样) | 渲染期解析为 `<img>`/`<a>`,href/src 用稳定入口 `/attachment.php?id=123`(再 302 到 S3) |
| markdown | 图片 `![name](attach:123)`;文件 `[name](attach:123)` | CommonMark 后把 `attach:123` 换成 `/attachment.php?id=123` |
| 场景 | 数据库保存 | 预览/前台输出 |
| -------- | -------------------------------------------------------- | --------------------------------------------------------------------------------- |
| raw/HTML | `[attach=123]`(原样);新媒体 `[media=123]` | 渲染期解析为 `<img>`/`<a>`;老附件走 `/attachment.php?id=123`,新媒体走媒体库 URL |
| markdown | 老附件 `![name](attach:123)`;新媒体 `![alt](media:123)` | CommonMark 后把 `attach:` / `media:` 换成当前公开地址 |
- **不在导入时写死 S3/CDN 路径**:避免换桶/换域名后正文失效;**平时 DB 只存引用,输出时转换**
- **站点设置**:`default_content_format=markdown`;导入模式由 CLI `--mode=raw|markdown` 决定(可覆盖 settings)
- **渲染**:`ContentRenderer::toHtml()` = 格式转换 + attach 水合 + Purifier
@@ -92,20 +103,22 @@ SaBlog-X 1.6(约二十年前设计)需迁移到现代 Laravel 栈。迁移
- Settings:站点、SEO、active_theme、LLM、存储、正文格式默认/导入策略(**无** trackback_enabled)
#### 导入映射(摘要)
| 源 | 目标 | 规则 |
|---|---|---|
| articles 核心字段 | articles.* | 保留 id/cid/uid/views/close_comment/read_password 等 |
| articles.content | content + content_format | 默认 `html`;若开启转换则转为 Markdown 且 format=`markdown` |
| articles.trackbacks / closetrackback | — | **忽略** |
| articles.attachments serialize | legacy_attachments + attachments 表 | 文件上云 |
| comments.visible | moderation_status | 1→approved,0→pending |
| tags.aids | article_tag | 拆 pivot |
| users.password | password_legacy | 无盐 MD5 → 登录升级 bcrypt |
| attachments.* | attachments.* | 逐条上云,保留 downloads |
| links / stylevars / settings | 对应表/Settings | 直通或键映射 |
| sablog_trackbacks / trackbacklog / searchindex / sessions | — | **跳过并计入报告** |
| 源 | 目标 | 规则 |
| --------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------- |
| articles 核心字段 | articles.* | 保留 id/cid/uid/views/close_comment/read_password 等 |
| articles.content | content + content_format | 默认 `html`;若开启转换则转为 Markdown 且 format=`markdown` |
| articles.trackbacks / closetrackback | — | **忽略** |
| articles.attachments serialize | legacy_attachments + attachments 表 | 文件上云 |
| comments.visible | moderation_status | 1→approved,0→pending |
| tags.aids | article_tag | 拆 pivot |
| users.password | password_legacy | 无盐 MD5 → 登录升级 bcrypt |
| attachments.* | attachments.* | 逐条上云,保留 downloads |
| links / stylevars / settings | 对应表/Settings | 直通或键映射 |
| sablog_trackbacks / trackbacklog / searchindex / sessions | — | **跳过并计入报告** |
### 附件与对象存储
- 一律 S3 兼容;临时文件用完即删
- `/attachment.php?id=`、`/attachments/{path}` → 去重计数后 302
- 正文 `[attach=id]` 解析为对象存储 URL
@@ -113,22 +126,27 @@ SaBlog-X 1.6(约二十年前设计)需迁移到现代 Laravel 栈。迁移
### URL 矩阵
#### Legacy 内容向(必须业务可达)
`/`、`/index.php?action=…`、`/show-{id}[-{page}].shtml`、`/category-{cid}[-{page}].shtml`、`/archives…`、`/tagslist…`、`/index.php?action=tags&item=`、`/comments…`、`/search…`、`/links.shtml`、`/reg.shtml`、`/login.shtml`、`/rss.xml[?cid=]`、`/sitemap.xml`、`/attachment.php`、`/attachments/{path}`、`/post.php`(登录/注册/评论/搜索)
#### 新 canonical
- `/tag/{name}`(自旧 tags query 301)
- `/llms.txt`
- 可选 `/posts/{slug}`(不取代 show-id)
#### 明确废弃(410,无业务)
- `/tburl.php`、`/trackback.php`
- 旧 `/admin/*.php`(非 Filament 面板)
- WAP 相关路径(若出现)
### 主题 / 插件 / Workerman / 导入
同前:主题回退、插件生命周期、AI 双队列、`sablog:import` 幂等与「跳过废弃表」报告;不删源数据。
## 验收标准
- [x] 内容向 Legacy URL 可达;文章/分类 ID 与源一致(`upsertWithId` + fixture 回归)
- [x] **无** Trackback 功能/表/设置;tb 入口 410
- [x] 导入报告含「跳过 trackbacks/searchindex/sessions」计数
@@ -140,17 +158,19 @@ SaBlog-X 1.6(约二十年前设计)需迁移到现代 Laravel 栈。迁移
- [x] 导入幂等且不改源
## 开放问题
- [x] 现代化:剔除 Trackback 等过时能力 — 已定
- [ ] 部署环境对象存储凭据;开发可用 MinIO / fake
## 变更记录
| 时间 | 原因 | 变更 |
|------|------|------|
| 2026-08-11 17:14 | 初稿 | 创建 SPEC |
| 2026-08-11 17:20 | qodercli A1–A8 | URL/附件/映射等 |
| 2026-08-11 17:25 | qodercli 映射缺口 | 补字段 |
| 2026-08-11 17:26 | 三轮无阻塞;用户确认 | 定稿 |
| 2026-08-11 17:35 | 用户:勿迁 trackback 等过时设计 | 增加现代化取舍;删除 Trackback 全链路;废弃入口 410;导入跳过垃圾表 |
| 2026-08-11 17:42 | 用户:HTML 旧文 / Markdown 未来 | 增加 content_format 双轨、站点/导入开关、可选 HTML→MD 转换 |
| 2026-08-11 17:50 | 用户:导入双份 + attach 与 MD 保留字 | 明确 DB 存引用、渲染期解析;`--mode=raw|markdown`;attach: 协议 |
| 2026-08-11 17:53 | 用户:自动配图/生成配图可后推 | 延期章节 + cover 字段/Job 空壳预留 |
| 时间 | 原因 | 变更 |
| ---------------- | ------------------------------------ | ------------------------------------------------------------------- |
| 2026-08-11 17:14 | 初稿 | 创建 SPEC |
| 2026-08-11 17:20 | qodercli A1–A8 | URL/附件/映射等 |
| 2026-08-11 17:25 | qodercli 映射缺口 | 补字段 |
| 2026-08-11 17:26 | 三轮无阻塞;用户确认 | 定稿 |
| 2026-08-11 17:35 | 用户:勿迁 trackback 等过时设计 | 增加现代化取舍;删除 Trackback 全链路;废弃入口 410;导入跳过垃圾表 |
| 2026-08-11 17:42 | 用户:HTML 旧文 / Markdown 未来 | 增加 content_format 双轨、站点/导入开关、可选 HTML→MD 转换 |
| 2026-08-11 17:50 | 用户:导入双份 + attach 与 MD 保留字 | 明确 DB 存引用、渲染期解析;`--mode=raw | markdown`;attach: 协议 |
| 2026-08-11 17:53 | 用户:自动配图/生成配图可后推 | 延期章节 + cover 字段/Job 空壳预留 |