写点什么

开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:站内信

作者:喵个咪
  • 2025-12-27
    湖南
  • 本文字数:7298 字

    阅读完需:约 24 分钟

开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:站内信

在企业级后台管理系统中,站内信是核心沟通组件之一,承担着系统通知、用户互动、业务提醒等关键场景需求。基于 Go 语言微服务框架 Kratos 构建的 Go Wind Admin,将站内信模块封装为「开箱即用」的标准化组件,无需从零开发即可快速集成,大幅降低开发成本。


本文将从功能价值、技术设计、实操使用、扩展场景四个维度,全面解析 Go Wind Admin 站内信模块。​

一、Go Wind Admin 与站内信的核心价值​

1.1 Go Wind Admin 定位​

Go Wind Admin 是基于 B 站 Kratos 微服务框架开发的企业级后台管理系统解决方案,内置用户管理、权限控制、日志审计、配置中心等核心模块,支持 Go 生态主流技术栈(GORM、Redis、ProtoBuf 等),主打「低代码集成」与「高扩展性」,适用于中小团队快速搭建后台系统。​

1.2 站内信功能的核心场景​

站内信作为系统内「非实时但可靠」的沟通载体,核心解决以下问题:​


  • 系统通知:如账号状态变更(禁用 / 启用)、权限调整、订单审核结果等业务通知;​

  • 用户互动:如管理员向指定用户发送定向提醒、用户间基于系统的留言沟通;​

  • 消息追溯:所有消息持久化存储,支持历史查询,满足审计与问题排查需求;​

  • 低干扰触达:区别于短信 / 邮件的外部推送,站内信仅在系统内展示,避免用户信息过载。

二、站内信核心技术设计​

Go Wind Admin 站内信模块遵循「简洁可靠、易于扩展」的设计原则,核心分为数据模型与业务逻辑两层。​

2.1 数据模型设计(Postgresql)​

CREATE TABLE public.internal_messages (    id          bigint generated by default as identity primary key COMMENT 'id',    created_at  timestamp with time zone COMMENT '创建时间',    updated_at  timestamp with time zone COMMENT '更新时间',    deleted_at  timestamp with time zone COMMENT '删除时间',    created_by  bigint COMMENT '创建者ID',    updated_by  bigint COMMENT '更新者ID',    deleted_by  bigint COMMENT '删除者ID',    tenant_id   bigint COMMENT '租户ID',    title       varchar COMMENT '消息标题',    content     varchar COMMENT '消息内容',    sender_id   bigint COMMENT '发送者用户ID',    category_id bigint COMMENT '分类ID',    status      varchar default 'DRAFT'::character varying COMMENT '消息状态',    type        varchar default 'NOTIFICATION'::character varying COMMENT '消息类型') COMMENT '站内信消息表';
CREATE TABLE public.internal_message_recipients ( id bigint generated by default as identity primary key COMMENT 'id', created_at timestamp with time zone COMMENT '创建时间', updated_at timestamp with time zone COMMENT '更新时间', deleted_at timestamp with time zone COMMENT '删除时间', tenant_id bigint COMMENT '租户ID', message_id bigint COMMENT '站内信内容ID', recipient_user_id bigint COMMENT '接收者用户ID', status varchar COMMENT '消息状态', received_at timestamp with time zone COMMENT '消息到达用户收件箱的时间', read_at timestamp with time zone COMMENT '用户阅读消息的时间') COMMENT '站内信消息用户接收信息表';
CREATE TABLE public.internal_message_categories ( id bigint generated by default as identity primary key COMMENT 'id', created_at timestamp with time zone COMMENT '创建时间', updated_at timestamp with time zone COMMENT '更新时间', deleted_at timestamp with time zone COMMENT '删除时间', created_by bigint COMMENT '创建者ID', updated_by bigint COMMENT '更新者ID', deleted_by bigint COMMENT '删除者ID', is_enabled boolean default true COMMENT '是否启用', sort_order integer default 0 COMMENT '排序顺序,值越小越靠前', remark varchar COMMENT '备注', tenant_id bigint COMMENT '租户ID', name varchar COMMENT '名称', code varchar COMMENT '编码', icon_url varchar COMMENT '图标URL', parent_id bigint constraint internal_message_categories_in_8a268228b9922ecb0c6e7d2099d6aa98 references public.internal_message_categories on delete set null COMMENT '父节点ID') COMMENT '站内信消息分类表';
复制代码


目前,站内信功能只设计了三张表,用于系统通知。


在 Go Wind Admin 中,数据模型已通过 Ent 的 Schema 进行了定义,开发者可直接调用:​


