需求:前台壁纸站支持切换到开源壁纸平台,并能展示后台上传的自家图库, 保留原有「浏览器实时生成」能力(扩展而非替换),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 不变)。
88 lines
3.3 KiB
Go
88 lines
3.3 KiB
Go
package wallpaper
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
|
|
wallpaperv1 "service.xpcool.com/api/wallpaper"
|
|
"service.xpcool.com/internal/model/dto"
|
|
wallpapersvc "service.xpcool.com/internal/service/wallpaper"
|
|
)
|
|
|
|
// OpenController 前台读取接口(免鉴权,供 xpcool.com 调用)。
|
|
type OpenController struct{}
|
|
|
|
// NewOpen 构造前台控制器。
|
|
func NewOpen() *OpenController { return &OpenController{} }
|
|
|
|
// Sources 返回全部来源清单。
|
|
//
|
|
// 除开源平台外还带回自建图库的启用数量:前台据此决定要不要显示
|
|
// 「我的图库」这个入口 —— 一张图都没有时显示它只会让人点进去看空白。
|
|
func (c *OpenController) Sources(ctx context.Context, req *wallpaperv1.OpenSourceListReq) (res *wallpaperv1.OpenSourceListRes, err error) {
|
|
svc := wallpapersvc.Wallpaper()
|
|
list, err := svc.Sources(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// 图库计数失败不影响平台列表:把 0 当成「没有自建图」,入口不显示即可,
|
|
// 没必要因为一个计数把整个来源接口拖垮。
|
|
mineCount, err := svc.MineCount(ctx)
|
|
if err != nil {
|
|
g.Log().Warningf(ctx, "统计自建图库数量失败: %v", err)
|
|
mineCount = 0
|
|
}
|
|
|
|
out := make([]*wallpaperv1.SourceInfo, 0, len(list))
|
|
for i := range list {
|
|
out = append(out, toAPISource(&list[i]))
|
|
}
|
|
return &wallpaperv1.OpenSourceListRes{List: out, MineCount: mineCount}, nil
|
|
}
|
|
|
|
// List 按来源分页取图。
|
|
func (c *OpenController) List(ctx context.Context, req *wallpaperv1.OpenListReq) (res *wallpaperv1.OpenListRes, err error) {
|
|
q := dto.WallpaperQuery{
|
|
Source: req.Source,
|
|
Query: req.Query,
|
|
Orientation: req.Orientation,
|
|
Tag: req.Tag,
|
|
Page: req.Page,
|
|
Size: req.Size,
|
|
}
|
|
|
|
items, total, err := wallpapersvc.Wallpaper().List(ctx, q)
|
|
if err != nil {
|
|
// 前台是公开接口,平台侧的原始错误(含 URL、Key 片段)不应直接抛给浏览器
|
|
g.Log().Warningf(ctx, "壁纸列表获取失败 source=%s: %v", req.Source, err)
|
|
return nil, gerror.New("该来源暂时不可用,请换一个来源试试")
|
|
}
|
|
return &wallpaperv1.OpenListRes{
|
|
List: toAPIItems(items),
|
|
Total: total,
|
|
Page: q.Page,
|
|
Size: q.Size,
|
|
}, nil
|
|
}
|
|
|
|
// Random 跨来源随机取一张。
|
|
func (c *OpenController) Random(ctx context.Context, req *wallpaperv1.OpenRandomReq) (res *wallpaperv1.OpenRandomRes, err error) {
|
|
item, err := wallpapersvc.Wallpaper().Random(ctx, req.Sources, req.Orientation)
|
|
if err != nil {
|
|
g.Log().Warningf(ctx, "随机取壁纸失败 sources=%v: %v", req.Sources, err)
|
|
return nil, gerror.New("暂时取不到壁纸,请稍后再试")
|
|
}
|
|
return &wallpaperv1.OpenRandomRes{Item: toAPIItem(item)}, nil
|
|
}
|
|
|
|
// TrackDownload 下载回调(目前仅 Unsplash 需要)。
|
|
// 刻意做成「永远返回成功」:这是统计性质的回调,不能因为它失败而挡住用户下载。
|
|
func (c *OpenController) TrackDownload(ctx context.Context, req *wallpaperv1.OpenTrackDownloadReq) (res *wallpaperv1.OpenTrackDownloadRes, err error) {
|
|
if err = wallpapersvc.Wallpaper().TrackDownload(ctx, req.Source, req.Id); err != nil {
|
|
g.Log().Warningf(ctx, "下载回调失败 source=%s id=%s: %v", req.Source, req.Id, err)
|
|
}
|
|
return &wallpaperv1.OpenTrackDownloadRes{Message: "ok"}, nil
|
|
}
|