news 2026/9/9 23:30:05

Halo 通知系统(NotificationCenter)架构设计与扩展实践:从订阅、模板到多通道通知器的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Halo 通知系统(NotificationCenter)架构设计与扩展实践:从订阅、模板到多通道通知器的完整指南

Halo 通知系统(NotificationCenter)架构设计与扩展实践:从订阅、模板到多通道通知器的完整指南

【免费下载链接】haloHalo 是一款强大易用的开源建站工具,从个人博客、知识库,到企业官网、在线商城,Halo 都能助您轻松实现,一站式满足您的多样化建站需求。项目地址: https://gitcode.com/GitHub_Trending/ha/halo

Halo 是一套基于自定义模型(Custom Resource)构建内容与系统能力的开源建站工具。当站点具备用户协作属性(访客评论、作者回复、账号注册、插件业务事件)后,"如何按订阅把事件推送给正确的人"成为刚需。本文以 Halo 官方通知功能设计文档为骨架,结合核心源码,系统讲解 Halo 通知中心的整体架构:事件模型ReasonType/Reason、订阅模型Subscription、用户偏好设置、Notification站内消息、模板选择规则、NotifierDescriptor通知器声明与ReactiveNotifier扩展点,以及个人中心自定义 API。读完本文,你将理解通知从"事件触发"到"模板渲染"再到"多渠道送达"的完整链路,并掌握为 Halo 编写自定义通知事件与通知器的方法。


一、背景:为什么 Halo 需要一个通知中心

Halo 是一个具有强"用户协作"属性的系统。典型场景如:用户发布文章后,访客留下评论,作者回复后希望提醒访客回访并继续互动。在没有通知功能前,这类需求无法被满足,例如:

  1. 访客只能在评论后的一段时间内反复回到文章页,人工查看是否被回复;
  2. Halo 的用户注册功能无法验证注册邮箱,无法阻止同一邮箱被占用、也难以约束恶意注册。

为了让用户收到通知或验证消息,并能够统一管理与处理这些通知,Halo 在2.10.0起引入了独立的通知模块(通知中心),负责根据用户订阅与偏好推送通知并管理通知

已有需求盘点

  • 访客评论文章后,希望收到"作者回复"通知;文章作者也希望收到"文章被评论"通知。
  • 用户注册希望验证邮箱,实现一个邮箱注册一个账号、防止占用他人邮箱,并减少恶意注册。
  • 应用市场类插件:管理员希望在用户下单后收到新订单通知。
  • 付费订阅类插件:需要向付费订阅用户推送付费文章的浏览链接。

目标与非目标

设计一个通知功能需要覆盖如下目标:

  • 支持扩展多种通知方式(邮件、短信、Slack 等);
  • 支持可扩展的通知条件(如"新文章发布"事件因付费等级不同只推送给部分用户,需通过通知条件扩展点实现);
  • 支持定制化选项(是否开启通知、通知时段等);
  • 支持通知全流程(发送、接收、查看、标记);
  • 通知内容支持多语言;
  • 事件类型可扩展,插件可以定义自己的事件以通知订阅用户(如应用市场插件)。

同时明确了非目标,帮助读者理解本阶段的边界:

  • Halo 核心只实现站内消息邮件两种通知方式,更多通知方式由插件扩展;
  • 定时通知、通知频率、摘要通知等属非必要功能,可交给插件扩展;
  • 多语言现阶段仅支持中文与英文;
  • 不做"可定制通知模板编辑器",默认模板由事件定义者提供,如需修改可考虑用特定 Notifier 适配事件。

这些边界与下方"结论"部分以及 docs/notification/README.md 中的设计描述一致,是理解该功能演进路线的起点。


二、核心数据模型:通知功能的六个自定义模型

通知功能完全建立在 Halo 自定义模型体系之上,所有模型均位于notification.halo.run/v1alpha1API 分组。核心扩展类集中在 api/src/main/java/run/halo/app/core/extension/notification/ 目录:

  • ReasonType:事件类别(描述"会发生什么事件"及事件属性的 Schema);
  • Reason:一次具体事件实例("这件事刚刚发生了",触发通知的载体);
  • Subscription:订阅关系(谁对什么事件感兴趣);
  • Notification:站内通知记录(送达用户的消息,独立于通知器);
  • NotificationTemplate:通知模板(按事件与语言渲染标题/正文);
  • NotifierDescriptor:通知器声明(描述通知器能力及配置入口)。

