1、优化零售明细相关支付金额不能为负值的缺陷,根据实际情况展示;

2、优化门店销售对比,时间改成审核时间;避免跨月订单统计不准确;
3、新增业务消息相关接口;
This commit is contained in:
chenlin 2025-07-01 11:54:23 +08:00
parent b3569fad57
commit 70b823b413
10 changed files with 1194 additions and 163 deletions

View File

@ -1,21 +1,257 @@
package message
import "github.com/gin-gonic/gin"
import (
"github.com/gin-gonic/gin"
"go-admin/app/admin/models"
orm "go-admin/common/global"
"go-admin/logger"
"go-admin/tools/app"
"net/http"
)
// BusMessageCreate 新增业务消息
// @Summary 新增业务消息
// @Tags 消息中心V1.5.0
// @Produce json
// @Accept json
// @Param request body models.BusMessageCreateReq true "新增业务消息模型"
// @Success 200 {object} models.BusMessageCreateResp
// @Router /api/v1/bus_message/create [post]
func BusMessageCreate(c *gin.Context) {
var req models.BusMessageCreateReq
if err := c.ShouldBindJSON(&req); err != nil {
app.Error(c, http.StatusBadRequest, err, "参数绑定失败")
return
}
// 校验业务类型、事件类型、模板变量
if err := models.ValidateBizEventAndTemplate(req.BizType, req.Event, req.ContentTemplate); err != nil {
app.Error(c, http.StatusBadRequest, err, "模板校验失败")
return
}
// 创建业务消息配置
msg := models.SystemMessageConfig{
MessageType: models.BusinessMessageType, // 业务消息
Title: req.Title,
ContentTemplate: req.ContentTemplate,
BizType: req.BizType,
Event: req.Event,
Status: req.Status,
}
if err := orm.Eloquent.Create(&msg).Error; err != nil {
app.Error(c, http.StatusInternalServerError, err, "创建业务消息失败")
return
}
// 创建接收对象配置
target := models.SystemMessageTarget{
MessageConfigID: uint(msg.ID),
TargetType: req.TargetType,
TargetID: req.TargetID,
}
if err := orm.Eloquent.Create(&target).Error; err != nil {
app.Error(c, http.StatusInternalServerError, err, "接收对象配置失败")
return
}
app.OK(c, models.BusMessageCreateResp{ID: uint(msg.ID)}, "创建成功")
}
func BusMessageSetStatus(c *gin.Context) {
}
func BusMessageDelete(c *gin.Context) {
}
// BusMessageList 业务消息列表
// @Summary 业务消息列表
// @Tags 消息中心V1.5.0
// @Accept json
// @Produce json
// @Param request body models.BusMessageListReq true "查询条件"
// @Success 200 {object} models.BusMessageListResp
// @Router /api/v1/bus_message/list [post]
func BusMessageList(c *gin.Context) {
var req models.BusMessageListReq
if err := c.ShouldBindJSON(&req); err != nil {
app.Error(c, http.StatusBadRequest, nil, "参数错误: "+err.Error())
return
}
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 10
}
// 基础查询:业务消息
db := orm.Eloquent.Table("system_message_config").
Where("message_type = ?", models.BusinessMessageType)
if req.BizType != "" {
db = db.Where("biz_type = ?", req.BizType)
}
if req.Event != "" {
db = db.Where("event = ?", req.Event)
}
if req.Status != 0 {
db = db.Where("status = ?", req.Status)
}
// 角色过滤JOIN subquery
if len(req.RoleID) > 0 {
subQuery := orm.Eloquent.Table("system_message_target").
Select("message_config_id").
Where("target_type = ?", models.TargetTypeByRole).
Where("target_id IN (?)", req.RoleID)
db = db.Where("id IN (?)", subQuery)
}
var total int64
if err := db.Count(&total).Error; err != nil {
app.Error(c, http.StatusInternalServerError, nil, "统计失败: "+err.Error())
return
}
var list []models.SystemMessageConfig
if err := db.
Order("created_at DESC").
Offset((req.Page - 1) * req.PageSize).
Limit(req.PageSize).
Find(&list).Error; err != nil {
app.Error(c, http.StatusInternalServerError, nil, "查询失败: "+err.Error())
return
}
var items []models.BusMessageItem
for _, cfg := range list {
item := models.BusMessageItem{
ID: cfg.ID,
CreatedAt: cfg.CreatedAt,
UpdatedAt: cfg.UpdatedAt,
ContentTemplate: cfg.ContentTemplate,
BizType: cfg.BizType,
Event: cfg.Event,
Status: cfg.Status,
}
// 查询该消息绑定的所有角色
var targets []models.SystemMessageTarget
if err := orm.Eloquent.Where("message_config_id = ? AND target_type = ?", cfg.ID,
models.TargetTypeByRole).Find(&targets).Error; err == nil {
for _, t := range targets {
item.RoleID = append(item.RoleID, t.TargetID)
}
}
items = append(items, item)
}
resp := models.BusMessageListResp{
List: items,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}
app.OK(c, resp, "查询成功")
}
// BusMessageSetStatus 设置业务消息状态(启用/禁用)
// @Summary 设置业务消息状态
// @Tags 消息中心V1.5.0
// @Accept json
// @Produce json
// @Param request body models.SysMessageSetStatusReq true "状态更新请求"
// @Success 200 {object} app.Response
// @Router /api/v1/bus_message/set_status [post]
func BusMessageSetStatus(c *gin.Context) {
var req models.SysMessageSetStatusReq
if err := c.ShouldBindJSON(&req); err != nil {
app.Error(c, http.StatusBadRequest, nil, "参数错误: "+err.Error())
return
}
var config models.SystemMessageConfig
if err := orm.Eloquent.Where("id = ? AND message_type = ?", req.ID, models.BusinessMessageType).
First(&config).Error; err != nil {
app.Error(c, http.StatusNotFound, nil, "业务消息不存在")
return
}
// 校验状态切换合法性
switch req.Status {
case 1: // 启用
if config.Status != models.MessageStatusOnDisable {
app.Error(c, http.StatusBadRequest, nil, "只有禁用状态才能启用")
return
}
case 2: // 禁用
if config.Status != models.MessageStatusOnStart {
app.Error(c, http.StatusBadRequest, nil, "只有启用状态才能禁用")
return
}
default:
app.Error(c, http.StatusBadRequest, nil, "不支持的目标状态")
return
}
// 状态更新
if err := orm.Eloquent.Model(&config).Update("status", req.Status).Error; err != nil {
app.Error(c, http.StatusInternalServerError, nil, "状态更新失败")
return
}
app.OK(c, nil, "状态更新成功")
}
// BusMessageDelete 删除业务消息(禁用状态可删)
// @Summary 删除业务消息
// @Tags 消息中心V1.5.0
// @Accept json
// @Produce json
// @Param request body models.SysMessageDeleteReq true "删除业务消息"
// @Success 200 {object} app.Response
// @Router /api/v1/bus_message/delete [post]
func BusMessageDelete(c *gin.Context) {
var req models.SysMessageDeleteReq
if err := c.ShouldBindJSON(&req); err != nil {
app.Error(c, http.StatusBadRequest, nil, "参数错误: "+err.Error())
return
}
var config models.SystemMessageConfig
if err := orm.Eloquent.Where("id = ? AND message_type = ?", req.ID, models.BusinessMessageType).
First(&config).Error; err != nil {
app.Error(c, http.StatusNotFound, nil, "业务消息不存在")
return
}
if config.Status != models.MessageStatusOnDisable {
app.Error(c, http.StatusBadRequest, nil, "仅禁用状态可删除")
return
}
begin := orm.Eloquent.Begin()
// 删除主配置(软删除)
if err := begin.Delete(&config).Error; err != nil {
begin.Rollback()
app.Error(c, http.StatusInternalServerError, nil, "删除公告失败")
return
}
// 删除接收对象配置
if err := begin.Where("message_config_id = ?", config.ID).
Delete(&models.SystemMessageTarget{}).Error; err != nil {
begin.Rollback()
app.Error(c, http.StatusInternalServerError, nil, "删除公告失败")
return
}
err := begin.Commit().Error
if err != nil {
begin.Rollback()
logger.Error("BusMessageDelete commit err:", logger.Field("err", err))
app.Error(c, http.StatusInternalServerError, nil, "删除公告失败")
return
}
app.OK(c, nil, "删除成功")
}
func BusMessageEdit(c *gin.Context) {

View File

@ -50,6 +50,11 @@ func SysMessageCreate(c *gin.Context) {
endTimePtr = &parsedEndTime
}
if req.Status != 0 && req.Status != 1 {
app.Error(c, http.StatusBadRequest, nil, "不支持的目标状态")
return
}
// 3. 插入 system_message_config公告
message := models.SystemMessageConfig{
MessageType: models.AnnouncementMessageType,
@ -68,14 +73,16 @@ func SysMessageCreate(c *gin.Context) {
}
// 4. 插入 system_message_target
target := models.SystemMessageTarget{
MessageConfigID: uint(message.ID),
TargetType: req.TargetType,
TargetID: req.TargetID,
}
if err = orm.Eloquent.Create(&target).Error; err != nil {
app.Error(c, http.StatusInternalServerError, nil, "接收对象配置失败: "+err.Error())
return
for _, id := range req.TargetIDs {
target := models.SystemMessageTarget{
MessageConfigID: uint(message.ID),
TargetType: req.TargetType,
TargetID: id,
}
if err = orm.Eloquent.Create(&target).Error; err != nil {
app.Error(c, http.StatusInternalServerError, nil, "接收对象配置失败: "+err.Error())
return
}
}
// 5. 判断是否需要立即下发用户消息
@ -92,15 +99,15 @@ func SysMessageCreate(c *gin.Context) {
}
case models.TargetTypeByRole: // 角色
err = orm.Eloquent.Table("sys_user").
Where("role_id = ? AND deleted_at IS NULL", req.TargetID).
Where("role_id in ? AND deleted_at IS NULL", req.TargetIDs).
Pluck("user_id", &userIDs).Error
if err != nil {
app.Error(c, http.StatusInternalServerError, nil, "获取角色用户失败: "+err.Error())
return
}
case models.TargetTypeByUser: // 单个用户
if req.TargetID != 0 {
userIDs = []int64{req.TargetID}
if len(req.TargetIDs) != 0 {
userIDs = req.TargetIDs
}
default:
app.Error(c, http.StatusInternalServerError, nil, "接收对象类型有误,不在取值范围")
@ -279,8 +286,8 @@ func SysMessageEdit(c *gin.Context) {
app.OK(c, models.SysMessageCreateResp{ID: uint32(req.ID)}, "编辑成功")
}
// SysMessageSetStatus 设置公告状态(启用/禁用)
// @Summary 设置公告状态
// SysMessageSetStatus 设置公告消息状态(启用/禁用)
// @Summary 设置公告消息状态
// @Tags 消息中心V1.5.0
// @Accept json
// @Produce json
@ -535,7 +542,7 @@ func SysMessageList(c *gin.Context) {
// 转换为展示数据
var items []models.SysAnnouncementItem
for _, cfg := range list {
items = append(items, models.SysAnnouncementItem{
item := models.SysAnnouncementItem{
ID: cfg.ID,
Title: cfg.Title,
ContentTemplate: cfg.ContentTemplate,
@ -546,7 +553,21 @@ func SysMessageList(c *gin.Context) {
Status: cfg.Status,
CreatedAt: cfg.CreatedAt,
UpdatedAt: cfg.UpdatedAt,
})
}
// 查询该消息的所有接收对象记录
var targets []models.SystemMessageTarget
if err := orm.Eloquent.
Where("message_config_id = ?", cfg.ID).
Find(&targets).Error; err == nil && len(targets) > 0 {
// 假设所有 target_type 都一致(如不是,可按需修改)
item.TargetType = targets[0].TargetType
for _, t := range targets {
item.TargetID = append(item.TargetID, t.TargetID)
}
}
items = append(items, item)
}
resp := models.SysMessageListResp{
@ -558,3 +579,51 @@ func SysMessageList(c *gin.Context) {
app.OK(c, resp, "查询成功")
}
// SysMessageDetail 公告消息详情
// @Summary 公告消息详情
// @Tags 消息中心V1.5.0
// @Accept json
// @Produce json
// @Param request body models.SysMessageDetailReq true "公告消息ID"
// @Success 200 {object} models.SysMessageDetailResp
// @Router /api/v1/sys_message/detail [post]
func SysMessageDetail(c *gin.Context) {
var req models.SysMessageDetailReq
if err := c.ShouldBindJSON(&req); err != nil {
app.Error(c, http.StatusBadRequest, nil, "参数错误: "+err.Error())
return
}
var config models.SystemMessageConfig
if err := orm.Eloquent.Where("id = ? AND message_type = 1", req.ID).First(&config).Error; err != nil {
app.Error(c, http.StatusNotFound, nil, "公告不存在")
return
}
var target models.SystemMessageTarget
_ = orm.Eloquent.Where("message_config_id = ?", config.ID).First(&target)
resp := models.SysMessageDetailResp{
ID: uint(config.ID),
Title: config.Title,
Content: config.ContentTemplate,
Level: config.Level,
DisplayMode: config.DisplayMode,
Status: config.Status,
CreatedAt: config.CreatedAt,
UpdatedAt: config.UpdatedAt,
StartTime: config.StartTime,
EndTime: config.EndTime,
}
if target.ID != 0 {
resp.Target = &models.SystemMessageTarget{
MessageConfigID: target.MessageConfigID,
TargetType: target.TargetType,
TargetID: target.TargetID,
}
}
app.OK(c, resp, "查询成功")
}

View File

@ -36,11 +36,13 @@ func UserMessageList(c *gin.Context) {
userID := tools.GetUserId(c)
query := orm.Eloquent.Where("user_id = ? AND is_visible = ?", userID, true)
if req.IsRead != 0 && req.IsRead != 1 {
if req.IsRead != 0 && req.IsRead != 1 && req.IsRead != 2 {
app.Error(c, http.StatusBadRequest, nil, "不支持的目标状态")
return
} else {
query = query.Where("is_read = ?", req.IsRead)
} else if req.IsRead == 1 {
query = query.Where("is_read = ?", true)
} else if req.IsRead == 2 {
query = query.Where("is_read = ?", false)
}
var total int64
@ -58,8 +60,57 @@ func UserMessageList(c *gin.Context) {
return
}
var result []models.UserMessageDetailRespItem
var configIDs []uint
for _, msg := range messages {
item := models.UserMessageDetailRespItem{
ID: uint(msg.ID),
UserID: msg.UserID,
MessageConfigID: msg.MessageConfigID,
MessageType: msg.MessageType,
Title: msg.Title,
Content: msg.Content,
IsRead: msg.IsRead,
ReadTime: msg.ReadTime,
IsVisible: msg.IsVisible,
}
if msg.MessageType == models.AnnouncementMessageType && msg.MessageConfigID != 0 {
configIDs = append(configIDs, msg.MessageConfigID)
}
result = append(result, item)
}
// 加载所有相关公告配置
var configs []models.SystemMessageConfig
if len(configIDs) > 0 {
err := orm.Eloquent.
Where("id IN (?)", configIDs).
Find(&configs).Error
if err != nil {
app.Error(c, http.StatusInternalServerError, nil, "加载公告配置失败: "+err.Error())
return
}
// 构建映射
configMap := make(map[uint]models.SystemMessageConfig)
for _, cfg := range configs {
configMap[uint(cfg.ID)] = cfg
}
// 填充扩展字段
for i, item := range result {
if item.MessageType == models.AnnouncementMessageType {
if cfg, ok := configMap[item.MessageConfigID]; ok {
result[i].Level = cfg.Level
result[i].DisplayMode = cfg.DisplayMode
result[i].StartTime = cfg.StartTime
result[i].EndTime = cfg.EndTime
}
}
}
}
app.OK(c, models.UserMessageListResp{
List: messages,
List: result,
Total: total,
Page: req.Page,
PageSize: req.PageSize,

View File

@ -3008,9 +3008,9 @@ func QueryStoreSalesData(req *ErpStoreSalesDataReq, c *gin.Context) (*ErpStoreSa
logger.Error("endTime parse err")
}
qs = qs.Where("maker_time BETWEEN ? AND ?", startTime, endTime)
qs = qs.Where("audit_time BETWEEN ? AND ?", startTime, endTime)
} else {
qs = qs.Where("maker_time IS NOT NULL")
qs = qs.Where("audit_time IS NOT NULL")
}
qs.Where("state = ?", ErpOrderStateAudited)

View File

@ -5843,10 +5843,10 @@ func subtractCashierData(cashierData, rejectedCashierData []ErpOrderCashier) []E
var result []ErpOrderCashier
for _, cashier := range cashierData {
adjustedAmount := cashier.Amount - rejectedMap[cashier.CashierId]
// 如果金额为负数则视为零
if adjustedAmount < 0 {
adjustedAmount = 0
}
//// 如果金额为负数则视为零
//if adjustedAmount < 0 {
// adjustedAmount = 0
//}
result = append(result, ErpOrderCashier{
CashierId: cashier.CashierId,
Name: cashier.Name,

View File

@ -1,11 +1,17 @@
package models
import (
"encoding/json"
"errors"
"fmt"
orm "go-admin/common/global"
"regexp"
"time"
)
const (
AnnouncementMessageType = 1 // 公告消息
BusinessMessageType = 2 // 业务消息
TargetTypeByAll = 1 // 接收对象类型1=全员
TargetTypeByRole = 2 // 接收对象类型2=角色
@ -58,6 +64,11 @@ type SystemEventRegistry struct {
IsEnabled bool `json:"is_enabled"` // 是否启用
}
type TemplateVarItem struct {
Key string `json:"key"`
Desc string `json:"desc"`
}
// SystemUserMessage 用户收到的消息记录表(实际发送记录)
type SystemUserMessage struct {
Model
@ -74,15 +85,15 @@ type SystemUserMessage struct {
// SysMessageCreateReq 新增公告请求结构体
type SysMessageCreateReq struct {
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" binding:"required,oneof=0 1"` // 状态0=草稿1=启用
TargetType uint8 `json:"target_type" binding:"required,oneof=1 2 3"` // 接收对象类型1=全员2=角色3=用户
TargetID int64 `json:"target_id,omitempty"` // 对象ID角色ID/用户ID
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=用户
TargetIDs []int64 `json:"target_id,omitempty"` // 对象ID角色ID/用户ID
}
// SysMessageCreateResp 创建成功响应
@ -99,7 +110,7 @@ type SysMessageEditReq struct {
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" binding:"required,oneof=0 1"` // 状态0 草稿、1 启用
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
}
@ -135,6 +146,8 @@ type SysAnnouncementItem struct {
StartTime *time.Time `json:"start_time,omitempty"` // 公告生效时间
EndTime *time.Time `json:"end_time,omitempty"` // 公告失效时间
Status uint8 `json:"status"` // 状态0=草稿1=启用2=禁用3=已过期
TargetType uint8 `json:"target_type"` // 接收对象类型1=全员2=角色3=用户
TargetID []int64 `json:"target_id,omitempty"` // 对象ID角色ID/用户ID
}
// SysMessageSetStatusReq 设置公告消息状态
@ -148,19 +161,55 @@ type SysMessageDeleteReq struct {
ID uint `json:"id" binding:"required"`
}
// SysMessageDetailReq 公告详情请求
type SysMessageDetailReq struct {
ID uint `json:"id" binding:"required"`
}
// SysMessageDetailResp 公告详情响应
type SysMessageDetailResp struct {
ID uint `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Level uint8 `json:"level"`
DisplayMode uint8 `json:"display_mode"`
Status uint8 `json:"status"`
StartTime *time.Time `json:"start_time,omitempty"` // 公告生效时间
EndTime *time.Time `json:"end_time,omitempty"` // 公告失效时间
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Target *SystemMessageTarget `json:"target,omitempty"` // 接收对象信息
}
// UserMessageListReq 用户消息列表-入参
type UserMessageListReq struct {
IsRead int `json:"is_read"` // 0-未读1-已读
IsRead int `json:"is_read"` // 0-全部1-已读2-未
Page int `json:"page"` // 页码
PageSize int `json:"page_size"` // 每页数量
}
// UserMessageListResp 用户消息列表-出参
type UserMessageListResp struct {
List []SystemUserMessage `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
List []UserMessageDetailRespItem `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
type UserMessageDetailRespItem struct {
ID uint `json:"id"`
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"` // 公告消息字段,公告失效时间
}
// UserMessageSetStatusReq 用户阅读消息-入参
@ -168,3 +217,82 @@ type UserMessageSetStatusReq struct {
ID uint `json:"id"` // 消息ID可选read_all 为 false 时必传)
ReadAll bool `json:"read_all"` // 是否设为全部已读
}
// BusMessageCreateReq 新增业务消息-入参
type BusMessageCreateReq struct {
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"` // 事件类型
TargetType uint8 `json:"target_type" binding:"required"` // 接收对象类型1全员2角色3用户
TargetID int64 `json:"target_id,omitempty"` // 接收对象ID角色ID或用户ID
Status uint8 `json:"status" binding:"required"` // 消息状态0草稿1启用2禁用
}
// BusMessageCreateResp 新增业务消息-出参
type BusMessageCreateResp struct {
ID uint `json:"id"`
}
// BusMessageListReq 业务消息列表 - 入参
type BusMessageListReq struct {
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"` // 页码
PageSize int `json:"page_size"` // 每页条数
}
// BusMessageListResp 业务消息列表 - 出参
type BusMessageListResp struct {
List []BusMessageItem `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
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"`
ContentTemplate string `json:"content_template"` // 消息模版
BizType string `json:"biz_type"` // 业务类型
Event string `json:"event"` // 事件类型
Status uint8 `json:"status"` // 状态1启用2禁用
RoleID []int64 `json:"role_id"` // 角色ID
}
// 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(&registry).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
}

View File

@ -16,6 +16,7 @@ func registerMessageCenterRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWT
r1.POST("set_status", message.SysMessageSetStatus) // 启用/禁用
r1.POST("delete", message.SysMessageDelete) // 删除
r1.POST("list", message.SysMessageList) // 查询列表
r1.POST("detail", message.SysMessageDetail) // 查询详情
}
// 业务消息(列表、新增、编辑、启用/禁用、删除)

View File

@ -15,6 +15,39 @@ const docTemplate = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/api/v1/bus_message/create": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"消息中心V1.5.0"
],
"summary": "新增业务消息",
"parameters": [
{
"description": "新增业务消息模型",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.BusMessageCreateReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.BusMessageCreateResp"
}
}
}
}
},
"/api/v1/cashier/create": {
"post": {
"consumes": [
@ -7333,6 +7366,39 @@ const docTemplate = `{
}
}
},
"/api/v1/sys_message/detail": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"消息中心V1.5.0"
],
"summary": "公告消息详情",
"parameters": [
{
"description": "公告消息ID",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.SysMessageDetailReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.SysMessageDetailResp"
}
}
}
}
},
"/api/v1/sys_message/edit": {
"post": {
"consumes": [
@ -8504,6 +8570,55 @@ const docTemplate = `{
}
}
},
"models.BusMessageCreateReq": {
"type": "object",
"required": [
"biz_type",
"content_template",
"event",
"status",
"target_type",
"title"
],
"properties": {
"biz_type": {
"description": "业务类型",
"type": "string"
},
"content_template": {
"description": "消息模板",
"type": "string"
},
"event": {
"description": "事件类型",
"type": "string"
},
"status": {
"description": "消息状态0草稿1启用2禁用",
"type": "integer"
},
"target_id": {
"description": "接收对象ID角色ID或用户ID",
"type": "integer"
},
"target_type": {
"description": "接收对象类型1全员2角色3用户",
"type": "integer"
},
"title": {
"description": "消息标题",
"type": "string"
}
}
},
"models.BusMessageCreateResp": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
}
},
"models.BusinessSummaryItem": {
"type": "object",
"properties": {
@ -21270,6 +21385,14 @@ const docTemplate = `{
"description": "状态0=草稿1=启用2=禁用3=已过期",
"type": "integer"
},
"target": {
"description": "接收对象信息(用于前端回显)",
"allOf": [
{
"$ref": "#/definitions/models.SystemMessageTarget"
}
]
},
"title": {
"description": "消息标题",
"type": "string"
@ -21581,7 +21704,6 @@ const docTemplate = `{
"content_template",
"display_mode",
"level",
"status",
"target_type",
"title"
],
@ -21618,11 +21740,7 @@ const docTemplate = `{
},
"status": {
"description": "状态0=草稿1=启用",
"type": "integer",
"enum": [
0,
1
]
"type": "integer"
},
"target_id": {
"description": "对象ID角色ID/用户ID",
@ -21662,6 +21780,62 @@ const docTemplate = `{
}
}
},
"models.SysMessageDetailReq": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "integer"
}
}
},
"models.SysMessageDetailResp": {
"type": "object",
"properties": {
"content": {
"type": "string"
},
"created_at": {
"type": "string"
},
"display_mode": {
"type": "integer"
},
"end_time": {
"description": "公告失效时间",
"type": "string"
},
"id": {
"type": "integer"
},
"level": {
"type": "integer"
},
"start_time": {
"description": "公告生效时间",
"type": "string"
},
"status": {
"type": "integer"
},
"target": {
"description": "接收对象信息",
"allOf": [
{
"$ref": "#/definitions/models.SystemMessageTarget"
}
]
},
"title": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"models.SysMessageEditReq": {
"type": "object",
"required": [
@ -21669,7 +21843,6 @@ const docTemplate = `{
"display_mode",
"id",
"level",
"status",
"target_type",
"title"
],
@ -21710,11 +21883,7 @@ const docTemplate = `{
},
"status": {
"description": "状态0 草稿、1 启用",
"type": "integer",
"enum": [
0,
1
]
"type": "integer"
},
"target_id": {
"description": "接收对象ID",
@ -22276,13 +22445,9 @@ const docTemplate = `{
}
}
},
"models.SystemUserMessage": {
"models.SystemMessageTarget": {
"type": "object",
"properties": {
"content": {
"description": "渲染后内容",
"type": "string"
},
"createdAt": {
"description": "创建时间",
"type": "string"
@ -22291,37 +22456,21 @@ const docTemplate = `{
"description": "数据库记录编号",
"type": "integer"
},
"is_read": {
"description": "是否阅读",
"type": "boolean"
},
"is_visible": {
"description": "是否展示给用户",
"type": "boolean"
},
"message_config_id": {
"description": "对应消息配置ID,可为空(被删时保留记录)",
"description": "对应消息配置 ID",
"type": "integer"
},
"message_type": {
"description": "消息类型1=公告2=业务",
"target_id": {
"description": "对象ID角色ID/用户ID",
"type": "integer"
},
"read_time": {
"description": "阅读时间",
"type": "string"
},
"title": {
"description": "消息标题快照",
"type": "string"
"target_type": {
"description": "接收对象类型1=全员2=角色3=用户",
"type": "integer"
},
"updatedAt": {
"description": "更新时间",
"type": "string"
},
"user_id": {
"description": "接收用户ID",
"type": "integer"
}
}
},
@ -22790,11 +22939,67 @@ const docTemplate = `{
}
}
},
"models.UserMessageDetailRespItem": {
"type": "object",
"properties": {
"content": {
"description": "渲染后内容",
"type": "string"
},
"display_mode": {
"description": "公告消息字段展示方式1=弹窗2=滚动3=弹窗+滚动",
"type": "integer"
},
"end_time": {
"description": "公告消息字段,公告失效时间",
"type": "string"
},
"id": {
"type": "integer"
},
"is_read": {
"description": "是否阅读",
"type": "boolean"
},
"is_visible": {
"description": "是否展示给用户",
"type": "boolean"
},
"level": {
"description": "公告消息字段消息等级1=普通2=重要3=紧急",
"type": "integer"
},
"message_config_id": {
"description": "对应消息配置ID可为空被删时保留记录",
"type": "integer"
},
"message_type": {
"description": "消息类型1=公告2=业务",
"type": "integer"
},
"read_time": {
"description": "阅读时间",
"type": "string"
},
"start_time": {
"description": "公告消息字段,公告生效时间",
"type": "string"
},
"title": {
"description": "消息标题快照",
"type": "string"
},
"user_id": {
"description": "接收用户ID",
"type": "integer"
}
}
},
"models.UserMessageListReq": {
"type": "object",
"properties": {
"is_read": {
"description": "0-未读1-已读",
"description": "0-全部1-已读2-未读",
"type": "integer"
},
"page": {
@ -22813,7 +23018,7 @@ const docTemplate = `{
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/models.SystemUserMessage"
"$ref": "#/definitions/models.UserMessageDetailRespItem"
}
},
"page": {

View File

@ -4,6 +4,39 @@
"contact": {}
},
"paths": {
"/api/v1/bus_message/create": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"消息中心V1.5.0"
],
"summary": "新增业务消息",
"parameters": [
{
"description": "新增业务消息模型",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.BusMessageCreateReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.BusMessageCreateResp"
}
}
}
}
},
"/api/v1/cashier/create": {
"post": {
"consumes": [
@ -7322,6 +7355,39 @@
}
}
},
"/api/v1/sys_message/detail": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"消息中心V1.5.0"
],
"summary": "公告消息详情",
"parameters": [
{
"description": "公告消息ID",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.SysMessageDetailReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.SysMessageDetailResp"
}
}
}
}
},
"/api/v1/sys_message/edit": {
"post": {
"consumes": [
@ -8493,6 +8559,55 @@
}
}
},
"models.BusMessageCreateReq": {
"type": "object",
"required": [
"biz_type",
"content_template",
"event",
"status",
"target_type",
"title"
],
"properties": {
"biz_type": {
"description": "业务类型",
"type": "string"
},
"content_template": {
"description": "消息模板",
"type": "string"
},
"event": {
"description": "事件类型",
"type": "string"
},
"status": {
"description": "消息状态0草稿1启用2禁用",
"type": "integer"
},
"target_id": {
"description": "接收对象ID角色ID或用户ID",
"type": "integer"
},
"target_type": {
"description": "接收对象类型1全员2角色3用户",
"type": "integer"
},
"title": {
"description": "消息标题",
"type": "string"
}
}
},
"models.BusMessageCreateResp": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
}
},
"models.BusinessSummaryItem": {
"type": "object",
"properties": {
@ -21259,6 +21374,14 @@
"description": "状态0=草稿1=启用2=禁用3=已过期",
"type": "integer"
},
"target": {
"description": "接收对象信息(用于前端回显)",
"allOf": [
{
"$ref": "#/definitions/models.SystemMessageTarget"
}
]
},
"title": {
"description": "消息标题",
"type": "string"
@ -21570,7 +21693,6 @@
"content_template",
"display_mode",
"level",
"status",
"target_type",
"title"
],
@ -21607,11 +21729,7 @@
},
"status": {
"description": "状态0=草稿1=启用",
"type": "integer",
"enum": [
0,
1
]
"type": "integer"
},
"target_id": {
"description": "对象ID角色ID/用户ID",
@ -21651,6 +21769,62 @@
}
}
},
"models.SysMessageDetailReq": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "integer"
}
}
},
"models.SysMessageDetailResp": {
"type": "object",
"properties": {
"content": {
"type": "string"
},
"created_at": {
"type": "string"
},
"display_mode": {
"type": "integer"
},
"end_time": {
"description": "公告失效时间",
"type": "string"
},
"id": {
"type": "integer"
},
"level": {
"type": "integer"
},
"start_time": {
"description": "公告生效时间",
"type": "string"
},
"status": {
"type": "integer"
},
"target": {
"description": "接收对象信息",
"allOf": [
{
"$ref": "#/definitions/models.SystemMessageTarget"
}
]
},
"title": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"models.SysMessageEditReq": {
"type": "object",
"required": [
@ -21658,7 +21832,6 @@
"display_mode",
"id",
"level",
"status",
"target_type",
"title"
],
@ -21699,11 +21872,7 @@
},
"status": {
"description": "状态0 草稿、1 启用",
"type": "integer",
"enum": [
0,
1
]
"type": "integer"
},
"target_id": {
"description": "接收对象ID",
@ -22265,13 +22434,9 @@
}
}
},
"models.SystemUserMessage": {
"models.SystemMessageTarget": {
"type": "object",
"properties": {
"content": {
"description": "渲染后内容",
"type": "string"
},
"createdAt": {
"description": "创建时间",
"type": "string"
@ -22280,37 +22445,21 @@
"description": "数据库记录编号",
"type": "integer"
},
"is_read": {
"description": "是否阅读",
"type": "boolean"
},
"is_visible": {
"description": "是否展示给用户",
"type": "boolean"
},
"message_config_id": {
"description": "对应消息配置ID,可为空(被删时保留记录)",
"description": "对应消息配置 ID",
"type": "integer"
},
"message_type": {
"description": "消息类型1=公告2=业务",
"target_id": {
"description": "对象ID角色ID/用户ID",
"type": "integer"
},
"read_time": {
"description": "阅读时间",
"type": "string"
},
"title": {
"description": "消息标题快照",
"type": "string"
"target_type": {
"description": "接收对象类型1=全员2=角色3=用户",
"type": "integer"
},
"updatedAt": {
"description": "更新时间",
"type": "string"
},
"user_id": {
"description": "接收用户ID",
"type": "integer"
}
}
},
@ -22779,11 +22928,67 @@
}
}
},
"models.UserMessageDetailRespItem": {
"type": "object",
"properties": {
"content": {
"description": "渲染后内容",
"type": "string"
},
"display_mode": {
"description": "公告消息字段展示方式1=弹窗2=滚动3=弹窗+滚动",
"type": "integer"
},
"end_time": {
"description": "公告消息字段,公告失效时间",
"type": "string"
},
"id": {
"type": "integer"
},
"is_read": {
"description": "是否阅读",
"type": "boolean"
},
"is_visible": {
"description": "是否展示给用户",
"type": "boolean"
},
"level": {
"description": "公告消息字段消息等级1=普通2=重要3=紧急",
"type": "integer"
},
"message_config_id": {
"description": "对应消息配置ID可为空被删时保留记录",
"type": "integer"
},
"message_type": {
"description": "消息类型1=公告2=业务",
"type": "integer"
},
"read_time": {
"description": "阅读时间",
"type": "string"
},
"start_time": {
"description": "公告消息字段,公告生效时间",
"type": "string"
},
"title": {
"description": "消息标题快照",
"type": "string"
},
"user_id": {
"description": "接收用户ID",
"type": "integer"
}
}
},
"models.UserMessageListReq": {
"type": "object",
"properties": {
"is_read": {
"description": "0-未读1-已读",
"description": "0-全部1-已读2-未读",
"type": "integer"
},
"page": {
@ -22802,7 +23007,7 @@
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/models.SystemUserMessage"
"$ref": "#/definitions/models.UserMessageDetailRespItem"
}
},
"page": {

View File

@ -367,6 +367,42 @@ definitions:
required:
- print_list_info
type: object
models.BusMessageCreateReq:
properties:
biz_type:
description: 业务类型
type: string
content_template:
description: 消息模板
type: string
event:
description: 事件类型
type: string
status:
description: 消息状态0草稿1启用2禁用
type: integer
target_id:
description: 接收对象ID角色ID或用户ID
type: integer
target_type:
description: 接收对象类型1全员2角色3用户
type: integer
title:
description: 消息标题
type: string
required:
- biz_type
- content_template
- event
- status
- target_type
- title
type: object
models.BusMessageCreateResp:
properties:
id:
type: integer
type: object
models.BusinessSummaryItem:
properties:
count:
@ -9615,6 +9651,10 @@ definitions:
status:
description: 状态0=草稿1=启用2=禁用3=已过期
type: integer
target:
allOf:
- $ref: '#/definitions/models.SystemMessageTarget'
description: 接收对象信息(用于前端回显)
title:
description: 消息标题
type: string
@ -9861,9 +9901,6 @@ definitions:
type: string
status:
description: 状态0=草稿1=启用
enum:
- 0
- 1
type: integer
target_id:
description: 对象ID角色ID/用户ID
@ -9882,7 +9919,6 @@ definitions:
- content_template
- display_mode
- level
- status
- target_type
- title
type: object
@ -9898,6 +9934,42 @@ definitions:
required:
- id
type: object
models.SysMessageDetailReq:
properties:
id:
type: integer
required:
- id
type: object
models.SysMessageDetailResp:
properties:
content:
type: string
created_at:
type: string
display_mode:
type: integer
end_time:
description: 公告失效时间
type: string
id:
type: integer
level:
type: integer
start_time:
description: 公告生效时间
type: string
status:
type: integer
target:
allOf:
- $ref: '#/definitions/models.SystemMessageTarget'
description: 接收对象信息
title:
type: string
updated_at:
type: string
type: object
models.SysMessageEditReq:
properties:
content_template:
@ -9928,9 +10000,6 @@ definitions:
type: string
status:
description: 状态0 草稿、1 启用
enum:
- 0
- 1
type: integer
target_id:
description: 接收对象ID
@ -9950,7 +10019,6 @@ definitions:
- display_mode
- id
- level
- status
- target_type
- title
type: object
@ -10347,41 +10415,26 @@ definitions:
description: 用户名
type: string
type: object
models.SystemUserMessage:
models.SystemMessageTarget:
properties:
content:
description: 渲染后内容
type: string
createdAt:
description: 创建时间
type: string
id:
description: 数据库记录编号
type: integer
is_read:
description: 是否阅读
type: boolean
is_visible:
description: 是否展示给用户
type: boolean
message_config_id:
description: 对应消息配置ID,可为空(被删时保留记录)
description: 对应消息配置 ID
type: integer
message_type:
description: 消息类型1=公告2=业务
target_id:
description: 对象ID角色ID/用户ID
type: integer
target_type:
description: 接收对象类型1=全员2=角色3=用户
type: integer
read_time:
description: 阅读时间
type: string
title:
description: 消息标题快照
type: string
updatedAt:
description: 更新时间
type: string
user_id:
description: 接收用户ID
type: integer
type: object
models.TableData:
properties:
@ -10722,10 +10775,51 @@ definitions:
description: 总条数
type: integer
type: object
models.UserMessageDetailRespItem:
properties:
content:
description: 渲染后内容
type: string
display_mode:
description: 公告消息字段展示方式1=弹窗2=滚动3=弹窗+滚动
type: integer
end_time:
description: 公告消息字段,公告失效时间
type: string
id:
type: integer
is_read:
description: 是否阅读
type: boolean
is_visible:
description: 是否展示给用户
type: boolean
level:
description: 公告消息字段消息等级1=普通2=重要3=紧急
type: integer
message_config_id:
description: 对应消息配置ID可为空被删时保留记录
type: integer
message_type:
description: 消息类型1=公告2=业务
type: integer
read_time:
description: 阅读时间
type: string
start_time:
description: 公告消息字段,公告生效时间
type: string
title:
description: 消息标题快照
type: string
user_id:
description: 接收用户ID
type: integer
type: object
models.UserMessageListReq:
properties:
is_read:
description: 0-未读1-已读
description: 0-全部1-已读2-未
type: integer
page:
description: 页码
@ -10738,7 +10832,7 @@ definitions:
properties:
list:
items:
$ref: '#/definitions/models.SystemUserMessage'
$ref: '#/definitions/models.UserMessageDetailRespItem'
type: array
page:
type: integer
@ -11048,6 +11142,27 @@ definitions:
info:
contact: {}
paths:
/api/v1/bus_message/create:
post:
consumes:
- application/json
parameters:
- description: 新增业务消息模型
in: body
name: request
required: true
schema:
$ref: '#/definitions/models.BusMessageCreateReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/models.BusMessageCreateResp'
summary: 新增业务消息
tags:
- 消息中心V1.5.0
/api/v1/cashier/create:
post:
consumes:
@ -15593,6 +15708,27 @@ paths:
summary: 删除公告消息
tags:
- 消息中心V1.5.0
/api/v1/sys_message/detail:
post:
consumes:
- application/json
parameters:
- description: 公告消息ID
in: body
name: request
required: true
schema:
$ref: '#/definitions/models.SysMessageDetailReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/models.SysMessageDetailResp'
summary: 公告消息详情
tags:
- 消息中心V1.5.0
/api/v1/sys_message/edit:
post:
consumes: