Files
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

115 lines
3.1 KiB
C#

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PCL.Core.App;
using PCL.Core.Logging;
using PCL.Core.Model.Tools.News;
using System;
using System.Collections.ObjectModel;
using System.Net;
using System.Threading.Tasks;
using PCL.Core.IO.Net.Http;
namespace PCL.Core.ViewModel.Homepage;
public partial class NewsViewModel : ObservableObject
{
private const string BaseApiUrl = "https://net-secondary.web.minecraft-services.net/api/v1.0/zh-cn/search";
private const int PageSize = 24;
private static readonly string[] AllowedNewsLinkHosts =
[
"minecraft.net",
"minecraft-services.net",
"microsoft.com"
];
private int _currentPage = 1;
public ObservableCollection<NewsItem> NewsItems { get; } = new();
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private string? _errorMessage;
public NewsViewModel()
{
_LoadDataCommand.Execute(null);
}
[RelayCommand]
#pragma warning disable IDE1006 // 命名样式
private async Task _LoadDataAsync()
#pragma warning restore IDE1006 // 命名样式
{
if (IsLoading) return;
IsLoading = true;
try
{
var url = $"{BaseApiUrl}?pageSize={PageSize}&sortType=Recent&category=News&newsOnly=true&page={_currentPage}";
using var resp = await HttpRequest
.Create(url)
.SendAsync();
resp.EnsureSuccessStatusCode();
var json = await resp.AsJsonAsync<ApiResponse>();
if (json?.Result?.Results is not null)
{
foreach (var item in json.Result.Results)
{
item.Description = WebUtility.HtmlDecode(item.Description);
NewsItems.Add(item);
}
}
}
catch (Exception ex)
{
ErrorMessage = $"加载错误: {ex.Message}";
LogWrapper.Error(ex, "Minecrft 信息流主页加载失败");
}
finally
{
IsLoading = false;
}
}
[RelayCommand]
#pragma warning disable IDE1006 // 命名样式
private void _OpenRead(string? url)
#pragma warning restore IDE1006 // 命名样式
{
if (!IsSafeNewsLink(url))
{
LogWrapper.Warn("Homepage", $"已拦截不安全的 Minecraft 信息流链接:{url ?? "<null>"}");
return;
}
Basics.OpenPath(url!);
}
internal static bool IsSafeNewsLink(string? url)
{
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) return false;
if (!uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) &&
!uri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase))
{
return false;
}
var host = uri.IdnHost;
foreach (var allowedHost in AllowedNewsLinkHosts)
{
if (host.Equals(allowedHost, StringComparison.OrdinalIgnoreCase) ||
host.EndsWith($".{allowedHost}", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
}