设计文档用一张图描述了各数据结构的交互关系(见下),建议在阅读下文前先建立整体印象:

2.1 事件类别 ReasonType 与事件 Reason

系统先通过定义"事件类别"来声明该事件包含的数据以及发送事件时默认使用的模板。

ReasonType是一个自定义模型,用于定义事件类别;一个事件类别下可以包含多个事件实例。以"收到评论"为例,它声明了通知中可用的属性(postName、postTitle、commenter、comment 等),每个属性带有nametypedescriptionoptional(是否必填,默认false):

apiVersion: notification.halo.run/v1alpha1 kind: ReasonType metadata: name: comment spec: displayName: "Comment Received" description: "The user has received a comment on an post." properties: - name: postName type: string description: "The name of the post." optional: false - name: postTitle type: string optional: true - name: commenter type: string description: "The email address of the user who has left the comment." optional: false - name: comment type: string description: "The content of the comment." optional: false

在源码中,ReasonType.java 通过@GVK(group = "notification.halo.run", version = "v1alpha1", kind = "ReasonType", ...)注册该模型,SpecdisplayName(必填)、description(必填)与properties(属性列表)构成;属性值类型包括 string、number、boolean 或 object。

ReasonReasonType的实例,表示一次具体的通知触发原因。当事件发生时(例如文章收到新评论),事件生产方创建一条Reason资源:

apiVersion: notification.halo.run/v1alpha1 kind: Reason metadata: name: comment-axgu spec: # ReasonType 的 metadata.name reasonType: comment author: 'guqing' subject: apiVersion: 'content.halo.run/v1alpha1' kind: Post name: 'post-axgu' title: 'Hello World' url: 'https://guqing.xyz/archives/1' attributes: postName: "post-fadp" commenter: "guqing" comment: "Hello! This is your first notification."

对应的 Reason.java 中,Spec包含:reasonType(所属事件类别,必填)、subject(事件主体资源,必填,含 apiVersion/kind/name/title/url)、author(创建者用户名或系统 actor)以及attributes(键值属性,将传给模板渲染)。JavaDoc 明确说明:Reason 可被理解为"触发通知的一个事件",而一个ReasonType可对应多个Reason实例。

触发时机:当有新Reason被创建时,NotificationTrigger.java(一个以Reason为扩展的 Reconciler Controller)负责把通知交给NotificationCenter。它会给 Reason 添加triggeredfinalizer 防止重复通知,确保一条 Reason 只发送一次通知,发送成功后随即删除该 Reason;控制器 worker 数量为 10,单次协调超时时间为 1 分钟。这也解释了为什么 Reason 资源通常是"用完即焚"的。

2.2 订阅 Subscription:定义"谁对什么感兴趣"

Subscription自定义模型定义了"特定事件发生时,通知哪个订阅者"的关系。其中subscriber表示订阅者用户,unsubscribeToken是用于退订的身份验证 token,reason表示订阅者感兴趣的事件。用户通过创建Subscription订阅感兴趣的事件,事件触发时即可收到通知:

apiVersion: notification.halo.run/v1alpha1 kind: Subscription metadata: name: user-a-sub spec: subscriber: name: guqing unsubscribeToken: xxxxxxxxxxxx reason: reasonType: new-comment-on-post subject: apiVersion: content.halo.run/v1alpha1 kind: Post name: 'post-axgu' # expression: 'props.owner == "guqing"'

匹配语义要点:

  • spec.reason.subject:用于按事件主体匹配感兴趣的事件。如果不指定name,则表示匹配"与指定kindapiVersion相同的一类事件"(即全部同类型主体);
  • spec.expression:按表达式匹配感兴趣的事件。例如props.owner == "guqing"表示仅当事件属性(reason attributes)中的owner等于 guqing 时才触发通知。表达式遵循 SpEL 语法,但结果只能是布尔值
  • 注意:当spec.expressionspec.reason.subject同时存在时,以spec.reason.subject的匹配结果为准,不建议两者同时使用

从源码 Subscription.java 可以看到两个值得关注的实现细节:

  1. InterestReason中的expression字段是2.15.0 起新增的;为了向后兼容,当subject为 null 时会通过ensureSubjectHasValue(...)自动补一个"不存在的默认 Subject"(kind 为NonexistentKind、apiVersion 为notification.halo.run/v1alpha1),isFallbackSubject(...)用于判断该对象是否为占位值,从而允许纯表达式订阅;
  2. unsubscribeTokengenerateUnsubscribeToken()生成,即UUID.randomUUID()

