实现访客端离线留言表单和客服端管理界面 - 添加离线留言数据库表和相关模型操作 - 实现访客端留言表单提交和状态显示 - 添加客服端留言列表、详情查看和回复功能 - 支持留言状态管理和批量操作 - 添加邮件通知功能 - 优化移动端显示适配pull/55/head
parent
67e960ff0c
commit
a196947646
@ -0,0 +1,490 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"goflylivechat/models"
|
||||
"goflylivechat/tools"
|
||||
"goflylivechat/ws"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SubmitOfflineMessage(c *gin.Context) {
|
||||
kefuId := c.PostForm("kefu_id")
|
||||
visitorId := c.PostForm("visitor_id")
|
||||
visitorName := c.PostForm("visitor_name")
|
||||
visitorEmail := c.PostForm("visitor_email")
|
||||
visitorPhone := c.PostForm("visitor_phone")
|
||||
subject := c.PostForm("subject")
|
||||
content := c.PostForm("content")
|
||||
refer := c.PostForm("refer")
|
||||
extra := c.PostForm("extra")
|
||||
|
||||
if kefuId == "" || content == "" {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "客服ID和留言内容不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if visitorName == "" {
|
||||
visitorName = "匿名访客"
|
||||
}
|
||||
|
||||
if !tools.LimitFreqSingle("offline_message:"+c.ClientIP(), 1, 60) {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "提交频率过快,请稍后再试",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
kefuInfo := models.FindUser(kefuId)
|
||||
if kefuInfo.ID == 0 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "客服不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if visitorId == "" {
|
||||
visitorId = tools.Uuid()
|
||||
}
|
||||
|
||||
sourceIp := c.ClientIP()
|
||||
|
||||
msgId := models.CreateOfflineMessage(
|
||||
kefuId,
|
||||
visitorId,
|
||||
visitorName,
|
||||
visitorEmail,
|
||||
visitorPhone,
|
||||
subject,
|
||||
content,
|
||||
sourceIp,
|
||||
refer,
|
||||
extra,
|
||||
)
|
||||
|
||||
if visitorEmail != "" {
|
||||
go SendOfflineMessageNotice(kefuInfo.Nickname, visitorName, content)
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "留言提交成功,客服将尽快回复您",
|
||||
"result": gin.H{
|
||||
"message_id": msgId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func GetOfflineMessages(c *gin.Context) {
|
||||
kefuName, _ := c.Get("kefu_name")
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pagesize", "10"))
|
||||
status := c.DefaultQuery("status", "")
|
||||
|
||||
if pageSize > 50 {
|
||||
pageSize = 50
|
||||
}
|
||||
|
||||
var query interface{}
|
||||
var args []interface{}
|
||||
|
||||
if status != "" {
|
||||
if !models.IsValidOfflineMessageStatus(status) {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "无效的状态值",
|
||||
})
|
||||
return
|
||||
}
|
||||
query = "offline_message.kefu_id = ? AND offline_message.status = ?"
|
||||
args = append(args, kefuName.(string), status)
|
||||
} else {
|
||||
query = "offline_message.kefu_id = ?"
|
||||
args = append(args, kefuName.(string))
|
||||
}
|
||||
|
||||
count := models.CountOfflineMessages(query, args...)
|
||||
list := models.FindOfflineMessagesByPage(uint(page), uint(pageSize), query, args...)
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"result": gin.H{
|
||||
"count": count,
|
||||
"page": page,
|
||||
"pagesize": pageSize,
|
||||
"list": list,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func GetOfflineMessageDetail(c *gin.Context) {
|
||||
kefuName, _ := c.Get("kefu_name")
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "无效的留言ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
msg := models.FindOfflineMessageDetailById(uint(id))
|
||||
if msg.ID == 0 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 404,
|
||||
"msg": "留言不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if msg.KefuId != kefuName.(string) {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 403,
|
||||
"msg": "无权访问此留言",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if msg.Status == models.OfflineMessageStatusUnread {
|
||||
models.MarkOfflineMessageAsRead(uint(id))
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"result": gin.H{
|
||||
"id": msg.ID,
|
||||
"kefu_id": msg.KefuId,
|
||||
"visitor_id": msg.VisitorId,
|
||||
"visitor_name": msg.VisitorName,
|
||||
"visitor_email": msg.VisitorEmail,
|
||||
"visitor_phone": msg.VisitorPhone,
|
||||
"subject": msg.Subject,
|
||||
"content": msg.Content,
|
||||
"status": msg.Status,
|
||||
"status_text": models.GetOfflineMessageStatusText(msg.Status),
|
||||
"reply_content": msg.ReplyContent,
|
||||
"reply_time": msg.ReplyTime,
|
||||
"replied_by": msg.RepliedBy,
|
||||
"source_ip": msg.SourceIp,
|
||||
"refer": msg.Refer,
|
||||
"visitor_avator": msg.VisitorAvator,
|
||||
"kefu_name": msg.KefuName,
|
||||
"kefu_avator": msg.KefuAvator,
|
||||
"create_time": msg.CreateTime,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func ReplyOfflineMessage(c *gin.Context) {
|
||||
kefuName, _ := c.Get("kefu_name")
|
||||
idStr := c.PostForm("id")
|
||||
replyContent := c.PostForm("reply_content")
|
||||
|
||||
if idStr == "" || replyContent == "" {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "留言ID和回复内容不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "无效的留言ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
msg := models.FindOfflineMessageById(uint(id))
|
||||
if msg.ID == 0 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 404,
|
||||
"msg": "留言不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if msg.KefuId != kefuName.(string) {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 403,
|
||||
"msg": "无权回复此留言",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
models.UpdateOfflineMessageReply(uint(id), replyContent, kefuName.(string))
|
||||
|
||||
if msg.VisitorEmail != "" {
|
||||
go SendOfflineReplyNotice(msg.VisitorName, msg.VisitorEmail, replyContent)
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "回复成功",
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateOfflineMessageStatus(c *gin.Context) {
|
||||
kefuName, _ := c.Get("kefu_name")
|
||||
idStr := c.Param("id")
|
||||
status := c.PostForm("status")
|
||||
|
||||
if idStr == "" || status == "" {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "留言ID和状态不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if !models.IsValidOfflineMessageStatus(status) {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "无效的状态值",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "无效的留言ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
msg := models.FindOfflineMessageById(uint(id))
|
||||
if msg.ID == 0 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 404,
|
||||
"msg": "留言不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if msg.KefuId != kefuName.(string) {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 403,
|
||||
"msg": "无权操作此留言",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
models.UpdateOfflineMessageStatus(uint(id), models.OfflineMessageStatus(status))
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "状态更新成功",
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteOfflineMessage(c *gin.Context) {
|
||||
kefuName, _ := c.Get("kefu_name")
|
||||
idStr := c.Param("id")
|
||||
|
||||
if idStr == "" {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "留言ID不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "无效的留言ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
msg := models.FindOfflineMessageById(uint(id))
|
||||
if msg.ID == 0 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 404,
|
||||
"msg": "留言不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if msg.KefuId != kefuName.(string) {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 403,
|
||||
"msg": "无权删除此留言",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
models.DeleteOfflineMessage(uint(id))
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "删除成功",
|
||||
})
|
||||
}
|
||||
|
||||
func GetOfflineMessageStats(c *gin.Context) {
|
||||
kefuName, _ := c.Get("kefu_name")
|
||||
stats := models.GetOfflineMessageStatsByKefuId(kefuName.(string))
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"result": gin.H{
|
||||
"unread": stats["unread"],
|
||||
"read": stats["read"],
|
||||
"replied": stats["replied"],
|
||||
"total": stats["total"],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func BatchMarkOfflineMessagesAsRead(c *gin.Context) {
|
||||
kefuName, _ := c.Get("kefu_name")
|
||||
idsJson := c.PostForm("ids")
|
||||
|
||||
now := time.Now()
|
||||
var updateCount int64
|
||||
|
||||
if idsJson == "" {
|
||||
result := models.DB.Model(&models.OfflineMessage{}).
|
||||
Where("kefu_id = ? AND status = ?", kefuName.(string), models.OfflineMessageStatusUnread).
|
||||
Updates(map[string]interface{}{
|
||||
"status": models.OfflineMessageStatusRead,
|
||||
"updated_at": now,
|
||||
})
|
||||
updateCount = result.RowsAffected
|
||||
} else {
|
||||
var ids []uint
|
||||
err := json.Unmarshal([]byte(idsJson), &ids)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "无效的ID格式",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if len(ids) == 0 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "没有有效的留言ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
msg := models.FindOfflineMessageById(id)
|
||||
if msg.ID == 0 || msg.KefuId != kefuName.(string) {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 403,
|
||||
"msg": fmt.Sprintf("无权操作留言ID: %d", id),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = models.BatchMarkAsRead(ids)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 500,
|
||||
"msg": "批量标记失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
updateCount = int64(len(ids))
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": fmt.Sprintf("已标记 %d 条消息为已读", updateCount),
|
||||
"result": gin.H{
|
||||
"count": updateCount,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func CheckKefuOnline(c *gin.Context) {
|
||||
kefuId := c.Query("kefu_id")
|
||||
if kefuId == "" {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 400,
|
||||
"msg": "客服ID不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
kefuInfo := models.FindUser(kefuId)
|
||||
if kefuInfo.ID == 0 {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 404,
|
||||
"msg": "客服不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
isOnline := false
|
||||
for name := range ws.KefuList {
|
||||
if name == kefuId {
|
||||
isOnline = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"result": gin.H{
|
||||
"kefu_id": kefuId,
|
||||
"nickname": kefuInfo.Nickname,
|
||||
"avator": kefuInfo.Avator,
|
||||
"is_online": isOnline,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func SendOfflineMessageNotice(kefuName, visitorName, content string) {
|
||||
smtp := models.FindConfig("NoticeEmailSmtp")
|
||||
email := models.FindConfig("NoticeEmailAddress")
|
||||
password := models.FindConfig("NoticeEmailPassword")
|
||||
if smtp == "" || email == "" || password == "" {
|
||||
return
|
||||
}
|
||||
subject := fmt.Sprintf("[离线留言] %s 发来新消息", visitorName)
|
||||
body := fmt.Sprintf("访客: %s\n留言内容: %s", visitorName, content)
|
||||
err := tools.SendSmtp(smtp, email, password, []string{email}, subject, body)
|
||||
if err != nil {
|
||||
log.Println("SendOfflineMessageNotice error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func SendOfflineReplyNotice(visitorName, visitorEmail, replyContent string) {
|
||||
smtp := models.FindConfig("NoticeEmailSmtp")
|
||||
email := models.FindConfig("NoticeEmailAddress")
|
||||
password := models.FindConfig("NoticeEmailPassword")
|
||||
if smtp == "" || email == "" || password == "" {
|
||||
return
|
||||
}
|
||||
subject := "[留言回复] 客服已回复您的留言"
|
||||
body := fmt.Sprintf("尊敬的 %s:\n\n客服已回复您的留言:\n%s", visitorName, replyContent)
|
||||
err := tools.SendSmtp(smtp, email, password, []string{visitorEmail}, subject, body)
|
||||
if err != nil {
|
||||
log.Println("SendOfflineReplyNotice error:", err)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,232 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type OfflineMessageStatus string
|
||||
|
||||
const (
|
||||
OfflineMessageStatusUnread OfflineMessageStatus = "unread"
|
||||
OfflineMessageStatusRead OfflineMessageStatus = "read"
|
||||
OfflineMessageStatusReplied OfflineMessageStatus = "replied"
|
||||
)
|
||||
|
||||
type OfflineMessage struct {
|
||||
Model
|
||||
KefuId string `json:"kefu_id"`
|
||||
VisitorId string `json:"visitor_id"`
|
||||
VisitorName string `json:"visitor_name"`
|
||||
VisitorEmail string `json:"visitor_email"`
|
||||
VisitorPhone string `json:"visitor_phone"`
|
||||
Subject string `json:"subject"`
|
||||
Content string `json:"content"`
|
||||
Status OfflineMessageStatus `json:"status"`
|
||||
ReplyContent string `json:"reply_content"`
|
||||
ReplyTime *time.Time `json:"reply_time"`
|
||||
RepliedBy string `json:"replied_by"`
|
||||
SourceIp string `json:"source_ip"`
|
||||
Refer string `json:"refer"`
|
||||
Extra string `json:"extra"`
|
||||
}
|
||||
|
||||
type OfflineMessageDetail struct {
|
||||
OfflineMessage
|
||||
VisitorAvator string `json:"visitor_avator"`
|
||||
KefuName string `json:"kefu_name"`
|
||||
KefuAvator string `json:"kefu_avator"`
|
||||
CreateTime string `json:"create_time"`
|
||||
}
|
||||
|
||||
func CreateOfflineMessage(kefuId, visitorId, visitorName, visitorEmail, visitorPhone, subject, content, sourceIp, refer, extra string) uint {
|
||||
msg := &OfflineMessage{
|
||||
KefuId: kefuId,
|
||||
VisitorId: visitorId,
|
||||
VisitorName: visitorName,
|
||||
VisitorEmail: visitorEmail,
|
||||
VisitorPhone: visitorPhone,
|
||||
Subject: subject,
|
||||
Content: content,
|
||||
Status: OfflineMessageStatusUnread,
|
||||
SourceIp: sourceIp,
|
||||
Refer: refer,
|
||||
Extra: extra,
|
||||
}
|
||||
msg.UpdatedAt = time.Now()
|
||||
DB.Create(msg)
|
||||
return msg.ID
|
||||
}
|
||||
|
||||
func FindOfflineMessageById(id uint) OfflineMessage {
|
||||
var msg OfflineMessage
|
||||
DB.Where("id = ?", id).First(&msg)
|
||||
return msg
|
||||
}
|
||||
|
||||
func FindOfflineMessageDetailById(id uint) OfflineMessageDetail {
|
||||
var msg OfflineMessageDetail
|
||||
DB.Table("offline_message").
|
||||
Select("offline_message.*, visitor.avator as visitor_avator, user.nickname as kefu_name, user.avator as kefu_avator").
|
||||
Joins("left join user on offline_message.kefu_id = user.name").
|
||||
Joins("left join visitor on offline_message.visitor_id = visitor.visitor_id").
|
||||
Where("offline_message.id = ?", id).
|
||||
First(&msg)
|
||||
msg.CreateTime = msg.CreatedAt.Format("2006-01-02 15:04:05")
|
||||
return msg
|
||||
}
|
||||
|
||||
func UpdateOfflineMessageStatus(id uint, status OfflineMessageStatus) {
|
||||
msg := &OfflineMessage{
|
||||
Status: status,
|
||||
}
|
||||
msg.UpdatedAt = time.Now()
|
||||
DB.Model(&OfflineMessage{}).Where("id = ?", id).Update(msg)
|
||||
}
|
||||
|
||||
func UpdateOfflineMessageReply(id uint, replyContent string, repliedBy string) {
|
||||
now := time.Now()
|
||||
msg := &OfflineMessage{
|
||||
Status: OfflineMessageStatusReplied,
|
||||
ReplyContent: replyContent,
|
||||
ReplyTime: &now,
|
||||
RepliedBy: repliedBy,
|
||||
}
|
||||
msg.UpdatedAt = now
|
||||
DB.Model(&OfflineMessage{}).Where("id = ?", id).Updates(msg)
|
||||
}
|
||||
|
||||
func DeleteOfflineMessage(id uint) {
|
||||
DB.Where("id = ?", id).Delete(OfflineMessage{})
|
||||
}
|
||||
|
||||
func CountOfflineMessagesByKefuId(kefuId string, status OfflineMessageStatus) uint {
|
||||
var count uint
|
||||
query := DB.Model(&OfflineMessage{}).Where("kefu_id = ?", kefuId)
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
query.Count(&count)
|
||||
return count
|
||||
}
|
||||
|
||||
func CountOfflineMessages(query interface{}, args ...interface{}) uint {
|
||||
var count uint
|
||||
DB.Model(&OfflineMessage{}).Where(query, args...).Count(&count)
|
||||
return count
|
||||
}
|
||||
|
||||
func FindOfflineMessagesByPage(page uint, pagesize uint, query interface{}, args ...interface{}) []*OfflineMessageDetail {
|
||||
offset := (page - 1) * pagesize
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
var messages []*OfflineMessageDetail
|
||||
baseQuery := DB.Table("offline_message").
|
||||
Select("offline_message.*, visitor.avator as visitor_avator, user.nickname as kefu_name, user.avator as kefu_avator").
|
||||
Joins("left join user on offline_message.kefu_id = user.name").
|
||||
Joins("left join visitor on offline_message.visitor_id = visitor.visitor_id")
|
||||
|
||||
if query != nil {
|
||||
baseQuery = baseQuery.Where(query, args...)
|
||||
}
|
||||
|
||||
baseQuery.Offset(offset).Limit(pagesize).Order("offline_message.created_at desc").Find(&messages)
|
||||
|
||||
for _, msg := range messages {
|
||||
msg.CreateTime = msg.CreatedAt.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
func FindOfflineMessagesByStatus(kefuId string, status OfflineMessageStatus) []OfflineMessage {
|
||||
var messages []OfflineMessage
|
||||
query := DB.Where("kefu_id = ?", kefuId)
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
query.Order("created_at desc").Find(&messages)
|
||||
return messages
|
||||
}
|
||||
|
||||
func FindUnreadOfflineMessagesByKefuId(kefuId string) []OfflineMessage {
|
||||
return FindOfflineMessagesByStatus(kefuId, OfflineMessageStatusUnread)
|
||||
}
|
||||
|
||||
func MarkOfflineMessageAsRead(id uint) {
|
||||
UpdateOfflineMessageStatus(id, OfflineMessageStatusRead)
|
||||
}
|
||||
|
||||
func IsValidOfflineMessageStatus(status string) bool {
|
||||
s := OfflineMessageStatus(status)
|
||||
return s == OfflineMessageStatusUnread ||
|
||||
s == OfflineMessageStatusRead ||
|
||||
s == OfflineMessageStatusReplied
|
||||
}
|
||||
|
||||
func (s OfflineMessageStatus) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func GetOfflineMessageStatusText(status OfflineMessageStatus) string {
|
||||
switch status {
|
||||
case OfflineMessageStatusUnread:
|
||||
return "未读"
|
||||
case OfflineMessageStatusRead:
|
||||
return "已读"
|
||||
case OfflineMessageStatusReplied:
|
||||
return "已回复"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
func BatchUpdateOfflineMessageStatus(ids []uint, status OfflineMessageStatus) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
now := time.Now()
|
||||
return DB.Model(&OfflineMessage{}).
|
||||
Where("id IN (?)", ids).
|
||||
Updates(map[string]interface{}{
|
||||
"status": status,
|
||||
"updated_at": now,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func BatchMarkAsRead(ids []uint) error {
|
||||
return BatchUpdateOfflineMessageStatus(ids, OfflineMessageStatusRead)
|
||||
}
|
||||
|
||||
func GetOfflineMessageStatsByKefuId(kefuId string) map[string]int64 {
|
||||
stats := make(map[string]int64)
|
||||
|
||||
var unreadCount int64
|
||||
DB.Model(&OfflineMessage{}).Where("kefu_id = ? AND status = ?", kefuId, OfflineMessageStatusUnread).Count(&unreadCount)
|
||||
stats["unread"] = unreadCount
|
||||
|
||||
var readCount int64
|
||||
DB.Model(&OfflineMessage{}).Where("kefu_id = ? AND status = ?", kefuId, OfflineMessageStatusRead).Count(&readCount)
|
||||
stats["read"] = readCount
|
||||
|
||||
var repliedCount int64
|
||||
DB.Model(&OfflineMessage{}).Where("kefu_id = ? AND status = ?", kefuId, OfflineMessageStatusReplied).Count(&repliedCount)
|
||||
stats["replied"] = repliedCount
|
||||
|
||||
var totalCount int64
|
||||
DB.Model(&OfflineMessage{}).Where("kefu_id = ?", kefuId).Count(&totalCount)
|
||||
stats["total"] = totalCount
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
func FindOfflineMessagesByVisitorId(visitorId string) []OfflineMessage {
|
||||
var messages []OfflineMessage
|
||||
DB.Where("visitor_id = ?", visitorId).Order("created_at desc").Find(&messages)
|
||||
return messages
|
||||
}
|
||||
|
||||
func FindLastOfflineMessageByVisitorId(visitorId string) OfflineMessage {
|
||||
var msg OfflineMessage
|
||||
DB.Where("visitor_id = ?", visitorId).Order("created_at desc").First(&msg)
|
||||
return msg
|
||||
}
|
||||
Loading…
Reference in new issue