feat: record ticket assignment progress

This commit is contained in:
mlogclub
2026-05-03 12:53:11 +08:00
parent f2a7eb3295
commit 2447efab33
2 changed files with 83 additions and 0 deletions
+42
View File
@@ -64,6 +64,32 @@ func normalizeTicketStaleHours(staleHours int) int {
}
}
func buildTicketAssignmentProgressContent(fromUser *models.User, toUser *models.User, reason string) string {
fromName := ticketAssignmentUserDisplayName(fromUser)
if fromName == "" {
fromName = "未分配"
}
toName := ticketAssignmentUserDisplayName(toUser)
if toName == "" && toUser != nil {
toName = toUser.Username
}
content := "指派处理人:" + fromName + " -> " + toName
if trimmedReason := strings.TrimSpace(reason); trimmedReason != "" {
content += ",原因:" + trimmedReason
}
return content
}
func ticketAssignmentUserDisplayName(user *models.User) string {
if user == nil {
return ""
}
if strings.TrimSpace(user.Nickname) != "" {
return strings.TrimSpace(user.Nickname)
}
return strings.TrimSpace(user.Username)
}
func (s *ticketService) Get(id int64) *models.Ticket {
return repositories.TicketRepository.Get(sqls.DB(), id)
}
@@ -475,6 +501,14 @@ func (s *ticketService) assignTicketTx(tx *gorm.DB, req request.AssignTicketRequ
if err := s.validateRequiredAssignee(req.ToUserID); err != nil {
return nil, err
}
toUser := repositories.UserRepository.Get(tx, req.ToUserID)
if toUser == nil || toUser.Status != enums.StatusOk {
return nil, errorsx.InvalidParam("负责人不存在")
}
var fromUser *models.User
if ticket.CurrentAssigneeID > 0 {
fromUser = repositories.UserRepository.Get(tx, ticket.CurrentAssigneeID)
}
now := time.Now()
if err := repositories.TicketRepository.Updates(tx, ticket.ID, map[string]any{
"current_assignee_id": req.ToUserID,
@@ -484,6 +518,14 @@ func (s *ticketService) assignTicketTx(tx *gorm.DB, req request.AssignTicketRequ
}); err != nil {
return nil, err
}
if err := repositories.TicketProgressRepository.Create(tx, &models.TicketProgress{
TicketID: ticket.ID,
Content: buildTicketAssignmentProgressContent(fromUser, toUser, req.Reason),
AuthorID: operator.UserID,
CreatedAt: now,
}); err != nil {
return nil, err
}
return &events.TicketAssignedEvent{
TicketID: ticket.ID,
FromUserID: ticket.CurrentAssigneeID,
+41
View File
@@ -265,6 +265,47 @@ func TestTicketServiceAssignTicketRejectsDisabledUser(t *testing.T) {
}
}
func TestTicketServiceAssignTicketCreatesProgressEntry(t *testing.T) {
setupTicketTestDB(t)
operator := createTestOperator(t, "assign-progress-operator")
firstAssignee := createTestOperator(t, "assign-progress-first")
nextAssignee := createTestOperator(t, "assign-progress-next")
ticket, err := services.TicketService.CreateTicket(request.CreateTicketRequest{
Title: "assign progress ticket",
Description: "assign progress description",
CurrentAssigneeID: firstAssignee.UserID,
}, operator)
if err != nil {
t.Fatalf("CreateTicket() error = %v", err)
}
if err := services.TicketService.AssignTicket(request.AssignTicketRequest{
TicketID: ticket.ID,
ToUserID: nextAssignee.UserID,
Reason: "需要二线继续跟进",
}, operator); err != nil {
t.Fatalf("AssignTicket() error = %v", err)
}
progresses := services.TicketProgressService.Find(sqls.NewCnd().Eq("ticket_id", ticket.ID).Asc("id"))
if len(progresses) != 2 {
t.Fatalf("expected create progress and assignment progress, got %d: %+v", len(progresses), progresses)
}
assignmentProgress := progresses[1]
if assignmentProgress.AuthorID != operator.UserID {
t.Fatalf("expected assignment progress author %d, got %d", operator.UserID, assignmentProgress.AuthorID)
}
if !strings.Contains(assignmentProgress.Content, "指派处理人") {
t.Fatalf("expected assignment progress content to mention assignment, got %q", assignmentProgress.Content)
}
if !strings.Contains(assignmentProgress.Content, firstAssignee.Username) || !strings.Contains(assignmentProgress.Content, nextAssignee.Username) {
t.Fatalf("expected assignment progress to include assignee names, got %q", assignmentProgress.Content)
}
if !strings.Contains(assignmentProgress.Content, "需要二线继续跟进") {
t.Fatalf("expected assignment reason in progress content, got %q", assignmentProgress.Content)
}
}
func TestTicketServiceCreateTicketRejectsMismatchedCustomerConversation(t *testing.T) {
setupTicketTestDB(t)
operator := createTestOperator(t, "mismatch-operator")