优化咨询客服
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
type ChatConversation struct {
|
||||
ID uint64 `gorm:"primaryKey" json:"id"`
|
||||
OrderID uint64 `gorm:"not null;uniqueIndex" json:"order_id"`
|
||||
OrderID *uint64 `gorm:"uniqueIndex" json:"order_id"`
|
||||
Type string `gorm:"size:32;not null;default:'order_group'" json:"type"`
|
||||
Title string `gorm:"size:128;not null" json:"title"`
|
||||
Status string `gorm:"size:32;not null;default:'active'" json:"status"`
|
||||
|
||||
@@ -9,7 +9,7 @@ type Principal struct {
|
||||
|
||||
type ConversationDTO struct {
|
||||
ID uint64 `json:"id"`
|
||||
OrderID uint64 `json:"order_id"`
|
||||
OrderID *uint64 `json:"order_id"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Status string `json:"status"`
|
||||
|
||||
@@ -81,6 +81,20 @@ func (h *Handler) OrderConversation(c *gin.Context) {
|
||||
response.OK(c, item)
|
||||
}
|
||||
|
||||
func (h *Handler) EnsureSupportConversation(c *gin.Context) {
|
||||
userID, ok := currentUserID(c)
|
||||
if !ok {
|
||||
response.Unauthorized(c, "缺少用户上下文")
|
||||
return
|
||||
}
|
||||
item, err := h.service.EnsureSupportConversation(userID)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, item)
|
||||
}
|
||||
|
||||
func (h *Handler) Messages(c *gin.Context) {
|
||||
userID, ok := currentUserID(c)
|
||||
if !ok {
|
||||
|
||||
@@ -43,7 +43,7 @@ func EnsureOrderConversation(tx *gorm.DB, order model.RentalOrder) (*model.ChatC
|
||||
|
||||
now := time.Now()
|
||||
conversation := model.ChatConversation{
|
||||
OrderID: order.ID,
|
||||
OrderID: &order.ID,
|
||||
Type: "order_group",
|
||||
Title: orderConversationTitle(order),
|
||||
Status: "active",
|
||||
@@ -182,6 +182,89 @@ func (r *Repository) FindOrderConversation(userID uint64, orderID uint64) (*Conv
|
||||
return &dto, nil
|
||||
}
|
||||
|
||||
func (r *Repository) EnsureSupportConversation(userID uint64) (*ConversationDTO, error) {
|
||||
var conversationID uint64
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
var existing model.ChatConversation
|
||||
err := tx.Table("chat_conversations AS c").
|
||||
Select("c.*").
|
||||
Joins("JOIN chat_participants AS cp ON cp.conversation_id = c.id").
|
||||
Where("c.type = ? AND cp.participant_type = ? AND cp.participant_id = ?", "general_support", "user", userID).
|
||||
Order("c.id ASC").
|
||||
First(&existing).Error
|
||||
if err == nil {
|
||||
conversationID = existing.ID
|
||||
return nil
|
||||
}
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
conversation := model.ChatConversation{
|
||||
Type: "general_support",
|
||||
Title: "平台客服",
|
||||
Status: "active",
|
||||
}
|
||||
if err := tx.Create(&conversation).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
participants := []model.ChatParticipant{
|
||||
{
|
||||
ConversationID: conversation.ID,
|
||||
ParticipantType: "user",
|
||||
ParticipantID: userID,
|
||||
Role: "customer",
|
||||
JoinedAt: now,
|
||||
},
|
||||
}
|
||||
if supportID := defaultSupportAdminID(tx); supportID > 0 {
|
||||
participants = append(participants, model.ChatParticipant{
|
||||
ConversationID: conversation.ID,
|
||||
ParticipantType: "admin",
|
||||
ParticipantID: supportID,
|
||||
Role: "support",
|
||||
JoinedAt: now,
|
||||
})
|
||||
}
|
||||
for _, participant := range participants {
|
||||
if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&participant).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
message := model.ChatMessage{
|
||||
ConversationID: conversation.ID,
|
||||
SenderType: "system",
|
||||
SenderRole: "system",
|
||||
ContentType: "system",
|
||||
Content: "您好,客服会尽快回复,请直接描述您遇到的问题。",
|
||||
AttachmentURLS: emptyJSONList(),
|
||||
}
|
||||
if err := tx.Create(&message).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
conversation.LastMessageID = &message.ID
|
||||
conversation.LastMessagePreview = truncatePreview(message.Content)
|
||||
conversation.LastMessageAt = &message.CreatedAt
|
||||
if err := tx.Save(&conversation).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
conversationID = conversation.ID
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item, err := r.FindConversation(Principal{Type: "user", ID: userID}, conversationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.NotifyNewConversation(conversationID)
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (r *Repository) Messages(principal Principal, conversationID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
page, pageSize = normalizePagination(page, pageSize)
|
||||
if _, err := r.findParticipant(r.db, principal, conversationID, false); err != nil {
|
||||
@@ -473,7 +556,7 @@ func (r *Repository) adminNames(ids []uint64) (map[uint64]string, error) {
|
||||
|
||||
type conversationRow struct {
|
||||
ID uint64
|
||||
OrderID uint64
|
||||
OrderID *uint64
|
||||
Type string
|
||||
Title string
|
||||
Status string
|
||||
|
||||
@@ -41,6 +41,16 @@ func (s *Service) FindOrderConversation(userID uint64, orderID uint64) (*Convers
|
||||
return s.repo.FindOrderConversation(userID, orderID)
|
||||
}
|
||||
|
||||
func (s *Service) EnsureSupportConversation(userID uint64) (*ConversationDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if userID == 0 {
|
||||
return nil, ErrPermissionDenied
|
||||
}
|
||||
return s.repo.EnsureSupportConversation(userID)
|
||||
}
|
||||
|
||||
func (s *Service) Messages(principal Principal, conversationID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
|
||||
@@ -247,6 +247,7 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
|
||||
if chatHubHandler != nil {
|
||||
chatRoutes.GET("/events", chatHubHandler.UserEvents)
|
||||
}
|
||||
chatRoutes.POST("/support", chatHandler.EnsureSupportConversation)
|
||||
chatRoutes.GET("", chatHandler.List)
|
||||
chatRoutes.GET("/:id", chatHandler.Detail)
|
||||
chatRoutes.GET("/:id/messages", chatHandler.Messages)
|
||||
|
||||
@@ -315,7 +315,7 @@ CREATE TABLE system_configs (
|
||||
|
||||
CREATE TABLE chat_conversations (
|
||||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||||
order_id BIGINT UNSIGNED NOT NULL,
|
||||
order_id BIGINT UNSIGNED NULL,
|
||||
type VARCHAR(32) NOT NULL DEFAULT 'order_group',
|
||||
title VARCHAR(128) NOT NULL,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE chat_conversations
|
||||
MODIFY order_id BIGINT UNSIGNED NULL;
|
||||
Reference in New Issue
Block a user