16 Commits

Author SHA1 Message Date
9061092b64 feat: basic render. 2026-07-21 23:07:55 +08:00
8964e760ee feat: FFI rust library.
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-07-21 21:12:09 +08:00
7faca1af6d fix: update system-text-json blog.
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 31s
Upgrade xunit to v3.

Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-07-20 20:41:23 +08:00
3f1711e789 blog: publish 2025-final
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 3m38s
2026-05-08 01:23:40 +08:00
455b716b15 blog: 2025-final (#23)
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 5m28s
Reviewed-on: #23
2026-05-07 02:04:01 +00:00
0c0e721e8f feat: update system-text-json
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 1m4s
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-04-03 17:16:52 +08:00
dfb66b4301 refact: introduce YaeBlog.Abstractions layer.
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 1m0s
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-29 16:14:47 +08:00
05f99f4b79 feat: Use seperate application to parse and run command line.
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-29 16:10:59 +08:00
5fef951d36 feat: publish blog tarjan-bridge.
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 1m20s
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-28 21:59:54 +08:00
c9f6d638b9 fix: set gitea api key header correctly.
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 1m4s
Rename Foonter to Footer.
Fix the heatmap not rendering until today bug.

Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-18 16:33:16 +08:00
6733bbbd2a fix: build action.
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 53s
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-14 21:17:37 +08:00
e10c8e7e75 fix: build action.
Some checks failed
Build blog docker image / Build-Blog-Image (push) Failing after 45s
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-14 21:15:33 +08:00
45f15c9bd9 fix: build action.
Some checks failed
Build blog docker image / Build-Blog-Image (push) Has been cancelled
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-14 19:51:00 +08:00
a1b5af5b0c fix: build action.
Some checks failed
Build blog docker image / Build-Blog-Image (push) Failing after 2s
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-14 19:50:06 +08:00
d8e4931d63 refact: Let host to handle command arguments.
Some checks failed
Build blog docker image / Build-Blog-Image (push) Failing after 0s
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-14 19:48:44 +08:00
462fbb28ac feat: rewrite about page for 2026. (#21)
Some checks failed
Build blog docker image / Build-Blog-Image (push) Failing after 14s
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
Reviewed-on: #21
2026-03-03 09:09:49 +00:00
422 changed files with 5846 additions and 1015 deletions

View File

@@ -12,7 +12,7 @@ indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[project.json]
[{project.json,appsettings.json,appsettings.*.json}]
indent_size = 2
[*.{yaml,yml}]

View File

@@ -8,19 +8,19 @@ jobs:
runs-on: archlinux
steps:
- name: Check out code.
uses: https://mirrors.rrricardo.top/actions/checkout.git@v4
uses: http://github-mirrors.infra.svc.cluster.local/actions/checkout.git@v4
with:
lfs: true
- name: Build project.
run: |
git submodule update --init
podman pull mcr.azure.cn/dotnet/aspnet:10.0
cd YaeBlog
pwsh build.ps1 build
- name: Workaround to make sure podman-login working.
run: |
mkdir /root/.docker
mkdir -p /root/.docker
- name: Login tencent cloud docker registry.
uses: https://mirrors.rrricardo.top/actions/podman-login.git@v1
uses: http://github-mirrors.infra.svc.cluster.local/actions/podman-login.git@v1
with:
registry: ccr.ccs.tencentyun.com
username: 100044380877

1
.gitignore vendored
View File

@@ -184,6 +184,7 @@ DocProject/Help/html
# Click-Once directory
publish/
out/
# Publish Web Output
*.[Pp]ublish.xml

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "third-party/BlazorSvgComponents"]
path = third-party/BlazorSvgComponents
url = https://git.rrricardo.top/jackfiled/BlazorSvgComponents.git

View File

@@ -7,9 +7,19 @@
<File Path=".editorconfig" />
<File Path=".gitattributes" />
<File Path=".gitignore" />
<File Path="build.ps1" />
<File Path="LICENSE" />
<File Path="README.md" />
</Folder>
<Project Path="YaeBlog.Tests/YaeBlog.Tests.csproj" />
<Project Path="YaeBlog/YaeBlog.csproj" />
<Folder Name="/src/">
<Project Path="src/YaeBlog.Abstractions/YaeBlog.Abstractions.csproj" />
<Project Path="src/YaeBlog.Tests/YaeBlog.Tests.csproj" />
<Project Path="src/YaeBlog.Typst/YaeBlog.Typst.csproj" />
<Project Path="src/YaeBlog/YaeBlog.csproj" />
</Folder>
<Folder Name="/third-party/" />
<Folder Name="/third-party/BlazorSvgComponents/" />
<Folder Name="/third-party/BlazorSvgComponents/src/">
<Project Path="third-party/BlazorSvgComponents/src/BlazorSvgComponents/BlazorSvgComponents.csproj" />
</Folder>
</Solution>

View File

@@ -1,36 +0,0 @@
using System.CommandLine.Binding;
using System.Text.Json;
using Microsoft.Extensions.Options;
using YaeBlog.Models;
namespace YaeBlog.Commands.Binders;
public sealed class BlogOptionsBinder : BinderBase<IOptions<BlogOptions>>
{
protected override IOptions<BlogOptions> GetBoundValue(BindingContext bindingContext)
{
bindingContext.AddService<IOptions<BlogOptions>>(_ =>
{
FileInfo settings = new(Path.Combine(Environment.CurrentDirectory, "appsettings.json"));
if (!settings.Exists)
{
throw new InvalidOperationException("Failed to load YaeBlog configurations.");
}
using StreamReader reader = settings.OpenText();
using JsonDocument document = JsonDocument.Parse(reader.ReadToEnd());
JsonElement root = document.RootElement;
JsonElement optionSection = root.GetProperty(BlogOptions.OptionName);
BlogOptions? result = optionSection.Deserialize<BlogOptions>();
if (result is null)
{
throw new InvalidOperationException("Failed to load YaeBlog configuration in appsettings.json.");
}
return new OptionsWrapper<BlogOptions>(result);
});
return bindingContext.GetRequiredService<IOptions<BlogOptions>>();
}
}

View File

@@ -1,32 +0,0 @@
using System.CommandLine.Binding;
using Microsoft.Extensions.Options;
using YaeBlog.Abstraction;
using YaeBlog.Models;
using YaeBlog.Services;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace YaeBlog.Commands.Binders;
public sealed class EssayScanServiceBinder : BinderBase<IEssayScanService>
{
protected override IEssayScanService GetBoundValue(BindingContext bindingContext)
{
bindingContext.AddService<IEssayScanService>(provider =>
{
DeserializerBuilder deserializerBuilder = new();
deserializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance);
deserializerBuilder.IgnoreUnmatchedProperties();
SerializerBuilder serializerBuilder = new();
serializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance);
IOptions<BlogOptions> options = provider.GetRequiredService<IOptions<BlogOptions>>();
ILogger<EssayScanService> logger = provider.GetRequiredService<ILogger<EssayScanService>>();
return new EssayScanService(serializerBuilder.Build(), deserializerBuilder.Build(), options, logger);
});
return bindingContext.GetRequiredService<IEssayScanService>();
}
}