订阅退订链接规则/apis/api.notification.halo.run/v1alpha1/subscriptions/{name}/unsubscribe?token={unsubscribeToken}。对应实现见 SubscriptionRouter.java,它注册了GET .../subscriptions/{name}/unsubscribe路由,校验 query 参数token与该订阅的unsubscribeToken是否一致,通过后将订阅标记为禁用。订阅创建时spec.disabled字段用于表示"通常发生在退订之后"的禁用状态。

订阅/退订的编程入口:接口 NotificationCenter.java 定义了notify(Reason)subscribe(subscriber, reason)与两组unsubscribe(...)方法。其中subscribe的默认实现会先移除已存在的同条件订阅再创建新订阅,新订阅的 metadata.name 使用subscription-前缀自动生成,并自动填充 unsubscribeToken。以 DefaultNotificationCenter.java 为参考实现。

2.3 用户通知偏好设置:事件类型 → 通知方式的映射

系统通过"用户偏好设置的 ConfigMap"中存储的一个notificationkey,保存事件类型与通知方式之间的关系。当用户订阅了某事件(如new-comment-on-post)时,系统读取该配置以确定用哪种通知方式发送:

apiVersion: v1alpha1 kind: ConfigMap metadata: name: user-preferences-guqing data: notification: | { reasonTypeNotification: { 'new-comment-on-post': { enabled: true, notifiers: [ email-notifier, sms-notifier ] }, new-post: { enabled: true, notifiers: [ email-notifier, webhook-router-notifier ] } }, }

该结构的语义是:对每种事件类型(reasonType),声明enabled(是否开启)与notifiers(启用的通知器名称列表)。核心实现见 UserNotificationPreferenceService.java(及其默认实现 UserNotificationPreferenceServiceImpl.java)与模型 UserNotificationPreference.java,而DefaultNotificationCenter.getNotifiersBySubscriber(...)正是"按用户偏好查事件对应的通知器"的实际调用点。

2.4 站内通知 Notification

当用户订阅的事件触发后,系统会创建一条Notification记录。它与通知方式(notifier)无关,recipient存用户名——类似站内信。例如用户guqing订阅了评论事件,当监听到评论事件时就会创建一条记录,可在个人中心的通知列表中看到:

apiVersion: notification.halo.run/v1alpha1 kind: Notification metadata: name: notification-abc spec: # username recipient: "guqing" reason: 'comment-axgu' title: 'notification-title' rawContent: 'notification-raw-body' htmlContent: 'notification-html' unread: true lastReadAt: '2023-08-04T17:01:45Z'

Notification.java 定义了NotificationSpec的全部字段:recipient(接收者用户名)、reason(产生该通知的 Reason 名称)、titlerawContent(纯文本)、htmlContent(HTML 内容)、unread(未读标记)与lastReadAt(最近一次标记已读时间)。该模型天然支持三类操作:标记已读/未读、读取最近已读时间、按接收者过滤。

从代码看,站内通知只面向已登录用户创建:在 DefaultNotificationCenter.java 的dispatchNotification(...)中,若订阅者是匿名用户则只走"发送通知"分支;只有非匿名用户才会同时执行createNotification(...)落库站内消息——它会先fetch(User)确认用户存在,再以notification-前缀生成名创建Notification

个人中心通知自定义 API

个人中心的用户通知管理由 UserNotificationEndpoint.java 实现(GroupVersion 为api.notification.halo.run/v1alpha1),在路径/userspaces/{username}下嵌套提供:

方法路径说明
GET/apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications获取当前用户的站内通知列表(支持分页与多条件查询,参数构造见 UserNotificationQuery.java)
PUT/apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications/{name}/mark-as-read将指定单条通知标记为已读
PUT/apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications/-/mark-specified-as-read批量将多条通知标记为已读(请求体携带names列表)
DELETE/apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications/{name}删除指定通知

说明:设计文档阶段提到的是列表、整体mark-as-readmark-specified-as-read两组接口;落地源码(UserNotificationEndpoint)进一步演化成了"单条标记已读 + 批量标记已读(路径为/-/mark-specified-as-read)",并额外支持删除单条通知。以当前仓库源码为准,更贴近实际可调用接口。

2.5 通知模板 NotificationTemplate 与多语言选择规则

NotificationTemplate用于定义事件的渲染模板。它通过reasonSelector引用事件类别(ReasonType),事件触发时依据用户语言偏好触发事件类别挑选最优模板:

