167 lines
6.6 KiB
Go
167 lines
6.6 KiB
Go
// 功能:从本人业务事实生成消息中心列表,并持久化已读回执;版本:1.0.0。
|
||
package user
|
||
|
||
import (
|
||
"fmt"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
|
||
"git.apinb.com/bsm-sdk/core/errcode"
|
||
"git.apinb.com/bsm-sdk/core/infra"
|
||
"git.apinb.com/heqiapp/platforms/backend/api/internal/impl"
|
||
"git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common"
|
||
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
|
||
"github.com/gin-gonic/gin"
|
||
"gorm.io/gorm/clause"
|
||
)
|
||
|
||
type userMessage struct {
|
||
Key, Category, Title, Summary, StatusText, Target, TargetIdentity string
|
||
OccurredAt time.Time
|
||
Read bool
|
||
}
|
||
|
||
// ListMessages 返回真实订单、工单和公告形成的消息,不生成不存在的安全告警。
|
||
func ListMessages(ctx *gin.Context) {
|
||
account, ok := common.UserAccount(ctx)
|
||
if !ok {
|
||
return
|
||
}
|
||
messages, err := loadUserMessages(account.ID)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
var receipts []models.UserMessageRead
|
||
if err = impl.DBService.Where("user_account_id = ?", account.ID).Find(&receipts).Error; err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
read := make(map[string]bool, len(receipts))
|
||
for _, receipt := range receipts {
|
||
read[receipt.MessageKey] = true
|
||
}
|
||
counts := map[string]int{"all": len(messages), "unread": 0, "safety": 0, "order": 0, "service": 0, "notice": 0}
|
||
rows := make([]gin.H, 0, len(messages))
|
||
for _, item := range messages {
|
||
item.Read = read[item.Key]
|
||
counts[item.Category]++
|
||
if !item.Read {
|
||
counts["unread"]++
|
||
}
|
||
rows = append(rows, gin.H{"key": item.Key, "category": item.Category, "title": item.Title, "summary": item.Summary,
|
||
"status_text": item.StatusText, "target": item.Target, "target_identity": item.TargetIdentity,
|
||
"occurred_at": item.OccurredAt, "read": item.Read})
|
||
}
|
||
infra.Response.Success(ctx, gin.H{"items": rows, "counts": counts})
|
||
}
|
||
|
||
// MarkMessagesRead 仅接受当前列表中存在的消息键,防止写入任意或他人对象标识。
|
||
func MarkMessagesRead(ctx *gin.Context) {
|
||
account, ok := common.UserAccount(ctx)
|
||
if !ok {
|
||
return
|
||
}
|
||
var request struct {
|
||
Keys []string `json:"keys" binding:"required,min=1,max=200,dive,required,max=96"`
|
||
}
|
||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return
|
||
}
|
||
messages, err := loadUserMessages(account.ID)
|
||
if err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
allowed := make(map[string]bool, len(messages))
|
||
for _, item := range messages {
|
||
allowed[item.Key] = true
|
||
}
|
||
rows := make([]models.UserMessageRead, 0, len(request.Keys))
|
||
seen := map[string]bool{}
|
||
for _, key := range request.Keys {
|
||
key = strings.TrimSpace(key)
|
||
if !allowed[key] || seen[key] {
|
||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||
return
|
||
}
|
||
seen[key] = true
|
||
rows = append(rows, models.UserMessageRead{Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, UserAccountID: account.ID, MessageKey: key, ReadAt: time.Now()})
|
||
}
|
||
if err = impl.DBService.Clauses(clause.OnConflict{DoNothing: true}).Create(&rows).Error; err != nil {
|
||
infra.Response.Error(ctx, err)
|
||
return
|
||
}
|
||
infra.Response.Success(ctx, gin.H{"read_count": len(rows)})
|
||
}
|
||
|
||
func loadUserMessages(userID uint64) ([]userMessage, error) {
|
||
messages := make([]userMessage, 0)
|
||
var gasOrders []models.GasorderBasic
|
||
if err := impl.DBService.Where("user_account_id = ? AND status <> ?", userID, common.StatusArchived).Order("updated_at desc").Limit(50).Find(&gasOrders).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
for _, row := range gasOrders {
|
||
messages = append(messages, orderMessage("gas", row.Identity, row.OrderNo, row.OrderStatus, row.UpdatedAt))
|
||
}
|
||
var shopOrders []models.EcOrder
|
||
if err := impl.DBService.Where("user_account_id = ? AND status <> ?", userID, common.StatusArchived).Order("updated_at desc").Limit(50).Find(&shopOrders).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
for _, row := range shopOrders {
|
||
messages = append(messages, orderMessage("shop", row.Identity, row.OrderNo, row.OrderStatus, row.UpdatedAt))
|
||
}
|
||
var tickets []models.CsTicket
|
||
if err := impl.DBService.Where("user_account_id = ? AND status <> ? AND category <> ?", userID, common.StatusArchived, "contract_change").Order("updated_at desc").Limit(50).Find(&tickets).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
for _, row := range tickets {
|
||
messages = append(messages, userMessage{Key: "ticket:" + row.Identity, Category: "service", Title: ticketTitle(row.TicketStatus), Summary: "报修工单 " + row.TicketNo, StatusText: statusText(row.TicketStatus), Target: "ticket", TargetIdentity: row.Identity, OccurredAt: row.UpdatedAt})
|
||
}
|
||
var notices []models.CmsContent
|
||
if err := impl.DBService.Where("content_type = ? AND publish_status = ? AND status = ?", "notice", "published", common.StatusEnable).Order("updated_at desc").Limit(50).Find(¬ices).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
for _, row := range notices {
|
||
messages = append(messages, userMessage{Key: "notice:" + row.Identity, Category: "notice", Title: row.Title, Summary: compactMessage(row.Body), StatusText: "平台公告", Target: "content", TargetIdentity: row.Identity, OccurredAt: row.UpdatedAt})
|
||
}
|
||
sort.SliceStable(messages, func(i, j int) bool { return messages[i].OccurredAt.After(messages[j].OccurredAt) })
|
||
return messages, nil
|
||
}
|
||
|
||
func orderMessage(kind, identity, number string, status int, at time.Time) userMessage {
|
||
name, target := "燃气订单", "gas_order"
|
||
if kind == "shop" {
|
||
name, target = "商城订单", "shop_order"
|
||
}
|
||
return userMessage{Key: kind + ":" + identity, Category: "order", Title: orderTitle(name, status), Summary: fmt.Sprintf("您的%s(订单号:%s)状态已更新", name, number), StatusText: statusText(status), Target: target, TargetIdentity: identity, OccurredAt: at}
|
||
}
|
||
|
||
func statusText(status int) string {
|
||
return map[int]string{16: "待支付", 18: "待配送", 21: "异常", 22: "已取消", 23: "已完成", 29: "运输中", 32: "待受理", 31: "处理中", 33: "配送中", 34: "待确认", 35: "已支付"}[status]
|
||
}
|
||
func orderTitle(name string, status int) string {
|
||
if value := statusText(status); value != "" {
|
||
return name + value
|
||
}
|
||
return name + "状态更新"
|
||
}
|
||
func ticketTitle(status int) string {
|
||
if status == common.StatusCompleted {
|
||
return "报修工单已完成"
|
||
}
|
||
if status == common.StatusRepairing {
|
||
return "报修工单处理中"
|
||
}
|
||
return "报修工单状态更新"
|
||
}
|
||
func compactMessage(value string) string {
|
||
value = strings.Join(strings.Fields(value), " ")
|
||
if len([]rune(value)) > 70 {
|
||
return string([]rune(value)[:70]) + "…"
|
||
}
|
||
return value
|
||
}
|