refactor(ticket): contract backend ticket core
This commit is contained in:
@@ -1,35 +1,23 @@
|
||||
package builders
|
||||
|
||||
import (
|
||||
"cs-agent/internal/models"
|
||||
"cs-agent/internal/pkg/dto/response"
|
||||
"cs-agent/internal/pkg/enums"
|
||||
"cs-agent/internal/pkg/utils"
|
||||
"cs-agent/internal/services"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
"cs-agent/internal/models"
|
||||
"cs-agent/internal/pkg/dto/response"
|
||||
"cs-agent/internal/pkg/utils"
|
||||
"cs-agent/internal/services"
|
||||
)
|
||||
|
||||
type TicketBuildContext struct {
|
||||
TagsByTicketID map[int64][]models.Tag
|
||||
Priorities map[int64]*models.TicketPriorityConfig
|
||||
ResolutionCodes map[string]*models.TicketResolutionCode
|
||||
Users map[int64]*models.User
|
||||
Teams map[int64]*models.AgentTeam
|
||||
Customers map[int64]*models.Customer
|
||||
SLAByTicketID map[int64][]models.TicketSLARecord
|
||||
WatchedTicketIDs map[int64]struct{}
|
||||
TagsByTicketID map[int64][]models.Tag
|
||||
Users map[int64]*models.User
|
||||
Customers map[int64]*models.Customer
|
||||
}
|
||||
|
||||
type TicketDetailBuildContext struct {
|
||||
Users map[int64]*models.User
|
||||
Teams map[int64]*models.AgentTeam
|
||||
AgentProfiles map[int64]*models.AgentProfile
|
||||
RelatedTickets map[int64]*models.Ticket
|
||||
Customers map[int64]*models.Customer
|
||||
AIAgents map[int64]*models.AIAgent
|
||||
Users map[int64]*models.User
|
||||
}
|
||||
|
||||
func BuildTicket(item *models.Ticket) *response.TicketResponse {
|
||||
@@ -41,100 +29,43 @@ func BuildTicketWithContext(item *models.Ticket, ctx *TicketBuildContext) *respo
|
||||
return nil
|
||||
}
|
||||
ret := &response.TicketResponse{
|
||||
ID: item.ID,
|
||||
TicketNo: item.TicketNo,
|
||||
Title: item.Title,
|
||||
Description: item.Description,
|
||||
Source: item.Source,
|
||||
Channel: item.Channel,
|
||||
CustomerID: item.CustomerID,
|
||||
ConversationID: item.ConversationID,
|
||||
Type: item.Type,
|
||||
Priority: item.Priority,
|
||||
Severity: item.Severity,
|
||||
Status: item.Status,
|
||||
CurrentTeamID: item.CurrentTeamID,
|
||||
CurrentAssigneeID: item.CurrentAssigneeID,
|
||||
PendingReason: item.PendingReason,
|
||||
CloseReason: item.CloseReason,
|
||||
ResolutionCode: item.ResolutionCode,
|
||||
ResolutionSummary: item.ResolutionSummary,
|
||||
FirstResponseAt: utils.FormatTimePtr(item.FirstResponseAt),
|
||||
ResolvedAt: utils.FormatTimePtr(item.ResolvedAt),
|
||||
ClosedAt: utils.FormatTimePtr(item.ClosedAt),
|
||||
DueAt: utils.FormatTimePtr(item.DueAt),
|
||||
NextReplyDeadlineAt: utils.FormatTimePtr(item.NextReplyDeadlineAt),
|
||||
ResolveDeadlineAt: utils.FormatTimePtr(item.ResolveDeadlineAt),
|
||||
ReopenedCount: item.ReopenedCount,
|
||||
CreatedAt: utils.FormatTime(item.CreatedAt),
|
||||
UpdatedAt: utils.FormatTime(item.UpdatedAt),
|
||||
}
|
||||
if ctx != nil {
|
||||
if _, ok := ctx.WatchedTicketIDs[item.ID]; ok {
|
||||
ret.WatchedByMe = true
|
||||
}
|
||||
ID: item.ID,
|
||||
TicketNo: item.TicketNo,
|
||||
Title: item.Title,
|
||||
Description: item.Description,
|
||||
Source: item.Source,
|
||||
Channel: item.Channel,
|
||||
CustomerID: item.CustomerID,
|
||||
ConversationID: item.ConversationID,
|
||||
Status: item.Status,
|
||||
CurrentAssigneeID: item.CurrentAssigneeID,
|
||||
CreatedBy: item.CreateUserID,
|
||||
CreatedByName: item.CreateUserName,
|
||||
HandledAt: utils.FormatTimePtr(item.HandledAt),
|
||||
CreatedAt: utils.FormatTime(item.CreatedAt),
|
||||
UpdatedAt: utils.FormatTime(item.UpdatedAt),
|
||||
}
|
||||
if ctx != nil && ctx.TagsByTicketID != nil {
|
||||
ret.Tags = BuildTagResponses(ctx.TagsByTicketID[item.ID])
|
||||
} else {
|
||||
ret.Tags = BuildTagResponses(services.TicketService.GetTags(item.ID))
|
||||
}
|
||||
if item.Priority > 0 && ctx != nil && ctx.Priorities != nil {
|
||||
if priority := ctx.Priorities[item.Priority]; priority != nil {
|
||||
ret.PriorityName = priority.Name
|
||||
if item.CurrentAssigneeID > 0 {
|
||||
if ctx != nil && ctx.Users != nil {
|
||||
ret.CurrentAssigneeName = buildTicketUserDisplayName(ctx.Users[item.CurrentAssigneeID])
|
||||
}
|
||||
} else if item.Priority > 0 {
|
||||
if priority := services.TicketPriorityConfigService.Get(item.Priority); priority != nil {
|
||||
ret.PriorityName = priority.Name
|
||||
if ret.CurrentAssigneeName == "" {
|
||||
ret.CurrentAssigneeName = buildTicketUserDisplayName(services.UserService.Get(item.CurrentAssigneeID))
|
||||
}
|
||||
}
|
||||
if item.ResolutionCode != "" && ctx != nil && ctx.ResolutionCodes != nil {
|
||||
if code := ctx.ResolutionCodes[item.ResolutionCode]; code != nil {
|
||||
ret.ResolutionCodeName = code.Name
|
||||
if item.CustomerID > 0 {
|
||||
if ctx != nil && ctx.Customers != nil {
|
||||
ret.Customer = BuildCustomer(ctx.Customers[item.CustomerID])
|
||||
}
|
||||
} else if item.ResolutionCode != "" {
|
||||
if code := services.TicketResolutionCodeService.Take("code = ? AND status <> ?", item.ResolutionCode, enums.StatusDeleted); code != nil {
|
||||
ret.ResolutionCodeName = code.Name
|
||||
if ret.Customer == nil {
|
||||
ret.Customer = BuildCustomer(services.CustomerService.Get(item.CustomerID))
|
||||
}
|
||||
}
|
||||
if item.CurrentAssigneeID > 0 && ctx != nil && ctx.Users != nil {
|
||||
if user := ctx.Users[item.CurrentAssigneeID]; user != nil {
|
||||
ret.CurrentAssigneeName = user.Nickname
|
||||
if ret.CurrentAssigneeName == "" {
|
||||
ret.CurrentAssigneeName = user.Username
|
||||
}
|
||||
}
|
||||
} else if item.CurrentAssigneeID > 0 {
|
||||
if user := services.UserService.Get(item.CurrentAssigneeID); user != nil {
|
||||
ret.CurrentAssigneeName = user.Nickname
|
||||
if ret.CurrentAssigneeName == "" {
|
||||
ret.CurrentAssigneeName = user.Username
|
||||
}
|
||||
}
|
||||
}
|
||||
if item.CurrentTeamID > 0 && ctx != nil && ctx.Teams != nil {
|
||||
if team := ctx.Teams[item.CurrentTeamID]; team != nil {
|
||||
ret.CurrentTeamName = team.Name
|
||||
}
|
||||
} else if item.CurrentTeamID > 0 {
|
||||
if team := services.AgentTeamService.Get(item.CurrentTeamID); team != nil {
|
||||
ret.CurrentTeamName = team.Name
|
||||
}
|
||||
}
|
||||
if item.CustomerID > 0 && ctx != nil && ctx.Customers != nil {
|
||||
ret.Customer = BuildCustomer(ctx.Customers[item.CustomerID])
|
||||
} else if item.CustomerID > 0 {
|
||||
ret.Customer = BuildCustomer(services.CustomerService.Get(item.CustomerID))
|
||||
}
|
||||
if ctx != nil && ctx.SLAByTicketID != nil {
|
||||
ret.SLA = BuildTicketSLAList(ctx.SLAByTicketID[item.ID])
|
||||
} else {
|
||||
ret.SLA = BuildTicketSLAList(
|
||||
services.TicketSLARecordService.Find(
|
||||
sqls.NewCnd().Eq("ticket_id", item.ID).Asc("id"),
|
||||
),
|
||||
)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -155,343 +86,79 @@ func BuildTicketListWithContext(list []models.Ticket, ctx *TicketBuildContext) [
|
||||
return results
|
||||
}
|
||||
|
||||
func BuildTicketComment(item *models.TicketComment) *response.TicketCommentResponse {
|
||||
return BuildTicketCommentWithContext(item, nil)
|
||||
func BuildTicketProgress(item *models.TicketProgress) *response.TicketProgressResponse {
|
||||
return BuildTicketProgressWithContext(item, nil)
|
||||
}
|
||||
|
||||
func BuildTicketCommentWithContext(item *models.TicketComment, ctx *TicketDetailBuildContext) *response.TicketCommentResponse {
|
||||
func BuildTicketProgressWithContext(item *models.TicketProgress, ctx *TicketDetailBuildContext) *response.TicketProgressResponse {
|
||||
if item == nil {
|
||||
return nil
|
||||
}
|
||||
ret := &response.TicketCommentResponse{
|
||||
ID: item.ID,
|
||||
TicketID: item.TicketID,
|
||||
CommentType: item.CommentType,
|
||||
AuthorType: item.AuthorType,
|
||||
AuthorID: item.AuthorID,
|
||||
ContentType: item.ContentType,
|
||||
Content: item.Content,
|
||||
Payload: item.Payload,
|
||||
CreatedAt: utils.FormatTime(item.CreatedAt),
|
||||
ret := &response.TicketProgressResponse{
|
||||
ID: item.ID,
|
||||
TicketID: item.TicketID,
|
||||
Content: item.Content,
|
||||
AuthorID: item.AuthorID,
|
||||
CreatedAt: utils.FormatTime(item.CreatedAt),
|
||||
}
|
||||
if item.AuthorID > 0 {
|
||||
if ctx != nil && ctx.Users != nil {
|
||||
ret.AuthorName = buildTicketUserDisplayName(ctx.Users[item.AuthorID])
|
||||
}
|
||||
if ret.AuthorName == "" {
|
||||
ret.AuthorName = buildTicketUserDisplayName(services.UserService.Get(item.AuthorID))
|
||||
}
|
||||
}
|
||||
ret.AuthorName = buildTicketOperatorName(item.AuthorType, item.AuthorID, ctx)
|
||||
return ret
|
||||
}
|
||||
|
||||
func BuildTicketCommentList(list []models.TicketComment) []response.TicketCommentResponse {
|
||||
return BuildTicketCommentListWithContext(list, nil)
|
||||
}
|
||||
|
||||
func BuildTicketCommentListWithContext(list []models.TicketComment, ctx *TicketDetailBuildContext) []response.TicketCommentResponse {
|
||||
func BuildTicketProgressListWithContext(list []models.TicketProgress, ctx *TicketDetailBuildContext) []response.TicketProgressResponse {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
results := make([]response.TicketCommentResponse, 0, len(list))
|
||||
results := make([]response.TicketProgressResponse, 0, len(list))
|
||||
for i := range list {
|
||||
if item := BuildTicketCommentWithContext(&list[i], ctx); item != nil {
|
||||
if item := BuildTicketProgressWithContext(&list[i], ctx); item != nil {
|
||||
results = append(results, *item)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func BuildTicketEventLog(item *models.TicketEventLog) *response.TicketEventLogResponse {
|
||||
return BuildTicketEventLogWithContext(item, nil)
|
||||
}
|
||||
|
||||
func BuildTicketEventLogWithContext(item *models.TicketEventLog, ctx *TicketDetailBuildContext) *response.TicketEventLogResponse {
|
||||
if item == nil {
|
||||
return nil
|
||||
}
|
||||
return &response.TicketEventLogResponse{
|
||||
ID: item.ID,
|
||||
TicketID: item.TicketID,
|
||||
EventType: item.EventType,
|
||||
OperatorType: item.OperatorType,
|
||||
OperatorID: item.OperatorID,
|
||||
OperatorName: buildTicketOperatorName(item.OperatorType, item.OperatorID, ctx),
|
||||
OldValue: item.OldValue,
|
||||
NewValue: item.NewValue,
|
||||
Content: item.Content,
|
||||
Payload: item.Payload,
|
||||
CreatedAt: utils.FormatTime(item.CreatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func BuildTicketEventLogList(list []models.TicketEventLog) []response.TicketEventLogResponse {
|
||||
return BuildTicketEventLogListWithContext(list, nil)
|
||||
}
|
||||
|
||||
func BuildTicketEventLogListWithContext(list []models.TicketEventLog, ctx *TicketDetailBuildContext) []response.TicketEventLogResponse {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
results := make([]response.TicketEventLogResponse, 0, len(list))
|
||||
for i := range list {
|
||||
if item := BuildTicketEventLogWithContext(&list[i], ctx); item != nil {
|
||||
results = append(results, *item)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func BuildTicketSLAList(list []models.TicketSLARecord) []response.TicketSLAResponse {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
results := make([]response.TicketSLAResponse, 0, len(list))
|
||||
for i := range list {
|
||||
item := &list[i]
|
||||
results = append(results, response.TicketSLAResponse{
|
||||
SLAType: item.SLAType,
|
||||
TargetMinutes: item.TargetMinutes,
|
||||
Status: item.Status,
|
||||
StartedAt: utils.FormatTimePtr(item.StartedAt),
|
||||
PausedAt: utils.FormatTimePtr(item.PausedAt),
|
||||
StoppedAt: utils.FormatTimePtr(item.StoppedAt),
|
||||
BreachedAt: utils.FormatTimePtr(item.BreachedAt),
|
||||
ElapsedMin: item.ElapsedMin,
|
||||
})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func BuildTicketDetail(aggregate *services.TicketDetailAggregate) *response.TicketDetailResponse {
|
||||
return BuildTicketDetailWithContext(aggregate, nil)
|
||||
}
|
||||
|
||||
func BuildTicketDetailWithContext(aggregate *services.TicketDetailAggregate, ctx *TicketDetailBuildContext) *response.TicketDetailResponse {
|
||||
if aggregate == nil || aggregate.Ticket == nil {
|
||||
return nil
|
||||
}
|
||||
if ctx == nil {
|
||||
users := make(map[int64]*models.User, len(aggregate.Users)+len(aggregate.OperatorUsers))
|
||||
for id, item := range aggregate.Users {
|
||||
users[id] = item
|
||||
}
|
||||
for id, item := range aggregate.OperatorUsers {
|
||||
users[id] = item
|
||||
}
|
||||
ctx = &TicketDetailBuildContext{
|
||||
Users: users,
|
||||
Teams: aggregate.Teams,
|
||||
AgentProfiles: aggregate.AgentProfiles,
|
||||
RelatedTickets: aggregate.RelatedMap,
|
||||
Customers: aggregate.OperatorCustomers,
|
||||
AIAgents: aggregate.OperatorAIAgents,
|
||||
}
|
||||
}
|
||||
ret := &response.TicketDetailResponse{
|
||||
Ticket: *BuildTicket(aggregate.Ticket),
|
||||
Watchers: BuildTicketWatcherListWithContext(aggregate.Watchers, ctx),
|
||||
Collaborators: BuildTicketCollaboratorListWithContext(aggregate.Collaborators, ctx),
|
||||
RelatedTickets: BuildTicketRelationListWithContext(aggregate.RelatedTickets, ctx),
|
||||
}
|
||||
if len(aggregate.Comments) > 0 {
|
||||
ret.Comments = make([]response.TicketCommentResponse, 0, len(aggregate.Comments))
|
||||
for i := range aggregate.Comments {
|
||||
if item := BuildTicketCommentWithContext(&aggregate.Comments[i], ctx); item != nil {
|
||||
ret.Comments = append(ret.Comments, *item)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(aggregate.Events) > 0 {
|
||||
ret.Events = make([]response.TicketEventLogResponse, 0, len(aggregate.Events))
|
||||
for i := range aggregate.Events {
|
||||
if item := BuildTicketEventLogWithContext(&aggregate.Events[i], ctx); item != nil {
|
||||
ret.Events = append(ret.Events, *item)
|
||||
}
|
||||
}
|
||||
ctx := &TicketBuildContext{
|
||||
TagsByTicketID: map[int64][]models.Tag{aggregate.Ticket.ID: aggregate.Tags},
|
||||
Users: aggregate.Users,
|
||||
Customers: map[int64]*models.Customer{},
|
||||
}
|
||||
if aggregate.Customer != nil {
|
||||
ret.Ticket.Customer = BuildCustomer(aggregate.Customer)
|
||||
ctx.Customers[aggregate.Customer.ID] = aggregate.Customer
|
||||
}
|
||||
ret.Ticket.Tags = BuildTagResponses(aggregate.Tags)
|
||||
ret.Ticket.SLA = BuildTicketSLAList(aggregate.SLAs)
|
||||
for i := range ret.Comments {
|
||||
if ret.Comments[i].AuthorName == "" {
|
||||
ret.Comments[i].AuthorName = buildTicketOperatorName(ret.Comments[i].AuthorType, ret.Comments[i].AuthorID, ctx)
|
||||
}
|
||||
}
|
||||
for i := range ret.Events {
|
||||
if ret.Events[i].OperatorName == "" {
|
||||
ret.Events[i].OperatorName = buildTicketOperatorName(ret.Events[i].OperatorType, ret.Events[i].OperatorID, ctx)
|
||||
}
|
||||
ret := &response.TicketDetailResponse{
|
||||
Ticket: *BuildTicketWithContext(aggregate.Ticket, ctx),
|
||||
}
|
||||
ret.Progresses = BuildTicketProgressListWithContext(aggregate.Progresses, &TicketDetailBuildContext{Users: aggregate.Users})
|
||||
return ret
|
||||
}
|
||||
|
||||
func BuildTicketRelation(item *models.TicketRelation) *response.TicketRelationResponse {
|
||||
return BuildTicketRelationWithContext(item, nil)
|
||||
}
|
||||
|
||||
func BuildTicketRelationWithContext(item *models.TicketRelation, ctx *TicketDetailBuildContext) *response.TicketRelationResponse {
|
||||
if item == nil {
|
||||
return nil
|
||||
}
|
||||
ret := &response.TicketRelationResponse{
|
||||
ID: item.ID,
|
||||
TicketID: item.TicketID,
|
||||
RelatedTicketID: item.RelatedTicketID,
|
||||
RelationType: item.RelationType,
|
||||
}
|
||||
related := (*models.Ticket)(nil)
|
||||
if ctx != nil && ctx.RelatedTickets != nil {
|
||||
related = ctx.RelatedTickets[item.RelatedTicketID]
|
||||
}
|
||||
if related == nil {
|
||||
related = services.TicketService.Get(item.RelatedTicketID)
|
||||
}
|
||||
if related != nil {
|
||||
ret.RelatedTicketNo = related.TicketNo
|
||||
ret.RelatedTicketTitle = related.Title
|
||||
ret.RelatedTicketStatus = related.Status
|
||||
ret.UpdatedAt = utils.FormatTime(related.UpdatedAt)
|
||||
if related.CurrentTeamID > 0 && ctx != nil && ctx.Teams != nil {
|
||||
if team := ctx.Teams[related.CurrentTeamID]; team != nil {
|
||||
ret.CurrentTeamName = team.Name
|
||||
}
|
||||
} else if related.CurrentTeamID > 0 {
|
||||
if team := services.AgentTeamService.Get(related.CurrentTeamID); team != nil {
|
||||
ret.CurrentTeamName = team.Name
|
||||
}
|
||||
}
|
||||
if related.CurrentAssigneeID > 0 && ctx != nil && ctx.Users != nil {
|
||||
ret.CurrentAssigneeName = buildTicketUserDisplayName(ctx.Users[related.CurrentAssigneeID])
|
||||
} else if related.CurrentAssigneeID > 0 {
|
||||
if user := services.UserService.Get(related.CurrentAssigneeID); user != nil {
|
||||
ret.CurrentAssigneeName = user.Nickname
|
||||
if ret.CurrentAssigneeName == "" {
|
||||
ret.CurrentAssigneeName = user.Username
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func BuildTicketRelationList(list []models.TicketRelation) []response.TicketRelationResponse {
|
||||
return BuildTicketRelationListWithContext(list, nil)
|
||||
}
|
||||
|
||||
func BuildTicketRelationListWithContext(list []models.TicketRelation, ctx *TicketDetailBuildContext) []response.TicketRelationResponse {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
results := make([]response.TicketRelationResponse, 0, len(list))
|
||||
for i := range list {
|
||||
if item := BuildTicketRelationWithContext(&list[i], ctx); item != nil {
|
||||
results = append(results, *item)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func BuildTicketSummary(summary *services.TicketSummaryAggregate) *response.TicketSummaryResponse {
|
||||
if summary == nil {
|
||||
return nil
|
||||
}
|
||||
return &response.TicketSummaryResponse{
|
||||
All: summary.All,
|
||||
Mine: summary.Mine,
|
||||
Watching: summary.Watching,
|
||||
Collaboration: summary.Collaboration,
|
||||
Participating: summary.Participating,
|
||||
Mentioned: summary.Mentioned,
|
||||
Unassigned: summary.Unassigned,
|
||||
PendingCustomer: summary.PendingCustomer,
|
||||
PendingInternal: summary.PendingInternal,
|
||||
Overdue: summary.Overdue,
|
||||
All: summary.All,
|
||||
Pending: summary.Pending,
|
||||
InProgress: summary.InProgress,
|
||||
Done: summary.Done,
|
||||
Unassigned: summary.Unassigned,
|
||||
Mine: summary.Mine,
|
||||
Stale: summary.Stale,
|
||||
}
|
||||
}
|
||||
|
||||
func BuildTicketRiskOverview(overview *services.TicketRiskOverviewAggregate) *response.TicketRiskOverviewResponse {
|
||||
if overview == nil {
|
||||
return nil
|
||||
}
|
||||
ret := &response.TicketRiskOverviewResponse{
|
||||
Overdue: overview.Overdue,
|
||||
HighRisk: overview.HighRisk,
|
||||
Unassigned: overview.Unassigned,
|
||||
PendingInternal: overview.PendingInternal,
|
||||
PendingCustomer: overview.PendingCustomer,
|
||||
RiskWindowMins: overview.RiskWindowMins,
|
||||
}
|
||||
if len(overview.Reasons) > 0 {
|
||||
ret.Reasons = make([]response.TicketRiskReasonResponse, 0, len(overview.Reasons))
|
||||
for _, item := range overview.Reasons {
|
||||
ret.Reasons = append(ret.Reasons, response.TicketRiskReasonResponse{
|
||||
Code: item.Code,
|
||||
Title: item.Title,
|
||||
Description: item.Description,
|
||||
Count: item.Count,
|
||||
})
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func BuildTicketWatcherList(list []models.TicketWatcher) []response.TicketWatcherResponse {
|
||||
return BuildTicketWatcherListWithContext(list, nil)
|
||||
}
|
||||
|
||||
func BuildTicketWatcherListWithContext(list []models.TicketWatcher, ctx *TicketDetailBuildContext) []response.TicketWatcherResponse {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
results := make([]response.TicketWatcherResponse, 0, len(list))
|
||||
for i := range list {
|
||||
item := &list[i]
|
||||
out := response.TicketWatcherResponse{
|
||||
ID: item.ID,
|
||||
UserID: item.UserID,
|
||||
}
|
||||
if ctx != nil && ctx.Users != nil {
|
||||
out.UserName = buildTicketUserDisplayName(ctx.Users[item.UserID])
|
||||
} else if user := services.UserService.Get(item.UserID); user != nil {
|
||||
out.UserName = buildTicketUserDisplayName(user)
|
||||
}
|
||||
results = append(results, out)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func BuildTicketCollaboratorList(list []models.TicketCollaborator) []response.TicketCollaboratorResponse {
|
||||
return BuildTicketCollaboratorListWithContext(list, nil)
|
||||
}
|
||||
|
||||
func BuildTicketCollaboratorListWithContext(list []models.TicketCollaborator, ctx *TicketDetailBuildContext) []response.TicketCollaboratorResponse {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
results := make([]response.TicketCollaboratorResponse, 0, len(list))
|
||||
for i := range list {
|
||||
item := &list[i]
|
||||
out := response.TicketCollaboratorResponse{
|
||||
ID: item.ID,
|
||||
UserID: item.UserID,
|
||||
}
|
||||
if ctx != nil && ctx.Users != nil {
|
||||
out.UserName = buildTicketUserDisplayName(ctx.Users[item.UserID])
|
||||
} else if user := services.UserService.Get(item.UserID); user != nil {
|
||||
out.UserName = buildTicketUserDisplayName(user)
|
||||
}
|
||||
if ctx != nil && ctx.AgentProfiles != nil {
|
||||
if profile := ctx.AgentProfiles[item.UserID]; profile != nil && profile.TeamID > 0 {
|
||||
if team := ctx.Teams[profile.TeamID]; team != nil {
|
||||
out.TeamName = team.Name
|
||||
}
|
||||
}
|
||||
} else if profile := services.AgentProfileService.GetByUserID(item.UserID); profile != nil && profile.TeamID > 0 {
|
||||
if team := services.AgentTeamService.Get(profile.TeamID); team != nil {
|
||||
out.TeamName = team.Name
|
||||
}
|
||||
}
|
||||
results = append(results, out)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func BuildTicketView(item *models.TicketView) *response.TicketViewResponse {
|
||||
if item == nil {
|
||||
return nil
|
||||
@@ -529,39 +196,3 @@ func buildTicketUserDisplayName(user *models.User) string {
|
||||
}
|
||||
return user.Username
|
||||
}
|
||||
|
||||
func buildTicketOperatorName(senderType enums.IMSenderType, senderID int64, ctx *TicketDetailBuildContext) string {
|
||||
if senderID <= 0 {
|
||||
return ""
|
||||
}
|
||||
switch senderType {
|
||||
case enums.IMSenderTypeAgent:
|
||||
if ctx != nil && ctx.Users != nil {
|
||||
if user := ctx.Users[senderID]; user != nil {
|
||||
return buildTicketUserDisplayName(user)
|
||||
}
|
||||
}
|
||||
if user := services.UserService.Get(senderID); user != nil {
|
||||
return buildTicketUserDisplayName(user)
|
||||
}
|
||||
case enums.IMSenderTypeCustomer:
|
||||
if ctx != nil && ctx.Customers != nil {
|
||||
if customer := ctx.Customers[senderID]; customer != nil {
|
||||
return customer.Name
|
||||
}
|
||||
}
|
||||
if customer := services.CustomerService.Get(senderID); customer != nil {
|
||||
return customer.Name
|
||||
}
|
||||
case enums.IMSenderTypeAI:
|
||||
if ctx != nil && ctx.AIAgents != nil {
|
||||
if ai := ctx.AIAgents[senderID]; ai != nil {
|
||||
return ai.Name
|
||||
}
|
||||
}
|
||||
if ai := services.AIAgentService.Get(senderID); ai != nil {
|
||||
return ai.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package builders
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cs-agent/internal/models"
|
||||
"cs-agent/internal/pkg/enums"
|
||||
)
|
||||
|
||||
func TestBuildLightweightTicket(t *testing.T) {
|
||||
now := time.Date(2026, 5, 2, 12, 30, 0, 0, time.Local)
|
||||
ticket := &models.Ticket{
|
||||
ID: 12,
|
||||
TicketNo: "TK202605020001",
|
||||
Title: "登录失败",
|
||||
Description: "客户反馈无法登录",
|
||||
Source: enums.TicketSourceManual,
|
||||
Channel: "web",
|
||||
CustomerID: 3,
|
||||
ConversationID: 4,
|
||||
Status: enums.TicketStatusPending,
|
||||
CurrentAssigneeID: 5,
|
||||
AuditFields: models.AuditFields{
|
||||
CreateUserID: 1,
|
||||
CreateUserName: "admin",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
}
|
||||
ctx := &TicketBuildContext{
|
||||
TagsByTicketID: map[int64][]models.Tag{
|
||||
12: {{ID: 8, Name: "登录", Status: enums.StatusOk}},
|
||||
},
|
||||
Users: map[int64]*models.User{
|
||||
5: {ID: 5, Username: "agent", Nickname: "客服"},
|
||||
},
|
||||
Customers: map[int64]*models.Customer{
|
||||
3: {ID: 3, Name: "客户"},
|
||||
},
|
||||
}
|
||||
|
||||
out := BuildTicketWithContext(ticket, ctx)
|
||||
if out == nil {
|
||||
t.Fatalf("expected ticket response")
|
||||
}
|
||||
if out.ID != ticket.ID || out.TicketNo != ticket.TicketNo || out.Status != ticket.Status {
|
||||
t.Fatalf("unexpected ticket response: %+v", out)
|
||||
}
|
||||
if out.CurrentAssigneeName != "客服" {
|
||||
t.Fatalf("expected assignee name, got %q", out.CurrentAssigneeName)
|
||||
}
|
||||
if len(out.Tags) != 1 || out.Tags[0].ID != 8 {
|
||||
t.Fatalf("expected tag response, got %+v", out.Tags)
|
||||
}
|
||||
if out.Customer == nil || out.Customer.ID != 3 {
|
||||
t.Fatalf("expected customer response, got %+v", out.Customer)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildTicketProgress(t *testing.T) {
|
||||
now := time.Date(2026, 5, 2, 12, 30, 0, 0, time.Local)
|
||||
progress := &models.TicketProgress{
|
||||
ID: 1,
|
||||
TicketID: 2,
|
||||
Content: "已联系客户",
|
||||
AuthorID: 3,
|
||||
CreatedAt: now,
|
||||
}
|
||||
ctx := &TicketDetailBuildContext{
|
||||
Users: map[int64]*models.User{
|
||||
3: {ID: 3, Username: "agent", Nickname: "客服"},
|
||||
},
|
||||
}
|
||||
|
||||
out := BuildTicketProgressWithContext(progress, ctx)
|
||||
if out == nil {
|
||||
t.Fatalf("expected progress response")
|
||||
}
|
||||
if out.ID != progress.ID || out.TicketID != progress.TicketID || out.Content != progress.Content {
|
||||
t.Fatalf("unexpected progress response: %+v", out)
|
||||
}
|
||||
if out.AuthorName != "客服" {
|
||||
t.Fatalf("expected author name, got %q", out.AuthorName)
|
||||
}
|
||||
if out.CreatedAt == "" {
|
||||
t.Fatalf("expected createdAt to be formatted")
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package builders
|
||||
|
||||
import (
|
||||
"cs-agent/internal/models"
|
||||
"cs-agent/internal/pkg/dto/response"
|
||||
)
|
||||
|
||||
func BuildTicketResolutionCode(item *models.TicketResolutionCode) *response.TicketResolutionCodeResponse {
|
||||
if item == nil {
|
||||
return nil
|
||||
}
|
||||
return &response.TicketResolutionCodeResponse{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
Code: item.Code,
|
||||
SortNo: item.SortNo,
|
||||
Status: item.Status,
|
||||
Remark: item.Remark,
|
||||
}
|
||||
}
|
||||
|
||||
func BuildTicketResolutionCodeList(list []models.TicketResolutionCode) []response.TicketResolutionCodeResponse {
|
||||
if len(list) == 0 {
|
||||
return make([]response.TicketResolutionCodeResponse, 0)
|
||||
}
|
||||
ret := make([]response.TicketResolutionCodeResponse, 0, len(list))
|
||||
for i := range list {
|
||||
if item := BuildTicketResolutionCode(&list[i]); item != nil {
|
||||
ret = append(ret, *item)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func BuildTicketPriorityConfig(item *models.TicketPriorityConfig) *response.TicketPriorityConfigResponse {
|
||||
if item == nil {
|
||||
return nil
|
||||
}
|
||||
return &response.TicketPriorityConfigResponse{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
SortNo: item.SortNo,
|
||||
FirstResponseMinutes: item.FirstResponseMinutes,
|
||||
ResolutionMinutes: item.ResolutionMinutes,
|
||||
Status: item.Status,
|
||||
Remark: item.Remark,
|
||||
}
|
||||
}
|
||||
|
||||
func BuildTicketPriorityConfigList(list []models.TicketPriorityConfig) []response.TicketPriorityConfigResponse {
|
||||
if len(list) == 0 {
|
||||
return make([]response.TicketPriorityConfigResponse, 0)
|
||||
}
|
||||
ret := make([]response.TicketPriorityConfigResponse, 0, len(list))
|
||||
for i := range list {
|
||||
if item := BuildTicketPriorityConfig(&list[i]); item != nil {
|
||||
ret = append(ret, *item)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
Reference in New Issue
Block a user