using System.Collections.Specialized;
using System.Net.Http;
using System.Threading;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Dpz.Core.Service;

public interface IBingWallpaper
{
    /// <summary>
    /// 随机获取一张壁纸
    /// </summary>
    /// <returns></returns>
    Task<Wallpaper> GetRandomWallpaperAsync();

    /// <summary>
    /// 获取今日的所有壁纸
    /// </summary>
    /// <returns></returns>
    Task<List<Wallpaper>> GetTodayWallpapersAsync();

    /// <summary>
    /// 清除缓存
    /// </summary>
    /// <returns></returns>
    Task ClearCacheAsync();
}

public class Wallpaper
{
    private const string Host = "https://cn.bing.com";

    private string _url = "";

    [JsonProperty("url")]
    public string Url
    {
        get => _url.StartsWith("http", StringComparison.OrdinalIgnoreCase) ? _url : Host + _url;
        set => _url = value;
    }

    [JsonProperty("copyright")]
    public string? CopyRight { get; set; }

    [JsonProperty("copyrightlink")]
    public string? CopyRightLink { get; set; }
}

public class BingWallpaper(
    ILogger<BingWallpaper> logger,
    IHttpClientFactory httpClientFactory,
    IFusionCache fusionCache
) : AbstractCacheService(fusionCache), IBingWallpaper
{
    private readonly Wallpaper _defaultWallpaper =
        new()
        {
            Url =
                "/th?id=OHR.RedAlley_ZH-CN2795378972_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp",
            CopyRight = "武侯祠内红墙和竹林掩映下的小巷,中国成都 (© Eastimages/Getty Images)",
            CopyRightLink =
                "https://www.bing.com/search?q=%E6%AD%A6%E4%BE%AF%E7%A5%A0&form=hpcapt&mkt=zh-cn",
        };

    private async Task<List<Wallpaper>> FetchWallpaperAsync(
        CancellationToken cancellationToken = default
    )
    {
        try
        {
            //var request = new RestRequest("/HPImageArchive.aspx", Method.GET);

            var parameters = new NameValueCollection
            {
                { "format", "js" },
                //请求图片截止天数 0 今天 -1 截止中明天 (预准备的) 1 截止至昨天,类推(目前最多获取到7天前的图片)
                { "idx", "0" },
                //1-8 返回请求数量,目前最多一次获取8张
                { "n", "8" },
                //地区
                { "mkt", "zh-CN" },
            };

            var httpClient = httpClientFactory.CreateClient("edge");
            var request = new HttpRequestMessage(
                HttpMethod.Get,
                $"https://cn.bing.com/HPImageArchive.aspx?{parameters.ToQueryString()}"
            );

            var response = await httpClient.SendAsync(request, cancellationToken);
            var content = await response.Content.ReadAsStringAsync(cancellationToken);
            var result = JObject.Parse(content)["images"]?.ToObject<List<Wallpaper>>() ?? [];
            return result;
        }
        catch (Exception e)
        {
            logger.LogError(e, "获取bing壁纸失败");
            var result = new List<Wallpaper> { _defaultWallpaper };
            return result;
        }
    }

    private async Task<List<Wallpaper>> GetWallpaperAsync()
    {
        return await GetOrSetCacheAsync<List<Wallpaper>>(
            nameof(GetWallpaperAsync),
            (_, cancellationToken) => FetchWallpaperAsync(cancellationToken)
        );
    }

    public async Task<Wallpaper> GetRandomWallpaperAsync()
    {
        return await GetOrSetCacheAsync<Wallpaper>(
            nameof(GetRandomWallpaperAsync),
            async (_, _) =>
            {
                var list = await GetWallpaperAsync();
                var result = list.OrderBy(_ => Guid.NewGuid()).FirstOrDefault();
                return result ?? _defaultWallpaper;
            }
        );
    }

    public async Task<List<Wallpaper>> GetTodayWallpapersAsync()
    {
        return await GetWallpaperAsync();
    }

    public async Task ClearCacheAsync()
    {
        await RemoveByPrefixAsync();
    }

    protected override TimeSpan CacheDefaultExpiration => TimeSpan.FromDays(1);
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

下面按模块逐项说明这段代码的功能与行为(中文说明):

总体

  • 定义了一个用于获取必应壁纸的服务接口 IBingWallpaper 及其实现 BingWallpaper。
  • 服务通过 HTTP 调用必应的 HPImageArchive.aspx 接口获取壁纸数据,并将结果以 Wallpaper 实体表示。
  • 实现包含缓存逻辑(继承自 AbstractCacheService,使用注入的 IFusionCache),并在请求失败时返回一个内置的默认壁纸。

接口 IBingWallpaper

  • Task GetRandomWallpaperAsync():随机返回一张壁纸。
  • Task<List> GetTodayWallpapersAsync():返回当天(最多最近 8 张)所有壁纸列表。
  • Task ClearCacheAsync():清除缓存。

实体 Wallpaper

  • 三个属性:
    • Url(映射 JSON 的 "url"):如果值是相对路径(非以 http 开头),会自动在前面拼接 Host 常量 "https://cn.bing.com" 以形成完整 URL。
    • CopyRight(映射 JSON 的 "copyright"):版权说明文字。
    • CopyRightLink(映射 JSON 的 "copyrightlink"):版权链接。
  • JsonProperty 特性用于与返回的 JSON 字段名映射。

实现类 BingWallpaper

  • 依赖注入的构造参数(通过构造器注入):ILogger logger、IHttpClientFactory httpClientFactory、IFusionCache fusionCache。
  • 继承自 AbstractCacheService,利用其提供的缓存方法(如 GetOrSetCacheAsync、RemoveByPrefixAsync);并重写 CacheDefaultExpiration 为 1 天。

FetchWallpaperAsync(私有方法)

  • 构建请求参数:
    • format=js(返回 JSON)
    • idx=0(起始天数;0 当天,-1 明天等)
    • n=8(请求数量,最多 8 张)
    • mkt=zh-CN(地区)
  • 使用 IHttpClientFactory 创建名为 "edge" 的 HttpClient,并对 https://cn.bing.com/HPImageArchive.aspx 发起 GET 请求(带上 query)。
  • 读取响应内容并解析为 JSON,然后取 images 节点并转换为 List 返回(若解析为空则返回空列表或后续处理的默认值)。
  • 在捕获到异常时记录日志(logger.LogError)并返回一个包含内置默认壁纸的列表作为兜底。

GetWallpaperAsync(私有)

  • 使用缓存方法 GetOrSetCacheAsync 将 FetchWallpaperAsync 的结果缓存(缓存键为 nameof(GetWallpaperAsync)),避免每次都发起网络请求。

GetRandomWallpaperAsync

  • 通过缓存键 nameof(GetRandomWallpaperAsync) 获取或设置缓存:
    • 从 GetWallpaperAsync 获取列表,然后随机选一张(实现是 list.OrderBy(_ => Guid.NewGuid()).FirstOrDefault() 的方式来打乱顺序并取第一个)。
    • 若列表为空则返回内置默认壁纸。

GetTodayWallpapersAsync

  • 直接返回 GetWallpaperAsync 的结果(即当天的壁纸列表,且受缓存控制)。

ClearCacheAsync

  • 调用 RemoveByPrefixAsync() 清除缓存(AbstractCacheService 提供的删除机制,通常是按前缀删除所有相关缓存项)。

其他细节

  • 默认壁纸 _defaultWallpaper:当从网络获取失败或没有数据时作为兜底项,注意它的 Url 是以相对路径形式给出,Wallpaper.Url getter 会自动拼接 Host 变成完整 URL。
  • HTTP 客户端是通过 IHttpClientFactory 创建并使用名为 "edge" 的客户端(配置可能在外部启动代码中定义)。
  • 方法内部对 CancellationToken 有支持(FetchWallpaperAsync 可接受 cancellationToken 并将其传给 SendAsync / ReadAsStringAsync)。

总结

  • 这个类封装了从必应获取当天壁纸、缓存结果、提供随机壁纸及清理缓存的功能;在出错时会返回一个预置的默认壁纸并记录错误日志。
评论加载中...