View File

@@ -1,21 +0,0 @@
using System.CommandLine.Binding;
using YaeBlog.Abstraction;
using YaeBlog.Services;
namespace YaeBlog.Commands.Binders;
public sealed class ImageCompressServiceBinder : BinderBase<ImageCompressService>
{
protected override ImageCompressService GetBoundValue(BindingContext bindingContext)
{
bindingContext.AddService(provider =>
{
IEssayScanService essayScanService = provider.GetRequiredService<IEssayScanService>();
ILogger<ImageCompressService> logger = provider.GetRequiredService<ILogger<ImageCompressService>>();
return new ImageCompressService(essayScanService, logger);
});
return bindingContext.GetRequiredService<ImageCompressService>();
}
}

View File

@@ -1,18 +0,0 @@
using System.CommandLine.Binding;
namespace YaeBlog.Commands.Binders;
public sealed class LoggerBinder<T> : BinderBase<ILogger<T>>
{
protected override ILogger<T> GetBoundValue(BindingContext bindingContext)
{
bindingContext.AddService(_ => LoggerFactory.Create(builder => builder.AddConsole()));
bindingContext.AddService<ILogger<T>>(provider =>
{
ILoggerFactory factory = provider.GetRequiredService<ILoggerFactory>();
return factory.CreateLogger<T>();
});
return bindingContext.GetRequiredService<ILogger<T>>();
}
}

View File

@@ -1,296 +0,0 @@
using System.CommandLine;
using Microsoft.Extensions.Options;
using YaeBlog.Abstraction;
using YaeBlog.Commands.Binders;
using YaeBlog.Components;
using YaeBlog.Extensions;
using YaeBlog.Models;
using YaeBlog.Services;
namespace YaeBlog.Commands;
public sealed class YaeBlogCommand
{
private readonly RootCommand _rootCommand = new("YaeBlog Cli");
public YaeBlogCommand()
{
AddServeCommand(_rootCommand);
AddWatchCommand(_rootCommand);
AddListCommand(_rootCommand);
AddNewCommand(_rootCommand);
AddUpdateCommand(_rootCommand);
AddPublishCommand(_rootCommand);
AddScanCommand(_rootCommand);
AddCompressCommand(_rootCommand);
}
public Task<int> RunAsync(string[] args)
{
return _rootCommand.InvokeAsync(args);
}
private static void AddServeCommand(RootCommand rootCommand)
{
Command serveCommand = new("serve", "Start http server.");
rootCommand.AddCommand(serveCommand);
serveCommand.SetHandler(async context =>
{
WebApplicationBuilder builder = WebApplication.CreateBuilder();
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddControllers();
builder.AddYaeBlog();
builder.AddServer();
WebApplication application = builder.Build();
application.MapStaticAssets();
application.UseAntiforgery();
application.UseYaeBlog();
application.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
application.MapControllers();
CancellationToken token = context.GetCancellationToken();
await application.RunAsync(token);
});
}
private static void AddWatchCommand(RootCommand rootCommand)
{
Command command = new("watch", "Start a blog watcher that re-render when file changes.");
rootCommand.AddCommand(command);
command.SetHandler(async context =>
{
WebApplicationBuilder builder = WebApplication.CreateBuilder();
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddControllers();
builder.AddYaeBlog();
builder.AddWatcher();
WebApplication application = builder.Build();
application.MapStaticAssets();
application.UseAntiforgery();
application.UseYaeBlog();
application.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
application.MapControllers();
CancellationToken token = context.GetCancellationToken();
await application.RunAsync(token);
});
}
private static void AddNewCommand(RootCommand rootCommand)
{
Command newCommand = new("new", "Create a new blog file and image directory.");
rootCommand.AddCommand(newCommand);
Argument<string> filenameArgument = new(name: "blog name", description: "The created blog filename.");
newCommand.AddArgument(filenameArgument);
newCommand.SetHandler(async (file, blogOption, _, essayScanService) =>
{
BlogContents contents = await essayScanService.ScanContents();
if (contents.Posts.Any(content => content.BlogName == file))
{
Console.WriteLine("There exists the same title blog in posts.");
return;
}
await essayScanService.SaveBlogContent(new BlogContent(
new FileInfo(Path.Combine(blogOption.Value.Root, "drafts", file + ".md")),
new MarkdownMetadata
{
Title = file,
Date = DateTimeOffset.Now.ToString("o"),
UpdateTime = DateTimeOffset.Now.ToString("o")
},
string.Empty, true, [], []));
Console.WriteLine($"Created new blog '{file}.");
}, filenameArgument, new BlogOptionsBinder(), new LoggerBinder<EssayScanService>(),
new EssayScanServiceBinder());
}
private static void AddUpdateCommand(RootCommand rootCommand)
{
Command newCommand = new("update", "Update the blog essay.");
rootCommand.AddCommand(newCommand);
Argument<string> filenameArgument = new(name: "blog name", description: "The blog filename to update.");
newCommand.AddArgument(filenameArgument);
newCommand.SetHandler(async (file, _, _, essayScanService) =>
{
Console.WriteLine("HINT: The update command only consider published blogs.");
BlogContents contents = await essayScanService.ScanContents();
BlogContent? content = contents.Posts.FirstOrDefault(c => c.BlogName == file);
if (content is null)
{
Console.WriteLine($"Target essay {file} is not exist.");
return;
}
content.Metadata.UpdateTime = DateTimeOffset.Now.ToString("o");
await essayScanService.SaveBlogContent(content, content.IsDraft);
}, filenameArgument,
new BlogOptionsBinder(), new LoggerBinder<EssayScanService>(), new EssayScanServiceBinder());
}
private static void AddListCommand(RootCommand rootCommand)
{
Command command = new("list", "List all blogs");
rootCommand.AddCommand(command);
command.SetHandler(async (_, _, essyScanService) =>
{
BlogContents contents = await essyScanService.ScanContents();
Console.WriteLine($"All {contents.Posts.Count} Posts:");
foreach (BlogContent content in contents.Posts.OrderBy(x => x.BlogName))
{
Console.WriteLine($" - {content.BlogName}");
}
Console.WriteLine($"All {contents.Drafts.Count} Drafts:");
foreach (BlogContent content in contents.Drafts.OrderBy(x => x.BlogName))
{
Console.WriteLine($" - {content.BlogName}");
}
}, new BlogOptionsBinder(), new LoggerBinder<EssayScanService>(), new EssayScanServiceBinder());
}
private static void AddScanCommand(RootCommand rootCommand)
{
Command command = new("scan", "Scan unused and not found images.");
rootCommand.AddCommand(command);
Option<bool> removeOption =
new(name: "--rm", description: "Remove unused images.", getDefaultValue: () => false);
command.AddOption(removeOption);
command.SetHandler(async (_, _, essayScanService, removeOptionValue) =>
{
BlogContents contents = await essayScanService.ScanContents();
List<BlogImageInfo> unusedImages = (from content in contents
from image in content.Images
where image is { IsUsed: false }
select image).ToList();
if (unusedImages.Count != 0)
{
Console.WriteLine("Found unused images:");
Console.WriteLine("HINT: use '--rm' to remove unused images.");
}
foreach (BlogImageInfo image in unusedImages)
{
Console.WriteLine($" - {image.File.FullName}");
}
if (removeOptionValue)
{
foreach (BlogImageInfo image in unusedImages)
{
image.File.Delete();
}
}
Console.WriteLine("Used not existed images:");
foreach (BlogContent content in contents)
{
foreach (FileInfo file in content.NotfoundImages)
{
Console.WriteLine($"- {file.Name} in {content.BlogName}");
}
}
}, new BlogOptionsBinder(), new LoggerBinder<EssayScanService>(), new EssayScanServiceBinder(), removeOption);
}
private static void AddPublishCommand(RootCommand rootCommand)
{
Command command = new("publish", "Publish a new blog file.");
rootCommand.AddCommand(command);
Argument<string> filenameArgument = new(name: "blog name", description: "The published blog filename.");
command.AddArgument(filenameArgument);
command.SetHandler(async (blogOptions, _, essayScanService, filename) =>
{
BlogContents contents = await essayScanService.ScanContents();
BlogContent? content = (from blog in contents.Drafts
where blog.BlogName == filename
select blog).FirstOrDefault();
if (content is null)
{
Console.WriteLine("Target blog does not exist.");
return;
}
// 设置发布的时间
content.Metadata.Date = DateTimeOffset.Now.ToString("o");
content.Metadata.UpdateTime = DateTimeOffset.Now.ToString("o");
// 将选中的博客文件复制到posts
await essayScanService.SaveBlogContent(content, isDraft: false);
// 复制图片文件夹
DirectoryInfo sourceImageDirectory =
new(Path.Combine(blogOptions.Value.Root, "drafts", content.BlogName));
DirectoryInfo targetImageDirectory =
new(Path.Combine(blogOptions.Value.Root, "posts", content.BlogName));
if (sourceImageDirectory.Exists)
{
targetImageDirectory.Create();
foreach (FileInfo file in sourceImageDirectory.EnumerateFiles())
{
file.CopyTo(Path.Combine(targetImageDirectory.FullName, file.Name), true);
}
sourceImageDirectory.Delete(true);
}
// 删除原始的文件
FileInfo sourceBlogFile = new(Path.Combine(blogOptions.Value.Root, "drafts", content.BlogName + ".md"));
sourceBlogFile.Delete();
}, new BlogOptionsBinder(),
new LoggerBinder<EssayScanService>(), new EssayScanServiceBinder(), filenameArgument);
}
private static void AddCompressCommand(RootCommand rootCommand)
{
Command command = new("compress", "Compress png/jpeg image to webp image to reduce size.");
rootCommand.Add(command);
Option<bool> dryRunOption = new("--dry-run", description: "Dry run the compression task but not write.",
getDefaultValue: () => false);
command.AddOption(dryRunOption);
command.SetHandler(ImageCommandHandler,
new BlogOptionsBinder(), new LoggerBinder<EssayScanService>(), new LoggerBinder<ImageCompressService>(),
new EssayScanServiceBinder(), new ImageCompressServiceBinder(), dryRunOption);
}
private static async Task ImageCommandHandler(IOptions<BlogOptions> _, ILogger<EssayScanService> _1,
ILogger<ImageCompressService> _2,
IEssayScanService _3, ImageCompressService imageCompressService, bool dryRun)
{
await imageCompressService.Compress(dryRun);
}
}