apiVersion: notification.halo.run/v1alpha1 kind: NotificationTemplate metadata: name: template-new-comment-on-post spec: reasonSelector: reasonType: new-comment-on-post language: zh_CN template: title: "你的文章 [(${postTitle})] 收到了一条新评论" body: | [(${commenter})] 评论了你的文章 [(${postTitle})],内容如下: [(${comment})]

模板选择规则如下:

  1. 按语言精确度降序匹配:根据用户设置的语言,从具体到不太具体的顺序依次匹配spec.reasonSelector.language。例如语言标签gl_ES的模板优先级高于gl的模板;
  2. 同语言取最新:当语言匹配成功后可能存在多个模板(如languagezh_CN的模板有三个),此时依据NotificationTemplatemetadata.creationTimestamp字段选择最新创建的一个。

这套规则允许用户个性化定制某些事件的模板内容。落地的选择器实现位于 ReasonNotificationTemplateSelectorImpl.java:它先按spec.reasonSelector.reasonType精确过滤,再按getLanguageKey()(language 为空时按"default"分组)分组,每个语言分组内用Collectors.maxBy(Comparator.comparing(creationTimestamp))取最新模板,最后用LanguageUtils.computeLangFromLocale(...)从语言标签变体到默认值做"逆序排序取首个存在项",恰好对应"更具体优先、默认兜底"的规则。

模板引擎与语法:模板使用 ThymeleafEngine 渲染。纯文本模板使用textual(文本)模板模式;HTML 模板则使用标准表达式语法在标签属性中取值。渲染核心见 NotificationTemplateRender.java 及 ReasonNotificationTemplateSelector.java。

模板可用属性(保留属性):在通知中心渲染模板时,会在ReasonAttributes(即事件 attributes)基础上额外提供以下属性,因此任何模板都能使用,但事件定义者需避免使用这些保留属性以免冲突:

属性含义
site.title站点标题
site.subtitle站点副标题
site.logo站点 LOGO
site.url站点访问地址
subscriber.id订阅者 ID:用户为用户名,匿名用户为anonymousUser#email
subscriber.displayName订阅者显示名:邮箱地址或@username
unsubscribeUrl退订链接,用于取消订阅

这些额外属性在 DefaultNotificationCenter.java 的inferenceTemplate(...)中注入:subscriber相关子属性在渲染前写入模板模型,unsubscribeUrl则通过SubscriptionRouter基于订阅名构造退订 URL 后写入。另外站点语言偏好在 LanguageUtils.java 与getLocaleFromSubscriber(...)(读取系统基本设置)中计算得出,作为模板语言匹配的输入。

2.6 通知器 NotifierDescriptor、配置与 ReactiveNotifier 扩展点

NotifierDescriptor用于声明通知器:描述通知器的名称、描述以及其关联的扩展(notifierExtName),让用户界面可以知道通知器是什么、能做什么,也让 NotificationCenter 知道如何加载通知器、准备通知器所需的设置。

apiVersion: notification.halo.run/v1alpha1 kind: NotifierDescriptor metadata: name: email-notifier spec: displayName: '邮件通知器' description: '支持通过邮件的方式发送通知。' notifierExtName: '通知对应的扩展名称' senderSettingRef: name: 'email-notifier' group: 'sender' receiverSettingRef: name: 'email-notifier' group: 'receiver'

其中senderSettingRef指向"发送方配置"(如 SMTP 服务器、账号密码,由管理员维护),receiverSettingRef指向"接收方配置"(如接收邮箱,由用户个人维护)。声明后,配置读写通过以下 API 暴露:

  • 管理员获取通知器发送方配置:GET /apis/api.console.halo.run/v1alpha1/notifiers/{name}/sender-config
  • 管理员保存通知器发送方配置:POST /apis/api.console.halo.run/v1alpha1/notifiers/{name}/sender-config
  • 用户(个人中心)获取通知器接收消息配置:GET /apis/api.notification.halo.run/v1alpha1/notifiers/{name}/receiver-config
  • 用户(个人中心)保存通知器接收消息配置:POST /apis/api.notification.halo.run/v1alpha1/notifiers/{name}/receiver-config

实现上,上述能力的核心类分别位于 NotifierConfigStore.java(及默认实现 DefaultNotifierConfigStore.java)与 SubscriptionRouter.java、ConsoleNotifierEndpoint.java、UserNotifierEndpoint.java。

通知器扩展点用于实现具体的通知发送方式。设计文档定义的契约接口(插件需要实现的扩展点)如下:

