service.xpcool.com/manifest/sql/018_wallpaper.sql
夏犀麟 582485ba2b feat(wallpaper): 壁纸模块后端(开源平台插件 + 自建图库 + 前后台接口)
需求:前台壁纸站支持切换到开源壁纸平台,并能展示后台上传的自家图库,
保留原有「浏览器实时生成」能力(扩展而非替换),PC / 移动双端一致。

设计要点(受服务器 5M 出口带宽约束):
- 开源平台图片一律走官方 CDN 直链,后端只代理元数据,零带宽消耗。
- 自建图库落盘到 /data/www/wallpaper,由 nginx 的 /wallpaper/ 直出,
  Go 服务不参与传图;三档尺寸(480 缩略 / 1920 预览 / 原图仅供下载)。
- 内存缓存平台响应(Bing 1h、搜索类 10min),避免撞第三方配额。

内容:
- 平台插件:Bing 每日一图、Picsum、Unsplash、Pexels、Wallhaven。
  新增平台 = 写一个 provider_xxx.go 并在 allProviders() 加一行,
  刻意不用 init() 自注册,避免隐式副作用。所有适配器支持 config.baseUrl
  覆盖,用于绕开 DNS 污染(实测 wallhaven.cc 解析到境外无关 IP)。
- 自建图库:上传(MD5 秒传去重 / 尺寸前置校验 / 失败清理孤儿文件)、
  元信息编辑、删除、统计;随机取图用「数总数 → 随机偏移」而非 ORDER BY RAND()。
- 接口:open 组 4 个(sources / list / random / download-track),
  admin 组 8 个(list / upload / save / delete / stats / source.list|save|test)。
  全部 POST 且 URL 无参数,符合工作空间接口规范;上传走 multipart 例外。
- 安全:purity 默认锁死 SFW;apiKey 只回传布尔不回传明文;
  保存时留空表示保留旧值;open 组错误信息对外脱敏。
- 配置:clientMaxBodySize 提到 64M(gf 默认 8M 会截断 20MB 原图);
  wallpaper.root / baseUrl 支持环境变量注入并在占位符未替换时兜底。

依赖:golang.org/x/image@v0.23.0(仅用于缩略图缩放,保持 go 1.23.0 不变)。
2026-09-14 01:39:09 +08:00

104 lines
8.1 KiB
Go
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.

