前端支持cms

This commit is contained in:
biss committed 2026-08-29 16:16:26 +08:00
1 parent 6b0c4d054d
commit f56a63d4ce
16 files changed
+439 -10

No files matched your search

@@ -0,0 +1,93 @@
using System.Net;
using System.Net.Http.Json;
namespace ElectionSystem.Api.Modules.Cms;
/// <summary>
/// Uses the CMS's deliberately small, read-only public contract. It never connects to the CMS database.
/// </summary>
public sealed class PublicContentClient(HttpClient httpClient, ILogger<PublicContentClient> logger)
{
public async Task<PublicContentFetch<IReadOnlyList<PublicContentSummary>>> ListAsync(
string? contentType,
int take,
CancellationToken cancellationToken)
{
var requestUri = contentType is null
? $"api/public-content?take={take}"
: $"api/public-content?contentType={Uri.EscapeDataString(contentType)}&take={take}";
try
{
using var response = await httpClient.GetAsync(requestUri, cancellationToken);
if (response.StatusCode == HttpStatusCode.NotFound)
{
return PublicContentFetch<IReadOnlyList<PublicContentSummary>>.Unavailable;
}
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadFromJsonAsync<List<PublicContentSummary>>(cancellationToken);
return new PublicContentFetch<IReadOnlyList<PublicContentSummary>>(content ?? [], true);
}
catch (HttpRequestException exception)
{
logger.LogWarning(exception, "The CMS public-content endpoint is unavailable.");
return PublicContentFetch<IReadOnlyList<PublicContentSummary>>.Unavailable;
}
catch (TaskCanceledException) when (!cancellationToken.IsCancellationRequested)
{
logger.LogWarning("The CMS public-content endpoint timed out.");
return PublicContentFetch<IReadOnlyList<PublicContentSummary>>.Unavailable;
}
}
public async Task<PublicContentFetch<PublicContentDetail?>> GetAsync(string contentItemId, CancellationToken cancellationToken)
{
try
{
using var response = await httpClient.GetAsync($"api/public-content/{Uri.EscapeDataString(contentItemId)}", cancellationToken);
if (response.StatusCode == HttpStatusCode.NotFound)
{
return new PublicContentFetch<PublicContentDetail?>(null, true);
}
response.EnsureSuccessStatusCode();
return new PublicContentFetch<PublicContentDetail?>(
await response.Content.ReadFromJsonAsync<PublicContentDetail>(cancellationToken),
true);
}
catch (HttpRequestException exception)
{
logger.LogWarning(exception, "The CMS public-content endpoint is unavailable.");
return PublicContentFetch<PublicContentDetail?>.Unavailable;
}
catch (TaskCanceledException) when (!cancellationToken.IsCancellationRequested)
{
logger.LogWarning("The CMS public-content endpoint timed out.");
return PublicContentFetch<PublicContentDetail?>.Unavailable;
}
}
}
public sealed record PublicContentFetch<T>(T? Value, bool IsAvailable)
{
public static PublicContentFetch<T> Unavailable { get; } = new(default, false);
}
public sealed record PublicContentSummary(
string ContentItemId,
string ContentType,
string Title,
string? RoutePath,
DateTime? PublishedUtc);
public sealed record PublicContentDetail(
string ContentItemId,
string ContentType,
string Title,
string? RoutePath,
string Html,
DateTime? PublishedUtc,
string? ElectionReference,
string? EffectiveFromUtc,
string? EffectiveToUtc);
@@ -0,0 +1,34 @@
@page "/content/item/{contentItemId}"
@model ElectionSystem.Api.Pages.Content.DetailModel
@{
ViewData["Title"] = Model.Item?.Title ?? "公开内容";
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - 选举与投票信息系统</title>
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header class="site-header"><a class="brand" href="/">选举与投票信息系统</a><nav aria-label="主导航"><a href="/content">信息公开</a></nav></header>
<main class="container article">
@if (!Model.CmsAvailable)
{
<p class="status-note">信息公开服务暂不可用,请稍后刷新页面。</p>
}
else if (Model.Item is { } item)
{
<p class="eyebrow">@item.ContentType</p>
<h1>@item.Title</h1>
@if (item.PublishedUtc is { } publishedUtc)
{
<p class="published-at">发布时间:<time datetime="@publishedUtc.ToString("O")">@publishedUtc.ToLocalTime().ToString("yyyy-MM-dd HH:mm")</time></p>
}
<div class="cms-body">@Html.Raw(item.Html)</div>
}
</main>
<footer class="site-footer">所有时间和结果以活动正式公告为准。</footer>
</body>
</html>
@@ -0,0 +1,19 @@
using ElectionSystem.Api.Modules.Cms;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace ElectionSystem.Api.Pages.Content;
public sealed class DetailModel(PublicContentClient publicContentClient) : PageModel
{
public PublicContentDetail? Item { get; private set; }
public bool CmsAvailable { get; private set; }
public async Task<IActionResult> OnGetAsync(string contentItemId, CancellationToken cancellationToken)
{
var result = await publicContentClient.GetAsync(contentItemId, cancellationToken);
CmsAvailable = result.IsAvailable;
Item = result.Value;
return !CmsAvailable ? Page() : Item is null ? NotFound() : Page();
}
}
Loaded 3 of 16 files, more files were not shown because too many files have changed in this diff. Show more