public interface ReactiveNotifier extends ExtensionPoint { /** * Notify user. * * @param context notification context must not be null */ Mono<Void> notify(NotificationContext context); } @Data public class NotificationContext { private Message message; private ObjectNode receiverConfig; private ObjectNode senderConfig; @Data static class Message { private MessagePayload payload; private Subject subject; private String recipient; private Instant timestamp; } @Data public static class Subject { private String apiVersion; private String kind; private String name; private String title; private String url; } @Data static class MessagePayload { private String title; private String rawBody; private String htmlBody; private ReasonAttributes attributes; } }

可以观察到NotificationContext分为两层:**Message(含 recipient、subject、payload 与时间戳)**描述"发给谁、关于什么、内容是什么",而receiverConfig/senderConfig(JSON ObjectNode)是通知器发送前的运行时配置,由DefaultNotificationCenter.notificationContextFrom(...)依据NotifierDescriptor上声明的 sender/receiver settingRef 自动装配(有声明才读取,配置不存在则置空)。

发送流程中的另一个关键点是异步化NotificationSender接口(见 NotificationSender.java)的 JavaDoc 明确说明"发送通知是耗时的,因此通过队列异步发送,且调用方很多情况下是同步阻塞调用NotificationCenter.notify(Reason),这里用队列保证不阻塞调用线程"。每个通知器发送失败会被onErrorResume捕获并记录日志,避免影响整体流程。


三、通知模块功能:发送、接收、查看与标记

结合上述模型,通知模块提供四类能力:

  • 发送通知:事件触发时,系统根据 subscriber 的偏好设置获取事件对应的通知方式(notifier 列表),再按偏好自动发送;
  • 接收通知:用户可选择接收通知的方式,如邮件、短信、自定义路由通知等(受 Halo 核心内置能力约束,现阶段只有站内与邮件,其余靠插件扩展);
  • 查看通知:用户可在 Halo 中查看全部通知,包括已读与未读;
  • 标记通知:用户可将通知标记为已读或未读,便于管理与处理。

将这些能力串成端到端链路,一次完整通知的流转为:

  1. 业务方(文章模块、评论模块或任意插件)创建一条Reason资源;
  2. NotificationTrigger.java 监听到新 Reason,加 finalizer 后调用NotificationCenter.notify(reason)
  3. DefaultNotificationCenter.java 先通过RecipientResolver解析出订阅者(见 RecipientResolver.java 与 Subscriber.java);
  4. 对每个订阅者,依据SubscriptionreasonType/subject/expression做事件匹配,命中后读取其用户偏好获取 notifier 名称列表;
  5. 按通知器逐一选取模板(NotificationTemplateSelector+ 语言匹配),渲染出 title/rawBody/htmlBody,并把 site.、subscriber.、unsubscribeUrl 等保留属性注入模型;
  6. 调用NotificationSender异步分发:通过扩展点找到具体ReactiveNotifier实现并发送;同时(仅对已登录用户)创建Notification站内记录;
  7. 用户在个人中心通过通知列表 API 查看、标记已读/未读或删除。

围绕该模块,仓库还提供了邮件发送的辅助实现(EmailNotifier.java、EmailSenderHelperImpl.java)以及邮件配置的校验端点 EmailConfigValidationEndpoint.java,可作为"实现一个 ReactiveNotifier"的参考样例。

UI 层面,通知功能包含设置页与个人中心消息列表两部分。下面是设计文档中的通知功能 UI 设计稿,展示了从偏好配置到消息中心管理的整体交互形态:


四、通知管理列表的条件筛选

通知列表支持以下条件筛选策略,相关查询参数构造集中在 UserNotificationQuery.java(接口上通过UserNotificationQuery.buildParameters(...)声明 OpenAPI 参数):

  • 按事件类型:列出特定类型的事件通知,例如新文章、新评论、状态更新等;
  • 按已读状态:根据通知是否已读列出,方便用户只看未读通知;
  • 按关键词:列出通知中包含特定关键词的事件通知,例如包含用户名、标题等关键词的通知;
  • 按时间:列出特定时间段内发生的事件通知,例如最近一周、最近一个月等。

五、可选的定制化选项

设计文档指出:若后续出现足够的使用场景,可以考虑在通知中心层面支持以下定制化能力(现阶段不属于 Halo 核心范围,更适合由插件承接):

  • 通知时间段:用户可设置通知推送的时间段,例如只在工作时间推送;
  • 通知频率:用户可设置通知汇总频率,例如每天、每周、每月;
  • 摘要通知:用户可开启每周摘要,把一周内的通知合并为一条,通过邮件等方式接收。

