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 }