mh_server/model/message.go

223 lines
7.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package model
import (
"bytes"
"encoding/json"
"fmt"
"text/template"
"time"
)
const (
AnnouncementMessageType = 1 // 公告消息
BusinessMessageType = 2 // 业务消息
TargetTypeByAll = 1 // 接收对象类型1=全员
TargetTypeByRole = 2 // 接收对象类型2=角色
TargetTypeByUser = 3 // 接收对象类型3=用户
MessageStatusOnDraft = 0 // 草稿
MessageStatusOnStart = 1 // 启用
MessageStatusOnDisable = 2 // 禁用
MessageStatusOnExpire = 3 // 已过期
MessageStart = 1 // 启用
MessageStop = 2 // 禁用
BizTypeOnAllot = "stock_transfer" // 库存调拨
BizTypeOnRentCard = "rent_card" // 租赁卡带
BizTypeOnBuyGoods = "buy_goods" // 购买商品(线上小程序)
EventOnAllot = "wait_receive" // 待收货
EventOnDeliver = "on_deliver" // 待发货
)
// SystemMessageConfig 消息配置主表(公告 + 业务统一)
// gen:qs
//
//go:generate goqueryset -in message.go
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 接收对象配置表(支持角色/部门/用户/全员)
// gen:qs
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
}
// SystemUserMessage 用户收到的消息记录表(实际发送记录)
// gen:qs
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"` // 是否展示给用户
}
// TriggerBizEvent 业务消息-触发消息模版
func TriggerBizEvent(bizType, event string, inputVars map[string]interface{}, optionalReceivers ...uint32) error {
fmt.Println("TriggerBizEvent-start")
// 1. 查找启用的消息配置
var cfg SystemMessageConfig
err := DB.Table("system_message_config").Where("message_type = ? AND biz_type = ? AND event = ? AND status = 1",
BusinessMessageType, bizType, event).Find(&cfg).Error
if err != nil {
fmt.Println("query system_message_config err:", err)
return nil // 无需发送
}
// 2. 渲染模板内容
content, err := RenderTemplate(cfg.ContentTemplate, inputVars)
if err != nil {
fmt.Println("模板渲染失败:", err)
return fmt.Errorf("模板渲染失败: %w", err)
}
fmt.Println("content is:", content)
// 查找门店ID
var storeId uint32
if len(optionalReceivers) > 0 {
if event == EventOnAllot || event == EventOnDeliver { // 库存调拨-待收货消息、租赁订单-待发货消息
storeId = optionalReceivers[0]
}
} else {
fmt.Println("没有传入 optionalReceivers使用默认配置")
}
// 3. 查找接收用户
receiverIDs, err := GetTargetUserIds(cfg.ID, storeId)
if err != nil {
fmt.Println("获取接收人失败:", err)
return fmt.Errorf("获取接收人失败: %w", err)
}
fmt.Println("receiverIDs is:", receiverIDs)
// 4. 写入 user_message 表
for _, uid := range receiverIDs {
msg := SystemUserMessage{
UserID: uid,
MessageConfigID: uint(cfg.ID),
MessageType: BusinessMessageType,
Title: cfg.Title,
Content: content,
IsRead: false,
IsVisible: true,
}
err = DB.Create(&msg).Error
if err != nil {
fmt.Println("插入消息失败:", err)
return err
}
}
fmt.Println("TriggerBizEvent-end")
return nil
}
// GetTargetUserIds 获取接收用户函数模板(支持角色)
func GetTargetUserIds(configID uint32, storeId uint32) ([]int64, error) {
fmt.Println("******enter GetTargetUserIds******")
fmt.Println("storeId is :", storeId)
// 1.查询角色绑定
var targets []SystemMessageTarget
err := DB.Where("message_config_id = ? AND target_type = ?", configID, TargetTypeByRole).
Find(&targets).Error
if err != nil {
return nil, err
}
// 2.查询该模版配置的所有角色
var roleIDs []int
for _, t := range targets {
roleIDs = append(roleIDs, int(t.TargetID))
}
// 3.查询该角色下的用户(包含字段 uid, store_data
var users []SysUser
err = DB.Debug().Table("sys_user").
Where("role_id IN (?)", 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.UserId))
}
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 == uint64(storeId) && expireTime.After(now) {
fmt.Println("UserId is:", user.UserId)
validUserIDs = append(validUserIDs, int64(user.UserId))
break
}
}
}
fmt.Println("******leave GetTargetUserIds******")
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
}