六、扩展开发者快速参考

若你是插件作者,想为 Halo 增加新的通知事件或新的通知方式,可以从以下几类任务出发:

  1. 定义新事件:创建ReasonType(声明事件 Schema)与NotificationTemplate(默认模板,注意避免使用site.*subscriber.*unsubscribeUrl保留属性);业务发生时创建Reason资源即可触发通知链路。
  2. 实现新通知方式:实现ReactiveNotifier扩展点(notify(NotificationContext)),并注册NotifierDescriptor声明名称、描述、notifierExtName与收发配置引用;如需要管理员/用户配置,可提供对应的 sender/receiver 配置存储与 UI。
  3. 按条件精确订阅:利用Subscriptionexpression(SpEL,结果必须为布尔)实现更灵活的事件匹配,替代或补充subject匹配。
  4. 监听订阅生命周期:通过NotificationCenter.subscribe/unsubscribe或退订路由保证订阅与退订的一致性(订阅会自动生成unsubscribeToken,已禁用订阅由spec.disabled标记)。

源码阅读地图

关注点文件
六个数据模型定义api/src/main/java/run/halo/app/core/extension/notification/
通知中心核心流程DefaultNotificationCenter.java、NotificationCenter.java
事件触发与去重NotificationTrigger.java
模板选择与渲染ReasonNotificationTemplateSelectorImpl.java、NotificationTemplateRender.java
用户偏好与配置存取UserNotificationPreferenceService.java、NotifierConfigStore.java
个人中心/退订/通知器 APIUserNotificationEndpoint.java、SubscriptionRouter.java、ConsoleNotifierEndpoint.java
邮件通知参考实现EmailNotifier.java、EmailSenderHelperImpl.java

七、结论

通过上述方案与实现,Halo 构建了一套完整的通知机制:以自定义模型承载事件与状态、以订阅关系驱动匹配、以用户偏好决定通道、以模板引擎完成多语言渲染、以扩展点抽象通知器,从而能够根据用户需求与偏好自动筛选并推送通知。同时,事件类型(ReasonType)、通知方式(ReactiveNotifier/NotifierDescriptor)与通知条件筛选策略都具有清晰的扩展边界,为后续支持更多事件类型、更多通知通道以及社区插件的深度定制保留了充分空间。

本文对应的原始设计文档为 docs/notification/README.md,若需进一步了解该功能在前后端中的完整形态,可结合 docs/extension-points/content.md 等扩展点文档继续阅读。

【免费下载链接】haloHalo 是一款强大易用的开源建站工具,从个人博客、知识库,到企业官网、在线商城,Halo 都能助您轻松实现,一站式满足您的多样化建站需求。项目地址: https://gitcode.com/GitHub_Trending/ha/halo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 23:26:48

FPGA实战:BT656接口720x576格式的Verilog实现与时序仿真

简介&#xff1a;一份基于Verilog HDL的BT656视频编码实现&#xff0c;面向FPGA开发者和数字视频接口学习者&#xff0c;解决RGB888像素格式到BT656标准数据流的转换&#xff0c;并适配720x576分辨率输出。压缩包共130个文件&#xff0c;大小约4.14MB&#xff0c;核心包含bt656…

作者头像 李华
网站建设 2026/9/9 23:26:45

Changes Made

Changes Made 【免费下载链接】oh-my-claudecode Teams-first Multi-agent orchestration for Claude Code 项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-claudecode file.ts:42-55: [what changed and why] Verification Build: [command] -> [pass/f…

作者头像 李华
网站建设 2026/9/9 23:25:00

基于YOLOv8的AI蒸汽除草机器人:从Ubuntu环境到目标检测实战

各位关注 AI 与机器人方向的朋友们&#xff0c;大家好。今天我想和大家分享一个非常有“落地感”的 AI 项目&#xff1a;AI 蒸汽除草机器人。最近看到明尼苏达州发明家打造无化学除草机器人的相关消息&#xff0c;确实让人眼前一亮。在环保要求越来越高的背景下&#xff0c;用高…

作者头像 李华
网站建设 2026/9/9 23:24:42

JAVA毕设项目:基于 Java 的小型宠物诊所管理系统的设计与实现 宠物诊所服务管理系统的设计与实现 (源码+文档,讲解、调试运行,定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华