From ed91928ae3cca90e7ae6168bcbe4964dcfd0a748 Mon Sep 17 00:00:00 2001 From: chenlin Date: Fri, 4 Jul 2025 18:16:20 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=A2=9E=E5=8A=A0=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E8=A7=A6=E5=8F=91=E6=B5=81=E7=A8=8B=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/game_card.go | 26 +++++ controller/order.go | 8 ++ model/message.go | 222 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 model/message.go diff --git a/controller/game_card.go b/controller/game_card.go index ea516e3..2b632ad 100644 --- a/controller/game_card.go +++ b/controller/game_card.go @@ -519,6 +519,12 @@ func PushWXPayNotice(c *gin.Context) { fundRecord.FundType = model.FundTypeExpressFee fundRecord.Remark = "借卡邮费" + // 触发消息 + model.TriggerBizEvent(model.BizTypeOnRentCard, model.EventOnDeliver, map[string]interface{}{ + "order_id": order.ID, + "uid": order.Uid, + "createdAt": order.CreatedAt, + }, uint32(order.StoreId)) } if notifyInfo.Attach == wxpay.WxPayMember { @@ -1045,6 +1051,13 @@ func PushWXPayNotice(c *gin.Context) { if err != nil { logger.Error("deliver task sub add err:", err) } + + // 触发消息 + model.TriggerBizEvent(model.BizTypeOnBuyGoods, model.EventOnDeliver, map[string]interface{}{ + "order_id": goodsOrder.OrderId, + "uid": goodsOrder.Uid, + "goods_name": goods.Name, + }) } fundRecord.Uid = uint32(goodsOrder.Uid) @@ -2053,6 +2066,12 @@ func HmPushWXPayNotice(c *gin.Context) { fundRecord.FundType = model.FundTypeExpressFee fundRecord.Remark = "借卡邮费" + // 触发消息 + model.TriggerBizEvent(model.BizTypeOnRentCard, model.EventOnDeliver, map[string]interface{}{ + "order_id": order.ID, + "uid": order.Uid, + "createdAt": order.CreatedAt, + }, uint32(order.StoreId)) } if record.Attach == wxpay.WxPayMember { @@ -2573,6 +2592,13 @@ func HmPushWXPayNotice(c *gin.Context) { if err != nil { logger.Error("deliver task sub add err:", err) } + + // 触发消息 + model.TriggerBizEvent(model.BizTypeOnBuyGoods, model.EventOnDeliver, map[string]interface{}{ + "order_id": goodsOrder.OrderId, + "uid": goodsOrder.Uid, + "goods_name": goods.Name, + }) } fundRecord.Uid = uint32(goodsOrder.Uid) diff --git a/controller/order.go b/controller/order.go index b7d5a99..23fd9e5 100644 --- a/controller/order.go +++ b/controller/order.go @@ -433,6 +433,14 @@ func RentCardOrderCreate(c *gin.Context) { } go model.ShoppingCartCreateOrder(uc.Uid, req.GameCardList) + + // 触发消息 + model.TriggerBizEvent(model.BizTypeOnRentCard, model.EventOnDeliver, map[string]interface{}{ + "order_id": order.ID, + "uid": order.Uid, + "createdAt": order.CreatedAt, + }, uint32(order.StoreId)) + RespOK(c, ret) return } diff --git a/model/message.go b/model/message.go new file mode 100644 index 0000000..93a34d7 --- /dev/null +++ b/model/message.go @@ -0,0 +1,222 @@ +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 +}