mirror of
https://git.boos.lat/aibo/BossChat.git
synced 2026-10-30 15:16:11 +00:00
fix(ticket): close backend core review gaps
This commit is contained in:
@@ -47,24 +47,16 @@ func BuildTicketWithContext(item *models.Ticket, ctx *TicketBuildContext) *respo
|
||||
}
|
||||
if ctx != nil && ctx.TagsByTicketID != nil {
|
||||
ret.Tags = BuildTagResponses(ctx.TagsByTicketID[item.ID])
|
||||
} else {
|
||||
ret.Tags = BuildTagResponses(services.TicketService.GetTags(item.ID))
|
||||
}
|
||||
if item.CurrentAssigneeID > 0 {
|
||||
if ctx != nil && ctx.Users != nil {
|
||||
ret.CurrentAssigneeName = buildTicketUserDisplayName(ctx.Users[item.CurrentAssigneeID])
|
||||
}
|
||||
if ret.CurrentAssigneeName == "" {
|
||||
ret.CurrentAssigneeName = buildTicketUserDisplayName(services.UserService.Get(item.CurrentAssigneeID))
|
||||
}
|
||||
}
|
||||
if item.CustomerID > 0 {
|
||||
if ctx != nil && ctx.Customers != nil {
|
||||
ret.Customer = BuildCustomer(ctx.Customers[item.CustomerID])
|
||||
}
|
||||
if ret.Customer == nil {
|
||||
ret.Customer = BuildCustomer(services.CustomerService.Get(item.CustomerID))
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -105,9 +97,6 @@ func BuildTicketProgressWithContext(item *models.TicketProgress, ctx *TicketDeta
|
||||
if ctx != nil && ctx.Users != nil {
|
||||
ret.AuthorName = buildTicketUserDisplayName(ctx.Users[item.AuthorID])
|
||||
}
|
||||
if ret.AuthorName == "" {
|
||||
ret.AuthorName = buildTicketUserDisplayName(services.UserService.Get(item.AuthorID))
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -58,6 +58,39 @@ func TestBuildLightweightTicket(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildTicketWithoutContextLeavesOptionalLookupsEmpty(t *testing.T) {
|
||||
now := time.Date(2026, 5, 2, 12, 30, 0, 0, time.Local)
|
||||
ticket := &models.Ticket{
|
||||
ID: 12,
|
||||
TicketNo: "TK202605020001",
|
||||
Title: "登录失败",
|
||||
Description: "客户反馈无法登录",
|
||||
CustomerID: 3,
|
||||
CurrentAssigneeID: 5,
|
||||
Status: enums.TicketStatusPending,
|
||||
AuditFields: models.AuditFields{
|
||||
CreateUserID: 1,
|
||||
CreateUserName: "admin",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
}
|
||||
|
||||
out := BuildTicket(ticket)
|
||||
if out == nil {
|
||||
t.Fatalf("expected ticket response")
|
||||
}
|
||||
if out.Tags != nil {
|
||||
t.Fatalf("expected tags to stay empty without context, got %+v", out.Tags)
|
||||
}
|
||||
if out.Customer != nil {
|
||||
t.Fatalf("expected customer to stay empty without context, got %+v", out.Customer)
|
||||
}
|
||||
if out.CurrentAssigneeName != "" {
|
||||
t.Fatalf("expected assignee name to stay empty without context, got %q", out.CurrentAssigneeName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildTicketProgress(t *testing.T) {
|
||||
now := time.Date(2026, 5, 2, 12, 30, 0, 0, time.Local)
|
||||
progress := &models.TicketProgress{
|
||||
|
||||
@@ -208,7 +208,7 @@ func (c *TicketController) PostChange_status() *web.JsonResult {
|
||||
}
|
||||
|
||||
func (c *TicketController) PostAdd_progress() *web.JsonResult {
|
||||
operator, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionTicketUpdate)
|
||||
operator, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionTicketProgress)
|
||||
if err != nil {
|
||||
return web.JsonError(err)
|
||||
}
|
||||
|
||||
@@ -73,17 +73,7 @@ var (
|
||||
PermissionTicketUpdate = Permission{Name: "更新工单", Code: "ticket.update", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/update", SortNo: 520}
|
||||
PermissionTicketAssign = Permission{Name: "指派工单", Code: "ticket.assign", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/assign", SortNo: 530}
|
||||
PermissionTicketChangeStatus = Permission{Name: "变更工单状态", Code: "ticket.changeStatus", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/change_status", SortNo: 540}
|
||||
PermissionTicketReply = Permission{Name: "回复工单", Code: "ticket.reply", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/reply", SortNo: 550}
|
||||
PermissionTicketClose = Permission{Name: "关闭工单", Code: "ticket.close", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/close", SortNo: 560}
|
||||
PermissionTicketReopen = Permission{Name: "重开工单", Code: "ticket.reopen", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/reopen", SortNo: 570}
|
||||
PermissionTicketResolutionCodeView = Permission{Name: "查看工单解决码", Code: "ticketResolutionCode.view", Type: "api", GroupName: "ticketResolutionCode", Method: "ANY", APIPath: "/api/dashboard/ticket-resolution-code/list", SortNo: 580}
|
||||
PermissionTicketResolutionCodeCreate = Permission{Name: "创建工单解决码", Code: "ticketResolutionCode.create", Type: "api", GroupName: "ticketResolutionCode", Method: "POST", APIPath: "/api/dashboard/ticket-resolution-code/create", SortNo: 590}
|
||||
PermissionTicketResolutionCodeUpdate = Permission{Name: "更新工单解决码", Code: "ticketResolutionCode.update", Type: "api", GroupName: "ticketResolutionCode", Method: "POST", APIPath: "/api/dashboard/ticket-resolution-code/update", SortNo: 600}
|
||||
PermissionTicketResolutionCodeDelete = Permission{Name: "删除工单解决码", Code: "ticketResolutionCode.delete", Type: "api", GroupName: "ticketResolutionCode", Method: "POST", APIPath: "/api/dashboard/ticket-resolution-code/delete", SortNo: 610}
|
||||
PermissionTicketPriorityConfigView = Permission{Name: "查看工单优先级", Code: "ticketPriorityConfig.view", Type: "api", GroupName: "ticketPriorityConfig", Method: "ANY", APIPath: "/api/dashboard/ticket-priority-config/list", SortNo: 620}
|
||||
PermissionTicketPriorityConfigCreate = Permission{Name: "创建工单优先级", Code: "ticketPriorityConfig.create", Type: "api", GroupName: "ticketPriorityConfig", Method: "POST", APIPath: "/api/dashboard/ticket-priority-config/create", SortNo: 630}
|
||||
PermissionTicketPriorityConfigUpdate = Permission{Name: "更新工单优先级", Code: "ticketPriorityConfig.update", Type: "api", GroupName: "ticketPriorityConfig", Method: "POST", APIPath: "/api/dashboard/ticket-priority-config/update", SortNo: 640}
|
||||
PermissionTicketPriorityConfigDelete = Permission{Name: "删除工单优先级", Code: "ticketPriorityConfig.delete", Type: "api", GroupName: "ticketPriorityConfig", Method: "POST", APIPath: "/api/dashboard/ticket-priority-config/delete", SortNo: 650}
|
||||
PermissionTicketProgress = Permission{Name: "更新工单进展", Code: "ticket.progress", Type: "api", GroupName: "ticket", Method: "POST", APIPath: "/api/dashboard/ticket/add_progress", SortNo: 550}
|
||||
|
||||
// 通知相关权限
|
||||
PermissionNotificationView = Permission{Name: "查看通知", Code: "notification.view", Type: "api", GroupName: "notification", Method: "ANY", APIPath: "/api/dashboard/notification/list", SortNo: 680}
|
||||
@@ -214,17 +204,7 @@ var Permissions = []Permission{
|
||||
PermissionTicketUpdate,
|
||||
PermissionTicketAssign,
|
||||
PermissionTicketChangeStatus,
|
||||
PermissionTicketReply,
|
||||
PermissionTicketClose,
|
||||
PermissionTicketReopen,
|
||||
PermissionTicketResolutionCodeView,
|
||||
PermissionTicketResolutionCodeCreate,
|
||||
PermissionTicketResolutionCodeUpdate,
|
||||
PermissionTicketResolutionCodeDelete,
|
||||
PermissionTicketPriorityConfigView,
|
||||
PermissionTicketPriorityConfigCreate,
|
||||
PermissionTicketPriorityConfigUpdate,
|
||||
PermissionTicketPriorityConfigDelete,
|
||||
PermissionTicketProgress,
|
||||
PermissionNotificationView,
|
||||
PermissionNotificationUpdate,
|
||||
PermissionQuickReplyView,
|
||||
@@ -324,9 +304,7 @@ var RolePermissions = map[string][]Permission{
|
||||
PermissionPermissionView, PermissionPermissionSync,
|
||||
PermissionSessionView, PermissionSessionRevoke,
|
||||
PermissionConversationView, PermissionConversationAssign, PermissionConversationTransfer, PermissionConversationClose, PermissionConversationSend, PermissionConversationTag, PermissionConversationHandover, PermissionConversationRecycle, PermissionConversationLinkCustomer,
|
||||
PermissionTicketView, PermissionTicketCreate, PermissionTicketUpdate, PermissionTicketAssign, PermissionTicketChangeStatus, PermissionTicketReply, PermissionTicketClose, PermissionTicketReopen,
|
||||
PermissionTicketResolutionCodeView, PermissionTicketResolutionCodeCreate, PermissionTicketResolutionCodeUpdate, PermissionTicketResolutionCodeDelete,
|
||||
PermissionTicketPriorityConfigView, PermissionTicketPriorityConfigCreate, PermissionTicketPriorityConfigUpdate, PermissionTicketPriorityConfigDelete,
|
||||
PermissionTicketView, PermissionTicketCreate, PermissionTicketUpdate, PermissionTicketAssign, PermissionTicketChangeStatus, PermissionTicketProgress,
|
||||
PermissionNotificationView, PermissionNotificationUpdate,
|
||||
PermissionQuickReplyView, PermissionQuickReplyCreate, PermissionQuickReplyUpdate, PermissionQuickReplyDelete,
|
||||
PermissionTagView, PermissionTagCreate, PermissionTagUpdate, PermissionTagDelete,
|
||||
@@ -347,9 +325,7 @@ var RolePermissions = map[string][]Permission{
|
||||
PermissionPermissionView,
|
||||
PermissionSessionView,
|
||||
PermissionConversationView, PermissionConversationClose, PermissionConversationSend, PermissionConversationTag, PermissionConversationHandover, PermissionConversationRecycle, PermissionConversationLinkCustomer,
|
||||
PermissionTicketView, PermissionTicketCreate, PermissionTicketUpdate, PermissionTicketAssign, PermissionTicketChangeStatus, PermissionTicketReply, PermissionTicketClose, PermissionTicketReopen,
|
||||
PermissionTicketResolutionCodeView, PermissionTicketResolutionCodeCreate, PermissionTicketResolutionCodeUpdate, PermissionTicketResolutionCodeDelete,
|
||||
PermissionTicketPriorityConfigView, PermissionTicketPriorityConfigCreate, PermissionTicketPriorityConfigUpdate, PermissionTicketPriorityConfigDelete,
|
||||
PermissionTicketView, PermissionTicketCreate, PermissionTicketUpdate, PermissionTicketAssign, PermissionTicketChangeStatus, PermissionTicketProgress,
|
||||
PermissionNotificationView, PermissionNotificationUpdate,
|
||||
PermissionQuickReplyView, PermissionQuickReplyCreate, PermissionQuickReplyUpdate, PermissionQuickReplyDelete,
|
||||
PermissionTagView, PermissionTagCreate, PermissionTagUpdate, PermissionTagDelete,
|
||||
@@ -369,8 +345,7 @@ var RolePermissions = map[string][]Permission{
|
||||
PermissionRoleView,
|
||||
PermissionPermissionView,
|
||||
PermissionConversationView,
|
||||
PermissionTicketView, PermissionTicketCreate, PermissionTicketReply,
|
||||
PermissionTicketResolutionCodeView, PermissionTicketPriorityConfigView,
|
||||
PermissionTicketView, PermissionTicketCreate, PermissionTicketProgress,
|
||||
PermissionNotificationView, PermissionNotificationUpdate,
|
||||
PermissionQuickReplyView,
|
||||
PermissionTagView,
|
||||
|
||||
@@ -462,7 +462,7 @@ func (s *ticketService) assignTicketTx(tx *gorm.DB, req request.AssignTicketRequ
|
||||
if ticket == nil {
|
||||
return nil, errorsx.InvalidParam("工单不存在")
|
||||
}
|
||||
if err := s.validateAssignee(req.ToUserID); err != nil {
|
||||
if err := s.validateRequiredAssignee(req.ToUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
now := time.Now()
|
||||
@@ -586,6 +586,13 @@ func (s *ticketService) validateAssignee(userID int64) error {
|
||||
if userID <= 0 {
|
||||
return nil
|
||||
}
|
||||
return s.validateRequiredAssignee(userID)
|
||||
}
|
||||
|
||||
func (s *ticketService) validateRequiredAssignee(userID int64) error {
|
||||
if userID <= 0 {
|
||||
return errorsx.InvalidParam("负责人不存在")
|
||||
}
|
||||
user := UserService.Get(userID)
|
||||
if user == nil || user.Status == enums.StatusDeleted {
|
||||
return errorsx.InvalidParam("负责人不存在")
|
||||
|
||||
@@ -190,6 +190,24 @@ func TestTicketServiceAddProgressStoresContentAndAuthor(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTicketServiceAssignTicketRequiresTargetUser(t *testing.T) {
|
||||
setupTicketTestDB(t)
|
||||
operator := createTestOperator(t, "assign-operator")
|
||||
ticket, err := services.TicketService.CreateTicket(createTestTicketRequest("assign-ticket"), operator)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateTicket() error = %v", err)
|
||||
}
|
||||
|
||||
err = services.TicketService.AssignTicket(request.AssignTicketRequest{
|
||||
TicketID: ticket.ID,
|
||||
ToUserID: 0,
|
||||
Reason: "invalid assignment",
|
||||
}, operator)
|
||||
if err == nil {
|
||||
t.Fatalf("expected AssignTicket() to reject empty target user")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTicketServiceSummaryCountsStaleTickets(t *testing.T) {
|
||||
setupTicketTestDB(t)
|
||||
operator := createTestOperator(t, "summary-operator")
|
||||
|
||||
Reference in New Issue
Block a user