新增rss功能
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
string Script,
|
||||
int PageSizeOfHomepage,
|
||||
int PageSizeOfListpage,
|
||||
int RssGenerationIntervalHours,
|
||||
string ArticleRoute,
|
||||
string PageRoute,
|
||||
string CategoryRoute,
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
|
||||
namespace Yes.Application.Blogs
|
||||
{
|
||||
|
||||
public record GetRssQuery() : IRequest<GetRssQueryResponse>;
|
||||
|
||||
public record GetRssQueryResponse(string Xml);
|
||||
|
||||
public class GetRssQueryHandler(
|
||||
IOptionsMonitor<BlogSettings> options,
|
||||
BlogDbContext db,
|
||||
IMapper mapper) : IRequestHandler<GetRssQuery, GetRssQueryResponse>
|
||||
{
|
||||
private readonly BlogSettings _settings = options.CurrentValue;
|
||||
private readonly BlogDbContext _db = db;
|
||||
public async Task<GetRssQueryResponse> Handle(GetRssQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var queryArticle = _db.Articles.Where(x => x.Type == ArticleTypeEnum.Article && x.Status == ArticleStatusEnum.Normal).AsQueryable();
|
||||
var pubDate = queryArticle.Any() ? _db.Articles.Min(x => x.PublishDate) : DateTime.MinValue;
|
||||
|
||||
var channel = new Channel
|
||||
{
|
||||
Title = _settings.Name,
|
||||
Link = _settings.Url,
|
||||
Description = _settings.Description,
|
||||
Language = "zh-CN",
|
||||
FeedUrl = $"{_settings.Url}/feed/",
|
||||
PubDate = pubDate
|
||||
};
|
||||
|
||||
// 添加扩展元素(示例)
|
||||
channel.Extensions.Add(new XElement("generator", _settings.Name));
|
||||
channel.Extensions.Add(new XElement("docs", "http://www.rssboard.org/rss-specification"));
|
||||
|
||||
var query = from article in queryArticle
|
||||
join categoryTemp in _db.Categories.AsNoTracking() on article.CategoryId equals categoryTemp.Id into tempCategory
|
||||
from category in tempCategory.DefaultIfEmpty()
|
||||
join userTemp in _db.Users.AsNoTracking() on article.UserId equals userTemp.Id into tempUser
|
||||
from user in tempUser.DefaultIfEmpty()
|
||||
orderby article.Id descending
|
||||
select new ArticleModel
|
||||
{
|
||||
Id = article.Id,
|
||||
CanComment = article.CanComment,
|
||||
CommentCount = article.CommentCount,
|
||||
CategoryId = article.CategoryId,
|
||||
ModifyDate = article.ModifyDate.ToSafeTime(),
|
||||
PublishDate = article.PublishDate.ToSafeTime(),
|
||||
CreateDate = article.CreateDate.ToSafeTime(),
|
||||
Content = article.Content,
|
||||
CoverUrl = article.CoverUrl,
|
||||
Slug = article.Slug,
|
||||
ReadCount = article.ReadCount,
|
||||
Title = article.Title,
|
||||
Summary = article.Summary,
|
||||
UserId = article.UserId,
|
||||
Category = new CategoryModel
|
||||
{
|
||||
CoverUrl = category.CoverUrl,
|
||||
Description = category.Description,
|
||||
Id = category.Id,
|
||||
Name = category.Name,
|
||||
Slug = category.Slug
|
||||
}
|
||||
};
|
||||
|
||||
var items = query.Select(x => new Item
|
||||
{
|
||||
Content = x.Content,
|
||||
Title = x.Title,
|
||||
Url = _settings.ArticleRoute.ToRoute(x.Category, x),
|
||||
Description = x.Summary,
|
||||
CommentsCount = x.CommentCount,
|
||||
Creator = x.UserId.ToString(),
|
||||
PublishDate = x.PublishDate,
|
||||
CommentsUrl = $"{_settings.ArticleRoute.ToRoute(x.Category, x)}#comments",
|
||||
CommentRssUrl = $"{_settings.Url}/feed/"
|
||||
});
|
||||
|
||||
|
||||
var generator = new RssGenerator();
|
||||
string xml = generator.GenerateRss(channel, items);
|
||||
|
||||
return new GetRssQueryResponse(xml);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
|
||||
namespace Yes.Application.Core.HostedServices
|
||||
{
|
||||
public class HangfireJobRegistrationService : BackgroundService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly BlogSettings _settings;
|
||||
|
||||
public HangfireJobRegistrationService(
|
||||
IServiceProvider serviceProvider,
|
||||
IOptionsMonitor<BlogSettings> options)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_settings = options.CurrentValue;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var recurringJobManager = scope.ServiceProvider.GetRequiredService<IRecurringJobManager>();
|
||||
|
||||
var intervalHours = _settings.RssGenerationIntervalHours;
|
||||
|
||||
//recurringJobManager.AddOrUpdate<RssGenerationJob>(
|
||||
// nameof(RssGenerationJob),
|
||||
// x => x.Execute(),
|
||||
// Cron.Hourly(intervalHours)
|
||||
//);
|
||||
|
||||
recurringJobManager.AddOrUpdate<RssGenerationJob>(
|
||||
nameof(RssGenerationJob),
|
||||
x => x.Execute(),
|
||||
Cron.Minutely
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Yes.Application/Core/HostedServices/RssGenerationJob.cs
Normal file
100
Yes.Application/Core/HostedServices/RssGenerationJob.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
namespace Yes.Application.Core.HostedServices
|
||||
{
|
||||
public class RssGenerationJob
|
||||
{
|
||||
private readonly BlogSettings _settings;
|
||||
private readonly BlogDbContext _db;
|
||||
private readonly ILogger<RssGenerationJob> _logger;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
public RssGenerationJob(
|
||||
IOptionsMonitor<BlogSettings> options,
|
||||
BlogDbContext db,
|
||||
IWebHostEnvironment env,
|
||||
ILogger<RssGenerationJob> logger)
|
||||
{
|
||||
_settings = options.CurrentValue;
|
||||
_db = db;
|
||||
_env = env;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute()
|
||||
{
|
||||
try
|
||||
{
|
||||
var queryArticle = _db.Articles.Where(x => x.Type == ArticleTypeEnum.Article && x.Status == ArticleStatusEnum.Normal).AsQueryable();
|
||||
var pubDate = queryArticle.Any() ? _db.Articles.Min(x => x.PublishDate) : DateTime.MinValue;
|
||||
|
||||
var channel = new Channel
|
||||
{
|
||||
Title = _settings.Name,
|
||||
Link = _settings.Url,
|
||||
Description = _settings.Description,
|
||||
Language = "zh-CN",
|
||||
FeedUrl = $"{_settings.Url}/feed/",
|
||||
PubDate = pubDate
|
||||
};
|
||||
|
||||
// 添加扩展元素(示例)
|
||||
channel.Extensions.Add(new XElement("generator", _settings.Name));
|
||||
channel.Extensions.Add(new XElement("docs", "http://www.rssboard.org/rss-specification"));
|
||||
|
||||
var query = from article in queryArticle
|
||||
join categoryTemp in _db.Categories.AsNoTracking() on article.CategoryId equals categoryTemp.Id into tempCategory
|
||||
from category in tempCategory.DefaultIfEmpty()
|
||||
join userTemp in _db.Users.AsNoTracking() on article.UserId equals userTemp.Id into tempUser
|
||||
from user in tempUser.DefaultIfEmpty()
|
||||
orderby article.Id descending
|
||||
select new ArticleModel
|
||||
{
|
||||
Id = article.Id,
|
||||
CanComment = article.CanComment,
|
||||
CommentCount = article.CommentCount,
|
||||
CategoryId = article.CategoryId,
|
||||
ModifyDate = article.ModifyDate.ToSafeTime(),
|
||||
PublishDate = article.PublishDate.ToSafeTime(),
|
||||
CreateDate = article.CreateDate.ToSafeTime(),
|
||||
Content = article.Content,
|
||||
CoverUrl = article.CoverUrl,
|
||||
Slug = article.Slug,
|
||||
ReadCount = article.ReadCount,
|
||||
Title = article.Title,
|
||||
Summary = article.Summary,
|
||||
UserId = article.UserId,
|
||||
Category = new CategoryModel
|
||||
{
|
||||
CoverUrl = category.CoverUrl,
|
||||
Description = category.Description,
|
||||
Id = category.Id,
|
||||
Name = category.Name,
|
||||
Slug = category.Slug
|
||||
}
|
||||
};
|
||||
|
||||
var items = query.Select(x => new Item
|
||||
{
|
||||
Content = x.Content,
|
||||
Title = x.Title,
|
||||
Url = $"{_settings.Url}/{_settings.ArticleRoute.ToRoute(x.Category, x)}",
|
||||
Description = x.Summary,
|
||||
CommentsCount = x.CommentCount,
|
||||
Creator = x.UserId.ToString(),
|
||||
PublishDate = x.PublishDate,
|
||||
CommentsUrl = $"{_settings.Url}/{_settings.ArticleRoute.ToRoute(x.Category, x)}#comments",
|
||||
CommentRssUrl = $"{_settings.Url}/feed/"
|
||||
});
|
||||
|
||||
|
||||
var generator = new RssGenerator();
|
||||
string xml = generator.GenerateRss(channel, items);
|
||||
|
||||
|
||||
File.WriteAllText(Path.Combine(_env.WebRootPath, "feed.xml"), xml);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "RSS generation failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,5 +39,6 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,3 +40,8 @@ global using System.Text;
|
||||
global using Yes.Infrastructure.Authorizations.Identity.Authroizations;
|
||||
global using System.Xml.Linq;
|
||||
global using Yes.Infrastructure.Rss;
|
||||
|
||||
global using Microsoft.Extensions.Configuration;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
global using Hangfire;
|
||||
global using Microsoft.Extensions.Hosting;
|
||||
@@ -11,6 +11,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.18" />
|
||||
<PackageReference Include="Hangfire.Core" Version="1.8.18" />
|
||||
<PackageReference Include="Hangfire.MySqlStorage" Version="2.0.3" />
|
||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.20.12" />
|
||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.18" />
|
||||
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Qiniu" Version="8.7.0" />
|
||||
|
||||
Reference in New Issue
Block a user