// app/admin/service/internal/data/ent/schema/internal_message.gopackage schema
import ( "entgo.io/ent" "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema" "entgo.io/ent/schema/field" "github.com/tx7do/go-utils/entgo/mixin")
// InternalMessage holds the schema definition for the InternalMessage entity.type InternalMessage struct { ent.Schema}
func (InternalMessage) Annotations() []schema.Annotation { return []schema.Annotation{ entsql.Annotation{ Table: "internal_messages", Charset: "utf8mb4", Collation: "utf8mb4_bin", }, entsql.WithComments(true), schema.Comment("站内信消息表"), }}
// Fields of the InternalMessage.func (InternalMessage) Fields() []ent.Field { return []ent.Field{ field.String("title"). Comment("消息标题"). Optional(). Nillable(),
field.String("content"). Comment("消息内容"). Optional(). Nillable(),
field.Uint32("sender_id"). Comment("发送者用户ID"). Optional(). Nillable(),
field.Uint32("category_id"). Comment("分类ID"). Optional(). Nillable(),
field.Enum("status"). Comment("消息状态"). NamedValues( "Draft", "DRAFT", "Published", "PUBLISHED", "Scheduled", "SCHEDULED", "Revoked", "REVOKED", "Archived", "ARCHIVED", "Deleted", "DELETED", ). Default("DRAFT"). Optional(). Nillable(),
field.Enum("type"). Comment("消息类型"). NamedValues( "Notification", "NOTIFICATION", "Private", "PRIVATE", "Group", "GROUP", ). Default("NOTIFICATION"). Optional(). Nillable(), }}
// Mixin of the InternalMessage.func (InternalMessage) Mixin() []ent.Mixin { return []ent.Mixin{ mixin.AutoIncrementId{}, mixin.TimeAt{}, mixin.OperatorID{}, mixin.TenantID{}, }}
复制代码

2.2 核心业务逻辑​

Go Wind Admin 已封装站内信全生命周期逻辑,核心流程如下:​


  1. 参数校验

  2. 数据组装

  3. 将站内信消息存入数据库;

  4. 将站内信消息分发给用户的收件箱;

  5. 通过 SSE 通知前端。


核心代码片段(发送逻辑):​


// app/admin/service/internal/service/internal_message_service.go
// SendMessage 发送消息func (s *InternalMessageService) SendMessage(ctx context.Context, req *internalMessageV1.SendMessageRequest) (*internalMessageV1.SendMessageResponse, error) { // 获取操作人信息 operator, err := auth.FromContext(ctx) if err != nil { return nil, err }
now := time.Now()
var msg *internalMessageV1.InternalMessage if msg, err = s.internalMessageRepo.Create(ctx, &internalMessageV1.CreateInternalMessageRequest{ Data: &internalMessageV1.InternalMessage{ Title: req.Title, Content: trans.Ptr(req.GetContent()), Status: trans.Ptr(internalMessageV1.InternalMessage_PUBLISHED), Type: trans.Ptr(req.GetType()), CategoryId: req.CategoryId, CreatedBy: trans.Ptr(operator.GetUserId()), CreatedAt: timeutil.TimeToTimestamppb(&now), }, }); err != nil { s.log.Errorf("create internal message failed: %s", err) return nil, err }
if req.GetTargetAll() { users, err := s.userRepo.List(ctx, &pagination.PagingRequest{NoPaging: trans.Ptr(true)}) if err != nil { s.log.Errorf("send message failed, list users failed, %s", err) } else { for _, user := range users.Items { _ = s.sendNotification(ctx, msg.GetId(), user.GetId(), operator.GetUserId(), &now, msg.GetTitle(), msg.GetContent()) } } } else { if req.RecipientUserId != nil { _ = s.sendNotification(ctx, msg.GetId(), req.GetRecipientUserId(), operator.GetUserId(), &now, msg.GetTitle(), msg.GetContent()) } else { if len(req.TargetUserIds) != 0 { for _, uid := range req.TargetUserIds { _ = s.sendNotification(ctx, msg.GetId(), uid, operator.GetUserId(), &now, msg.GetTitle(), msg.GetContent()) } } } }
return &internalMessageV1.SendMessageResponse{ MessageId: msg.GetId(), }, nil}
// sendNotification 向客户端发送通知消息func (s *InternalMessageService) sendNotification(ctx context.Context, messageId uint32, recipientUserId uint32, senderUserId uint32, now *time.Time, title, content string) error { recipient := &internalMessageV1.InternalMessageRecipient{ MessageId: trans.Ptr(messageId), RecipientUserId: trans.Ptr(recipientUserId), Status: trans.Ptr(internalMessageV1.InternalMessageRecipient_SENT), CreatedBy: trans.Ptr(senderUserId), CreatedAt: timeutil.TimeToTimestamppb(now), Title: trans.Ptr(title), Content: trans.Ptr(content), }
var err error var entity *internalMessageV1.InternalMessageRecipient if entity, err = s.internalMessageRecipientRepo.Create(ctx, recipient); err != nil { s.log.Errorf("send message failed, send to user failed, %s", err) return err } recipient.Id = entity.Id
recipientJson, _ := json.Marshal(recipient)
recipientStreamIds := s.userToken.GetAccessToken(ctx, recipientUserId) for _, streamId := range recipientStreamIds { s.sseServer.Publish(ctx, sse.StreamID(streamId), &sse.Event{ ID: []byte(uuid.New().String()), Data: recipientJson, Event: []byte("notification"), }) }
return nil}
复制代码

三、API 接口设计与使用​

