service.xpcool.com/api/wallpaper/wallpaper.go
夏犀麟 d0a084b80e
Some checks failed
Build and Deploy (service.xpcool.com) / build-and-deploy (push) Failing after 28s
feat(wallpaper): 平台配置脱敏回显 + 图库元字段补齐
后台做完界面才发现的两个接口缺口:

1) 平台配置不回显 → 用户只改一个开关,defaultQuery / purity / baseUrl
   会被一起清空(SaveSource 只对 apiKey/apiSecret 做了"留空即保留")。
   现在 Sources() 返回脱敏后的 config(凭据一律置空,是否已设置由
   HasApiKey 表达),前台接口不下发该字段 —— toAPISource 加 withConfig
   开关,公开接口没必要泄露自建反代地址等内部信息。

2) Item 缺 enabled / sort / remark → 后台列表无法显示启用状态,
   编辑抽屉也拿不到当前值。补齐字段并在 library.toItem 中填充,
   开源平台条目保持默认值(这三个字段对前台无意义)。

另抽出 sanitizeSourceConfig 统一处理凭据抹除,避免以后再加配置项时漏抹。
2026-09-14 01:48:23 +08:00

258 lines
9.7 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.

// Package wallpaper_v1 壁纸模块接口契约。
//
// 分成两组:
// - open 组(/api/service/open/wallpaper/*):给 xpcool.com 前台调用,免鉴权;
// - admin 组(/api/service/admin/wallpaper/*):后台管理,走 RBAC 权限。
//
// 规范2026-08-27 起):全部 POSTURL 不含参数;入参一律 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"`
// Enabled / Sort / Remark 仅自建图库有值,供后台列表展示与就地编辑;
// 开源平台条目恒为默认值,前台不关心。
Enabled int `json:"enabled"`
Sort int `json:"sort"`
Remark string `json:"remark"`
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"` // 只回传「是否已设置」,不回传明文
// Config 是脱敏后的平台配置,供后台编辑界面回显(前台接口不下发)。
// apiKey / apiSecret 恒为空串 —— 提交时留空即表示「不修改已保存的 Key」。
Config *SourceConfig `json:"config"`
}
// SourceConfig 是平台可配置项,与 dto.WallpaperSourceConfig 一一对应。
type SourceConfig struct {
ApiKey string `json:"apiKey"`
ApiSecret string `json:"apiSecret"`
DefaultQuery string `json:"defaultQuery"`
// Purity 分级过滤Wallhavensfw / sketchy / nsfw默认只放 sfw。
Purity string `json:"purity"`
// Categories 分类Wallhavengeneral/anime/people 组合。
Categories string `json:"categories"`
// BaseUrl 覆盖平台接口地址(自建反代或镜像)。
BaseUrl string `json:"baseUrl"`
}
// ---------------------------------------------------------------------------
// 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"`
}