View File

@@ -1,30 +0,0 @@
<div class="flex flex-col text-center py-2">
<div>
<p class="text-md">
2021 - @(DateTimeOffset.Now.Year) ©
<Anchor Address="https://rrricardot.top" Text="Ricardo Ren"/>
,由
<Anchor Address="https://dotnet.microsoft.com" Text="@DotnetVersion"/>
驱动。
</p>
<p class="text-md">
Build Commit #
<Anchor Address="@BuildCommitUrl" Text="@BuildCommitId"/>
</p>
</div>
<div>
<p class="text-md">
<Anchor Address="https://beian.miit.gov.cn" Text="蜀ICP备2022004429号-1" NewPage="true"/>
</p>
</div>
</div>
@code
{
private string DotnetVersion => $".NET {Environment.Version}";
private string BuildCommitId => Environment.GetEnvironmentVariable("COMMIT_ID") ?? "local_build";
private string BuildCommitUrl => $"https://git.rrricardo.top/jackfiled/YaeBlog/commit/{BuildCommitId}";
}

View File

@@ -1,79 +0,0 @@
@page "/about"
<PageTitle>
关于
</PageTitle>
<div class="flex flex-col">
<div>
<h1 class="text-4xl">关于</h1>
</div>
<div class="py-4">
<span class="italic">把字刻在石头上!(・’ω’・)</span>
</div>
<div class="flex flex-col p-2">
<div class="flex flex-col p-2">
<div class="pb-2">
<h3 class="text-2xl">关于我</h3>
</div>
<div class="py-2">
计算机科学与技术在读大学生,明光村幼儿园附属大学所属。正处于读书和失业的叠加态。
一般在互联网上使用<span class="italic">初冬的朝阳</span>或者<span class="italic">jackfiled</span>的名字活动。
<span class="line-through">都是ICP备案过的人了网名似乎没有太大的用处</span>
</div>
<div class="py-2">
主要是一个C#程序员目前也在尝试写一点Rust。
总体上对于编程语言的态度是“<span>大家都是我的翅膀.jpg</span>”。
前后端分离的项目本当上手。
常常因为现实的压力而写一些C/C++。
<span class="line-through">对于Java和Go的评价很低。</span>
日常使用ArchLinux。
</div>
<div class="py-2">
100%社恐。日常生活是宅在电脑前面自言自语。
兴趣活动是读书和看番,目前在玩原神和三角洲。
</div>
<div class="py-4">
常常被人批评没有梦想,这里就随便瞎编一下。
成为嵌入式工程师,修好桌面上的<a href="https://www.bilibili.com/video/BV1VA411p7MD">HoloCubic</a>。
完成第一个不是课程设计的个人开源项目。
遇到能够搭伙过日子的人也算是一大梦想,虽然社恐人根本不知道从何开始的说,
<span class="line-through">什么时候天上才能掉美少女?</span>
</div>
<div class="py-2">
公开的联系渠道是<a href="mailto:shicangjuner@outlook.com">电子邮件</a>。
也可以试试在各大平台搜索上面提到的名字。
</div>
</div>
<div class="flex flex-col p-2">
<div class="pb-2">
<h3 class="text-2xl">关于本站</h3>
</div>
<div class="py-2">
本站肇始于2021年下半年在开始的两年中个人网站和博客是分别的两个网站个人网站是裸HTML写的博客是用
<a href="https://hexo.io">Hexo</a>渲染的。
</div>
<div class="py-2">
2024年我们决定使用.NET技术完全重构两个网站合二为一。虽然目前这个版本还是一个半成品但是我们一定会努力的~(确信。
</div>
<div class="py-2">
2025年我们将使用的样式库从Bootstrap迁移到Tailwind CSS将现代的前端技术同Blazor结合起来。
</div>
</div>
</div>
</div>
@code {
}

View File

@@ -1,59 +0,0 @@
@page "/"
<PageTitle>
Ricardo's Index
</PageTitle>
<div class="mx-20">
<div class="grid grid-cols-3 py-4">
<div class="col-span-3 md:col-span-1 p-5 p-lg-0">
<img src="@Assets["images/avatar.png"]" alt="Ricardo's Avatar" class="h-auto max-w-full">
</div>
<div class="col-span-3 md:col-span-2">
<div class="flex flex-col px-3 gap-y-3">
<div class="">
<div class="text-3xl font-bold">初冬的朝阳 (Ricardo Ren)</div>
</div>
<div class="">
<p class="text-lg">a.k.a jackfiled</p>
</div>
<div class="">
<p class="text-lg italic">世界很大,时间很长。</p>
</div>
<div class="">
<p class="text-lg">
学过一些基础的计算机知识,略懂一些代码。
</p>
</div>
</div>
</div>
</div>
<div class="py-5">
<p class="text-lg">恕我不能亲自为您沏茶(?),还是非常欢迎您能够来到我的主页。</p>
</div>
<div>
<p class="text-lg py-1">
如果您想四处看看,了解一下屏幕对面的人,可以在我的 <Anchor Address="/blog/" Text="博客"/> 看看。
如果您对于明光村幼儿园某附属技校的计算机教学感兴趣,您可以移步到
<Anchor Address="https://jackfiled.github.io/wiki/" Text="我的学习笔记"/>
<span class="fs-5 text-decoration-line-through">虽然这笔记我自己也木有看过。</span>
如果您想批判一下我的代码,在
<Anchor Address="https://github.com/jackfiled/" Text="Github"/> 和
<Anchor Address="https://git.rrricardo.top/jackfiled/" Text="Gitea"/>
都可以找到。
</p>
<p class="text-lg py-1">
如果您真的很闲,也可以四处搜寻一下,也许存在着一些不为人知的彩蛋。
</p>
</div>
</div>
@code {
}

View File

@@ -1,33 +0,0 @@
using Markdig;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace YaeBlog.Extensions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMarkdig(this IServiceCollection collection)
{
MarkdownPipelineBuilder builder = new();
builder.UseAdvancedExtensions();
collection.AddSingleton<MarkdownPipeline>(_ => builder.Build());
return collection;
}
public static IServiceCollection AddYamlParser(this IServiceCollection collection)
{
DeserializerBuilder deserializerBuilder = new();
deserializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance);
deserializerBuilder.IgnoreUnmatchedProperties();
collection.AddSingleton(deserializerBuilder.Build());
SerializerBuilder serializerBuilder = new();
serializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance);
collection.AddSingleton(serializerBuilder.Build());
return collection;
}
}

