需求:前台壁纸站支持切换到开源壁纸平台,并能展示后台上传的自家图库, 保留原有「浏览器实时生成」能力(扩展而非替换),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 不变)。
237 lines
8.7 KiB
Go
237 lines
8.7 KiB
Go
// Package wallpaper_v1 壁纸模块接口契约。
|
||
//
|
||
// 分成两组:
|
||
// - open 组(/api/service/open/wallpaper/*):给 xpcool.com 前台调用,免鉴权;
|
||
// - admin 组(/api/service/admin/wallpaper/*):后台管理,走 RBAC 权限。
|
||
//
|
||
// 规范(2026-08-27 起):全部 POST;URL 不含参数;入参一律 body。
|
||
// 唯一例外是「上传」——文件必须走 multipart,其余字段随之走表单而不是 JSON。
|
||
package wallpaper
|
||
|
||
import "github.com/gogf/gf/v2/frame/g"
|
||
import "github.com/gogf/gf/v2/net/ghttp"
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 公共数据结构
|
||
// ---------------------------------------------------------------------------
|
||
|
||
// Item 是壁纸的统一输出条目。开源平台与自建图库填的是同一个结构,
|
||
// 前端因此只需要一套渲染逻辑。
|
||
type Item struct {
|
||
Id string `json:"id"`
|
||
Title string `json:"title"`
|
||
Width int `json:"width"`
|
||
Height int `json:"height"`
|
||
Orientation int `json:"orientation"` // 0未知 1横版 2竖版 3方形
|
||
ThumbUrl string `json:"thumbUrl"` // 列表网格用(480px)
|
||
PreviewUrl string `json:"previewUrl"` // 全屏展示用(长边 1920)
|
||
FullUrl string `json:"fullUrl"` // 下载用(原图)
|
||
Source string `json:"source"`
|
||
SourceName string `json:"sourceName"`
|
||
FromOpen bool `json:"fromOpen"`
|
||
Author string `json:"author"`
|
||
AuthorUrl string `json:"authorUrl"`
|
||
PageUrl string `json:"pageUrl"`
|
||
License string `json:"license"`
|
||
Tags string `json:"tags"`
|
||
Category string `json:"category"`
|
||
Filesize int64 `json:"filesize"`
|
||
CreatedAt string `json:"createdAt"`
|
||
}
|
||
|
||
// SourceInfo 开源平台的可用性描述。
|
||
type SourceInfo struct {
|
||
Code string `json:"code"`
|
||
Name string `json:"name"`
|
||
Enabled int `json:"enabled"`
|
||
Sort int `json:"sort"`
|
||
Configured bool `json:"configured"` // 凭据是否齐备
|
||
Available bool `json:"available"` // 启用 且 凭据齐备
|
||
Hint string `json:"hint"` // 不可用原因
|
||
Remark string `json:"remark"`
|
||
HasApiKey bool `json:"hasApiKey"` // 只回传「是否已设置」,不回传明文
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// open 组:前台
|
||
// ---------------------------------------------------------------------------
|
||
|
||
// OpenSourceListReq 取全部来源清单(含开源平台 + 自建图库计数)。
|
||
type OpenSourceListReq struct {
|
||
g.Meta `path:"/wallpaper/sources" method:"post" tags:"Open/Wallpaper" summary:"壁纸来源清单"`
|
||
}
|
||
|
||
type OpenSourceListRes struct {
|
||
List []*SourceInfo `json:"list"`
|
||
// MineCount 只统计「启用」的自建图片数量,为 0 时前台隐藏「我的图库」入口
|
||
MineCount int `json:"mineCount"`
|
||
}
|
||
|
||
// OpenListReq 按来源分页取图。
|
||
type OpenListReq struct {
|
||
g.Meta `path:"/wallpaper/list" method:"post" tags:"Open/Wallpaper" summary:"壁纸列表(按来源)"`
|
||
Source string `json:"source" d:"mine" dc:"来源编码:mine=自建图库,其余为平台编码"`
|
||
Query string `json:"query" dc:"搜索词(仅部分平台与自建图库支持)"`
|
||
Orientation int `json:"orientation" dc:"0不限 1横版 2竖版 3方形"`
|
||
Tag string `json:"tag" dc:"标签(仅自建图库)"`
|
||
Page int `json:"page" d:"1"`
|
||
Size int `json:"size" d:"24"`
|
||
}
|
||
|
||
type OpenListRes struct {
|
||
List []*Item `json:"list"`
|
||
Total int `json:"total"`
|
||
Page int `json:"page"`
|
||
Size int `json:"size"`
|
||
}
|
||
|
||
// OpenRandomReq 跨来源随机取一张。
|
||
type OpenRandomReq struct {
|
||
g.Meta `path:"/wallpaper/random" method:"post" tags:"Open/Wallpaper" summary:"随机取一张壁纸"`
|
||
Sources []string `json:"sources" dc:"限定来源;为空则在所有可用来源中随机"`
|
||
Orientation int `json:"orientation" dc:"0不限 1横版 2竖版 3方形"`
|
||
}
|
||
|
||
type OpenRandomRes struct {
|
||
Item *Item `json:"item"`
|
||
}
|
||
|
||
// OpenTrackDownloadReq 通知平台「这张图被下载了」。
|
||
// 目前只有 Unsplash 需要(其 API 许可的硬性要求),其它平台为空操作。
|
||
type OpenTrackDownloadReq struct {
|
||
g.Meta `path:"/wallpaper/download-track" method:"post" tags:"Open/Wallpaper" summary:"下载回调(平台统计)"`
|
||
Source string `json:"source"`
|
||
Id string `json:"id"`
|
||
}
|
||
|
||
type OpenTrackDownloadRes struct {
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// admin 组:自建图库
|
||
// ---------------------------------------------------------------------------
|
||
|
||
// AdminListReq 后台图库列表(含已停用项)。
|
||
type AdminListReq struct {
|
||
g.Meta `path:"/wallpaper/list" method:"post" tags:"Admin/Wallpaper" summary:"图库列表(含停用)"`
|
||
Query string `json:"query"`
|
||
Orientation int `json:"orientation"`
|
||
Enabled int `json:"enabled" d:"-1" dc:"-1全部 0停用 1启用"`
|
||
Page int `json:"page" d:"1"`
|
||
Size int `json:"size" d:"24"`
|
||
}
|
||
|
||
type AdminListRes struct {
|
||
List []*Item `json:"list"`
|
||
Total int `json:"total"`
|
||
}
|
||
|
||
// AdminUploadReq 上传壁纸(文件走 multipart,其余字段为表单字段)。
|
||
type AdminUploadReq struct {
|
||
g.Meta `path:"/wallpaper/upload" method:"post" mime:"multipart/form-data" tags:"Admin/Wallpaper" summary:"上传壁纸(可多选)"`
|
||
Files []*ghttp.UploadFile `json:"files" type:"file" dc:"图片文件,可多选"`
|
||
Title string `json:"title" dc:"标题,留空则用文件名"`
|
||
Tags string `json:"tags"`
|
||
Category string `json:"category"`
|
||
Sort int `json:"sort"`
|
||
}
|
||
|
||
// UploadItem 单个文件的处理结果。
|
||
// 批量上传时允许部分失败,故逐条返回,而不是整体成功/失败。
|
||
type UploadItem struct {
|
||
FileName string `json:"fileName"`
|
||
Ok bool `json:"ok"`
|
||
Duplicated bool `json:"duplicated"` // 内容重复,命中了已有图片(秒传)
|
||
Message string `json:"message"`
|
||
Item *Item `json:"item"`
|
||
}
|
||
|
||
type AdminUploadRes struct {
|
||
List []*UploadItem `json:"list"`
|
||
OkCount int `json:"okCount"`
|
||
FailCount int `json:"failCount"`
|
||
}
|
||
|
||
// AdminSaveReq 编辑壁纸元数据(不涉及文件)。
|
||
type AdminSaveReq struct {
|
||
g.Meta `path:"/wallpaper/save" method:"post" tags:"Admin/Wallpaper" summary:"保存壁纸信息"`
|
||
Id uint64 `json:"id" v:"required"`
|
||
Title string `json:"title"`
|
||
Tags string `json:"tags"`
|
||
Category string `json:"category"`
|
||
Enabled int `json:"enabled"`
|
||
Sort int `json:"sort"`
|
||
Remark string `json:"remark"`
|
||
}
|
||
|
||
type AdminSaveRes struct {
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
// AdminDeleteReq 删除壁纸(同时清理原图与派生图)。
|
||
type AdminDeleteReq struct {
|
||
g.Meta `path:"/wallpaper/delete" method:"post" tags:"Admin/Wallpaper" summary:"删除壁纸"`
|
||
Id uint64 `json:"id" v:"required"`
|
||
}
|
||
|
||
type AdminDeleteRes struct {
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
// AdminStatsReq 图库概览。
|
||
type AdminStatsReq struct {
|
||
g.Meta `path:"/wallpaper/stats" method:"post" tags:"Admin/Wallpaper" summary:"图库统计"`
|
||
}
|
||
|
||
type AdminStatsRes struct {
|
||
Total int `json:"total"`
|
||
Enabled int `json:"enabled"`
|
||
Disabled int `json:"disabled"`
|
||
Portrait int `json:"portrait"`
|
||
Landscape int `json:"landscape"`
|
||
TotalBytes int64 `json:"totalBytes"`
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// admin 组:开源平台配置
|
||
// ---------------------------------------------------------------------------
|
||
|
||
// AdminSourceListReq 平台配置列表。
|
||
type AdminSourceListReq struct {
|
||
g.Meta `path:"/wallpaper/source/list" method:"post" tags:"Admin/Wallpaper" summary:"平台配置列表"`
|
||
}
|
||
|
||
type AdminSourceListRes struct {
|
||
List []*SourceInfo `json:"list"`
|
||
}
|
||
|
||
// AdminSourceSaveReq 保存平台配置。
|
||
type AdminSourceSaveReq struct {
|
||
g.Meta `path:"/wallpaper/source/save" method:"post" tags:"Admin/Wallpaper" summary:"保存平台配置"`
|
||
Code string `json:"code" v:"required"`
|
||
Name string `json:"name"`
|
||
Enabled int `json:"enabled"`
|
||
Sort int `json:"sort"`
|
||
Remark string `json:"remark"`
|
||
// Config 是平台配置的 JSON 字符串。
|
||
// 其中 apiKey 留空表示「不修改已保存的 Key」—— 界面不回传密钥明文,
|
||
// 用户只改开关时不必重新粘贴。
|
||
Config string `json:"config"`
|
||
}
|
||
|
||
type AdminSourceSaveRes struct {
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
// AdminSourceTestReq 测试平台连通性。
|
||
type AdminSourceTestReq struct {
|
||
g.Meta `path:"/wallpaper/source/test" method:"post" tags:"Admin/Wallpaper" summary:"测试平台连通性"`
|
||
Code string `json:"code" v:"required"`
|
||
}
|
||
|
||
type AdminSourceTestRes struct {
|
||
Ok bool `json:"ok"`
|
||
Message string `json:"message"`
|
||
}
|