2026-03-04 16:43:10 +08:00
|
|
|
using System;
|
2026-03-05 18:46:32 +08:00
|
|
|
using System.Collections.Generic;
|
2026-03-04 02:02:34 +08:00
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2026-03-04 15:22:52 +08:00
|
|
|
using LanMountainDesktop.Models;
|
2026-03-04 02:02:34 +08:00
|
|
|
|
2026-03-04 15:22:52 +08:00
|
|
|
namespace LanMountainDesktop.Services;
|
2026-03-04 02:02:34 +08:00
|
|
|
|
|
|
|
|
public sealed record DailyArtworkQuery(
|
|
|
|
|
string? Locale = null,
|
2026-03-05 12:34:39 +08:00
|
|
|
string? MirrorSource = null,
|
2026-03-04 02:02:34 +08:00
|
|
|
bool ForceRefresh = false);
|
|
|
|
|
|
|
|
|
|
public sealed record DailyPoetryQuery(
|
|
|
|
|
string? Locale = null,
|
|
|
|
|
bool ForceRefresh = false);
|
|
|
|
|
|
2026-03-05 18:46:32 +08:00
|
|
|
public sealed record DailyNewsQuery(
|
|
|
|
|
string? Locale = null,
|
2026-03-05 20:17:28 +08:00
|
|
|
int? ItemCount = null,
|
2026-03-05 18:46:32 +08:00
|
|
|
bool ForceRefresh = false);
|
|
|
|
|
|
2026-03-06 22:24:59 +08:00
|
|
|
public sealed record IfengNewsQuery(
|
|
|
|
|
string? Locale = null,
|
|
|
|
|
int? ItemCount = null,
|
|
|
|
|
string? ChannelType = null,
|
|
|
|
|
bool ForceRefresh = false);
|
|
|
|
|
|
2026-03-06 00:29:40 +08:00
|
|
|
public sealed record BilibiliHotSearchQuery(
|
|
|
|
|
string? Locale = null,
|
|
|
|
|
int? ItemCount = null,
|
|
|
|
|
bool ForceRefresh = false);
|
|
|
|
|
|
2026-03-06 22:24:59 +08:00
|
|
|
public sealed record BaiduHotSearchQuery(
|
|
|
|
|
string? Locale = null,
|
|
|
|
|
int? ItemCount = null,
|
|
|
|
|
string? SourceType = null,
|
|
|
|
|
bool ForceRefresh = false);
|
|
|
|
|
|
2026-03-05 18:46:32 +08:00
|
|
|
public sealed record DailyWordQuery(
|
|
|
|
|
string? Locale = null,
|
|
|
|
|
bool ForceRefresh = false);
|
|
|
|
|
|
2026-03-06 08:53:45 +08:00
|
|
|
public sealed record Stcn24ForumPostsQuery(
|
|
|
|
|
string? Locale = null,
|
|
|
|
|
int? ItemCount = null,
|
2026-03-06 10:32:02 +08:00
|
|
|
string? SourceType = null,
|
2026-03-06 08:53:45 +08:00
|
|
|
bool ForceRefresh = false);
|
|
|
|
|
|
2026-03-06 00:29:40 +08:00
|
|
|
public sealed record ExchangeRateQuery(
|
|
|
|
|
string? BaseCurrency = null,
|
|
|
|
|
string? TargetCurrency = null,
|
|
|
|
|
bool ForceRefresh = false);
|
|
|
|
|
|
2026-03-04 02:02:34 +08:00
|
|
|
public sealed record RecommendationQueryResult<T>(
|
|
|
|
|
bool Success,
|
|
|
|
|
T? Data,
|
|
|
|
|
string? ErrorCode = null,
|
|
|
|
|
string? ErrorMessage = null)
|
|
|
|
|
{
|
|
|
|
|
public static RecommendationQueryResult<T> Ok(T data)
|
|
|
|
|
{
|
|
|
|
|
return new RecommendationQueryResult<T>(true, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static RecommendationQueryResult<T> Fail(string errorCode, string errorMessage)
|
|
|
|
|
{
|
|
|
|
|
return new RecommendationQueryResult<T>(false, default, errorCode, errorMessage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-04 16:43:10 +08:00
|
|
|
public sealed record RecommendationApiOptions
|
2026-03-04 02:02:34 +08:00
|
|
|
{
|
|
|
|
|
public string JinriShiciPoetryUrl { get; init; } = "https://v1.jinrishici.com/all.json";
|
|
|
|
|
|
|
|
|
|
public string ArtInstituteArtworkApiTemplate { get; init; } =
|
2026-03-05 12:34:39 +08:00
|
|
|
"https://api.artic.edu/api/v1/artworks?page={0}&limit={1}&fields=id,title,artist_title,artist_display,date_display,image_id,api_link,thumbnail";
|
2026-03-04 02:02:34 +08:00
|
|
|
|
|
|
|
|
public string ArtInstituteImageUrlTemplate { get; init; } =
|
|
|
|
|
"https://www.artic.edu/iiif/2/{0}/full/843,/0/default.jpg";
|
|
|
|
|
|
2026-03-05 12:34:39 +08:00
|
|
|
public string DomesticArtworkApiUrl { get; init; } =
|
|
|
|
|
"https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8&mkt=zh-CN";
|
|
|
|
|
|
|
|
|
|
public string DomesticArtworkHost { get; init; } = "https://cn.bing.com";
|
|
|
|
|
|
2026-03-05 18:46:32 +08:00
|
|
|
public string CnrDailyNewsListUrl { get; init; } = "https://www.cnr.cn/newscenter/native/gd/";
|
|
|
|
|
|
|
|
|
|
public IReadOnlyList<string> CnrDailyNewsRssFeedUrls { get; init; } =
|
|
|
|
|
[
|
|
|
|
|
"https://www.cnr.cn/rss.xml",
|
|
|
|
|
"https://news.cnr.cn/rss.xml",
|
|
|
|
|
"https://www.cnr.cn/newscenter/native/gd/rss.xml",
|
|
|
|
|
"https://news.cnr.cn/native/gd/rss.xml"
|
|
|
|
|
];
|
|
|
|
|
|
2026-03-06 22:24:59 +08:00
|
|
|
public IReadOnlyList<string> IfengNewsComprehensiveRssFeedUrls { get; init; } =
|
|
|
|
|
[
|
|
|
|
|
"https://rss.injahow.cn/ifeng/news",
|
|
|
|
|
"https://rsshub.shuaizheng.org/ifeng/news"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public IReadOnlyList<string> IfengNewsMainlandRssFeedUrls { get; init; } =
|
|
|
|
|
[
|
|
|
|
|
"https://rss.injahow.cn/ifeng/news/shanklist/3-35197-/",
|
|
|
|
|
"https://rsshub.shuaizheng.org/ifeng/news/shanklist/3-35197-/"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public IReadOnlyList<string> IfengNewsTaiwanRssFeedUrls { get; init; } =
|
|
|
|
|
[
|
|
|
|
|
"https://rss.injahow.cn/ifeng/news/shanklist/3-35199-/",
|
|
|
|
|
"https://rsshub.shuaizheng.org/ifeng/news/shanklist/3-35199-/"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public string IfengNewsComprehensiveListPageUrl { get; init; } = "https://news.ifeng.com/";
|
|
|
|
|
|
|
|
|
|
public string IfengNewsMainlandListPageUrl { get; init; } = "https://news.ifeng.com/shanklist/3-35197-/";
|
|
|
|
|
|
|
|
|
|
public string IfengNewsTaiwanListPageUrl { get; init; } = "https://news.ifeng.com/shanklist/3-35199-/";
|
|
|
|
|
|
2026-03-06 00:29:40 +08:00
|
|
|
public string BilibiliHotSearchApiTemplate { get; init; } =
|
|
|
|
|
"https://api.bilibili.com/x/web-interface/search/square?limit={0}";
|
|
|
|
|
|
|
|
|
|
public string BilibiliSearchDefaultApiUrl { get; init; } =
|
|
|
|
|
"https://api.bilibili.com/x/web-interface/search/default";
|
|
|
|
|
|
|
|
|
|
public string BilibiliSearchPageUrl { get; init; } = "https://search.bilibili.com/all";
|
|
|
|
|
|
2026-03-06 22:24:59 +08:00
|
|
|
public string BaiduHotSearchRssFeedUrl { get; init; } = "https://rss.aishort.top/?type=baidu";
|
|
|
|
|
|
|
|
|
|
public string BaiduHotSearchBoardUrl { get; init; } = "https://top.baidu.com/board?tab=realtime";
|
|
|
|
|
|
2026-03-06 08:53:45 +08:00
|
|
|
public string SmartTeachForumApiTemplate { get; init; } =
|
|
|
|
|
"https://forum.smart-teach.cn/api/discussions?filter[q]={0}&sort=-createdAt&page[limit]={1}&include=user";
|
|
|
|
|
|
|
|
|
|
public string SmartTeachForumBaseUrl { get; init; } = "https://forum.smart-teach.cn";
|
|
|
|
|
|
|
|
|
|
public string SmartTeachStcnKeyword { get; init; } = "STCN";
|
|
|
|
|
|
2026-03-05 18:46:32 +08:00
|
|
|
public string YoudaoDictionaryApiTemplate { get; init; } = "https://dict.youdao.com/jsonapi?q={0}";
|
|
|
|
|
|
|
|
|
|
public string YoudaoDictionaryWordPageTemplate { get; init; } = "https://dict.youdao.com/w/eng/{0}/";
|
|
|
|
|
|
2026-03-06 00:29:40 +08:00
|
|
|
public string ExchangeRateApiTemplate { get; init; } = "https://open.er-api.com/v6/latest/{0}";
|
|
|
|
|
|
2026-03-05 18:46:32 +08:00
|
|
|
public IReadOnlyList<string> YoudaoDailyWordCandidates { get; init; } =
|
|
|
|
|
[
|
|
|
|
|
"illustrate",
|
|
|
|
|
"resilient",
|
|
|
|
|
"meticulous",
|
|
|
|
|
"coherent",
|
|
|
|
|
"subtle",
|
|
|
|
|
"constrain",
|
|
|
|
|
"tangible",
|
|
|
|
|
"versatile",
|
|
|
|
|
"pragmatic",
|
|
|
|
|
"derive",
|
|
|
|
|
"intricate",
|
|
|
|
|
"notion",
|
|
|
|
|
"facilitate",
|
|
|
|
|
"sustain",
|
|
|
|
|
"clarify",
|
|
|
|
|
"convey",
|
|
|
|
|
"nuance",
|
|
|
|
|
"transform",
|
|
|
|
|
"navigate",
|
|
|
|
|
"align",
|
|
|
|
|
"elevate",
|
|
|
|
|
"refine",
|
|
|
|
|
"vivid",
|
|
|
|
|
"compile",
|
|
|
|
|
"inspect",
|
|
|
|
|
"aggregate",
|
|
|
|
|
"optimize",
|
|
|
|
|
"resonate",
|
|
|
|
|
"persist",
|
|
|
|
|
"adapt",
|
|
|
|
|
"emerge",
|
|
|
|
|
"concrete",
|
|
|
|
|
"articulate",
|
|
|
|
|
"validate",
|
|
|
|
|
"insight",
|
|
|
|
|
"concise",
|
|
|
|
|
"robust",
|
|
|
|
|
"reliable",
|
|
|
|
|
"spectrum",
|
|
|
|
|
"landscape",
|
|
|
|
|
"context",
|
|
|
|
|
"constraint",
|
|
|
|
|
"iterative",
|
|
|
|
|
"foundation",
|
|
|
|
|
"priority",
|
|
|
|
|
"workflow",
|
|
|
|
|
"synthesize",
|
|
|
|
|
"anchor",
|
|
|
|
|
"precision",
|
|
|
|
|
"momentum",
|
|
|
|
|
"integrate",
|
|
|
|
|
"observe",
|
|
|
|
|
"structure",
|
|
|
|
|
"essence",
|
|
|
|
|
"framework",
|
|
|
|
|
"drift",
|
|
|
|
|
"discern",
|
|
|
|
|
"compose",
|
|
|
|
|
"modulate",
|
|
|
|
|
"stability",
|
|
|
|
|
"trajectory",
|
|
|
|
|
"analyze",
|
|
|
|
|
"diagnose",
|
|
|
|
|
"mitigate",
|
|
|
|
|
"transparent",
|
|
|
|
|
"progressive",
|
|
|
|
|
"boundary",
|
|
|
|
|
"allocate",
|
|
|
|
|
"evaluate",
|
|
|
|
|
"reconcile",
|
|
|
|
|
"strategic",
|
|
|
|
|
"holistic",
|
|
|
|
|
"incremental",
|
|
|
|
|
"temporal",
|
|
|
|
|
"semantic",
|
|
|
|
|
"parallel",
|
|
|
|
|
"explicit",
|
|
|
|
|
"objective",
|
|
|
|
|
"capacity",
|
|
|
|
|
"durable",
|
|
|
|
|
"scalable",
|
|
|
|
|
"residual",
|
|
|
|
|
"verify",
|
|
|
|
|
"discover",
|
|
|
|
|
"curate",
|
|
|
|
|
"invoke",
|
|
|
|
|
"artistry",
|
|
|
|
|
"sincere",
|
|
|
|
|
"substantive",
|
|
|
|
|
"deliberate",
|
|
|
|
|
"dynamic",
|
|
|
|
|
"intentional",
|
|
|
|
|
"initiative",
|
|
|
|
|
"evidence",
|
|
|
|
|
"infuse",
|
|
|
|
|
"harmony",
|
|
|
|
|
"vitality",
|
|
|
|
|
"polish",
|
|
|
|
|
"portrait",
|
|
|
|
|
"rhythm",
|
|
|
|
|
"accent",
|
|
|
|
|
"gradient",
|
|
|
|
|
"palette",
|
|
|
|
|
"pattern",
|
|
|
|
|
"eclipse",
|
|
|
|
|
"horizon",
|
|
|
|
|
"luminous",
|
|
|
|
|
"serene",
|
|
|
|
|
"vantage",
|
|
|
|
|
"kinetic",
|
|
|
|
|
"refactor",
|
|
|
|
|
"calibrate",
|
|
|
|
|
"orchestrate",
|
|
|
|
|
"prototype",
|
|
|
|
|
"curiosity",
|
|
|
|
|
"discipline",
|
|
|
|
|
"inscribe",
|
|
|
|
|
"engage",
|
|
|
|
|
"spark",
|
|
|
|
|
"zenith",
|
|
|
|
|
"clarity",
|
|
|
|
|
"resolve",
|
|
|
|
|
"aptitude"
|
|
|
|
|
];
|
|
|
|
|
|
2026-03-04 02:02:34 +08:00
|
|
|
public TimeSpan CacheDuration { get; init; } = TimeSpan.FromMinutes(20);
|
|
|
|
|
|
|
|
|
|
public TimeSpan RequestTimeout { get; init; } = TimeSpan.FromSeconds(8);
|
|
|
|
|
|
|
|
|
|
public int DefaultArtworkCandidateCount { get; init; } = 50;
|
2026-03-05 18:46:32 +08:00
|
|
|
|
|
|
|
|
public int DefaultDailyNewsCount { get; init; } = 2;
|
2026-03-06 00:29:40 +08:00
|
|
|
|
2026-03-06 22:24:59 +08:00
|
|
|
public int DefaultIfengNewsCount { get; init; } = 4;
|
|
|
|
|
|
2026-03-06 00:29:40 +08:00
|
|
|
public int DefaultBilibiliHotSearchCount { get; init; } = 5;
|
2026-03-06 08:53:45 +08:00
|
|
|
|
2026-03-06 22:24:59 +08:00
|
|
|
public int DefaultBaiduHotSearchCount { get; init; } = 4;
|
|
|
|
|
|
2026-03-06 08:53:45 +08:00
|
|
|
public int DefaultStcn24ForumPostCount { get; init; } = 4;
|
2026-03-04 02:02:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface IRecommendationInfoService
|
|
|
|
|
{
|
|
|
|
|
Task<RecommendationQueryResult<DailyArtworkSnapshot>> GetDailyArtworkAsync(
|
|
|
|
|
DailyArtworkQuery query,
|
|
|
|
|
CancellationToken cancellationToken = default);
|
|
|
|
|
|
|
|
|
|
Task<RecommendationQueryResult<DailyPoetrySnapshot>> GetDailyPoetryAsync(
|
|
|
|
|
DailyPoetryQuery query,
|
|
|
|
|
CancellationToken cancellationToken = default);
|
|
|
|
|
|
2026-03-05 18:46:32 +08:00
|
|
|
Task<RecommendationQueryResult<DailyNewsSnapshot>> GetDailyNewsAsync(
|
|
|
|
|
DailyNewsQuery query,
|
|
|
|
|
CancellationToken cancellationToken = default);
|
|
|
|
|
|
2026-03-06 22:24:59 +08:00
|
|
|
Task<RecommendationQueryResult<DailyNewsSnapshot>> GetIfengNewsAsync(
|
|
|
|
|
IfengNewsQuery query,
|
|
|
|
|
CancellationToken cancellationToken = default);
|
|
|
|
|
|
2026-03-06 00:29:40 +08:00
|
|
|
Task<RecommendationQueryResult<BilibiliHotSearchSnapshot>> GetBilibiliHotSearchAsync(
|
|
|
|
|
BilibiliHotSearchQuery query,
|
|
|
|
|
CancellationToken cancellationToken = default);
|
|
|
|
|
|
2026-03-06 22:24:59 +08:00
|
|
|
Task<RecommendationQueryResult<BaiduHotSearchSnapshot>> GetBaiduHotSearchAsync(
|
|
|
|
|
BaiduHotSearchQuery query,
|
|
|
|
|
CancellationToken cancellationToken = default);
|
|
|
|
|
|
2026-03-05 18:46:32 +08:00
|
|
|
Task<RecommendationQueryResult<DailyWordSnapshot>> GetDailyWordAsync(
|
|
|
|
|
DailyWordQuery query,
|
|
|
|
|
CancellationToken cancellationToken = default);
|
|
|
|
|
|
2026-03-06 08:53:45 +08:00
|
|
|
Task<RecommendationQueryResult<Stcn24ForumPostsSnapshot>> GetStcn24ForumPostsAsync(
|
|
|
|
|
Stcn24ForumPostsQuery query,
|
|
|
|
|
CancellationToken cancellationToken = default);
|
|
|
|
|
|
2026-03-06 00:29:40 +08:00
|
|
|
Task<RecommendationQueryResult<ExchangeRateSnapshot>> GetExchangeRateAsync(
|
|
|
|
|
ExchangeRateQuery query,
|
|
|
|
|
CancellationToken cancellationToken = default);
|
|
|
|
|
|
2026-03-04 02:02:34 +08:00
|
|
|
void ClearCache();
|
|
|
|
|
}
|