View File

@@ -1,47 +0,0 @@
using AngleSharp;
using Microsoft.Extensions.Options;
using YaeBlog.Abstraction;
using YaeBlog.Services;
using YaeBlog.Models;
using YaeBlog.Processors;
namespace YaeBlog.Extensions;
public static class WebApplicationBuilderExtensions
{
public static WebApplicationBuilder AddYaeBlog(this WebApplicationBuilder builder)
{
builder.Services.Configure<BlogOptions>(builder.Configuration.GetSection(BlogOptions.OptionName));
builder.Services.AddHttpClient();
builder.Services.AddMarkdig();
builder.Services.AddYamlParser();
builder.Services.AddSingleton<AngleSharp.IConfiguration>(_ => Configuration.Default);
builder.Services.AddSingleton<IEssayScanService, EssayScanService>();
builder.Services.AddSingleton<RendererService>();
builder.Services.AddSingleton<IEssayContentService, EssayContentService>();
builder.Services.AddTransient<ImagePostRenderProcessor>();
builder.Services.AddTransient<HeadlinePostRenderProcessor>();
builder.Services.AddTransient<EssayStylesPostRenderProcessor>();
builder.Services.AddTransient<BlogOptions>(provider =>
provider.GetRequiredService<IOptions<BlogOptions>>().Value);
return builder;
}
public static WebApplicationBuilder AddServer(this WebApplicationBuilder builder)
{
builder.Services.AddHostedService<BlogHostedService>();
return builder;
}
public static WebApplicationBuilder AddWatcher(this WebApplicationBuilder builder)
{
builder.Services.AddTransient<BlogChangeWatcher>();
builder.Services.AddHostedService<BlogHotReloadService>();
return builder;
}
}

View File

@@ -1,64 +0,0 @@
namespace YaeBlog.Models;
public class BlogEssay : IComparable<BlogEssay>
{
public required string Title { get; init; }
public required string FileName { get; init; }
public required bool IsDraft { get; init; }
public required DateTimeOffset PublishTime { get; init; }
public required DateTimeOffset UpdateTime { get; init; }
public required string Description { get; init; }
public required uint WordCount { get; init; }
public required string ReadTime { get; init; }
public List<string> Tags { get; } = [];
public required string HtmlContent { get; init; }
public BlogEssay WithNewHtmlContent(string newHtmlContent)
{
var essay = new BlogEssay
{
Title = Title,
FileName = FileName,
IsDraft = IsDraft,
PublishTime = PublishTime,
UpdateTime = UpdateTime,
Description = Description,
WordCount = WordCount,
ReadTime = ReadTime,
HtmlContent = newHtmlContent
};
essay.Tags.AddRange(Tags);
return essay;
}
public int CompareTo(BlogEssay? other)
{
if (other is null)
{
return -1;
}
// 草稿文章应当排在前面
if (IsDraft != other.IsDraft)
{
return IsDraft ? -1 : 1;
}
return other.PublishTime.CompareTo(PublishTime);
}
public override string ToString()
{
return $"{Title}-{PublishTime}";
}
}

View File

@@ -1,26 +0,0 @@
namespace YaeBlog.Models;
public class BlogOptions
{
public const string OptionName = "Blog";
/// <summary>
/// 博客markdown文件的根目录
/// </summary>
public required string Root { get; set; }
/// <summary>
/// 博客正文的广而告之
/// </summary>
public required string Announcement { get; set; }
/// <summary>
/// 博客的起始年份
/// </summary>
public required int StartYear { get; set; }
/// <summary>
/// 博客的友链
/// </summary>
public required List<FriendLink> Links { get; set; }
}