-- 018_wallpaper.sql
-- 壁纸模块自建图库表 + 开源平台配置表 + 菜单/权限种子
--
-- 设计要点
-- 1) 自建图库wallpaper原图 + 缩略图 + 预览图三份路径
-- 原图只用于下载列表用缩略图480px全屏展示用预览图长边 1920
-- 这样 5M 出口带宽不会被大图打满三份文件都由 nginx 直出不过 Go 服务
-- 2) 开源平台wallpaper_source只存配置 API Key**不存图**
-- 图片本体走各平台官方 CDN 直链零带宽消耗
-- 加平台只需插一行数据 + 后端注册一个 Provider不改表结构
-- 3) 幂等CREATE TABLE IF NOT EXISTS菜单 INSERT ... ON DUPLICATE KEY UPDATE
-- 种子 INSERT ... ON DUPLICATE KEY UPDATE不覆盖用户已改的 enabled/config
--
-- 依赖service 主库admin_menu / admin_role_menu 已存在001_core.sql
-- ============ 1) 自建壁纸图库 ============
CREATE TABLE IF NOT EXISTS `wallpaper` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`title` VARCHAR(200) NOT NULL DEFAULT '' COMMENT '标题',
`tags` VARCHAR(300) NOT NULL DEFAULT '' COMMENT '标签(英文逗号分隔)',
`category` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '分类(自由文本)',
`orientation` TINYINT NOT NULL DEFAULT 0 COMMENT '朝向 0未知 1横版 2竖版 3方形',
`width` INT NOT NULL DEFAULT 0 COMMENT '原图宽度(px)',
`height` INT NOT NULL DEFAULT 0 COMMENT '原图高度(px)',
`filesize` BIGINT NOT NULL DEFAULT 0 COMMENT '原图字节数',
`mime` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '原图MIME(image/jpeg等)',
`path` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '原图相对路径(相对存储根)',
`thumb_path` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '缩略图相对路径(480px)',
`preview_path` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '预览图相对路径(长边1920)',
`hash` CHAR(32) NOT NULL DEFAULT '' COMMENT '原图MD5(秒传去重)',
`enabled` TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用 0 1',
`sort` INT NOT NULL DEFAULT 0 COMMENT '排序小的在前',
`view_count` INT NOT NULL DEFAULT 0 COMMENT '浏览次数',
`download_count` INT NOT NULL DEFAULT 0 COMMENT '下载次数',
`remark` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '备注',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_enabled_sort` (`enabled`, `sort`),
KEY `idx_orientation` (`orientation`),
KEY `idx_hash` (`hash`),
KEY `idx_created` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='自建壁纸图库';
-- ============ 2) 开源平台配置 ============
CREATE TABLE IF NOT EXISTS `wallpaper_source` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`code` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '平台编码 bing/picsum/unsplash/pexels/wallhaven',
`name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '平台显示名',
`enabled` TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用 0 1',
`sort` INT NOT NULL DEFAULT 0 COMMENT '排序小的在前',
`config` VARCHAR(1000) NOT NULL DEFAULT '' COMMENT '平台配置JSON(apiKey/defaultQuery等)',
`remark` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '备注',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='壁纸开源平台配置';
-- ============ 3) 平台种子 ============
-- bing / picsum 零配置即可用其余三个需要各自申请 API Key故默认停用
-- 后台填入 Key 并打开开关即可生效无需改代码无需重启
-- 注意ON DUPLICATE KEY UPDATE 只更新展示名与排序**不动 enabled config**
-- 避免重放本脚本时把用户已填的 Key 或已改的开关状态覆盖掉
INSERT INTO `wallpaper_source` (`code`, `name`, `enabled`, `sort`, `config`, `remark`) VALUES
('bing', 'Bing 每日一图', 1, 1, '', '微软必应每日壁纸无需 API Key每日更新一张'),
('picsum', 'Lorem Picsum', 1, 2, '', 'Lorem Picsum 随机摄影图无需 API Key'),
('unsplash', 'Unsplash', 0, 3, '', '需在 unsplash.com/developers 申请 Access Key'),
('pexels', 'Pexels', 0, 4, '', '需在 pexels.com/api 申请 API Key'),
('wallhaven', 'Wallhaven', 0, 5, '', '需在 wallhaven.cc 申请 API Key可按分辨率与横竖版筛选')
ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `sort` = VALUES(`sort`), `remark` = VALUES(`remark`);
-- ============ 4) 菜单壁纸中心 ============
-- id 段选择说明一级菜单已用 1/5/9/90/97/98/99故取 100
-- 其下子菜单取 1001/1002 API 权限 10011.. / 10021..与既有编号无冲突
INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES
(100, 0, '壁纸中心', 'mdi:image-multiple-outline', 1, '/wallpaper', '', 'wallpaper:center', 10, 1, 0, NOW(), NOW()),
(1001, 100, '图库管理', 'mdi:image-album', 1, 'library', 'wallpaper/library/index', 'wallpaper:library', 1, 1, 0, NOW(), NOW()),
(1002, 100, '平台配置', 'mdi:cloud-cog-outline', 1, 'source', 'wallpaper/source/index', 'wallpaper:source', 2, 1, 0, NOW(), NOW())
ON DUPLICATE KEY UPDATE parent_id=VALUES(parent_id), name=VALUES(name), icon=VALUES(icon), type=VALUES(type),
path=VALUES(path), component=VALUES(component), permission=VALUES(permission), sort=VALUES(sort),
status=VALUES(status), hidden=VALUES(hidden), deleted_at=NULL;
-- ============ 5) type=2 API 权限 ============
-- path 必须与实际路由完全一致中间件按方法+路径匹配 POST URL 参数
INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES
(10011, 1001, '查询', '', 2, 'POST /api/service/admin/wallpaper/list', '', 'wallpaper:library:list', 1, 1, 0, NOW(), NOW()),
(10012, 1001, '上传', '', 2, 'POST /api/service/admin/wallpaper/upload', '', 'wallpaper:library:upload', 2, 1, 0, NOW(), NOW()),
(10013, 1001, '编辑', '', 2, 'POST /api/service/admin/wallpaper/save', '', 'wallpaper:library:save', 3, 1, 0, NOW(), NOW()),
(10014, 1001, '删除', '', 2, 'POST /api/service/admin/wallpaper/delete', '', 'wallpaper:library:delete', 4, 1, 0, NOW(), NOW()),
(10015, 1001, '统计', '', 2, 'POST /api/service/admin/wallpaper/stats', '', 'wallpaper:library:stats', 5, 1, 1, NOW(), NOW()),
(10021, 1002, '查询', '', 2, 'POST /api/service/admin/wallpaper/source/list', '', 'wallpaper:source:list', 1, 1, 0, NOW(), NOW()),
(10022, 1002, '保存', '', 2, 'POST /api/service/admin/wallpaper/source/save', '', 'wallpaper:source:save', 2, 1, 0, NOW(), NOW()),
(10023, 1002, '测试', '', 2, 'POST /api/service/admin/wallpaper/source/test', '', 'wallpaper:source:test', 3, 1, 0, NOW(), NOW())
ON DUPLICATE KEY UPDATE parent_id=VALUES(parent_id), name=VALUES(name), type=VALUES(type),
path=VALUES(path), permission=VALUES(permission), sort=VALUES(sort), status=VALUES(status), hidden=VALUES(hidden), deleted_at=NULL;
-- ============ 6) 超管角色绑定 ============
-- role_id=1 为超级管理员 004_seed.sql / 009_admin_account_v2.sql
INSERT IGNORE INTO admin_role_menu (role_id, menu_id, created_at, updated_at)
SELECT 1, id, NOW(), NOW() FROM admin_menu
WHERE id IN (100, 1001, 1002, 10011, 10012, 10013, 10014, 10015, 10021, 10022, 10023);