Go Wind Admin 站内信模块提供 RESTful 风格 API,基于 ProtoBuf 定义接口规范,支持跨语言调用。​

3.1 核心 API 列表(Proto 定义)​

// api/protos/admin/service/v1/i_internal_message.protosyntax = "proto3";
package admin.service.v1;
import "gnostic/openapi/v3/annotations.proto";import "google/api/annotations.proto";import "google/protobuf/empty.proto";
import "pagination/v1/pagination.proto";
import "internal_message/service/v1/internal_message.proto";
// 站内信消息管理服务service InternalMessageService { // 查询站内信消息列表 rpc ListMessage(pagination.PagingRequest) returns (internal_message.service.v1.ListInternalMessageResponse) { option (google.api.http) = { get: "/admin/v1/internal-message/messages" }; }
// 查询站内信消息详情 rpc GetMessage(internal_message.service.v1.GetInternalMessageRequest) returns (internal_message.service.v1.InternalMessage) { option (google.api.http) = { get: "/admin/v1/internal-message/messages/{id}" }; }
// 更新站内信消息 rpc UpdateMessage(internal_message.service.v1.UpdateInternalMessageRequest) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/admin/v1/internal-message/messages/{data.id}" body: "*" }; }
// 删除站内信消息 rpc DeleteMessage(internal_message.service.v1.DeleteInternalMessageRequest) returns (google.protobuf.Empty) { option (google.api.http) = { delete: "/admin/v1/internal-message/messages/{id}" }; }

// 发送消息 rpc SendMessage(internal_message.service.v1.SendMessageRequest) returns (internal_message.service.v1.SendMessageResponse) { option (google.api.http) = { post: "/admin/v1/internal-message/send" body: "*" }; }
// 获取用户的收件箱列表 (通知类) rpc ListUserInbox(pagination.PagingRequest) returns (internal_message.service.v1.ListUserInboxResponse) { option (google.api.http) = { get: "/admin/v1/internal-message/inbox" }; }
// 删除用户收件箱中的通知记录 rpc DeleteNotificationFromInbox(internal_message.service.v1.DeleteNotificationFromInboxRequest) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/admin/v1/internal-message/inbox/delete" body: "*" }; }
// 将通知标记为已读 rpc MarkNotificationAsRead(internal_message.service.v1.MarkNotificationAsReadRequest) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/admin/v1/internal-message/read" body: "*" }; }
// 撤销某条消息 rpc RevokeMessage(internal_message.service.v1.RevokeMessageRequest) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/admin/v1/internal-message/revoke" body: "*" }; }}
复制代码

3.2 API 调用示例(curl)​

(1)发送系统通知​

curl -X POST http://127.0.0.1:8000/api/v1/internal-message/send \​-H "Content-Type: application/json" \​-H "Authorization: Bearer {admin_token}" \​-d '{​    "type": "NOTIFICATION",    "recipientUserId": 0,    "conversationId": 0,    "categoryId": 0,    "targetAll": true,    "title": "账号权限更新",​    "content": "您的账号已添加「订单审核」权限,生效时间:2024-10-01",}'
复制代码


响应结果:​


{  "messageId": 0}
复制代码

四、前端对接

在站内信功能中,「实时性」是提升用户体验的关键 —— 用户无需刷新页面,就能即时收到新消息提醒。这段代码基于 SSE(Server-Sent Events,服务器发送事件) 实现前端实时通知接收,适配 Go Wind Admin 后端的推送能力。


// apps/admin/src/layouts/basic.vue
function handleSseNotification( data: InternalMessageRecipient, event: MessageEvent,) { console.log('SSE', event, data);
if (!hasMessage(data)) { notifications.value.unshift(convertInternalMessageRecipient(data)); }}
function initSseClient() { const targetSseUrl = `${import.meta.env.VITE_GLOB_SSE_URL}?stream=${encodeURIComponent(accessStore.accessToken)}`; const sseClient = new SSEClient({ url: targetSseUrl, withCredentials: false, });
sseClient.connect(); sseClient.on<InternalMessageRecipient>('notification', handleSseNotification);}
复制代码

五、总结与展望​

Go Wind Admin 站内信模块通过「标准化数据模型 + 封装核心逻辑 + 开放 API 接口」,实现了「开箱即用」的特性,开发者无需关注底层存储与流程设计,仅需通过 API 即可快速集成。目前模块已支持消息发送、读取、过期清理等基础功能,未来将进一步优化:​


  • 新增消息撤回功能(支持发送后 N 分钟内撤回);​

  • 支持消息标签(如「重要」「工作」),提升筛选效率;​

  • 集成消息搜索(基于 Elasticsearch),支持全文检索。​


若你在使用过程中遇到问题,可通过 Go Wind Admin 官方 GitHub 提交 Issue,或参与社区讨论获取支持。

项目代码

参考资料


发布于: 刚刚阅读数: 4
用户头像

喵个咪

关注

还未添加个人签名 2025-12-16 加入

还未添加个人简介

评论

发布
暂无评论
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:站内信_Go_喵个咪_InfoQ写作社区