View File

@@ -1,27 +0,0 @@
namespace YaeBlog.Models;
/// <summary>
/// 友链模型类
/// </summary>
public class FriendLink
{
/// <summary>
/// 友链名称
/// </summary>
public required string Name { get; set; }
/// <summary>
/// 友链的简单介绍
/// </summary>
public required string Description { get; set; }
/// <summary>
/// 友链地址
/// </summary>
public required string Link { get; set; }
/// <summary>
/// 头像地址
/// </summary>
public required string AvatarImage { get; set; }
}

View File

@@ -1,4 +0,0 @@
using YaeBlog.Commands;
YaeBlogCommand command = new();
await command.RunAsync(args);

View File

@@ -1,23 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<ItemGroup>
<PackageReference Include="ImageFlow.NativeRuntime.ubuntu-x86_64" Version="2.1.0-rc11" Condition="$([MSBuild]::IsOsPlatform('Linux'))"/>
<PackageReference Include="ImageFlow.NativeRuntime.osx-arm64" Version="2.1.0-rc11" Condition="$([MSBuild]::IsOsPlatform('OSX'))"/>
<PackageReference Include="ImageFlow.Net" Version="0.13.2"/>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1"/>
<PackageReference Include="AngleSharp" Version="1.1.0"/>
<PackageReference Include="Markdig" Version="0.38.0"/>
<PackageReference Include="YamlDotNet" Version="16.2.1"/>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup>
<ClientAssetsRestoreCommand>pnpm install</ClientAssetsRestoreCommand>
<ClientAssetsBuildCommand>pwsh build.ps1 tailwind</ClientAssetsBuildCommand>
</PropertyGroup>
</Project>

View File

