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

简介: GoWind Admin 基于 Kratos 框架,提供开箱即用的站内信模块,支持消息推送、实时通知、分类管理与多用户收发,助力企业级后台高效集成通信功能。

开箱即用的 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.go
package 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.proto
syntax = "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,或参与社区讨论获取支持。

项目代码

参考资料

目录
相关文章
|
26天前
|
前端开发 JavaScript Go
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:为什么选 Golang+Vue3 这套组合?
go-wind-admin 采用 Golang + Vue3 技术栈,融合高性能后端与高效前端生态。后端基于 go-kratos、ent/gorm 灵活适配复杂业务,前端结合 Vue3、TypeScript 与 Vben Admin,提升开发效率与可维护性,兼顾性能、扩展性与企业级需求,是中后台系统的理想选择。(239字)
163 6
|
15天前
|
存储 JavaScript 中间件
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:Casbin集成指南
GoWind Admin(风行)是企业级前后端一体中后台框架,集成Casbin实现灵活权限管理。支持RBAC、ABAC等模型,提供开箱即用的权限控制方案,助力构建安全可靠的中后台系统。
94 0
|
前端开发 Go API
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:数据脱敏和隐私保护
GoWind Admin基于Protobuf生态,集成protoc-gen-redact插件,实现开箱即用的数据脱敏与隐私保护。通过注解定义规则,自动生成脱敏代码,支持多语言、灵活配置,零侵入业务逻辑,适用于微服务、日志、前端等场景,保障数据安全合规。
112 0
|
16天前
|
监控 NoSQL 开发者
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:极速搭建微服务应用
GoWind Admin(风行)是基于Go语言的企业级前后端一体中后台框架,集成kratos生态,支持一键生成微服务、多协议(gRPC/REST)、多数据层(gorm/ent/redis),开箱即用,大幅降低架构成本,助力快速构建高可用微服务应用。
105 2
|
1月前
|
缓存 运维 监控
一次内存诊断,让资源利用率提升 40%:揭秘隐式内存治理
阿里云云监控 2.0 推出 SysOM 底层操作系统诊断能力,基于 eBPF + BTF 协同分析,无需侵入业务,即可一键完成从物理页到文件路径、再到容器进程的全栈内存归因,让“黑盒内存”无所遁形。
535 79
|
1月前
|
监控 应用服务中间件 nginx
Agentic 时代必备技能:手把手为 Dify 应用构建全链路可观测系统
本文讲述 Dify 平台在 Agentic 应用开发中面临的可观测性挑战,从开发者与运维方双重视角出发,系统分析了当前 Dify 可观测能力的现状、局限与改进方向。
409 48
|
22天前
|
Kubernetes Cloud Native Nacos
MCP 网关实战:基于 Higress + Nacos 的零代码工具扩展方案
本文会围绕如何基于 Higress 和 Nacos 的 docker 镜像在 K8s 集群上进行分角色部署。
238 32
|
前端开发 安全 API
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:自动化解放双手,初学者快速搭建系统并自动生成前端接口
GoWind Admin 是基于 Go-Kratos 与 Vue3 的企业级中后台框架,开箱即用,集成用户、权限、租户等核心模块。搭配 protoc-gen-typescript-http,可从 Protobuf 自动生成类型安全的前端接口,大幅降低联调成本,提升开发效率,助力初学者快速搭建系统,实现前后端高效协作。
201 0
|
26天前
|
关系型数据库 API Go
初学者友好:Go-Kratos 集成 go-crud,GORM ORM CRUD 无需重复编码,轻松上手
本文介绍如何在Go-Kratos微服务中集成go-curd与GORM,实现CRUD操作免重复编码。基于kratos-gorm-example项目,通过step-by-step教程,帮助初学者快速上手:从环境搭建、模型定义到API开发,全程简化数据操作,显著提升开发效率,适合Go新手快速构建微服务应用。
116 2
|
1月前
|
JavaScript Java 关系型数据库
基于微信小程序的防诈骗管理系统
本系统基于Spring Boot与Vue框架,采用B/S架构和MySQL数据库,构建集诈骗信息库、防骗知识传播、咨询举报功能于一体的防诈骗平台,旨在提升公众防范意识,降低诈骗风险,助力社会稳定与安全。