Files
YwxAppThink/addon/blog/library/BlogSchema.php
T
yangwx 18101cf11a feat: 上线前安全加固与发布工程化;appmall 由插件迁移为独立应用
安全(P0)
- 发布包签名:新增 scripts/package_keygen.php、scripts/package_verify.php、
  ywxapp\service\PackageVerifier;FrameworkService::apply() 解压前强制验签
  (ywxapp.framework_package_verify,默认开启)
- JWT 收紧:config/jwt.php 移除公开默认密钥,JwtService::assertStrongSecret()
  校验非空 / 长度≥32 / 已知弱值;.env.production.example 键名由 JWT_SECRET_KEY 修正为 JWT_SECRET
- /api/v1/Article:免登录白名单收窄为只读接口,写操作字段白名单 + assertOwner() 越权校验
- 开发者令牌:查找接口不再回显明文、统一失败文案 + 限流
- TemplateInstaller:文件名白名单 + realpath 归一化 + 目录内断言,修复 Zip Slip;补 use ZipArchive
- 后台登录:接入验证码、失败锁定与登录限流(config/ratelimit.php)
- 缓存 / Session 驱动可由 .env 覆盖(CACHE_DRIVER / SESSION_TYPE),集群部署改 redis
- paycenter 支付日志开关改为 PAY_LOG,不再跟随 APP_DEBUG

工程化 / 交付
- 新增 CHANGELOG.md 与 .env.production.example;composer.lock 入库锁定依赖版本
- 插件离线安装 CLI 增加 --uid,后台上传安装透传 uid,付费插件授权校验可通过
- 授权拒绝文案区分「未携带 uid」与「未找到有效授权」,便于排障
- .gitignore 放行 .env.production.example / .env.addon.example / .gitattributes / public/.htaccess

结构
- appmall 由 addon/appmall 迁移为独立应用 app/appmall(市场 / 开发者中心 / 审核 / 分润)
2026-10-22 00:53:39 +08:00

85 lines
3.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace addon\blog\library;
use think\facade\Db;
use ywxapp\model\BaseModel;
/**
* 博客(blog)插件专属表结构自愈。
*
* 归属原则:blog_* 表属于 blog 插件私有表,其列兜底逻辑内聚在本插件,
* 不污染核心框架 ywxapp/library/BaseModel 自愈引擎(核心只保留通用引擎)。
*
* 状态语义:0草稿 1待审 2通过 3驳回。
*
* 为什么需要:框架 install.sql 导入有白名单,只执行 CREATE TABLE / INSERT,
* ALTER 会被跳过并记日志 —— 老库升级缺列只能靠这里补(由 Addon::upgrade() 调用)。
*/
class BlogSchema
{
/**
* 补齐 blog_* 扩展列与索引,幂等。
*/
public static function ensure(): void
{
$p = BaseModel::currentPrefix();
BaseModel::ensureColumn("{$p}blog_article", 'audit_status', "tinyint NOT NULL DEFAULT 2 COMMENT '审核状态:0草稿 1待审 2通过 3驳回'");
BaseModel::ensureColumn("{$p}blog_article", 'lat', "decimal(10,7) NOT NULL DEFAULT '0.0000000' COMMENT '纬度(可选)'");
BaseModel::ensureColumn("{$p}blog_article", 'lng', "decimal(10,7) NOT NULL DEFAULT '0.0000000' COMMENT '经度(可选)'");
// 分享数:代码与后台/前端都在用,老库缺列会导致分享接口报错
BaseModel::ensureColumn("{$p}blog_article", 'share_count', "int DEFAULT '0' COMMENT '分享数'");
self::ensureIndexes($p);
}
/**
* 清理历史遗留表(install.sql 已移除且无任何代码引用)
*
* 注意:只删「确认无代码读写」的表;blog_visit 有 Stat/Dashboard 在读,
* 不能删(已补写入逻辑),blog_comment 等在用表更不可动。
*/
public static function dropLegacyTables(): void
{
$p = BaseModel::currentPrefix();
try {
Db::execute("DROP TABLE IF EXISTS `{$p}blog_user`");
} catch (\Throwable $e) {
// 忽略:表不存在或权限不足
}
}
/**
* 关键索引兜底(新装由 install.sql 建立,老库升级靠这里补)
*/
private static function ensureIndexes(string $p): void
{
// 前台列表/详情:status + audit_status 过滤后按 create_at 排序
self::ensureIndex("{$p}blog_article", 'idx_published', '`status`,`audit_status`,`create_at`');
// 置顶/推荐排序
self::ensureIndex("{$p}blog_article", 'idx_top', '`is_top`');
// 标签查文章(关联表按 tid 反查,原先只有 PRIMARY(aid,tid))
self::ensureIndex("{$p}blog_article_tag", 'idx_tid', '`tid`');
// 评论:按文章取可见评论
self::ensureIndex("{$p}blog_comment", 'idx_article', '`aid`,`status`,`pid`');
}
/**
* 索引不存在则添加(失败静默,不影响主流程)
*/
private static function ensureIndex(string $table, string $name, string $cols): void
{
try {
foreach (Db::query("SHOW INDEX FROM `{$table}`") as $row) {
if (($row['Key_name'] ?? '') === $name) {
return;
}
}
Db::execute("ALTER TABLE `{$table}` ADD INDEX `{$name}` ({$cols})");
} catch (\Throwable $e) {
// 表不存在或不支持:忽略
}
}
}