@@ -1,44 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Tailwind": {
"InputFile": "wwwroot/input.css",
"OutputFile": "wwwroot/output.css"
},
"Blog": {
"Root": "source",
"Announcement": "博客锐意装修中,敬请期待!测试阶段如有问题还请海涵。",
"StartYear": 2021,
"Links": [
{
"Name": "Ichirinko",
"Description": "这是个大哥",
"Link": "https://ichirinko.top",
"AvatarImage": "https://ichirinko-blog-img-1.oss-cn-shenzhen.aliyuncs.com/Pic_res/img/202209122110798.png"
},
{
"Name": "志田千陽",
"Description": "日出多值得",
"Link": "https://zzachary.top/",
"AvatarImage": "https://zzachary.top/img/ztqy_hub928259802d192ff5718c06370f0f2c4_48203_300x0_resize_q75_box.jpg"
},
{
"Name": "不会写程序的晨旭",
"Description": "一个普通大学生",
"Link": "https://chenxutalk.top",
"AvatarImage": "https://www.chenxutalk.top/img/photo.png"
},
{
"Name": "万木长风",
"Description": "世界渲染中...",
"Link": "https://ryohai.fun",
"AvatarImage": "https://ryohai.fun/static/favicons/favicon-32x32.png"
}
]
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -3,14 +3,31 @@
[cmdletbinding()]
param(
[Parameter(Mandatory = $true, Position = 0, HelpMessage = "Specify the build target")]
[ValidateSet("tailwind", "publish", "compress", "build", "dev", "new")]
[ValidateSet("publish", "compress", "build", "dev", "new", "watch", "serve")]
[string]$Target,
[string]$Output = "wwwroot",
[string]$Essay,
[switch]$Compress
[switch]$Compress,
[string]$Root = "source"
)
begin {
if (($Target -eq "tailwind") -or ($Target -eq "build"))
{
# Handle tailwind specially.
return
}
# Set the content root.
$fullRootPath = Join-Path $(Get-Location) $Root
if (-not (Test-Path $fullRootPath))
{
Write-Error "Content root $fullRootPath not existed."
exit 1
}
Write-Host "Use content from" $fullRootPath
$env:BLOG__ROOT=$fullRootPath
Write-Host "Building $Target..."
if ($Target -eq "publish")
@@ -30,6 +47,9 @@ begin {
exit 1
}
}
# Set to the current location.
Push-Location src/YaeBlog
}
process {
@@ -61,8 +81,11 @@ process {
function Build-Image
{
$commitId = git rev-parse --short=10 HEAD
dotnet publish
podman build . -t ccr.ccs.tencentyun.com/jackfiled/blog --build-arg COMMIT_ID=$commitId
dotnet publish ./src/YaeBlog/YaeBlog.csproj -o out
Write-Host "Succeed to build blog appliocation."
podman build . -t ccr.ccs.tencentyun.com/jackfiled/blog --build-arg COMMIT_ID=$commitId `
-f ./src/YaeBlog/Dockerfile
Write-Host "Succeed to build ccr.ccs.tencentyun.com/jackfiled/blog image."
}
function Start-Develop {
@@ -87,13 +110,9 @@ process {
}
}
switch ($Target)
{
"tailwind" {
Write-Host "Build tailwind css into $Output."
pnpm tailwindcss -i wwwroot/tailwind.css -o $Output/tailwind.g.css
break
}
"publish" {
Write-Host "Publish essay $Essay..."
dotnet run -- publish $Essay
@@ -119,6 +138,17 @@ process {
"new" {
dotnet run -- new $Essay
}
"watch" {
dotnet run -- watch
break
}
"serve" {
dotnet run -- serve
break
}
}
}
end {
Pop-Location
}

5
global.json Normal file
View File

@@ -0,0 +1,5 @@
{
"test": {
"runner": "Microsoft.Testing.Platform"
}
}

174
source/posts/2025-final.md Normal file
View File

@@ -0,0 +1,174 @@
---
title: 2025年终总结
date: 2026-05-08T01:22:18.6904350+08:00
updateTime: 2026-05-08T01:22:18.8859180+08:00
tags:
- 杂谈
- 年终总结
---
越来越晚的年终总结是本站不得不品的一大特色在这样下去2026年的年终总结要到2028年才能面世了绝望。不过2025年确实是非常丰富多彩的一年这一年的经历是如此的多样以至于真正需要提笔写下来的时候反而不知道可以写什么。
<!--more-->
### 毕业
在2025年的6月我从明光村幼儿园附属大学的括号学院毕业了。
毕业本身似乎并不是什么特别需要记录的事情,不过是一篇论文、两张证书和几次合照,现在回想起来只有一种如堕梦中的感觉。
虽然对于其他的事情都已经几乎淡忘但是因为我提前自愿选修了研究生课程《高性能计算》而这门课的期末考试日期甚至在毕业典礼之后。这不得不使得我在毕业季各种事务缠身的情况下还得抽出时间准备考试而这个《高性能计算》课程的内容又多又杂实在是又难学又难背。这使得我对于这个毕业季影响最深刻的事情不是什么毕业合照而是SMP和CUDA
不过说都说到这里了,顺便回忆一下我的四年大学生活吧。
### 我的大学
还记得2021年的暑假我还在和一个名叫Microsoft Visual Studio的神秘软件搏斗并不知道这个软件中某个叫作.NET SDK的东西会成为这四年中的一个重要组成部分当时还创作了本博客的[第一篇文章](https://rrricardo.top/blog/essays/question-in-install-vs-2019)。
2021年的9月第一次来到校园不得不说该附属大学的偏远校区还是很符合我对于大学校园的刻板印象的。标准的四人间、高大的图书馆迷宫一样的教学楼设计充分满足了我对于大学生活的一切想象甚至到主校区需要坐一个半小时地铁也是刻板印象的一部分。
不过这样的“幸福大学生活”只持续了四个月。在2022年9月份就回到了宇宙中心的南边中国最宽公路的东边我们伟大的明光村。很高情商地说在这个校区中学习和生活可以**随时随地**地品味到这个学校深厚的历史底蕴。例如我现在居住的宿舍在20年前也居住过我的导师。课间在窗边踱步你可以欣赏到墙上的照片里脚下的教学楼在1956年落成时的雄姿。
还是谈谈在这四年中我所修过的那些课程吧。本来在2025年6月毕业之后我打算写一篇文章结合我四年中的经历详细分析一下我所经历的计算机科学本科教学。但是迟迟没有时间落笔就在这里简单评述一下。
![image-20260503175234105](./2025-final/image-20260503175234105.webp)
首先是培养方案中归类为“数学与自然科学”的课程《高等数学A》和《线性代数》是必修课其中《高等数学A》因为代课老师江彦是一个**认真负责**的好老师这里给到一个夯。《线性代数》则是因为课程设计和老师只能给到一个NPC说实话本来想给到一个拉完了但是考虑到这门课算是计算机科学比较重要的数学基础还是给到NPC。
两门概率论课程我选的是《概率论与随机过程》,同样因为代课老师鞠红杰给到一个顶级,而且随机过程本来就是一个非常有趣的研究课题,这门课的课程论文就是博客中的[原神抽卡研究](https://rrricardo.top/blog/essays/genshin-gacha-1)。至于《组合数学》、《运筹学》、《数学建模与模拟》和《矩阵理论与方法》四选一我选择的是《数学建模与模拟》这个课程只能给到NPC听了和没听一样老师脾气还不太好。《大学物理C》同样只能给到一个NPC虽然我对于物理还是挺感兴趣的~~还参加了大学生物理竞赛~~,但是这实在和计算机学科关系不太大,安排这个不如多安排一些通信和电子的内容。
然后是培养方案中“学科基础”大类课程,这个课程组中的课程都是必修课程。《计算导论与程序设计》是大一上学期的唯一一门专业课,也是计算机科学的入门课,我会说它很好的完成了这个任务,虽然还有不少的优化空间,这里给到一个顶级。
《电路与电子学基础》我直接给到一个顶级,老师非常风趣幽默,教学内容不能说是和计算机科学直接相关,但也算是计算机的基石了。《离散数学》这里是一个拉完了,从理论上来说离散数学的教学内容可以算是实际上的计算机科学核心,但是正是这种极端重要性更加凸显出了这个课程拉完了的程度,而且还是中英文混合教学,我的评价是没有金刚钻就别揽瓷器活。
《数字逻辑与数字系统》,还可以,归类到人上人,课程内容本身比较重要,算是之后的计算机组成原理的先修课程。《形式语言与自动机》,这门课的代课老师石川老师算是北邮的风云人物,其他的东西不评价,但是他在这门课程上的表现还是值得肯定的,这里归到人上人。
下面正式进入计算机科学中的真·核心课程,即培养方案中的“专业基础”课程组。首先是《数据结构》,因为考试要求手写代码直接给到一个拉完了,我得承认这个有我自己的一些偏见。《算法设计与分析》本来想以同样的理由给到一个拉完了,~~但是考虑到我的成绩比《数据结构》高~~这里给到一个NPC。《计算机系统基础》课程使用的教材是大名鼎鼎的*Computer System: A Programmer Perspective* 虽然课程并没有完全覆盖CSAPP中所有内容但是还是给到一个夯
《操作系统》按道理讲也是计算机科学中的核心课程但是课程内容我现在就记得一个信号量控制抢水果和挑水老师也非常莫名其妙直接给到拉完了正所谓学完这门课你也不懂操作系统。然后是《编译原理与技术》可以算是计算机科学中最难的课程之一但是因为课程核心都放在了编译器的前端部分教学语言还是老掉牙的Pascal只能给到一个人上人。
《计算机组成原理》,考虑老师直接在课程推销王道考研,直接给到拉完了。《计算机系统结构》,算是《计算机组成原理》的进阶课,老师讲得也非常不错,和之前的《计算机组成原理》相比简直是天差地别,直接给到一个顶级,如果实验部分改进一下,和《计算机组成原理课程设计》结合一下,就可以直接给到夯了。《计算机网络》直接给到夯,算是课程与现实结合最紧密的课程,和《离散数学》同样都是中英文混合教学,只能说人与人之间亦有差距。《数据库系统原理》获得夯的理由和《计算机网络》比较一致,都是同现实紧密关联的课程,任课老师教学非常不错,也是中英文混合教学。
《软件工程》和《现代交换原理》则是难兄难弟,两门都是拉完了:《软件工程》被称为计算机科学中的政治,我认为与其死记硬背一些非常抽象的概念、参加两次考试不如多写几行代码来得体会深刻。而且这门课的实践部分也非常抽象,要求几个小组联合验收:而且这些小组的前后端之间需要可以随机组合。虽然这个要求可以说是非常的软件工程,但是和你合作的哥们可能并不是特别拟人,只能唉唉唉。至于《现代交换原理》,第一次看到这门课的时候我很怀疑这是否是打印错误,虽然代课老师挺不错的,但是它唯一的问题就是不该出现在计算机科学的培养方案上。退一步说就算学校设计培养方案的时候希望计算机科学的同学也懂一点通信,也是应该设计简单一点的通信原理而不是设计一门不知所云的“古代交换原理”课程(毕竟课程的主体部分是电话交换机)。
然后是专业课选修课组部分。在“网络&开发技术模块”我选修的课程是《下一代Internet技术与协议》、《移动互联网技术及应用》和《Python程序设计》三门课。其中《下一代Internet技术与协议》是中规中矩的选修课程只能归到NPC一档课程的主要内容就是讲IPv6协议栈考虑到现在IPv6协议栈已经在大规模的普及推进建议把课程名称中的“下一代”去掉。《移动互联网技术及应用》就是教写Android应用考虑到这门课的老师是北邮少数敢于在课程上打开IDE写代码的老师出于对这位老师的敬佩这里给到人上人。《Python程序设计》也是一门平平无奇的选修课同时是出于对踢球骨折了还拄着拐杖来上课的老师之敬佩这里给到人上人。
在“大数据技术模块”我选修的课程是《大数据技术基础》和《信息与知识获取》。其中的《大数据技术基础》的实验部分非常抽象要求使用容器技术模拟出多个节点来搭建大数据系统比如Hadoop和Spark还需要在华为的ARM云服务器上进行实验与其叫作是《大数据技术基础》不如改名为《Linux系统运维基础》。不过考虑到这是我本科期间唯一一门愿意给100分的课程这门还是给到人上人。《信息与知识获取》则是“大海呀你全部都是水~”这里给到NPC。
在“技术拓展模块”我选修的课程是《人工智能原理》和《程序设计实践》。这两门都给到NPC《人工智能原理》是因为它讲授的原理有点太古老了连深度神经网络都没有涉及还需要考试只能给到NPC。《程序设计实践》则是普通的水课项目开源在[github](https://github.com/jackfiled/Katheryne)上,看上去这个题目也是祖传题目了。
然后我们可以来谈论一下最激动人心的实践课课组。首先是《物理实验A》由于是线上教学做实验直接给到一个拉完了。《计算导论与程序设计课程设计》不太可能会有“普通人”在大一的下学期就选择《程序设计竞赛基础》吧是大一下学期开设课程设计的题目是公交车的调度对大一新人来说还是挺合适的这里给到一个人上人。面向对象的程序设计实践我选择的是Java这个只能说拉完了Java感觉没学到什么有用的最后交作业的时候还要求提交一大堆软件工程的UML图。
《计算机组成原理课程设计》和《数字逻辑与数字系统课程设计》二选一我选择是《计算机组成原理课程设计》题目是设计一个单周期的CPU非常好课程直接给到夯唯一的问题似乎因为小学期时间安排的问题导致最后只有三四天的时间来完成整个作业这种课程就应该设计为一个必修实验而且最好需要设计一个完整的五段流水线CPU。
《操作系统课程设计》和《编译原理课程设计》二选一我选择了《编译原理课程设计》这更多是一个历史和“人民”的选择当时我们课程班100多个人最后只有一个小组大概8个人选择了《操作系统课程设计》。不过《编译原理课程设计》确实可以给到夯首先是老师非常不错然后课程设计的内容也确实可以说是循序渐进。唯一的问题是设计的编译器是从Pascal翻译到C和编译原理课程本身的衔接也不是特别紧密尤其是考虑到编译原理课程本身自带的两个实验就是词法分析和语法分析课程设计还把重点放在前端上就有点不太好了。最好是把题目修改到C到某门汇编语言例如RISC-V然后和《编译原理》课程本身的实验衔接设计课程设计的重点就可以放在代码生成和代码优化上面了。
至于最后的三选二选修课在《数据结构课程设计》、《计算机网络课程设计》和《数据库系统原理课程设计》三门中我选择的前两门。《数据结构课程设计》有一个非常奇葩的要求不能使用自带的数据结构实现比如说标准库中的列表、哈希表等而是要求自行实现但是在最后验收时并没有突出这一点这里只能给到一个人上人算是实践课程中比较低的评价了。《计算机网络课程设计》要求设计一个DNS Relay服务器比较典型的计算机网络课程设计的要求唯一的问题是要求必须要用C语言完成只能给到一个顶级。这里我感觉老师做出这个限制的主要理由是希望大家多钻研一下和网络相关的高并发设计但是直接限制到C语言级别有点强人所难了。
最后锐评一下我选过的公选课和体育课在我的培养方案中我需要选修两门以上的公选课人文艺术类型和四门体育课其中一门必修的《体育基础》。我为了满足培养方案选修的公选课是《人工智能与社会发展》和《显示技术发展与游戏应用》两门普通的水课没什么好说的给到NPC。不过在培养方案之外我还选修了一门《基于Arduino的开源手机设计》非常好的选修课课程内容是基于ESP8266设计一个支持2G和Wi-Fi的按键手机。所有上课的同学都可以免费获得一个板子值得选修这里给到一个夯。
至于体育课,因为我本人体育苦手,评价略有失真之处,仅供参考。首先是在**线上**进行的《体育基础》课直接给到NPC至于《健美》和《乒乓球》考虑到我学得不好应该是我的问题给到一个顶级。至于最后的《太极拳》因为体育苦手的缘故简直就是我等的福音给到夯
### 实习与工作
在毕业之后不能躲过的话题自然就是工作,虽然正式的找工作离我还有~~三年~~两年半的时间。尤其是考虑到我们伟大的学校并不打算为保研的同学提供宿舍在6月被赶出校门之后一个比较正确的选择就是找一份实习。
大概从2025年的5月份我就开始在BOSS直聘上找实习首选的工作意向就是和我现在研究方向相关联的编译器、高性能计算和AI Infra方向考虑到这些方向的工作岗位数量如同食堂番茄蛋花汤中的蛋花我也填上了后端方向作为备选。不过找工作真的好痛苦啊从事后统计来看在那半个月的时间里我大概投出去了60多份简历其中只有个位数的HR回复了消息只约上了一场面试。这便是今天的主角——理想汽车。
说起来也奇怪招聘的这个岗位理想汽车的图编译器开发并不是我自己找上门去的而是对方的HR主动要走了我的简历。下面就简单记录一下理想汽车的图编译器开发实习生的面经吧不过说实话这已经是接近一年之前的事情了如果有错漏之处还请谅解。
上来还是自我介绍起手,并介绍自己简历上的一些项目。因为我当时没有任何的实习经历,简历上主要的项目其实是本科的毕业设计。不过因为这个毕业设计题目取得非常高大上,但是实现上非常一坨,所以拷打的过程个人感觉漏洞百出。
然后是一些基础的编译器知识比如说你是否知道什么是静态单赋值形式MLIR中的IR是否是SSA等等还结合MLIR问了很多MLIR中的细节开发问题如果你是否定义过Dialect是否写过operation的parser和printer等等。还有MLIR中的pass分为什么是否了解这些pass是如何运行的有一些非常细节的问题我当时就直接回答不知道了捂脸
中间还拷打了一些`C++`开发的知识比如说CRTP这种在MLIR中非常常用的模板范式不过我当时因为对`C++`开发还不是特别熟悉,将中间的`static_cast`说成了`dynamic_cast`,非常的尴尬。面试官还问了问我研究生和本科的主修课程。
编程的题目是手撕堆排序但是面试官找了半天都没有找到测试的题目只让我口述了算法的过程不过我当时过度紧张毕竟是人生的第一场面试什么归并快排桶排堆排的都丢到九霄云外去了只能阿巴阿巴地说一些建堆、排序之内的车轱辘话幸好面试官也没有特别纠结这一块。反问的环节我是询问了一下我们工作的对象理想汽车自研汽车芯片的情况也就是现在即将发布的马赫100芯片。
幸好最终的结果还是比较好的顺利拿到了offer并入职理想。不过当时暑假期间的工资是按照本科生算的只有230一天考虑到当时我在北京还需要租房居住差点付费上班幸好还有第二份兼职。而且当时部门正处于芯片即将上车的集中开发阶段8月份还把我们一起打包送到杭州去上班血亏一个月房租~~不过住一个月的酒店还是蛮爽的,谢谢想哥~~。
不过说来我还是非常感谢在理想的这段实习经历的。在进入理想实习之前虽然我已经在导师的手下干了一年但是感觉对于编译器尤其是这种面向NPU的AI编译器总感觉还是有一种雾里看花的感觉。但是在实习过程中实际接触了编译器的开发和优化流程瞬间感觉过程论文和课本中的编译器活了过来。同样也是在这段经历中我对于MLIR的了解和认识也是飞速增长。
在理想的实习经历之外,~~为了赚房租~~为了进一步提高自己的能力同时在为我在开源世界中开辟一块立锥之地我报名并参加了2025年的开源之夏OSPP项目中选的题目是[RustSBI原型系统引导生态完善](https://summer-ospp.ac.cn/org/prodetail/256590172?lang=zh&list=pro)。不过这次OSPP的经历我打算单出一篇文章来分享挖坑x1这里就不多赘述了。
### 研究生
讲道理在9月份开学之后我就回到明光村幼儿园附属大学继续攻读我的硕士学位了这段研究生时光也占据了2025年度三分之一的长度。但是我在撰写本年终总结的时候却对于这段时光一点回忆也没有真是令人感到好奇
### 第一台NAS
上面拖拖拉拉地讲了一堆令人悲伤的话题,还是一转聊一聊一些开心的话题吧。
经过若干个月的精心筹备在2025年的一季度我终于组建了我的个人NAS虽然这台NAS里面目前只有1块4T 3.5英寸硬盘和几块从笔记本上拆下来的2.5英寸硬盘显得我精心挑选的8 STAT接口主板和8个3.5英寸硬盘位的机箱显得很呆。不过还是很开心谁叫组装好硬件就遇上了AI导致的硬件大涨价呢。
![](./2025-final/IMG_20250301_164145.webp)
这里也是简单列一下硬件配置:
1. CPUIntel Xeon E3-1245v5
2. 内存镁光DDR4 ECC 16G 2400 x2
3. 主板超微X11SSH-F
4. 电源:全汉 蓝暴经典PLUS 450W
5. 机箱:联宇 见方L
6. CPU散热器利民AXP90-X36
这里我也打算后面单独出一篇文章分享设计和配置这个NAS的点点滴滴挖坑x2这里就简单吐槽一件令我非常绷不住的事。这里选择E3-1245v5这颗带有核显的CPU简直就是我最大的错误我设计的时候考虑的是这颗核显可以极大地加快NAS上各种媒体的解码速度。但是E3-1245v5这颗CPU带的核显简直就是垃圾中的垃圾它不支持H265/HEVC 10 bit的解码简直就是屁用没用
而且这颗CPU在秋季开学之后就挂掉了还贡献了我的第一张Linux蓝屏~~扫码即可查看当时的Kernel日志~~
![](./2025-final/IMG_20251021_160338.webp)
我不得不购入了第二块CPU也就是现在机器上使用的Intel Xeon E3-1240v5。谢天谢地这颗CPU到目前还工作正常而它比它带核显的兄弟足足便宜了三分之二。
### 第一台台式机
除了这台,我还在暑假的时候组装了我人生的第一台台式机:
![](./2025-final/IMG_20260506_201126.webp)
这里也简单列举一下这台主机的配置:
- CPU AMD 锐龙 5 9600X
- 内存:光威龙武弈 DDR5 长鑫颗粒 6000 CL36 16G x2
- 主板华硕TX B650EM WIFI W
- GPUONIX Intel ARC B580 LUMI 12G
- 电源:鑫谷 GM650W 金牌全模组
- CPU散热器九州风神 玄冰400V5 晴雪白
- 机箱散热器酷冷至尊莫比乌斯120白色 x2
- 立牌psplive 2025校园主题亚克力立牌-[李豆沙_Channel](https://space.bilibili.com/1703797642)
组装这台主机的时间非常巧妙我是在7月初的时候下定决心组装这台台式机这几乎是在电子产品大涨价之前最后一个上车的时间窗口了。
关于我选择的这些配置我也想简单讨论一下。主要的争议点可能是我选择的显卡当时我的决赛圈里面几乎就是了两张显卡NVIDIA的RTX 5060和Intel的Arc B580。最终驱动我做出决定的因素主要是预算RTX 5060的价格基本上都在2500元以上而我拿下的这块Intel Arc B580只花了我1800元。虽然当时网上对于Intel显卡驱动的批评甚嚣尘上但是我决定对Intel这家老牌的半导体厂商保持基本的信任至少到目前为止Intel还没有辜负我的信任。而且考虑到我实际上也是一个Linux用户NVIDIA的Linux驱动是个什么样子是不言自明的至少Intel的Linux支持情况要好得多。
而且在我目前的日常游玩的所有游戏中这块显卡都表现正常例如《三角洲行动》在我目前的2K分辨率下可以取得90~110FPS的水平。《原神》和《戴森球计划》这种对于显卡需求比较低的游戏更是轻松拿下。
### 玫瑰色生活
2025年也是一个旅行颇多的年份。
首先是趁着毕业的时候去北京一个著名的小众冷门这是否矛盾景点——中央礼品文物管理中心参观算是一个非常有首都特色的景点了。然后是忙碌的8月份在北京和杭州之间飞来飞去很幸运地是在其中的一次坐到了国产大飞机C919前往杭州
![](./2025-final/IMG_20250824_183058.webp)
还前往了内蒙古的鄂尔多斯参加全国高性能计算学术年会CCF HPC China 2025。学术上的东西在下人微言轻不做评价不过至少HPC China在茶歇上比之前去参加了CNCC 2024要慷慨的多。其他的内容可以参见同行者的[文章](https://www.chenxutalk.top/posts/life/travel/neimenggu/)。
正所谓“读万卷书行万里路”。在2025年还读了下面这几本书
- [*GOSICK*](https://mzh.moegirl.org.cn/GOSICK),樱庭一树著,不算是一般意义上的轻小说,而是比较严肃的推理小说,好看!
- 《浪潮之巅》,吴军创作的科技产业发展史的书籍,对于计算机相关领域从业者来说可以说是必读的经典书籍了。
还看了不少的电影:一部是看上去比较冷门的电影《商海通牒》,原名*Margin Call*记录的是2008年到2009年发生金融危机时一家华尔街投行中发生的故事。另一部则是学校组织放映的《窗外是蓝星》记录的是神舟十三号乘组首次在中国空间站执行在轨驻留六个月任务的故事。岁月如梭啊现在已经是神舟二十一号乘组了。
本来在年终总结的末尾按照传统还是应该放一下B站的观看时长但是在撰写本文时B站似乎已经下架了2025年度报告的查看页面了

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Some files were not shown because too many files have changed in this diff Show More