'integer', 'is_top' => 'integer', 'start_time' => 'integer', 'end_time' => 'integer', 'view_count' => 'integer', 'sort' => 'integer', 'status' => 'integer', 'create_at' => 'integer', 'update_at' => 'integer', 'delete_at' => 'integer', ]; // 只读字段 protected $readonly = ['view_count']; /** * 模型配置(BaseModel::applyOptions 约定) */ protected function getOptions(): array { return [ 'strict' => true, 'name' => 'notice', 'autoWriteTimestamp' => 'int', 'createTime' => 'create_at', 'updateTime' => 'update_at', ]; } /** * 公告类型映射 */ public static function typeList(): array { return [ self::TYPE_MAINTAIN => '系统维护', self::TYPE_ACTIVITY => '活动公告', self::TYPE_UPDATE => '版本更新', self::TYPE_OTHER => '其他', ]; } /** * 取全站生效的置顶公告(用于前台公告条)。 * 过滤:status=1 + 在有效期内(start_time<=now<=end_time 或 0=长期) + is_top=1 */ public static function getActiveTopNotices(): array { self::ensureSchema(); $now = time(); return (new self()) ->where('status', 1) ->where('is_top', 1) ->where(function ($q) use ($now) { $q->where('start_time', 0)->whereOr('start_time', '<=', $now); }) ->where(function ($q) use ($now) { $q->where('end_time', 0)->whereOr('end_time', '>=', $now); }) ->order('sort', 'desc') ->order('id', 'desc') ->limit(5) ->select() ->toArray(); } /** * 取前台公告列表(分页),按置顶优先 + 排序 + 时间倒序 */ public static function getList(int $page = 1, int $limit = 10, int $type = 0): \think\Paginator { self::ensureSchema(); $now = time(); $query = (new self()) ->where('status', 1) ->where(function ($q) use ($now) { $q->where('start_time', 0)->whereOr('start_time', '<=', $now); }) ->where(function ($q) use ($now) { $q->where('end_time', 0)->whereOr('end_time', '>=', $now); }); if ($type > 0) { $query->where('type', $type); } return $query->order('is_top', 'desc') ->order('sort', 'desc') ->order('id', 'desc') ->paginate(['page' => $page, 'list_rows' => $limit]); } /** * 运行时结构自愈:老库缺新列时自动补齐(install.sql 为唯一事实源,此处兜底) */ public static function ensureSchema(): void { try { $p = self::currentPrefix(); $prefix = $p; $table = $prefix . 'notice'; // 缺表建表(install.sql L618 同款 DDL,运行时前缀兜底) self::ensureTable($table, "CREATE TABLE IF NOT EXISTS `{$p}notice` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(200) NOT NULL DEFAULT '' COMMENT '公告标题', `content` text COMMENT '公告内容', `author` varchar(50) NOT NULL DEFAULT '' COMMENT '发布人', `type` tinyint(1) NOT NULL DEFAULT 1 COMMENT '类型 1系统维护 2活动公告 3版本更新 4其他', `is_top` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否置顶 0否 1是', `start_time` int(11) NOT NULL DEFAULT 0 COMMENT '展示开始时间 0=长期', `end_time` int(11) NOT NULL DEFAULT 0 COMMENT '展示结束时间 0=长期', `view_count` int(11) NOT NULL DEFAULT 0 COMMENT '浏览量', `sort` int(11) NOT NULL DEFAULT 0 COMMENT '排序', `status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态 0禁用 1启用', `create_at` int(11) NOT NULL DEFAULT 0 COMMENT '创建时间', `update_at` int(11) NOT NULL DEFAULT 0 COMMENT '更新时间', `delete_at` int(11) NOT NULL DEFAULT 0 COMMENT '删除时间', PRIMARY KEY (`id`), KEY `idx_type` (`type`), KEY `idx_is_top` (`is_top`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='站点公告'"); // 老库扩展列兜底(幂等) self::ensureColumn($table, 'type', "tinyint(1) NOT NULL DEFAULT 1 COMMENT '公告类型 1系统维护 2活动公告 3版本更新 4其他'"); self::ensureColumn($table, 'is_top', "tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否置顶 0否 1是'"); self::ensureColumn($table, 'start_time', "int(11) NOT NULL DEFAULT 0 COMMENT '展示开始时间 0=长期'"); self::ensureColumn($table, 'end_time', "int(11) NOT NULL DEFAULT 0 COMMENT '展示结束时间 0=长期'"); self::ensureColumn($table, 'view_count', "int(11) NOT NULL DEFAULT 0 COMMENT '浏览量'"); } catch (\Throwable $e) { // 忽略自愈失败,由业务暴露 } } }