2025-06-27 09:39:36 +00:00
|
|
|
|
package models
|
|
|
|
|
|
|
|
|
|
import (
|
2025-07-04 10:07:19 +00:00
|
|
|
|
"bytes"
|
2025-07-01 03:54:23 +00:00
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2025-07-04 10:07:19 +00:00
|
|
|
|
"github.com/codinl/go-logger"
|
2025-07-01 03:54:23 +00:00
|
|
|
|
orm "go-admin/common/global"
|
|
|
|
|
"regexp"
|
2025-07-04 10:07:19 +00:00
|
|
|
|
"text/template"
|
2025-06-27 09:39:36 +00:00
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
AnnouncementMessageType = 1 // 公告消息
|
2025-07-01 03:54:23 +00:00
|
|
|
|
BusinessMessageType = 2 // 业务消息
|
2025-06-27 09:39:36 +00:00
|
|
|
|
|
|
|
|
|
TargetTypeByAll = 1 // 接收对象类型:1=全员
|
|
|
|
|
TargetTypeByRole = 2 // 接收对象类型:2=角色
|
|
|
|
|
TargetTypeByUser = 3 // 接收对象类型:3=用户
|
|
|
|
|
|
|
|
|
|
MessageStatusOnDraft = 0 // 草稿
|
|
|
|
|
MessageStatusOnStart = 1 // 启用
|
|
|
|
|
MessageStatusOnDisable = 2 // 禁用
|
|
|
|
|
MessageStatusOnExpire = 3 // 已过期
|
|
|
|
|
|
|
|
|
|
MessageStart = 1 // 启用
|
|
|
|
|
MessageStop = 2 // 禁用
|
2025-07-04 10:07:19 +00:00
|
|
|
|
|
|
|
|
|
BizTypeOnAllot = "stock_transfer" // 库存调拨
|
|
|
|
|
EventOnAllot = "wait_receive" //待收货
|
2025-06-27 09:39:36 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// SystemMessageConfig 消息配置主表(公告 + 业务统一)
|
|
|
|
|
type SystemMessageConfig struct {
|
|
|
|
|
Model
|
|
|
|
|
|
|
|
|
|
MessageType uint8 `json:"message_type"` // 消息类型:1=公告,2=业务
|
|
|
|
|
Title string `json:"title"` // 消息标题
|
|
|
|
|
ContentTemplate string `json:"content_template"` // 消息模板内容,支持{{变量}}
|
|
|
|
|
Level uint8 `json:"level"` // 消息等级:1=普通,2=重要,3=紧急
|
|
|
|
|
DisplayMode uint8 `json:"display_mode"` // 展示方式:1=弹窗,2=滚动,3=弹窗+滚动
|
|
|
|
|
StartTime *time.Time `json:"start_time,omitempty"` // 公告生效时间
|
|
|
|
|
EndTime *time.Time `json:"end_time,omitempty"` // 公告失效时间
|
|
|
|
|
BizType string `json:"biz_type,omitempty"` // 业务类型
|
|
|
|
|
Event string `json:"event,omitempty"` // 事件类型
|
|
|
|
|
Status uint8 `json:"status"` // 状态:0=草稿,1=启用,2=禁用,3=已过期
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SystemMessageTarget 接收对象配置表(支持角色/部门/用户/全员)
|
|
|
|
|
type SystemMessageTarget struct {
|
|
|
|
|
Model
|
|
|
|
|
|
|
|
|
|
MessageConfigID uint `json:"message_config_id"` // 对应消息配置 ID
|
|
|
|
|
TargetType uint8 `json:"target_type"` // 接收对象类型:1=全员,2=角色,3=用户
|
|
|
|
|
TargetID int64 `json:"target_id,omitempty"` // 对象ID(角色ID/用户ID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SystemEventRegistry 业务事件注册表(用于事件枚举管理)
|
|
|
|
|
type SystemEventRegistry struct {
|
|
|
|
|
Model
|
|
|
|
|
|
|
|
|
|
BizType string `json:"biz_type"` // 业务类型
|
|
|
|
|
Event string `json:"event"` // 事件名
|
|
|
|
|
BizName string `json:"biz_name"` // 业务名称(展示用)
|
|
|
|
|
EventName string `json:"event_name"` // 事件名称(展示用)
|
|
|
|
|
Description string `json:"description"` // 说明
|
|
|
|
|
TemplateVariables string `json:"template_variables"` // 可用变量 JSON 字符串
|
|
|
|
|
IsEnabled bool `json:"is_enabled"` // 是否启用
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-01 03:54:23 +00:00
|
|
|
|
type TemplateVarItem struct {
|
|
|
|
|
Key string `json:"key"`
|
|
|
|
|
Desc string `json:"desc"`
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-27 09:39:36 +00:00
|
|
|
|
// SystemUserMessage 用户收到的消息记录表(实际发送记录)
|
|
|
|
|
type SystemUserMessage struct {
|
|
|
|
|
Model
|
|
|
|
|
|
|
|
|
|
UserID int64 `json:"user_id"` // 接收用户ID
|
|
|
|
|
MessageConfigID uint `json:"message_config_id"` // 对应消息配置ID,可为空(被删时保留记录)
|
|
|
|
|
MessageType uint8 `json:"message_type"` // 消息类型:1=公告,2=业务
|
|
|
|
|
Title string `json:"title"` // 消息标题快照
|
|
|
|
|
Content string `json:"content"` // 渲染后内容
|
|
|
|
|
IsRead bool `json:"is_read"` // 是否阅读
|
|
|
|
|
ReadTime *time.Time `json:"read_time,omitempty"` // 阅读时间
|
|
|
|
|
IsVisible bool `json:"is_visible"` // 是否展示给用户
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SysMessageCreateReq 新增公告请求结构体
|
|
|
|
|
type SysMessageCreateReq struct {
|
2025-07-01 03:54:23 +00:00
|
|
|
|
Title string `json:"title" binding:"required"` // 消息标题
|
|
|
|
|
ContentTemplate string `json:"content_template" binding:"required"` // 消息内容
|
|
|
|
|
Level uint8 `json:"level" binding:"required,oneof=1 2 3"` // 消息等级:1=普通,2=重要,3=紧急
|
|
|
|
|
DisplayMode uint8 `json:"display_mode" binding:"required,oneof=1 2 3"` // 展示方式:1=弹窗,2=滚动,3=弹窗+滚动
|
|
|
|
|
StartTime string `json:"start_time,omitempty"` // 公告生效时间
|
|
|
|
|
EndTime string `json:"end_time,omitempty"` // 公告失效时间
|
|
|
|
|
Status uint8 `json:"status"` // 状态:0=草稿,1=启用
|
|
|
|
|
TargetType uint8 `json:"target_type" binding:"required,oneof=1 2 3"` // 接收对象类型:1=全员,2=角色,3=用户
|
2025-07-04 10:07:19 +00:00
|
|
|
|
TargetID []int64 `json:"target_id,omitempty"` // 对象ID(角色ID/用户ID)
|
2025-06-27 09:39:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SysMessageCreateResp 创建成功响应
|
|
|
|
|
type SysMessageCreateResp struct {
|
|
|
|
|
ID uint32 `json:"id"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SysMessageEditReq 编辑公告消息-入参
|
|
|
|
|
type SysMessageEditReq struct {
|
2025-07-04 10:07:19 +00:00
|
|
|
|
ID uint `json:"id" binding:"required"` // 公告ID(必须)
|
|
|
|
|
Title string `json:"title" binding:"required"` // 标题
|
|
|
|
|
ContentTemplate string `json:"content_template" binding:"required"` // 内容模板
|
|
|
|
|
Level uint8 `json:"level" binding:"required,oneof=1 2 3"` // 重要等级
|
|
|
|
|
DisplayMode uint8 `json:"display_mode" binding:"required,oneof=1 2 3"` // 展示模式
|
|
|
|
|
StartTime string `json:"start_time"` // 生效时间(可空)
|
|
|
|
|
EndTime string `json:"end_time"` // 失效时间(可空)
|
|
|
|
|
Status uint8 `json:"status"` // 状态:0 草稿、1 启用
|
|
|
|
|
TargetType uint8 `json:"target_type" binding:"required,oneof=1 2 3"` // 接收对象类型:1=全员,2=角色,3=用户
|
|
|
|
|
TargetID []int64 `json:"target_id,omitempty"` // 接收对象ID
|
2025-06-27 09:39:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SysMessageListReq 公告消息列表-入参
|
|
|
|
|
type SysMessageListReq struct {
|
|
|
|
|
Title string `form:"title"` // 消息标题
|
|
|
|
|
Level uint8 `form:"level"` // 消息等级:1=普通,2=重要,3=紧急
|
|
|
|
|
DisplayMode uint8 `form:"display_mode"` // 展示方式:1=弹窗,2=滚动,3=弹窗+滚动
|
|
|
|
|
Status uint8 `form:"status"` // 状态:0=草稿,1=启用,2=禁用,3=已过期
|
|
|
|
|
TargetType uint8 `form:"target_type"` // 从接收对象配置表中筛选
|
2025-07-10 06:53:24 +00:00
|
|
|
|
Page int `form:"page_index,default=1"` // 页码
|
2025-06-27 09:39:36 +00:00
|
|
|
|
PageSize int `form:"page_size,default=10"` // 每页条数
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SysMessageListResp 公告消息列表-出参
|
|
|
|
|
type SysMessageListResp struct {
|
|
|
|
|
List []SysAnnouncementItem `json:"list"`
|
2025-07-10 06:53:24 +00:00
|
|
|
|
Total int64 `json:"total"` // 总条数
|
|
|
|
|
Page int `json:"page_index"` // 页码
|
|
|
|
|
PageSize int `json:"page_size"` // 每页条数
|
2025-06-27 09:39:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SysAnnouncementItem 公告消息展示结构体
|
|
|
|
|
type SysAnnouncementItem struct {
|
|
|
|
|
ID uint32 `json:"id"`
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
|
|
Title string `json:"title"` // 消息标题
|
|
|
|
|
ContentTemplate string `json:"content_template"` // 消息内容
|
|
|
|
|
Level uint8 `json:"level"` // 消息等级:1=普通,2=重要,3=紧急
|
|
|
|
|
DisplayMode uint8 `json:"display_mode"` // 展示方式:1=弹窗,2=滚动,3=弹窗+滚动
|
|
|
|
|
StartTime *time.Time `json:"start_time,omitempty"` // 公告生效时间
|
|
|
|
|
EndTime *time.Time `json:"end_time,omitempty"` // 公告失效时间
|
|
|
|
|
Status uint8 `json:"status"` // 状态:0=草稿,1=启用,2=禁用,3=已过期
|
2025-07-01 03:54:23 +00:00
|
|
|
|
TargetType uint8 `json:"target_type"` // 接收对象类型:1=全员,2=角色,3=用户
|
|
|
|
|
TargetID []int64 `json:"target_id,omitempty"` // 对象ID(角色ID/用户ID)
|
2025-06-27 09:39:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SysMessageSetStatusReq 设置公告消息状态
|
|
|
|
|
type SysMessageSetStatusReq struct {
|
|
|
|
|
ID uint `json:"id" binding:"required"`
|
|
|
|
|
Status uint8 `json:"status" binding:"required"` // 目标状态:1=启用,2=禁用
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SysMessageDeleteReq 删除公告消息
|
|
|
|
|
type SysMessageDeleteReq struct {
|
|
|
|
|
ID uint `json:"id" binding:"required"`
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-01 03:54:23 +00:00
|
|
|
|
// SysMessageDetailReq 公告详情请求
|
|
|
|
|
type SysMessageDetailReq struct {
|
|
|
|
|
ID uint `json:"id" binding:"required"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SysMessageDetailResp 公告详情响应
|
|
|
|
|
type SysMessageDetailResp struct {
|
2025-07-04 10:07:19 +00:00
|
|
|
|
ID uint `json:"id"`
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
|
|
Title string `json:"title"` // 消息标题
|
|
|
|
|
Content string `json:"content"` // 消息内容
|
|
|
|
|
Level uint8 `json:"level"` // 消息等级:1=普通,2=重要,3=紧急
|
|
|
|
|
DisplayMode uint8 `json:"display_mode"` // 展示方式:1=弹窗,2=滚动,3=弹窗+滚动
|
|
|
|
|
Status uint8 `json:"status"` // 状态:0=草稿,1=启用,2=禁用,3=已过期
|
|
|
|
|
StartTime *time.Time `json:"start_time,omitempty"` // 公告生效时间
|
|
|
|
|
EndTime *time.Time `json:"end_time,omitempty"` // 公告失效时间
|
|
|
|
|
TargetType uint8 `json:"target_type"` // 接收对象类型:1=全员,2=角色,3=用户
|
|
|
|
|
TargetID []int64 `json:"target_id,omitempty"` // 对象ID(角色ID/用户ID)
|
2025-07-01 03:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-27 09:39:36 +00:00
|
|
|
|
// UserMessageListReq 用户消息列表-入参
|
|
|
|
|
type UserMessageListReq struct {
|
2025-07-10 06:53:24 +00:00
|
|
|
|
IsRead int `json:"is_read"` // 0-全部;1-已读;2-未读
|
|
|
|
|
Page int `json:"page_index"` // 页码
|
|
|
|
|
PageSize int `json:"page_size"` // 每页数量
|
2025-06-27 09:39:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UserMessageListResp 用户消息列表-出参
|
|
|
|
|
type UserMessageListResp struct {
|
2025-07-01 03:54:23 +00:00
|
|
|
|
List []UserMessageDetailRespItem `json:"list"`
|
|
|
|
|
Total int64 `json:"total"`
|
2025-07-10 06:53:24 +00:00
|
|
|
|
Page int `json:"page_index"`
|
2025-07-01 03:54:23 +00:00
|
|
|
|
PageSize int `json:"page_size"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type UserMessageDetailRespItem struct {
|
|
|
|
|
ID uint `json:"id"`
|
2025-07-10 06:53:24 +00:00
|
|
|
|
CreatedAt time.Time `json:"created_at"` // 接收时间
|
2025-07-01 03:54:23 +00:00
|
|
|
|
UserID int64 `json:"user_id"` // 接收用户ID
|
|
|
|
|
MessageConfigID uint `json:"message_config_id"` // 对应消息配置ID,可为空(被删时保留记录)
|
|
|
|
|
MessageType uint8 `json:"message_type"` // 消息类型:1=公告,2=业务
|
|
|
|
|
Title string `json:"title"` // 消息标题快照
|
|
|
|
|
Content string `json:"content"` // 渲染后内容
|
|
|
|
|
IsRead bool `json:"is_read"` // 是否阅读
|
|
|
|
|
ReadTime *time.Time `json:"read_time,omitempty"` // 阅读时间
|
|
|
|
|
IsVisible bool `json:"is_visible"` // 是否展示给用户
|
|
|
|
|
Level uint8 `json:"level,omitempty"` // 公告消息字段,消息等级:1=普通,2=重要,3=紧急
|
|
|
|
|
DisplayMode uint8 `json:"display_mode,omitempty"` // 公告消息字段,展示方式:1=弹窗,2=滚动,3=弹窗+滚动
|
|
|
|
|
StartTime *time.Time `json:"start_time,omitempty"` // 公告消息字段,公告生效时间
|
|
|
|
|
EndTime *time.Time `json:"end_time,omitempty"` // 公告消息字段,公告失效时间
|
2025-06-27 09:39:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UserMessageSetStatusReq 用户阅读消息-入参
|
|
|
|
|
type UserMessageSetStatusReq struct {
|
2025-07-10 06:53:24 +00:00
|
|
|
|
ID []uint `json:"id"` // 消息ID(可选,read_all 为 false 时必传)
|
|
|
|
|
ReadAll bool `json:"read_all"` // 是否设为全部已读
|
2025-06-27 09:39:36 +00:00
|
|
|
|
}
|
2025-07-01 03:54:23 +00:00
|
|
|
|
|
|
|
|
|
// BusMessageCreateReq 新增业务消息-入参
|
|
|
|
|
type BusMessageCreateReq struct {
|
2025-07-04 10:07:19 +00:00
|
|
|
|
Title string `json:"title" binding:"required"` // 消息标题
|
|
|
|
|
ContentTemplate string `json:"content_template" binding:"required"` // 消息模板
|
|
|
|
|
BizType string `json:"biz_type" binding:"required"` // 业务类型
|
|
|
|
|
Event string `json:"event" binding:"required"` // 事件类型
|
|
|
|
|
RoleID []int64 `json:"role_id" binding:"required"` // 接收对象:角色ID列表
|
|
|
|
|
Status uint8 `json:"status" binding:"required"` // 消息状态:1启用,2禁用
|
2025-07-01 03:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BusMessageCreateResp 新增业务消息-出参
|
|
|
|
|
type BusMessageCreateResp struct {
|
|
|
|
|
ID uint `json:"id"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BusMessageListReq 业务消息列表 - 入参
|
|
|
|
|
type BusMessageListReq struct {
|
2025-07-10 06:53:24 +00:00
|
|
|
|
BizType string `json:"biz_type"` // 业务类型
|
|
|
|
|
Event string `json:"event"` // 事件类型
|
|
|
|
|
Status uint8 `json:"status"` // 状态:1启用,2禁用
|
|
|
|
|
RoleID []int64 `json:"role_id"` // 角色ID
|
|
|
|
|
Page int `json:"page_index"` // 页码
|
|
|
|
|
PageSize int `json:"page_size"` // 每页条数
|
2025-07-01 03:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BusMessageListResp 业务消息列表 - 出参
|
|
|
|
|
type BusMessageListResp struct {
|
|
|
|
|
List []BusMessageItem `json:"list"`
|
|
|
|
|
Total int64 `json:"total"`
|
2025-07-10 06:53:24 +00:00
|
|
|
|
Page int `json:"page_index"`
|
2025-07-01 03:54:23 +00:00
|
|
|
|
PageSize int `json:"page_size"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BusMessageItem 单条业务消息展示项
|
|
|
|
|
type BusMessageItem struct {
|
|
|
|
|
ID uint32 `json:"id"`
|
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
|
UpdatedAt time.Time `json:"updated_at"`
|
2025-07-04 10:07:19 +00:00
|
|
|
|
Title string `json:"title"` // 消息标题
|
2025-07-01 03:54:23 +00:00
|
|
|
|
ContentTemplate string `json:"content_template"` // 消息模版
|
|
|
|
|
BizType string `json:"biz_type"` // 业务类型
|
2025-07-04 10:07:19 +00:00
|
|
|
|
BizName string `json:"biz_name"` // 业务名称
|
2025-07-01 03:54:23 +00:00
|
|
|
|
Event string `json:"event"` // 事件类型
|
2025-07-04 10:07:19 +00:00
|
|
|
|
EventName string `json:"event_name"` // 事件名称
|
2025-07-01 03:54:23 +00:00
|
|
|
|
Status uint8 `json:"status"` // 状态:1启用,2禁用
|
|
|
|
|
RoleID []int64 `json:"role_id"` // 角色ID
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-04 10:07:19 +00:00
|
|
|
|
// BusMessageEditReq 编辑业务消息 - 入参
|
|
|
|
|
type BusMessageEditReq struct {
|
|
|
|
|
ID uint32 `json:"id" binding:"required"` // 消息ID
|
|
|
|
|
Title string `json:"title" binding:"required"` // 消息标题
|
|
|
|
|
BizType string `json:"biz_type" binding:"required"` // 业务类型
|
|
|
|
|
Event string `json:"event" binding:"required"` // 事件类型
|
|
|
|
|
ContentTemplate string `json:"content_template" binding:"required"` // 消息模版
|
|
|
|
|
Status uint8 `json:"status" binding:"required"` // 状态:1启用,2禁用
|
|
|
|
|
RoleID []int64 `json:"role_id" binding:"required"` // 接收对象:角色ID列表
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BusMessageSetStatusReq 设置业务消息状态
|
|
|
|
|
type BusMessageSetStatusReq struct {
|
|
|
|
|
ID uint `json:"id" binding:"required"`
|
|
|
|
|
Status uint8 `json:"status" binding:"required"` // 目标状态:1=启用,2=禁用
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BusMessageDeleteReq 删除业务消息
|
|
|
|
|
type BusMessageDeleteReq struct {
|
|
|
|
|
ID uint `json:"id" binding:"required"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BusMessageDetailReq 业务消息详情 - 入参
|
|
|
|
|
type BusMessageDetailReq struct {
|
|
|
|
|
ID uint64 `json:"id" binding:"required"` // 业务消息ID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BizTypeListReq 查询业务类型列表 - 入参
|
|
|
|
|
type BizTypeListReq struct {
|
2025-07-10 06:53:24 +00:00
|
|
|
|
Page int `json:"page_index"`
|
2025-07-04 10:07:19 +00:00
|
|
|
|
PageSize int `json:"page_size"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BizTypeListResp 查询业务类型列表 - 出参
|
|
|
|
|
type BizTypeListResp struct {
|
2025-07-10 06:53:24 +00:00
|
|
|
|
List []BizTypeItem `json:"list"` // 业务类型列表
|
|
|
|
|
Total int64 `json:"total"` // 总数
|
|
|
|
|
Page int `json:"page_index"` // 当前页
|
|
|
|
|
PageSize int `json:"page_size"` // 页大小
|
2025-07-04 10:07:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BizTypeItem 单条业务类型展示项
|
|
|
|
|
type BizTypeItem struct {
|
|
|
|
|
BizType string `json:"biz_type"` // 业务类型编码
|
|
|
|
|
BizName string `json:"biz_name"` // 业务名称
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EventListReq 查询事件类型列表 - 入参
|
|
|
|
|
type EventListReq struct {
|
|
|
|
|
BizType string `json:"biz_type" binding:"required"`
|
2025-07-10 06:53:24 +00:00
|
|
|
|
Page int `json:"page_index"`
|
2025-07-04 10:07:19 +00:00
|
|
|
|
PageSize int `json:"page_size"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EventListResp 查询事件类型列表 - 出参
|
|
|
|
|
type EventListResp struct {
|
|
|
|
|
List []EventItem `json:"list"`
|
|
|
|
|
Total int64 `json:"total"`
|
2025-07-10 06:53:24 +00:00
|
|
|
|
Page int `json:"page_index"`
|
2025-07-04 10:07:19 +00:00
|
|
|
|
PageSize int `json:"page_size"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EventItem 单条事件类型展示项
|
|
|
|
|
type EventItem struct {
|
|
|
|
|
Event string `json:"event"` // 事件类型编码
|
|
|
|
|
EventName string `json:"event_name"` // 业务类型编码
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TemplateVarReq 查询模板变量字段列表 - 入参
|
|
|
|
|
type TemplateVarReq struct {
|
|
|
|
|
BizType string `json:"biz_type" binding:"required"` // 业务类型
|
|
|
|
|
Event string `json:"event" binding:"required"` // 事件类型
|
2025-07-10 06:53:24 +00:00
|
|
|
|
Page int `json:"page_index"` // 当前页
|
2025-07-04 10:07:19 +00:00
|
|
|
|
PageSize int `json:"page_size"` // 每页大小
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TemplateVarListResp 查询模板变量字段列表 - 出参
|
|
|
|
|
type TemplateVarListResp struct {
|
2025-07-10 06:53:24 +00:00
|
|
|
|
List []TemplateVariable `json:"list"` // 模板变量列表
|
|
|
|
|
Total int64 `json:"total"` // 总数
|
|
|
|
|
Page int `json:"page_index"` // 当前页
|
|
|
|
|
PageSize int `json:"page_size"` // 每页条数
|
2025-07-04 10:07:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TemplateVariable 模板变量项
|
|
|
|
|
type TemplateVariable struct {
|
|
|
|
|
Key string `json:"key"` // 模板变量键
|
|
|
|
|
Desc string `json:"desc"` // 模板变量描述
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-10 06:53:24 +00:00
|
|
|
|
// UserMessageDeleteReq 用户消息-删除 请求参数
|
|
|
|
|
type UserMessageDeleteReq struct {
|
|
|
|
|
IDs []uint `json:"ids"` // 消息ID列表(DeleteAll=false时必填)
|
|
|
|
|
DeleteAll bool `json:"delete_all"` // 是否删除全部
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-01 03:54:23 +00:00
|
|
|
|
// ValidateBizEventAndTemplate 校验业务类型、事件类型以及模板变量是否合法
|
|
|
|
|
func ValidateBizEventAndTemplate(bizType, event, contentTemplate string) error {
|
|
|
|
|
var registry SystemEventRegistry
|
|
|
|
|
err := orm.Eloquent.Where("biz_type = ? AND event = ? AND is_enabled = ?", bizType, event, true).First(®istry).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.New("业务类型或事件类型无效")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 支持结构体数组格式:[{"key":"user_name","desc":"用户名"}]
|
|
|
|
|
var allowedItems []TemplateVarItem
|
|
|
|
|
if err := json.Unmarshal([]byte(registry.TemplateVariables), &allowedItems); err != nil {
|
|
|
|
|
return errors.New("事件注册表中变量格式非法")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取合法 key
|
|
|
|
|
allowedMap := make(map[string]bool)
|
|
|
|
|
for _, item := range allowedItems {
|
|
|
|
|
allowedMap[item.Key] = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取模板中的变量名
|
|
|
|
|
varRegex := regexp.MustCompile(`\{\{\s*(\w+)\s*\}\}`)
|
|
|
|
|
matches := varRegex.FindAllStringSubmatch(contentTemplate, -1)
|
|
|
|
|
for _, match := range matches {
|
|
|
|
|
varName := match[1]
|
|
|
|
|
if !allowedMap[varName] {
|
|
|
|
|
return fmt.Errorf("模板中使用了未注册变量:%s", varName)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-07-04 10:07:19 +00:00
|
|
|
|
|
|
|
|
|
// TriggerBizEvent 业务消息-触发消息模版
|
|
|
|
|
func TriggerBizEvent(bizType, event string, inputVars map[string]interface{}, optionalReceivers ...uint32) error {
|
|
|
|
|
// 1. 查找启用的消息配置
|
|
|
|
|
var cfg SystemMessageConfig
|
|
|
|
|
err := orm.Eloquent.Where("message_type = ? AND biz_type = ? AND event = ? AND status = 1",
|
|
|
|
|
BusinessMessageType, bizType, event).Find(&cfg).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil // 无需发送
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 渲染模板内容
|
|
|
|
|
content, err := RenderTemplate(cfg.ContentTemplate, inputVars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("模板渲染失败: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查找门店ID
|
|
|
|
|
var storeId uint32
|
|
|
|
|
if len(optionalReceivers) > 0 {
|
|
|
|
|
if event == EventOnAllot { // 库存调拨-待收货消息
|
|
|
|
|
storeId = optionalReceivers[0]
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println("没有传入 optionalReceivers,使用默认配置")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 查找接收用户
|
|
|
|
|
receiverIDs, err := GetTargetUserIds(cfg.ID, storeId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("获取接收人失败: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 写入 user_message 表
|
|
|
|
|
var msgs []SystemUserMessage
|
|
|
|
|
for _, uid := range receiverIDs {
|
|
|
|
|
msgs = append(msgs, SystemUserMessage{
|
|
|
|
|
UserID: uid,
|
|
|
|
|
MessageConfigID: uint(cfg.ID),
|
|
|
|
|
MessageType: BusinessMessageType,
|
|
|
|
|
Title: cfg.Title,
|
|
|
|
|
Content: content,
|
|
|
|
|
IsRead: false,
|
|
|
|
|
IsVisible: true,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return orm.Eloquent.Create(&msgs).Error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetTargetUserIds 获取接收用户函数模板(支持角色)
|
|
|
|
|
func GetTargetUserIds(configID uint32, storeId uint32) ([]int64, error) {
|
|
|
|
|
// 1.查询角色绑定
|
|
|
|
|
var targets []SystemMessageTarget
|
|
|
|
|
err := orm.Eloquent.Where("message_config_id = ? AND target_type = ?", configID, TargetTypeByRole).
|
|
|
|
|
Find(&targets).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2.查询该模版配置的所有角色
|
|
|
|
|
var roleIDs []int64
|
|
|
|
|
for _, t := range targets {
|
|
|
|
|
roleIDs = append(roleIDs, t.TargetID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3.查询该角色下的用户(包含字段 uid, store_data)
|
|
|
|
|
var users []SysUser
|
|
|
|
|
err = orm.Eloquent.Table("sys_user").
|
|
|
|
|
Where("role_id IN ? AND deleted_at IS NULL", roleIDs).
|
|
|
|
|
Find(&users).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果 storeId == 0,直接返回所有 uid
|
|
|
|
|
if storeId == 0 {
|
|
|
|
|
var allUserIDs []int64
|
|
|
|
|
for _, user := range users {
|
|
|
|
|
allUserIDs = append(allUserIDs, int64(user.Uid))
|
|
|
|
|
}
|
|
|
|
|
return allUserIDs, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4.筛选出门店有效用户
|
|
|
|
|
now := time.Now()
|
|
|
|
|
var validUserIDs []int64
|
|
|
|
|
|
|
|
|
|
for _, user := range users {
|
|
|
|
|
var storeList []StoreInfo
|
|
|
|
|
if err := json.Unmarshal([]byte(user.StoreData), &storeList); err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, store := range storeList {
|
|
|
|
|
// 解析时间
|
|
|
|
|
expireTime, err := time.Parse(StoreDateTimeFormat, store.ExpireTime)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// 包含当天有效时间
|
|
|
|
|
expireTime = expireTime.Add(24*time.Hour - time.Second)
|
|
|
|
|
if store.StoreID == int(storeId) && expireTime.After(now) {
|
|
|
|
|
validUserIDs = append(validUserIDs, int64(user.UserId))
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return validUserIDs, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RenderTemplate 渲染模版内容
|
|
|
|
|
func RenderTemplate(templateStr string, data map[string]interface{}) (string, error) {
|
|
|
|
|
// 创建模板实例
|
|
|
|
|
tmpl, err := template.New("msg").Option("missingkey=zero").Parse(templateStr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 渲染
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
err = tmpl.Execute(&buf, data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return buf.String(), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CheckExpiredSystemMessages 检查公告消息是否过期,如果过期则更新状态为3
|
|
|
|
|
func CheckExpiredSystemMessages() {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
|
|
|
|
// 查询所有状态为启用(1)、类型为公告(1)、且 EndTime 不为空、已过期的消息
|
|
|
|
|
var expiredMessages []SystemMessageConfig
|
|
|
|
|
err := orm.Eloquent.
|
|
|
|
|
Table("system_message_config").
|
|
|
|
|
Where("message_type = ? AND status = ? AND end_time IS NOT NULL AND end_time < ?",
|
|
|
|
|
AnnouncementMessageType, MessageStatusOnStart, now).
|
|
|
|
|
Find(&expiredMessages).Error
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Errorf("Failed to query expired messages: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(expiredMessages) == 0 {
|
|
|
|
|
logger.Info("No expired announcements found.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 遍历并更新状态
|
|
|
|
|
for _, msg := range expiredMessages {
|
|
|
|
|
err := orm.Eloquent.
|
|
|
|
|
Table("system_message_config").
|
|
|
|
|
Where("id = ?", msg.ID).
|
|
|
|
|
Update("status", MessageStatusOnExpire).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Errorf("Failed to update status for message ID %d: %v", msg.ID, err)
|
|
|
|
|
} else {
|
|
|
|
|
logger.Infof("Message ID %d marked as expired.", msg.ID)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|