Refactor error handling in services to use internationalized error messages
- Updated OSSStorage validation errors to use internationalized messages. - Changed error messages in provider.go for unsupported file storage types. - Refactored tag_service.go to replace hardcoded error messages with internationalized versions. - Updated ticket_service.go to use internationalized error messages for various validation checks. - Refactored ticket_tag_service.go to use internationalized error messages for tag validation. - Changed ticket_view_service.go to use internationalized error messages for view validation. - Updated tool_catalog_service.go to use internationalized error messages for tool code validation. - Refactored user_service.go to replace error messages with internationalized versions. - Updated ws_service.go to use internationalized error messages for WebSocket handling. - Refactored wxwork_kf_inbound_service.go to use internationalized error messages for message handling. - Updated wxwork_kf_outbound_service.go to use internationalized error messages for outbound message handling. - Refactored wxwork_login_service.go to use internationalized error messages for login handling. - Updated login.go in wxwork package to use internationalized error messages for login state and ticket validation.
This commit is contained in:
@@ -88,7 +88,7 @@ func (s *agentProfileService) GetDispatchAgents(teamIds []int64) []models.AgentP
|
||||
|
||||
func (s *agentProfileService) CreateAgentProfile(req request.CreateAgentProfileRequest, operator *dto.AuthPrincipal) (*models.AgentProfile, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item, err := s.buildProfileModel(0, req)
|
||||
if err != nil {
|
||||
@@ -104,11 +104,11 @@ func (s *agentProfileService) CreateAgentProfile(req request.CreateAgentProfileR
|
||||
|
||||
func (s *agentProfileService) UpdateAgentProfile(req request.UpdateAgentProfileRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(req.ID)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("客服档案不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0164")
|
||||
}
|
||||
item, err := s.buildProfileModel(req.ID, req.CreateAgentProfileRequest)
|
||||
if err != nil {
|
||||
@@ -139,7 +139,7 @@ func (s *agentProfileService) UpdateAgentProfile(req request.UpdateAgentProfileR
|
||||
func (s *agentProfileService) DeleteAgentProfile(id int64) error {
|
||||
current := s.Get(id)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("客服档案不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0164")
|
||||
}
|
||||
repositories.AgentProfileRepository.Delete(sqls.DB(), id)
|
||||
return nil
|
||||
@@ -147,33 +147,33 @@ func (s *agentProfileService) DeleteAgentProfile(id int64) error {
|
||||
|
||||
func (s *agentProfileService) buildProfileModel(id int64, req request.CreateAgentProfileRequest) (*models.AgentProfile, error) {
|
||||
if req.UserID <= 0 {
|
||||
return nil, errorsx.InvalidParam("请选择关联用户")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0325")
|
||||
}
|
||||
if UserService.Get(req.UserID) == nil {
|
||||
return nil, errorsx.InvalidParam("关联用户不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0127")
|
||||
}
|
||||
if req.TeamID <= 0 {
|
||||
return nil, errorsx.InvalidParam("请选择所属客服组")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0328")
|
||||
}
|
||||
if AgentTeamService.Get(req.TeamID) == nil {
|
||||
return nil, errorsx.InvalidParam("所属客服组不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0205")
|
||||
}
|
||||
req.AgentCode = strings.TrimSpace(req.AgentCode)
|
||||
req.DisplayName = strings.TrimSpace(req.DisplayName)
|
||||
if req.AgentCode == "" || req.DisplayName == "" {
|
||||
return nil, errorsx.InvalidParam("客服工号和展示名不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0162")
|
||||
}
|
||||
if exists := s.Take("user_id = ? AND id <> ?", req.UserID, id); exists != nil {
|
||||
return nil, errorsx.InvalidParam("该用户已存在客服档案")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0314")
|
||||
}
|
||||
if exists := s.Take("agent_code = ? AND id <> ?", req.AgentCode, id); exists != nil {
|
||||
return nil, errorsx.InvalidParam("客服工号已存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0163")
|
||||
}
|
||||
if !enums.IsValidServiceStatus(req.ServiceStatus) {
|
||||
return nil, errorsx.InvalidParam("客服状态不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0165")
|
||||
}
|
||||
if req.MaxConcurrentCount < 0 {
|
||||
return nil, errorsx.InvalidParam("最大并发接待数不能小于 0")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0229")
|
||||
}
|
||||
return &models.AgentProfile{
|
||||
UserID: req.UserID,
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"agent-desk/internal/pkg/dto/request"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/pkg/errorsx"
|
||||
"agent-desk/internal/pkg/i18nx"
|
||||
"agent-desk/internal/pkg/utils"
|
||||
"agent-desk/internal/repositories"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -101,7 +101,7 @@ func (s *agentTeamScheduleService) FindCalendarSchedules(req request.AgentTeamSc
|
||||
return nil, err
|
||||
}
|
||||
if !endAtValue.After(startAtValue) {
|
||||
return nil, errorsx.InvalidParam("结束时间必须晚于开始时间")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0296")
|
||||
}
|
||||
return repositories.AgentTeamScheduleRepository.FindByTimeRange(sqls.DB(), startAtValue, endAtValue, req.TeamID), nil
|
||||
}
|
||||
@@ -128,7 +128,7 @@ func (s *agentTeamScheduleService) Delete(id int64) {
|
||||
|
||||
func (s *agentTeamScheduleService) CreateAgentTeamSchedule(req request.CreateAgentTeamScheduleRequest, operator *dto.AuthPrincipal) (*models.AgentTeamSchedule, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
s.writeMu.Lock()
|
||||
item, err := s.buildScheduleModel(0, req.TeamID, req.StartAt, req.EndAt, req.Remark)
|
||||
@@ -148,12 +148,12 @@ func (s *agentTeamScheduleService) CreateAgentTeamSchedule(req request.CreateAge
|
||||
|
||||
func (s *agentTeamScheduleService) UpdateAgentTeamSchedule(req request.UpdateAgentTeamScheduleRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
s.writeMu.Lock()
|
||||
if s.Get(req.ID) == nil {
|
||||
s.writeMu.Unlock()
|
||||
return errorsx.InvalidParam("客服组排班不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0172")
|
||||
}
|
||||
item, err := s.buildScheduleModel(req.ID, req.TeamID, req.StartAt, req.EndAt, req.Remark)
|
||||
if err != nil {
|
||||
@@ -179,7 +179,7 @@ func (s *agentTeamScheduleService) UpdateAgentTeamSchedule(req request.UpdateAge
|
||||
|
||||
func (s *agentTeamScheduleService) DeleteAgentTeamSchedule(id int64) error {
|
||||
if s.Get(id) == nil {
|
||||
return errorsx.InvalidParam("客服组排班不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0172")
|
||||
}
|
||||
repositories.AgentTeamScheduleRepository.Delete(sqls.DB(), id)
|
||||
return nil
|
||||
@@ -187,19 +187,19 @@ func (s *agentTeamScheduleService) DeleteAgentTeamSchedule(id int64) error {
|
||||
|
||||
func (s *agentTeamScheduleService) BatchPreview(req request.AgentTeamScheduleBatchRequest, operator *dto.AuthPrincipal) (*AgentTeamScheduleBatchPreviewResult, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
candidates, err := s.buildBatchScheduleCandidates(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conflicts := s.findBatchConflict(candidates)
|
||||
conflicts := s.findBatchConflict(candidates, req.Locale)
|
||||
return buildBatchPreviewResult(candidates, conflicts), nil
|
||||
}
|
||||
|
||||
func (s *agentTeamScheduleService) BatchGenerate(req request.AgentTeamScheduleBatchRequest, operator *dto.AuthPrincipal) (*AgentTeamScheduleBatchGenerateResult, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
s.writeMu.Lock()
|
||||
candidates, err := s.buildBatchScheduleCandidates(req)
|
||||
@@ -207,11 +207,11 @@ func (s *agentTeamScheduleService) BatchGenerate(req request.AgentTeamScheduleBa
|
||||
s.writeMu.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
conflicts := s.findBatchConflict(candidates)
|
||||
conflicts := s.findBatchConflict(candidates, req.Locale)
|
||||
for _, conflict := range conflicts {
|
||||
if conflict != "" {
|
||||
s.writeMu.Unlock()
|
||||
return nil, errorsx.InvalidParam("存在冲突排班,请先处理冲突")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0151")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,10 +227,10 @@ func (s *agentTeamScheduleService) BatchGenerate(req request.AgentTeamScheduleBa
|
||||
})
|
||||
}
|
||||
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
conflicts := s.findBatchConflictByDB(ctx.Tx, candidates)
|
||||
conflicts := s.findBatchConflictByDB(ctx.Tx, candidates, req.Locale)
|
||||
for _, conflict := range conflicts {
|
||||
if conflict != "" {
|
||||
return errorsx.InvalidParam("存在冲突排班,请先处理冲突")
|
||||
return errorsx.InvalidParamI18n("error.e0151")
|
||||
}
|
||||
}
|
||||
return repositories.AgentTeamScheduleRepository.CreateBatch(ctx.Tx, schedules)
|
||||
@@ -247,14 +247,14 @@ func (s *agentTeamScheduleService) BatchGenerate(req request.AgentTeamScheduleBa
|
||||
|
||||
func (s *agentTeamScheduleService) buildScheduleModel(id, teamID int64, startAt, endAt, remark string) (*models.AgentTeamSchedule, error) {
|
||||
if teamID <= 0 {
|
||||
return nil, errorsx.InvalidParam("请选择客服组")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0326")
|
||||
}
|
||||
team := AgentTeamService.Get(teamID)
|
||||
if team == nil {
|
||||
return nil, errorsx.InvalidParam("客服组不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0169")
|
||||
}
|
||||
if !slices.Contains(enums.StatusValues, team.Status) {
|
||||
return nil, errorsx.InvalidParam("客服组状态不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0174")
|
||||
}
|
||||
startAtValue, err := parseRequiredDateTime(startAt, "开始时间格式错误")
|
||||
if err != nil {
|
||||
@@ -265,18 +265,18 @@ func (s *agentTeamScheduleService) buildScheduleModel(id, teamID int64, startAt,
|
||||
return nil, err
|
||||
}
|
||||
if !endAtValue.After(startAtValue) {
|
||||
return nil, errorsx.InvalidParam("结束时间必须晚于开始时间")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0296")
|
||||
}
|
||||
if !sameLocalDay(startAtValue, endAtValue) {
|
||||
return nil, errorsx.InvalidParam("单条排班记录不能跨天")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0132")
|
||||
}
|
||||
if startAtValue.Before(startOfLocalDay(time.Now())) {
|
||||
return nil, errorsx.InvalidParam("不能添加或修改历史日期的排班")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0085")
|
||||
}
|
||||
overlapping := repositories.AgentTeamScheduleRepository.FindOverlappingByTeamIDsAndTimeRange(sqls.DB(), []int64{teamID}, startAtValue, endAtValue)
|
||||
for _, item := range overlapping {
|
||||
if item.ID != id {
|
||||
return nil, errorsx.InvalidParam("该客服组在所选时间段已存在排班")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0309")
|
||||
}
|
||||
}
|
||||
return &models.AgentTeamSchedule{
|
||||
@@ -290,7 +290,7 @@ func (s *agentTeamScheduleService) buildScheduleModel(id, teamID int64, startAt,
|
||||
func (s *agentTeamScheduleService) buildBatchScheduleCandidates(req request.AgentTeamScheduleBatchRequest) ([]batchScheduleCandidate, error) {
|
||||
teamIDs := uniquePositiveInt64s(req.TeamIDs)
|
||||
if len(teamIDs) == 0 {
|
||||
return nil, errorsx.InvalidParam("请选择客服组")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0326")
|
||||
}
|
||||
weekdays, err := normalizeBatchWeekdays(req.Weekdays)
|
||||
if err != nil {
|
||||
@@ -305,10 +305,10 @@ func (s *agentTeamScheduleService) buildBatchScheduleCandidates(req request.Agen
|
||||
return nil, err
|
||||
}
|
||||
if endDate.Before(startDate) {
|
||||
return nil, errorsx.InvalidParam("结束日期必须晚于或等于开始日期")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0295")
|
||||
}
|
||||
if startDate.Before(startOfLocalDay(time.Now())) {
|
||||
return nil, errorsx.InvalidParam("不能添加或修改历史日期的排班")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0085")
|
||||
}
|
||||
startClock, err := parseRequiredClock(req.StartTime, "开始时间格式错误")
|
||||
if err != nil {
|
||||
@@ -321,7 +321,7 @@ func (s *agentTeamScheduleService) buildBatchScheduleCandidates(req request.Agen
|
||||
firstStartAt := combineDateAndClock(startDate, startClock)
|
||||
firstEndAt := combineDateAndClock(startDate, endClock)
|
||||
if !firstEndAt.After(firstStartAt) {
|
||||
return nil, errorsx.InvalidParam("结束时间必须晚于开始时间")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0296")
|
||||
}
|
||||
|
||||
teams := AgentTeamService.FindByIds(teamIDs)
|
||||
@@ -332,10 +332,10 @@ func (s *agentTeamScheduleService) buildBatchScheduleCandidates(req request.Agen
|
||||
for _, teamID := range teamIDs {
|
||||
team, ok := teamsByID[teamID]
|
||||
if !ok || team.Status == enums.StatusDeleted {
|
||||
return nil, errorsx.InvalidParam("客服组不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0169")
|
||||
}
|
||||
if !slices.Contains(enums.StatusValues, team.Status) {
|
||||
return nil, errorsx.InvalidParam("客服组状态不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0174")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ func (s *agentTeamScheduleService) buildBatchScheduleCandidates(req request.Agen
|
||||
continue
|
||||
}
|
||||
if len(candidates) >= maxAgentTeamScheduleBatchItems {
|
||||
return nil, errorsx.InvalidParam(fmt.Sprintf("单次最多生成 %d 条排班", maxAgentTeamScheduleBatchItems))
|
||||
return nil, errorsx.InvalidParamI18n("error.agentTeamSchedule.batchLimit", maxAgentTeamScheduleBatchItems)
|
||||
}
|
||||
candidates = append(candidates, batchScheduleCandidate{
|
||||
TeamID: teamID,
|
||||
@@ -365,7 +365,7 @@ func (s *agentTeamScheduleService) buildBatchScheduleCandidates(req request.Agen
|
||||
}
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
return nil, errorsx.InvalidParam("未生成任何排班")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0232")
|
||||
}
|
||||
return candidates, nil
|
||||
}
|
||||
@@ -430,11 +430,11 @@ func buildBatchPreviewResult(candidates []batchScheduleCandidate, conflicts map[
|
||||
}
|
||||
}
|
||||
|
||||
func (s *agentTeamScheduleService) findBatchConflict(candidates []batchScheduleCandidate) map[int]string {
|
||||
return s.findBatchConflictByDB(sqls.DB(), candidates)
|
||||
func (s *agentTeamScheduleService) findBatchConflict(candidates []batchScheduleCandidate, locale string) map[int]string {
|
||||
return s.findBatchConflictByDB(sqls.DB(), candidates, locale)
|
||||
}
|
||||
|
||||
func (s *agentTeamScheduleService) findBatchConflictByDB(db *gorm.DB, candidates []batchScheduleCandidate) map[int]string {
|
||||
func (s *agentTeamScheduleService) findBatchConflictByDB(db *gorm.DB, candidates []batchScheduleCandidate, locale string) map[int]string {
|
||||
conflicts := make(map[int]string)
|
||||
if len(candidates) == 0 {
|
||||
return conflicts
|
||||
@@ -458,7 +458,7 @@ func (s *agentTeamScheduleService) findBatchConflictByDB(db *gorm.DB, candidates
|
||||
continue
|
||||
}
|
||||
if item.StartAt.Before(candidate.EndAt) && item.EndAt.After(candidate.StartAt) {
|
||||
conflicts[i] = fmt.Sprintf("该客服组在 %s 至 %s 已存在排班", item.StartAt.Format(time.DateTime), item.EndAt.Format(time.DateTime))
|
||||
conflicts[i] = i18nx.Getf(locale, "error.agentTeamSchedule.conflictRange", item.StartAt.Format(time.DateTime), item.EndAt.Format(time.DateTime))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -471,7 +471,7 @@ func normalizeBatchWeekdays(values []int) ([]int, error) {
|
||||
ret := make([]int, 0, len(values))
|
||||
for _, value := range values {
|
||||
if value < 1 || value > 7 {
|
||||
return nil, errorsx.InvalidParam("星期必须在 1 到 7 之间")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0228")
|
||||
}
|
||||
if _, ok := seen[value]; ok {
|
||||
continue
|
||||
@@ -480,7 +480,7 @@ func normalizeBatchWeekdays(values []int) ([]int, error) {
|
||||
ret = append(ret, value)
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
return nil, errorsx.InvalidParam("请选择星期")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0329")
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -532,7 +532,7 @@ func parseDateTimeValue(value string) (time.Time, error) {
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
return time.Time{}, errorsx.InvalidParam("时间格式错误")
|
||||
return time.Time{}, errorsx.InvalidParamI18n("error.e0227")
|
||||
}
|
||||
|
||||
func startOfLocalDay(value time.Time) time.Time {
|
||||
|
||||
@@ -79,7 +79,7 @@ func (s *agentTeamService) Delete(id int64) {
|
||||
|
||||
func (s *agentTeamService) CreateAgentTeam(req request.CreateAgentTeamRequest, operator *dto.AuthPrincipal) (*models.AgentTeam, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item, err := s.buildTeamModel(0, req.Name, req.LeaderUserID, req.Status, req.Description, req.Remark)
|
||||
if err != nil {
|
||||
@@ -94,11 +94,11 @@ func (s *agentTeamService) CreateAgentTeam(req request.CreateAgentTeamRequest, o
|
||||
|
||||
func (s *agentTeamService) UpdateAgentTeam(req request.UpdateAgentTeamRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(req.ID)
|
||||
if current == nil || current.Status == enums.StatusDeleted {
|
||||
return errorsx.InvalidParam("客服组不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0169")
|
||||
}
|
||||
item, err := s.buildTeamModel(req.ID, req.Name, req.LeaderUserID, req.Status, req.Description, req.Remark)
|
||||
if err != nil {
|
||||
@@ -119,17 +119,17 @@ func (s *agentTeamService) UpdateAgentTeam(req request.UpdateAgentTeamRequest, o
|
||||
|
||||
func (s *agentTeamService) DeleteAgentTeam(id int64, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(id)
|
||||
if current == nil || current.Status == enums.StatusDeleted {
|
||||
return errorsx.InvalidParam("客服组不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0169")
|
||||
}
|
||||
if AgentProfileService.Take("team_id = ?", id) != nil {
|
||||
return errorsx.Forbidden("客服组下仍有关联客服档案,无法删除")
|
||||
return errorsx.ForbiddenI18n("error.e0167")
|
||||
}
|
||||
if AgentTeamScheduleService.Take("team_id = ?", id) != nil {
|
||||
return errorsx.Forbidden("客服组下仍有关联组排班,无法删除")
|
||||
return errorsx.ForbiddenI18n("error.e0168")
|
||||
}
|
||||
if AIAgentService.Take(
|
||||
"(team_ids = ? OR team_ids LIKE ? OR team_ids LIKE ? OR team_ids LIKE ?) AND status <> ?",
|
||||
@@ -139,7 +139,7 @@ func (s *agentTeamService) DeleteAgentTeam(id int64, operator *dto.AuthPrincipal
|
||||
"%,"+utils.JoinInt64s([]int64{id})+",%",
|
||||
enums.StatusDeleted,
|
||||
) != nil {
|
||||
return errorsx.Forbidden("客服组下仍有关联 AI Agent,无法删除")
|
||||
return errorsx.ForbiddenI18n("error.e0166")
|
||||
}
|
||||
return repositories.AgentTeamRepository.Updates(sqls.DB(), id, map[string]any{
|
||||
"status": enums.StatusDeleted,
|
||||
@@ -152,16 +152,16 @@ func (s *agentTeamService) DeleteAgentTeam(id int64, operator *dto.AuthPrincipal
|
||||
func (s *agentTeamService) buildTeamModel(id int64, name string, leaderUserID int64, status int, description, remark string) (*models.AgentTeam, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("客服组名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0170")
|
||||
}
|
||||
if exists := s.Take("name = ? AND status <> ? AND id <> ?", name, enums.StatusDeleted, id); exists != nil {
|
||||
return nil, errorsx.InvalidParam("客服组名称已存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0171")
|
||||
}
|
||||
if leaderUserID > 0 && UserService.Get(leaderUserID) == nil {
|
||||
return nil, errorsx.InvalidParam("组长用户不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0294")
|
||||
}
|
||||
if status != 0 && status != 1 {
|
||||
return nil, errorsx.InvalidParam("客服组状态不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0174")
|
||||
}
|
||||
return &models.AgentTeam{
|
||||
Name: name,
|
||||
|
||||
@@ -66,7 +66,7 @@ func (s *aIAgentService) FindByIds(ids []int64) []models.AIAgent {
|
||||
|
||||
func (s *aIAgentService) CreateAIAgent(req request.CreateAIAgentRequest, operator *dto.AuthPrincipal) (*models.AIAgent, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item, err := s.buildAIAgentModel(0, req)
|
||||
if err != nil {
|
||||
@@ -83,10 +83,10 @@ func (s *aIAgentService) CreateAIAgent(req request.CreateAIAgentRequest, operato
|
||||
|
||||
func (s *aIAgentService) UpdateAIAgent(req request.UpdateAIAgentRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if s.Get(req.ID) == nil {
|
||||
return errorsx.InvalidParam("AI Agent 不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0002")
|
||||
}
|
||||
item, err := s.buildAIAgentModel(req.ID, req.CreateAIAgentRequest)
|
||||
if err != nil {
|
||||
@@ -117,10 +117,10 @@ func (s *aIAgentService) UpdateAIAgent(req request.UpdateAIAgentRequest, operato
|
||||
func (s *aIAgentService) DeleteAIAgent(id int64, operator *dto.AuthPrincipal) error {
|
||||
current := s.Get(id)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("AI Agent 不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0002")
|
||||
}
|
||||
if ChannelService.Take("ai_agent_id = ?", id) != nil {
|
||||
return errorsx.Forbidden("已有接入渠道绑定该 AI Agent,无法删除")
|
||||
return errorsx.ForbiddenI18n("error.e0185")
|
||||
}
|
||||
return repositories.AIAgentRepository.Updates(sqls.DB(), id, map[string]any{
|
||||
"status": enums.StatusDeleted,
|
||||
@@ -133,23 +133,23 @@ func (s *aIAgentService) DeleteAIAgent(id int64, operator *dto.AuthPrincipal) er
|
||||
func (s *aIAgentService) buildAIAgentModel(id int64, req request.CreateAIAgentRequest) (*models.AIAgent, error) {
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("AI Agent 名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0005")
|
||||
}
|
||||
if exists := s.Take("name = ? AND id <> ?", name, id); exists != nil {
|
||||
return nil, errorsx.InvalidParam("AI Agent 名称已存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0006")
|
||||
}
|
||||
if req.AIConfigID <= 0 {
|
||||
return nil, errorsx.InvalidParam("AI 配置不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0010")
|
||||
}
|
||||
aiConfig := AIConfigService.Get(req.AIConfigID)
|
||||
if aiConfig == nil {
|
||||
return nil, errorsx.InvalidParam("AI 配置不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0009")
|
||||
}
|
||||
if aiConfig.Status != enums.StatusOk {
|
||||
return nil, errorsx.InvalidParam("AI 配置未启用")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0011")
|
||||
}
|
||||
if !slices.Contains(enums.IMConversationServiceModeValues, req.ServiceMode) {
|
||||
return nil, errorsx.InvalidParam("服务模式不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0230")
|
||||
}
|
||||
teamIDs, err := s.normalizeTeamIDs(req.TeamIDs)
|
||||
if err != nil {
|
||||
@@ -157,19 +157,19 @@ func (s *aIAgentService) buildAIAgentModel(id int64, req request.CreateAIAgentRe
|
||||
}
|
||||
|
||||
if !slices.Contains(enums.AIAgentHandoffModeValues, enums.AIAgentHandoffMode(req.HandoffMode)) {
|
||||
return nil, errorsx.InvalidParam("转人工模式不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0336")
|
||||
}
|
||||
if req.FallbackMode == 0 {
|
||||
req.FallbackMode = enums.AIAgentFallbackModeNoAnswer
|
||||
}
|
||||
if !slices.Contains(enums.AIAgentFallbackModeValues, enums.AIAgentFallbackMode(req.FallbackMode)) {
|
||||
return nil, errorsx.InvalidParam("兜底策略不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0123")
|
||||
}
|
||||
if enums.AIAgentHandoffMode(req.HandoffMode) == enums.AIAgentHandoffModeDefaultTeamPool && len(teamIDs) == 0 {
|
||||
return nil, errorsx.InvalidParam("默认客服组待接入池模式必须至少选择一个客服组")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0347")
|
||||
}
|
||||
if req.ReplyTimeoutSeconds < 0 {
|
||||
return nil, errorsx.InvalidParam("回复超时秒数不能小于 0")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0144")
|
||||
}
|
||||
|
||||
knowledgeIDs, err := s.normalizeKnowledgeIDs(req.KnowledgeIDs)
|
||||
@@ -177,7 +177,7 @@ func (s *aIAgentService) buildAIAgentModel(id int64, req request.CreateAIAgentRe
|
||||
return nil, err
|
||||
}
|
||||
if len(knowledgeIDs) == 0 {
|
||||
return nil, errorsx.InvalidParam("请至少选择一个知识库")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0320")
|
||||
}
|
||||
skillIDs, err := s.normalizeSkillIDs(req.SkillIDs)
|
||||
if err != nil {
|
||||
@@ -195,7 +195,7 @@ func (s *aIAgentService) buildAIAgentModel(id int64, req request.CreateAIAgentRe
|
||||
if len(directTools) > 0 {
|
||||
buf, marshalErr := json.Marshal(directTools)
|
||||
if marshalErr != nil {
|
||||
return nil, errorsx.InvalidParam("Direct Tools 配置格式不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0021")
|
||||
}
|
||||
directToolsJSON = string(buf)
|
||||
}
|
||||
@@ -203,7 +203,7 @@ func (s *aIAgentService) buildAIAgentModel(id int64, req request.CreateAIAgentRe
|
||||
if len(graphTools) > 0 {
|
||||
buf, marshalErr := json.Marshal(graphTools)
|
||||
if marshalErr != nil {
|
||||
return nil, errorsx.InvalidParam("Graph Tools 配置格式不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0028")
|
||||
}
|
||||
graphToolsJSON = string(buf)
|
||||
}
|
||||
@@ -241,7 +241,7 @@ func (s *aIAgentService) normalizeTeamIDs(input []int64) ([]int64, error) {
|
||||
continue
|
||||
}
|
||||
// if team.Status != enums.StatusOk {
|
||||
// return nil, errorsx.InvalidParam("客服组未启用")
|
||||
// return nil, errorsx.InvalidParamI18n("error.e0173")
|
||||
// }
|
||||
seen[id] = struct{}{}
|
||||
ret = append(ret, id)
|
||||
@@ -265,7 +265,7 @@ func (s *aIAgentService) normalizeKnowledgeIDs(input []int64) ([]int64, error) {
|
||||
continue
|
||||
}
|
||||
// if kb.Status != enums.StatusOk {
|
||||
// return nil, errorsx.InvalidParam("知识库未启用")
|
||||
// return nil, errorsx.InvalidParamI18n("error.e0285")
|
||||
// }
|
||||
seen[id] = struct{}{}
|
||||
ret = append(ret, id)
|
||||
@@ -288,7 +288,7 @@ func (s *aIAgentService) normalizeSkillIDs(input []int64) ([]int64, error) {
|
||||
continue
|
||||
}
|
||||
// if skill.Status != enums.StatusOk {
|
||||
// return nil, errorsx.InvalidParam("Skill 未启用")
|
||||
// return nil, errorsx.InvalidParamI18n("error.e0056")
|
||||
// }
|
||||
seen[id] = struct{}{}
|
||||
ret = append(ret, id)
|
||||
@@ -311,7 +311,7 @@ func (s *aIAgentService) normalizeDirectTools(input []request.AIAgentMCPToolRequ
|
||||
continue
|
||||
}
|
||||
if toolx.ResolveToolSourceType(normalized.ToolCode) != enums.ToolSourceTypeMCP {
|
||||
return nil, errorsx.InvalidParam("Direct Tools 仅允许配置 MCP 工具")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0020")
|
||||
}
|
||||
if err := ToolCatalogService.ValidateToolCode(normalized.ToolCode); err != nil {
|
||||
return nil, err
|
||||
@@ -338,7 +338,7 @@ func (s *aIAgentService) normalizeGraphTools(input []string) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
if !toolx.IsAgentDirectGraphToolCode(toolCode) {
|
||||
return nil, errorsx.InvalidParam("Graph Tools 仅允许配置 Graph Tool")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0027")
|
||||
}
|
||||
if _, exists := seen[toolCode]; exists {
|
||||
continue
|
||||
@@ -362,14 +362,14 @@ func (s *aIAgentService) UpdateSort(ids []int64) error {
|
||||
|
||||
func (s *aIAgentService) UpdateStatus(id int64, status int, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(id)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("AI Agent 不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0002")
|
||||
}
|
||||
if status != int(enums.StatusOk) && status != int(enums.StatusDisabled) {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
|
||||
return repositories.AIAgentRepository.Updates(sqls.DB(), id, map[string]any{
|
||||
|
||||
@@ -77,7 +77,7 @@ func (s *aIConfigService) Delete(id int64) {
|
||||
|
||||
func (s *aIConfigService) CreateAIConfig(req request.CreateAIConfigRequest, operator *dto.AuthPrincipal) (*models.AIConfig, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item, err := s.buildAIConfigModel(req)
|
||||
if err != nil {
|
||||
@@ -100,11 +100,11 @@ func (s *aIConfigService) CreateAIConfig(req request.CreateAIConfigRequest, oper
|
||||
|
||||
func (s *aIConfigService) UpdateAIConfig(req request.UpdateAIConfigRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(req.ID)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("AI配置不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0012")
|
||||
}
|
||||
item, err := s.buildAIConfigModel(req.CreateAIConfigRequest)
|
||||
if err != nil {
|
||||
@@ -141,7 +141,7 @@ func (s *aIConfigService) DeleteAIConfig(id int64, operator *dto.AuthPrincipal)
|
||||
return nil
|
||||
}
|
||||
if current.Status == enums.StatusOk {
|
||||
return errorsx.Forbidden("启用中的AI配置不允许删除")
|
||||
return errorsx.ForbiddenI18n("error.e0143")
|
||||
}
|
||||
return repositories.AIConfigRepository.Updates(sqls.DB(), id, map[string]any{
|
||||
"status": enums.StatusDeleted,
|
||||
@@ -153,14 +153,14 @@ func (s *aIConfigService) DeleteAIConfig(id int64, operator *dto.AuthPrincipal)
|
||||
|
||||
func (s *aIConfigService) UpdateStatus(id int64, status enums.Status, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(id)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("AI配置不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0012")
|
||||
}
|
||||
if status != enums.StatusOk && status != enums.StatusDisabled {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
|
||||
return sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
@@ -206,19 +206,19 @@ func (s *aIConfigService) buildAIConfigModel(req request.CreateAIConfigRequest)
|
||||
modelName := strings.TrimSpace(req.ModelName)
|
||||
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("配置名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0339")
|
||||
}
|
||||
if strs.IsBlank(string(req.Provider)) {
|
||||
return nil, errorsx.InvalidParam("供应商不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0122")
|
||||
}
|
||||
if baseURL == "" {
|
||||
return nil, errorsx.InvalidParam("基础地址不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0147")
|
||||
}
|
||||
if strs.IsBlank(string(req.ModelType)) {
|
||||
return nil, errorsx.InvalidParam("模型类型不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0243")
|
||||
}
|
||||
if modelName == "" {
|
||||
return nil, errorsx.InvalidParam("模型名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0242")
|
||||
}
|
||||
if req.Dimension < 0 {
|
||||
req.Dimension = 0
|
||||
|
||||
@@ -48,7 +48,7 @@ func (s *assetService) FindPageByCnd(cnd *sqls.Cnd) (list []models.Asset, paging
|
||||
func (s *assetService) OpenReader(asset *models.Asset) (io.ReadCloser, error) {
|
||||
cfg := config.Current()
|
||||
if asset == nil {
|
||||
return nil, errorsx.InvalidParam("图片资源不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0146")
|
||||
}
|
||||
switch asset.Provider {
|
||||
case "", enums.AssetProviderLocal:
|
||||
@@ -56,7 +56,7 @@ func (s *assetService) OpenReader(asset *models.Asset) (io.ReadCloser, error) {
|
||||
case enums.AssetProviderOSS:
|
||||
return storage.NewOSSStorage(cfg.Storage.OSS).Read(asset.StorageKey)
|
||||
default:
|
||||
return nil, errorsx.InvalidParam("当前暂不支持该存储类型的文件读取")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0195")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,12 +73,12 @@ func (s *assetService) UploadBytes(data []byte, prefix, filename string, princip
|
||||
|
||||
func (s *assetService) UploadFile(file *multipart.FileHeader, prefix string, principal *dto.AuthPrincipal) (*models.Asset, error) {
|
||||
if file == nil {
|
||||
return nil, errorsx.InvalidParam("请选择上传文件")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0323")
|
||||
}
|
||||
|
||||
cfg := config.Current()
|
||||
if file.Size > cfg.Storage.MaxUploadSizeBytes() {
|
||||
return nil, errorsx.InvalidParam("上传文件超过大小限制")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0079")
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
@@ -137,10 +137,10 @@ func (s *assetService) Upload(reader io.Reader, info storage.UploadInfo) (*model
|
||||
func (s *assetService) GetSignedURL(id int64) (string, error) {
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return "", errorsx.InvalidParam("文件不存在")
|
||||
return "", errorsx.InvalidParamI18n("error.e0214")
|
||||
}
|
||||
if item.Status != enums.AssetStatusSuccess {
|
||||
return "", errorsx.InvalidParam("文件不可访问")
|
||||
return "", errorsx.InvalidParamI18n("error.e0213")
|
||||
}
|
||||
|
||||
provider, err := storage.NewProvider(item.Provider)
|
||||
@@ -153,11 +153,11 @@ func (s *assetService) GetSignedURL(id int64) (string, error) {
|
||||
|
||||
func (s *assetService) DeleteAsset(id int64, principal *dto.AuthPrincipal) error {
|
||||
if principal == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("文件不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0214")
|
||||
}
|
||||
return repositories.AssetRepository.Updates(sqls.DB(), id, map[string]any{
|
||||
"status": enums.AssetStatusDeleted,
|
||||
|
||||
@@ -70,11 +70,11 @@ func (s *authService) RequirePermission(ctx *gin.Context, permission constants.P
|
||||
}
|
||||
|
||||
if principal == nil {
|
||||
return nil, errorsx.Forbidden("无权限执行该操作")
|
||||
return nil, errorsx.ForbiddenI18n("error.e0225")
|
||||
}
|
||||
|
||||
if !s.HasPermission(ctx, permission.Code) {
|
||||
return principal, errorsx.Forbidden("无权限执行该操作")
|
||||
return principal, errorsx.ForbiddenI18n("error.e0225")
|
||||
}
|
||||
return principal, nil
|
||||
}
|
||||
@@ -84,22 +84,22 @@ func (s *authService) Login(req request.LoginRequest, authCfg config.AuthConfig,
|
||||
principal := normalizeLoginPrincipal(username)
|
||||
password := req.Password
|
||||
if username == "" || strings.TrimSpace(password) == "" {
|
||||
return nil, errorsx.InvalidParam("用户名和密码不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0258")
|
||||
}
|
||||
|
||||
if s.isCredentialLocked(principal, authCfg) {
|
||||
_ = s.createLoginCredentialLog(principal, 0, false, clientIP, userAgent, "credential locked")
|
||||
return nil, errorsx.CredentialLocked("登录失败次数过多,请稍后再试")
|
||||
return nil, errorsx.CredentialLockedI18n("error.e0270")
|
||||
}
|
||||
|
||||
user := UserService.GetByUsername(username)
|
||||
if user == nil || user.Status != enums.StatusOk {
|
||||
_ = s.createLoginCredentialLog(principal, 0, false, clientIP, userAgent, "user not found")
|
||||
return nil, errorsx.InvalidAccount("用户名或密码错误")
|
||||
return nil, errorsx.InvalidAccountI18n("error.e0260")
|
||||
}
|
||||
if strs.IsBlank(user.Password) || bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) != nil {
|
||||
_ = s.createLoginCredentialLog(principal, user.ID, false, clientIP, userAgent, "password mismatch")
|
||||
return nil, errorsx.InvalidAccount("用户名或密码错误")
|
||||
return nil, errorsx.InvalidAccountI18n("error.e0260")
|
||||
}
|
||||
|
||||
var ret *response.LoginResponse
|
||||
@@ -153,7 +153,7 @@ func (s *authService) Authenticate(ctx *gin.Context) (*dto.AuthPrincipal, error)
|
||||
token = strings.TrimSpace(ctx.Query("accessToken"))
|
||||
}
|
||||
if token == "" {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
|
||||
session, err := s.validateSessionToken(token)
|
||||
@@ -163,7 +163,7 @@ func (s *authService) Authenticate(ctx *gin.Context) (*dto.AuthPrincipal, error)
|
||||
|
||||
user := UserService.Get(session.UserID)
|
||||
if user == nil || user.Status != enums.StatusOk {
|
||||
return nil, errorsx.Unauthorized("用户不存在或已被禁用")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0256")
|
||||
}
|
||||
|
||||
roles, permissions, err := s.loadUserAuthScope(sqls.DB(), user.ID)
|
||||
@@ -276,17 +276,17 @@ func (s *authService) resolveTokenTTL(authCfg config.AuthConfig) time.Duration {
|
||||
|
||||
func (s *authService) validateSessionToken(token string) (*models.LoginSession, error) {
|
||||
if strings.TrimSpace(token) == "" {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
session := LoginSessionService.FindOne(sqls.NewCnd().Eq("token", token))
|
||||
if session == nil {
|
||||
return nil, errorsx.InvalidToken("登录凭证无效")
|
||||
return nil, errorsx.InvalidTokenI18n("error.e0269")
|
||||
}
|
||||
if session.RevokedAt != nil {
|
||||
return nil, errorsx.InvalidToken("登录凭证已失效")
|
||||
return nil, errorsx.InvalidTokenI18n("error.e0267")
|
||||
}
|
||||
if time.Now().After(session.ExpiredAt) {
|
||||
return nil, errorsx.InvalidToken("登录凭证已过期")
|
||||
return nil, errorsx.InvalidTokenI18n("error.e0268")
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func (s *channelService) UpdateColumn(id int64, name string, value interface{})
|
||||
|
||||
func (s *channelService) CreateChannel(req request.CreateChannelRequest, operator *dto.AuthPrincipal) (*models.Channel, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item, err := s.buildChannelModel(0, req)
|
||||
if err != nil {
|
||||
@@ -95,11 +95,11 @@ func (s *channelService) CreateChannel(req request.CreateChannelRequest, operato
|
||||
|
||||
func (s *channelService) UpdateChannel(req request.UpdateChannelRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(req.ID)
|
||||
if current == nil || current.Status == enums.StatusDeleted {
|
||||
return errorsx.InvalidParam("接入渠道不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0208")
|
||||
}
|
||||
item, err := s.buildChannelModel(req.ID, req.CreateChannelRequest)
|
||||
if err != nil {
|
||||
@@ -121,14 +121,14 @@ func (s *channelService) UpdateChannel(req request.UpdateChannelRequest, operato
|
||||
|
||||
func (s *channelService) UpdateStatus(id int64, status int, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := s.Get(id)
|
||||
if item == nil || item.Status == enums.StatusDeleted {
|
||||
return errorsx.InvalidParam("接入渠道不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0208")
|
||||
}
|
||||
if status != int(enums.StatusOk) && status != int(enums.StatusDisabled) {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
return s.Updates(id, map[string]any{
|
||||
"status": status,
|
||||
@@ -140,11 +140,11 @@ func (s *channelService) UpdateStatus(id int64, status int, operator *dto.AuthPr
|
||||
|
||||
func (s *channelService) DeleteChannel(id int64, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := s.Get(id)
|
||||
if item == nil || item.Status == enums.StatusDeleted {
|
||||
return errorsx.InvalidParam("接入渠道不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0208")
|
||||
}
|
||||
return s.Updates(id, map[string]any{
|
||||
"status": enums.StatusDeleted,
|
||||
@@ -233,7 +233,7 @@ func (s *channelService) ParseWebChannelConfig(raw string) (*dto.WebChannelConfi
|
||||
cfg.Position = "right"
|
||||
}
|
||||
if cfg.Position != "left" && cfg.Position != "right" {
|
||||
return nil, errorsx.InvalidParam("Web渠道配置 position 只能为 left 或 right")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0059")
|
||||
}
|
||||
cfg.Width = strings.TrimSpace(cfg.Width)
|
||||
if cfg.Width == "" {
|
||||
@@ -292,14 +292,14 @@ func (s *channelService) GetUserTokenSecret(channel *models.Channel) string {
|
||||
|
||||
func (s *channelService) ResetUserTokenSecret(channelID int64, operator *dto.AuthPrincipal) (string, error) {
|
||||
if operator == nil {
|
||||
return "", errorsx.Unauthorized("未登录或登录已过期")
|
||||
return "", errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
channel := s.Get(channelID)
|
||||
if channel == nil || channel.Status == enums.StatusDeleted {
|
||||
return "", errorsx.InvalidParam("接入渠道不存在")
|
||||
return "", errorsx.InvalidParamI18n("error.e0208")
|
||||
}
|
||||
if channel.ChannelType != enums.ChannelTypeWeb && channel.ChannelType != enums.ChannelTypeWechatMP {
|
||||
return "", errorsx.InvalidParam("当前渠道不支持用户 JWT Secret")
|
||||
return "", errorsx.InvalidParamI18n("error.e0196")
|
||||
}
|
||||
secret, err := generateUserTokenSecret()
|
||||
if err != nil {
|
||||
@@ -385,32 +385,32 @@ func (s *channelService) GetEnabledChannel(ctx *gin.Context) *models.Channel {
|
||||
func (s *channelService) buildChannelModel(id int64, req request.CreateChannelRequest) (*models.Channel, error) {
|
||||
channelType := strings.TrimSpace(req.ChannelType)
|
||||
if channelType != enums.ChannelTypeWeb && channelType != enums.ChannelTypeWechatMP && channelType != enums.ChannelTypeWxWorkKF {
|
||||
return nil, errorsx.InvalidParam("渠道类型不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0250")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("渠道名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0247")
|
||||
}
|
||||
if req.AIAgentID <= 0 {
|
||||
return nil, errorsx.InvalidParam("请选择 AI Agent")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0321")
|
||||
}
|
||||
aiAgent := AIAgentService.Get(req.AIAgentID)
|
||||
if aiAgent == nil || aiAgent.Status != enums.StatusOk {
|
||||
return nil, errorsx.InvalidParam("AI Agent 不存在或未启用")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0004")
|
||||
}
|
||||
status := enums.Status(req.Status)
|
||||
if req.Status == 0 {
|
||||
status = enums.StatusOk
|
||||
}
|
||||
if status != enums.StatusOk && status != enums.StatusDisabled {
|
||||
return nil, errorsx.InvalidParam("渠道状态不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0249")
|
||||
}
|
||||
|
||||
channelID := ""
|
||||
if id > 0 {
|
||||
current := s.Get(id)
|
||||
if current == nil || current.Status == enums.StatusDeleted {
|
||||
return nil, errorsx.InvalidParam("接入渠道不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0208")
|
||||
}
|
||||
channelID = strings.TrimSpace(current.ChannelID)
|
||||
}
|
||||
@@ -421,11 +421,11 @@ func (s *channelService) buildChannelModel(id int64, req request.CreateChannelRe
|
||||
channelID = strs.UUID()
|
||||
}
|
||||
if exists := s.Take("channel_id = ? AND status <> ? AND id <> ?", channelID, enums.StatusDeleted, id); exists != nil {
|
||||
return nil, errorsx.InvalidParam("渠道标识已存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0248")
|
||||
}
|
||||
cfg, err := s.ParseWebChannelConfig(configJSON)
|
||||
if err != nil {
|
||||
return nil, errorsx.InvalidParam("Web渠道配置不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0060")
|
||||
}
|
||||
if strings.TrimSpace(cfg.UserTokenSecret) == "" {
|
||||
secret, err := generateUserTokenSecret()
|
||||
@@ -444,11 +444,11 @@ func (s *channelService) buildChannelModel(id int64, req request.CreateChannelRe
|
||||
channelID = strs.UUID()
|
||||
}
|
||||
if exists := s.Take("channel_id = ? AND status <> ? AND id <> ?", channelID, enums.StatusDeleted, id); exists != nil {
|
||||
return nil, errorsx.InvalidParam("渠道标识已存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0248")
|
||||
}
|
||||
cfg, err := s.ParseWechatMPChannelConfig(configJSON)
|
||||
if err != nil {
|
||||
return nil, errorsx.InvalidParam("微信公众号渠道配置不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0201")
|
||||
}
|
||||
if strings.TrimSpace(cfg.UserTokenSecret) == "" {
|
||||
secret, err := generateUserTokenSecret()
|
||||
@@ -467,17 +467,17 @@ func (s *channelService) buildChannelModel(id int64, req request.CreateChannelRe
|
||||
channelID = strs.UUID()
|
||||
}
|
||||
if exists := s.Take("channel_id = ? AND status <> ? AND id <> ?", channelID, enums.StatusDeleted, id); exists != nil {
|
||||
return nil, errorsx.InvalidParam("渠道标识已存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0248")
|
||||
}
|
||||
cfg, err := s.ParseWxWorkKFChannelConfig(configJSON)
|
||||
if err != nil {
|
||||
return nil, errorsx.InvalidParam("企业微信渠道配置不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0102")
|
||||
}
|
||||
if cfg == nil || cfg.OpenKfID == "" {
|
||||
return nil, errorsx.InvalidParam("企业微信渠道配置缺少 openKfId")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0103")
|
||||
}
|
||||
if channel := s.GetEnabledWxWorkKFChannelByOpenKfID(cfg.OpenKfID); channel != nil && channel.ID != id {
|
||||
return nil, errorsx.InvalidParam("openKfId 已被其他渠道使用")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0069")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,16 +58,16 @@ func (s *companyService) Count(cnd *sqls.Cnd) int64 {
|
||||
|
||||
func (s *companyService) CreateCompany(req request.CreateCompanyRequest, operator *dto.AuthPrincipal) (*models.Company, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("公司名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0125")
|
||||
}
|
||||
|
||||
existing := repositories.CompanyRepository.GetByName(sqls.DB(), name)
|
||||
if existing != nil && existing.Status != enums.StatusDeleted {
|
||||
return nil, errorsx.InvalidParam("公司名称已存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0126")
|
||||
}
|
||||
|
||||
item := &models.Company{
|
||||
@@ -85,20 +85,20 @@ func (s *companyService) CreateCompany(req request.CreateCompanyRequest, operato
|
||||
|
||||
func (s *companyService) UpdateCompany(req request.UpdateCompanyRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := s.Get(req.ID)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("公司不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0124")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return errorsx.InvalidParam("公司名称不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0125")
|
||||
}
|
||||
|
||||
existing := repositories.CompanyRepository.GetByName(sqls.DB(), name)
|
||||
if existing != nil && existing.ID != req.ID {
|
||||
return errorsx.InvalidParam("公司名称已存在")
|
||||
return errorsx.InvalidParamI18n("error.e0126")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
@@ -118,7 +118,7 @@ func (s *companyService) UpdateCompany(req request.UpdateCompanyRequest, operato
|
||||
func (s *companyService) DeleteCompany(id int64, operator dto.AuthPrincipal) error {
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("公司不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0124")
|
||||
}
|
||||
|
||||
return repositories.CompanyRepository.Updates(sqls.DB(), id, map[string]any{
|
||||
@@ -131,14 +131,14 @@ func (s *companyService) DeleteCompany(id int64, operator dto.AuthPrincipal) err
|
||||
|
||||
func (s *companyService) UpdateStatus(id int64, status int, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("公司不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0124")
|
||||
}
|
||||
if status != int(enums.StatusOk) && status != int(enums.StatusDisabled) {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
now := time.Now()
|
||||
return repositories.CompanyRepository.Updates(sqls.DB(), id, map[string]any{
|
||||
|
||||
@@ -52,7 +52,7 @@ func (s *conversationHumanDispatchService) TryOffHoursHandoffByAI(conversationID
|
||||
func (s *conversationHumanDispatchService) TryOffHoursHandoffByAIWithRequestID(conversationID int64, aiAgent models.AIAgent, reason string, requestID string) (bool, error) {
|
||||
conversation := ConversationService.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return false, errorsx.InvalidParam("会话不存在")
|
||||
return false, errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
teamIDs := orderedPositiveIDs(aiAgent.TeamIDs)
|
||||
activeTeamIDs := ConversationDispatchService.findActiveScheduleTeamIDs(teamIDs, time.Now())
|
||||
@@ -75,7 +75,7 @@ func (s *conversationHumanDispatchService) HandoffByAI(conversationID int64, aiA
|
||||
func (s *conversationHumanDispatchService) HandoffByAIWithRequestID(conversationID int64, aiAgent models.AIAgent, reason string, requestID string) (*HandoffDecisionResult, error) {
|
||||
conversation := ConversationService.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return nil, errorsx.InvalidParam("会话不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
teamIDs := orderedPositiveIDs(aiAgent.TeamIDs)
|
||||
activeTeamIDs := ConversationDispatchService.findActiveScheduleTeamIDs(teamIDs, time.Now())
|
||||
@@ -110,10 +110,10 @@ func (s *conversationHumanDispatchService) ApplyHumanOnlyCreate(conversationID i
|
||||
func (s *conversationHumanDispatchService) DispatchPendingConversation(conversationID int64, aiAgent models.AIAgent) (*HandoffDecisionResult, error) {
|
||||
conversation := ConversationService.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return nil, errorsx.InvalidParam("会话不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if conversation.Status != enums.IMConversationStatusPending || conversation.CurrentAssigneeID > 0 {
|
||||
return nil, errorsx.InvalidParam("只有待接入未分配会话允许自动分配")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0137")
|
||||
}
|
||||
activeTeamIDs := ConversationDispatchService.findActiveScheduleTeamIDs(orderedPositiveIDs(aiAgent.TeamIDs), time.Now())
|
||||
if len(activeTeamIDs) == 0 {
|
||||
@@ -227,7 +227,7 @@ func (s *conversationHumanDispatchService) moveToTeamPoolWithRequestID(conversat
|
||||
err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
current := repositories.ConversationRepository.Get(ctx.Tx, conversationID)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if err := ConversationAssignmentService.FinishActiveAssignments(ctx, conversationID, now); err != nil {
|
||||
return err
|
||||
@@ -273,7 +273,7 @@ func (s *conversationHumanDispatchService) moveToGlobalPool(conversationID int64
|
||||
return sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
conversation := repositories.ConversationRepository.Get(ctx.Tx, conversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if err := repositories.ConversationRepository.Updates(ctx.Tx, conversationID, map[string]any{
|
||||
"status": enums.IMConversationStatusPending,
|
||||
|
||||
@@ -37,7 +37,7 @@ type readerCursor struct {
|
||||
|
||||
func agentReaderCursor(operator *dto.AuthPrincipal) (readerCursor, error) {
|
||||
if operator == nil {
|
||||
return readerCursor{}, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return readerCursor{}, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
return readerCursor{
|
||||
readerType: enums.IMSenderTypeAgent,
|
||||
@@ -50,7 +50,7 @@ func agentReaderCursor(operator *dto.AuthPrincipal) (readerCursor, error) {
|
||||
|
||||
func customerReaderCursor(external *openidentity.ExternalUser) (readerCursor, error) {
|
||||
if external == nil || strings.TrimSpace(external.ExternalID) == "" {
|
||||
return readerCursor{}, errorsx.Unauthorized("外部用户标识不能为空")
|
||||
return readerCursor{}, errorsx.UnauthorizedI18n("error.e0149")
|
||||
}
|
||||
extID := strings.TrimSpace(external.ExternalID)
|
||||
name := strings.TrimSpace(external.ExternalName)
|
||||
@@ -195,7 +195,7 @@ func (s *conversationReadStateService) markReadTxWithCursor(ctx *sqls.TxContext,
|
||||
return nil, nil
|
||||
}
|
||||
if c.readerType != enums.IMSenderTypeAgent && c.readerType != enums.IMSenderTypeCustomer {
|
||||
return nil, errorsx.InvalidParam("不支持的已读操作类型")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0081")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
@@ -74,7 +74,7 @@ func (s *conversationService) ListConversations(userID int64, filter request.Age
|
||||
case request.AgentConversationFilterClosed:
|
||||
cnd.Eq("current_assignee_id", userID).Eq("status", enums.IMConversationStatusClosed).Desc("last_active_at").Desc("id")
|
||||
default:
|
||||
return nil, nil, errorsx.InvalidParam("会话筛选项不合法")
|
||||
return nil, nil, errorsx.InvalidParamI18n("error.e0121")
|
||||
}
|
||||
|
||||
list, paging := repositories.ConversationRepository.FindPageByCnd(sqls.DB(), cnd)
|
||||
@@ -103,7 +103,7 @@ func (s *conversationService) getLatestNotFinishedByCustomerID(db *gorm.DB, cust
|
||||
func (s *conversationService) Create(externalUser openidentity.ExternalUser, channelID, aiAgentID int64) (*models.Conversation, error) {
|
||||
aiAgent := AIAgentService.Get(aiAgentID)
|
||||
if aiAgent == nil || aiAgent.Status != enums.StatusOk {
|
||||
return nil, errorsx.InvalidParam("AI Agent 不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0002")
|
||||
}
|
||||
|
||||
var conversation *models.Conversation
|
||||
@@ -184,20 +184,20 @@ func (s *conversationService) Create(externalUser openidentity.ExternalUser, cha
|
||||
|
||||
func (s *conversationService) AssignConversation(req request.AssignConversationRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
targetProfile := AgentProfileService.GetByUserID(req.AssigneeID)
|
||||
if targetProfile == nil || targetProfile.Status != enums.StatusOk {
|
||||
return errorsx.InvalidParam("目标客服不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0276")
|
||||
}
|
||||
var assignedEvent events.ConversationAssignedEvent
|
||||
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
conversation := repositories.ConversationRepository.Get(ctx.Tx, req.ConversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if conversation.Status != enums.IMConversationStatusPending {
|
||||
return errorsx.InvalidParam("只有待接入会话允许分配")
|
||||
return errorsx.InvalidParamI18n("error.e0135")
|
||||
}
|
||||
now := time.Now()
|
||||
if err := ConversationAssignmentService.FinishActiveAssignments(ctx, req.ConversationID, now); err != nil {
|
||||
@@ -245,62 +245,62 @@ func (s *conversationService) AssignConversation(req request.AssignConversationR
|
||||
|
||||
func (s *conversationService) AutoAssignConversation(conversationID int64, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
|
||||
conversation := s.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if conversation.Status != enums.IMConversationStatusPending {
|
||||
return errorsx.InvalidParam("只有待接入会话允许自动分配")
|
||||
return errorsx.InvalidParamI18n("error.e0136")
|
||||
}
|
||||
if conversation.CurrentAssigneeID > 0 {
|
||||
return errorsx.InvalidParam("当前会话已分配客服")
|
||||
return errorsx.InvalidParamI18n("error.e0190")
|
||||
}
|
||||
|
||||
aiAgent := AIAgentService.Get(conversation.AIAgentID)
|
||||
if aiAgent == nil || aiAgent.Status != enums.StatusOk {
|
||||
return errorsx.InvalidParam("AI Agent 不存在或已停用")
|
||||
return errorsx.InvalidParamI18n("error.e0003")
|
||||
}
|
||||
result, err := ConversationHumanDispatchService.DispatchPendingConversation(conversationID, *aiAgent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result == nil || result.Decision == HandoffDecisionOffHours {
|
||||
return errorsx.InvalidParam("当前暂不在人工客服服务时间内")
|
||||
return errorsx.InvalidParamI18n("error.e0194")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *conversationService) TransferConversation(conversationID, toUserID int64, reason string, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if toUserID <= 0 {
|
||||
return errorsx.InvalidParam("目标客服不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0278")
|
||||
}
|
||||
targetProfile := AgentProfileService.GetByUserID(toUserID)
|
||||
if targetProfile == nil || targetProfile.Status != enums.StatusOk {
|
||||
return errorsx.InvalidParam("目标客服不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0276")
|
||||
}
|
||||
var assignedEvent events.ConversationAssignedEvent
|
||||
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
conversation := repositories.ConversationRepository.Get(ctx.Tx, conversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if !s.canTransferConversation(conversation, operator) {
|
||||
return errorsx.Forbidden("无权转接该会话")
|
||||
return errorsx.ForbiddenI18n("error.e0223")
|
||||
}
|
||||
if conversation.Status != enums.IMConversationStatusActive {
|
||||
return errorsx.InvalidParam("只有处理中会话允许转接")
|
||||
return errorsx.InvalidParamI18n("error.e0134")
|
||||
}
|
||||
if conversation.CurrentAssigneeID <= 0 {
|
||||
return errorsx.InvalidParam("当前会话未分配客服")
|
||||
return errorsx.InvalidParamI18n("error.e0193")
|
||||
}
|
||||
if conversation.CurrentAssigneeID == toUserID {
|
||||
return errorsx.InvalidParam("目标客服不能与当前指派人相同")
|
||||
return errorsx.InvalidParamI18n("error.e0277")
|
||||
}
|
||||
now := time.Now()
|
||||
if err := ConversationAssignmentService.FinishActiveAssignments(ctx, conversationID, now); err != nil {
|
||||
@@ -352,7 +352,7 @@ func (s *conversationService) HandoffByAI(conversationID int64, aiAgent models.A
|
||||
|
||||
func (s *conversationService) HandoffByAIWithRequestID(conversationID int64, aiAgent models.AIAgent, reason string, requestID string) error {
|
||||
if conversationID <= 0 {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
_, err := ConversationHumanDispatchService.HandoffByAIWithRequestID(conversationID, aiAgent, reason, requestID)
|
||||
if err != nil {
|
||||
@@ -371,7 +371,7 @@ func (s *conversationService) TryOffHoursHandoffByAI(conversationID int64, aiAge
|
||||
|
||||
func (s *conversationService) TryOffHoursHandoffByAIWithRequestID(conversationID int64, aiAgent models.AIAgent, reason string, requestID string) (bool, error) {
|
||||
if conversationID <= 0 {
|
||||
return false, errorsx.InvalidParam("会话不存在")
|
||||
return false, errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
handled, err := ConversationHumanDispatchService.TryOffHoursHandoffByAIWithRequestID(conversationID, aiAgent, reason, requestID)
|
||||
if err != nil {
|
||||
@@ -386,7 +386,7 @@ func (s *conversationService) TryOffHoursHandoffByAIWithRequestID(conversationID
|
||||
|
||||
func (s *conversationService) CloseConversation(conversationID int64, closeReason string, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
return s.closeConversation(conversationID, enums.IMSenderTypeAgent, closeReason, operator)
|
||||
}
|
||||
@@ -394,10 +394,10 @@ func (s *conversationService) CloseConversation(conversationID int64, closeReaso
|
||||
func (s *conversationService) CloseCustomerConversation(conversationID int64, externalUser openidentity.ExternalUser) error {
|
||||
conversation := s.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if !s.IsCustomerConversationOwner(conversation, externalUser) {
|
||||
return errorsx.Forbidden("无权访问该会话")
|
||||
return errorsx.ForbiddenI18n("error.e0222")
|
||||
}
|
||||
return s.closeConversation(conversationID, enums.IMSenderTypeCustomer, "", nil)
|
||||
}
|
||||
@@ -406,7 +406,7 @@ func (s *conversationService) closeConversation(conversationID int64, senderType
|
||||
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
conversation := repositories.ConversationRepository.Get(ctx.Tx, conversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if conversation.Status == enums.IMConversationStatusClosed {
|
||||
return nil
|
||||
@@ -414,7 +414,7 @@ func (s *conversationService) closeConversation(conversationID int64, senderType
|
||||
if conversation.Status != enums.IMConversationStatusAIServing &&
|
||||
conversation.Status != enums.IMConversationStatusPending &&
|
||||
conversation.Status != enums.IMConversationStatusActive {
|
||||
return errorsx.InvalidParam("当前状态不允许关闭会话")
|
||||
return errorsx.InvalidParamI18n("error.e0197")
|
||||
}
|
||||
var (
|
||||
now = time.Now()
|
||||
@@ -427,13 +427,13 @@ func (s *conversationService) closeConversation(conversationID int64, senderType
|
||||
eventDesc = "客户关闭会话"
|
||||
} else {
|
||||
if operator == nil {
|
||||
return errorsx.InvalidParam("无权限操作")
|
||||
return errorsx.InvalidParamI18n("error.e0226")
|
||||
}
|
||||
if closeReason == "" {
|
||||
return errorsx.InvalidParam("关闭原因不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0128")
|
||||
}
|
||||
if !s.canCloseConversation(conversation, operator) {
|
||||
return errorsx.Forbidden("无权关闭该会话")
|
||||
return errorsx.ForbiddenI18n("error.e0221")
|
||||
}
|
||||
operatorID = operator.UserID
|
||||
operatorName = operator.Nickname
|
||||
@@ -471,11 +471,11 @@ func (s *conversationService) closeConversation(conversationID int64, senderType
|
||||
// MarkAgentConversationReadToMessage 控制台客服将会话已读推进到指定消息。
|
||||
func (s *conversationService) MarkAgentConversationReadToMessage(conversationID, messageID int64, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
conversation := s.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
changed, err := s.markConversationReadWithActor(conversation, messageID, agentConversationReadActor{operator: operator})
|
||||
if err != nil {
|
||||
@@ -492,14 +492,14 @@ func (s *conversationService) MarkAgentConversationReadToMessage(conversationID,
|
||||
// MarkCustomerConversationReadToMessage IM 客户将会话已读推进到指定消息(需为会话归属外部身份)。
|
||||
func (s *conversationService) MarkCustomerConversationReadToMessage(conversationID, messageID int64, external *openidentity.ExternalUser) error {
|
||||
if external == nil || strings.TrimSpace(external.ExternalID) == "" {
|
||||
return errorsx.Unauthorized("外部用户标识不能为空")
|
||||
return errorsx.UnauthorizedI18n("error.e0149")
|
||||
}
|
||||
conversation := s.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if !s.IsCustomerConversationOwner(conversation, *external) {
|
||||
return errorsx.Forbidden("无权访问该会话")
|
||||
return errorsx.ForbiddenI18n("error.e0222")
|
||||
}
|
||||
changed, err := s.markConversationReadWithActor(conversation, messageID, customerConversationReadActor{external: external})
|
||||
if err != nil {
|
||||
@@ -574,7 +574,7 @@ func (a customerConversationReadActor) conversationUpdateAudit() (int64, string)
|
||||
|
||||
func (s *conversationService) markConversationReadWithActor(conversation *models.Conversation, messageID int64, actor conversationReadActor) (bool, error) {
|
||||
if conversation == nil {
|
||||
return false, errorsx.InvalidParam("会话不存在")
|
||||
return false, errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
targetMessage, err := MessageService.GetConversationReadTarget(conversation.ID, messageID)
|
||||
if err != nil {
|
||||
@@ -615,7 +615,7 @@ func (s *conversationService) markConversationReadWithActor(conversation *models
|
||||
err = sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
currentConversation := repositories.ConversationRepository.Get(ctx.Tx, conversation.ID)
|
||||
if currentConversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if err := actor.markRead(ctx, currentConversation, targetMessage); err != nil {
|
||||
return err
|
||||
@@ -741,30 +741,30 @@ func (s *conversationService) buildEventPayload(payload map[string]any) string {
|
||||
// LinkConversationCustomer 将会话绑定到指定客户。
|
||||
func (s *conversationService) LinkConversationCustomer(conversationID, customerID int64, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if conversationID <= 0 || customerID <= 0 {
|
||||
return errorsx.InvalidParam("参数不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0133")
|
||||
}
|
||||
cust := CustomerService.Get(customerID)
|
||||
if cust == nil || cust.Status == enums.StatusDeleted {
|
||||
return errorsx.InvalidParam("客户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
conv := s.Get(conversationID)
|
||||
if conv == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if conv.Status == enums.IMConversationStatusClosed {
|
||||
return errorsx.InvalidParam("已关闭的会话无法关联客户")
|
||||
return errorsx.InvalidParamI18n("error.e0183")
|
||||
}
|
||||
if !s.canLinkConversationCustomer(conv, operator) {
|
||||
return errorsx.Forbidden("无权限关联该会话")
|
||||
return errorsx.ForbiddenI18n("error.e0224")
|
||||
}
|
||||
|
||||
err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
current := repositories.ConversationRepository.Get(ctx.Tx, conversationID)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
now := time.Now()
|
||||
return repositories.ConversationRepository.Updates(ctx.Tx, conversationID, map[string]any{
|
||||
|
||||
@@ -167,7 +167,7 @@ func (s *customerContactService) ReplaceAllForCustomerInTx(
|
||||
operator *dto.AuthPrincipal,
|
||||
) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
type line struct {
|
||||
id *int64
|
||||
@@ -184,7 +184,7 @@ func (s *customerContactService) ReplaceAllForCustomerInTx(
|
||||
continue
|
||||
}
|
||||
if !enums.IsValidContactType(ct) {
|
||||
return errorsx.InvalidParam("联系方式类型不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0301")
|
||||
}
|
||||
items = append(items, line{
|
||||
id: r.ID,
|
||||
@@ -204,7 +204,7 @@ func (s *customerContactService) ReplaceAllForCustomerInTx(
|
||||
if primaryCount == 0 {
|
||||
items[0].primary = true
|
||||
} else if primaryCount > 1 {
|
||||
return errorsx.InvalidParam("仅能指定一条主联系方式")
|
||||
return errorsx.InvalidParamI18n("error.e0092")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,10 +241,10 @@ func (s *customerContactService) ReplaceAllForCustomerInTx(
|
||||
if it.id != nil && *it.id > 0 {
|
||||
row := repositories.CustomerContactRepository.Get(ctx.Tx, *it.id)
|
||||
if row == nil || row.CustomerID != customerID || row.Status == enums.StatusDeleted {
|
||||
return errorsx.InvalidParam("联系方式不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0299")
|
||||
}
|
||||
if s.hasDuplicateContact(ctx.Tx, customerID, it.ct, it.val, *it.id) {
|
||||
return errorsx.InvalidParam("该联系方式已存在")
|
||||
return errorsx.InvalidParamI18n("error.e0318")
|
||||
}
|
||||
if it.primary {
|
||||
if err := s.clearPrimaryExcept(ctx.Tx, customerID, *it.id); err != nil {
|
||||
@@ -265,7 +265,7 @@ func (s *customerContactService) ReplaceAllForCustomerInTx(
|
||||
continue
|
||||
}
|
||||
if s.hasDuplicateContact(ctx.Tx, customerID, it.ct, it.val, 0) {
|
||||
return errorsx.InvalidParam("该联系方式已存在")
|
||||
return errorsx.InvalidParamI18n("error.e0318")
|
||||
}
|
||||
if deleted := s.findSoftDeletedContactByNaturalKey(ctx.Tx, customerID, it.ct, it.val); deleted != nil {
|
||||
if it.primary {
|
||||
@@ -331,10 +331,10 @@ func (s *customerContactService) clearPrimaryExcept(db *gorm.DB, customerID int6
|
||||
|
||||
func (s *customerContactService) validateContactStatus(status int) error {
|
||||
if !enums.IsValidStatus(status) {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
if status == int(enums.StatusDeleted) {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -342,21 +342,21 @@ func (s *customerContactService) validateContactStatus(status int) error {
|
||||
// CreateCustomerContact 创建联系方式;主联系方式在同一客户下唯一。
|
||||
func (s *customerContactService) CreateCustomerContact(req request.CreateCustomerContactRequest, operator *dto.AuthPrincipal) (*models.CustomerContact, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if req.CustomerID <= 0 {
|
||||
return nil, errorsx.InvalidParam("客户不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
if CustomerService.Get(req.CustomerID) == nil {
|
||||
return nil, errorsx.InvalidParam("客户不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
ct := strings.TrimSpace(req.ContactType)
|
||||
if !enums.IsValidContactType(ct) {
|
||||
return nil, errorsx.InvalidParam("联系方式类型不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0301")
|
||||
}
|
||||
val := strings.TrimSpace(req.ContactValue)
|
||||
if val == "" {
|
||||
return nil, errorsx.InvalidParam("联系方式不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0300")
|
||||
}
|
||||
if err := s.validateContactStatus(req.Status); err != nil {
|
||||
return nil, err
|
||||
@@ -369,7 +369,7 @@ func (s *customerContactService) CreateCustomerContact(req request.CreateCustome
|
||||
var created *models.CustomerContact
|
||||
err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
if s.hasDuplicateContact(ctx.Tx, req.CustomerID, enums.ContactType(ct), val, 0) {
|
||||
return errorsx.InvalidParam("该联系方式已存在")
|
||||
return errorsx.InvalidParamI18n("error.e0318")
|
||||
}
|
||||
now := time.Now()
|
||||
if deleted := s.findSoftDeletedContactByNaturalKey(ctx.Tx, req.CustomerID, enums.ContactType(ct), val); deleted != nil {
|
||||
@@ -439,22 +439,22 @@ func (s *customerContactService) CreateCustomerContact(req request.CreateCustome
|
||||
// UpdateCustomerContact 更新联系方式。
|
||||
func (s *customerContactService) UpdateCustomerContact(req request.UpdateCustomerContactRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if req.ID <= 0 {
|
||||
return errorsx.InvalidParam("联系方式不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0299")
|
||||
}
|
||||
current := s.Get(req.ID)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("联系方式不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0299")
|
||||
}
|
||||
ct := strings.TrimSpace(req.ContactType)
|
||||
if !enums.IsValidContactType(ct) {
|
||||
return errorsx.InvalidParam("联系方式类型不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0301")
|
||||
}
|
||||
val := strings.TrimSpace(req.ContactValue)
|
||||
if val == "" {
|
||||
return errorsx.InvalidParam("联系方式不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0300")
|
||||
}
|
||||
if err := s.validateContactStatus(req.Status); err != nil {
|
||||
return err
|
||||
@@ -462,7 +462,7 @@ func (s *customerContactService) UpdateCustomerContact(req request.UpdateCustome
|
||||
|
||||
return sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
if s.hasDuplicateContact(ctx.Tx, current.CustomerID, enums.ContactType(ct), val, req.ID) {
|
||||
return errorsx.InvalidParam("该联系方式已存在")
|
||||
return errorsx.InvalidParamI18n("error.e0318")
|
||||
}
|
||||
if req.IsPrimary {
|
||||
if err := s.clearPrimaryExcept(ctx.Tx, current.CustomerID, req.ID); err != nil {
|
||||
@@ -500,14 +500,14 @@ func (s *customerContactService) UpdateCustomerContact(req request.UpdateCustome
|
||||
// DeleteCustomerContact 软删除联系方式并同步客户主联系方式冗余字段。
|
||||
func (s *customerContactService) DeleteCustomerContact(id int64, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if id <= 0 {
|
||||
return errorsx.InvalidParam("联系方式不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0299")
|
||||
}
|
||||
current := s.Get(id)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("联系方式不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0299")
|
||||
}
|
||||
return sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
now := time.Now()
|
||||
|
||||
@@ -116,12 +116,12 @@ func (s *customerService) CountByCompanyIDs(companyIDs []int64) map[int64]int64
|
||||
|
||||
func (s *customerService) EnsureExternalCustomer(ctx *sqls.TxContext, externalUser openidentity.ExternalUser) (int64, error) {
|
||||
if ctx == nil || ctx.Tx == nil {
|
||||
return 0, errorsx.InvalidParam("事务上下文不能为空")
|
||||
return 0, errorsx.InvalidParamI18n("error.e0086")
|
||||
}
|
||||
externalSource := externalUser.ExternalSource
|
||||
externalID := strings.TrimSpace(externalUser.ExternalID)
|
||||
if strings.TrimSpace(string(externalSource)) == "" || externalID == "" {
|
||||
return 0, errorsx.Unauthorized("外部用户标识不能为空")
|
||||
return 0, errorsx.UnauthorizedI18n("error.e0149")
|
||||
}
|
||||
now := time.Now()
|
||||
if identity := repositories.CustomerIdentityRepository.GetBy(ctx.Tx, externalSource, externalID); identity != nil {
|
||||
@@ -189,17 +189,17 @@ func hashUUID(uuid string) string {
|
||||
|
||||
func (s *customerService) CreateCustomer(req request.CreateCustomerRequest, operator *dto.AuthPrincipal) (*models.Customer, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("客户名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0156")
|
||||
}
|
||||
|
||||
if req.CompanyID > 0 {
|
||||
company := CompanyService.Get(req.CompanyID)
|
||||
if company == nil {
|
||||
return nil, errorsx.InvalidParam("所属公司不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0204")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,21 +222,21 @@ func (s *customerService) CreateCustomer(req request.CreateCustomerRequest, oper
|
||||
|
||||
func (s *customerService) UpdateCustomer(req request.UpdateCustomerRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := s.Get(req.ID)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("客户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return errorsx.InvalidParam("客户名称不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0156")
|
||||
}
|
||||
|
||||
if req.CompanyID > 0 {
|
||||
company := CompanyService.Get(req.CompanyID)
|
||||
if company == nil {
|
||||
return errorsx.InvalidParam("所属公司不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0204")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ func (s *customerService) UpdateCustomer(req request.UpdateCustomerRequest, oper
|
||||
func (s *customerService) DeleteCustomer(id int64, operator dto.AuthPrincipal) error {
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("客户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
return repositories.CustomerRepository.Updates(sqls.DB(), id, map[string]any{
|
||||
"status": enums.StatusDeleted,
|
||||
@@ -289,14 +289,14 @@ func (s *customerService) syncConversationCustomerName(db *gorm.DB, customerID i
|
||||
|
||||
func (s *customerService) UpdateStatus(id int64, status int, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("客户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
if status != int(enums.StatusOk) && status != int(enums.StatusDisabled) {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
return repositories.CustomerRepository.Updates(sqls.DB(), id, map[string]any{
|
||||
"status": status,
|
||||
@@ -309,15 +309,15 @@ func (s *customerService) UpdateStatus(id int64, status int, operator *dto.AuthP
|
||||
// SaveCustomerProfile 单事务保存客户主信息与联系方式全量(新建或更新)。
|
||||
func (s *customerService) SaveCustomerProfile(req request.SaveCustomerProfileRequest, operator *dto.AuthPrincipal) (*models.Customer, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("客户名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0156")
|
||||
}
|
||||
if req.CompanyID > 0 {
|
||||
if CompanyService.Get(req.CompanyID) == nil {
|
||||
return nil, errorsx.InvalidParam("所属公司不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0204")
|
||||
}
|
||||
}
|
||||
createMode := req.ID == nil || *req.ID <= 0
|
||||
@@ -345,7 +345,7 @@ func (s *customerService) SaveCustomerProfile(req request.SaveCustomerProfileReq
|
||||
customerID = *req.ID
|
||||
cur := repositories.CustomerRepository.Get(ctx.Tx, customerID)
|
||||
if cur == nil {
|
||||
return errorsx.InvalidParam("客户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
now := time.Now()
|
||||
if err := repositories.CustomerRepository.Updates(ctx.Tx, customerID, map[string]any{
|
||||
|
||||
@@ -54,7 +54,7 @@ type CustomerSessionVerifyResult struct {
|
||||
|
||||
func (s *customerSessionService) Exchange(channel *models.Channel, externalUser openidentity.ExternalUser) (*response.CustomerSessionExchangeResponse, error) {
|
||||
if channel == nil || channel.Status != enums.StatusOk {
|
||||
return nil, errorsx.InvalidParam("接入渠道不存在或已停用")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0209")
|
||||
}
|
||||
var customerID int64
|
||||
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
@@ -69,7 +69,7 @@ func (s *customerSessionService) Exchange(channel *models.Channel, externalUser
|
||||
}
|
||||
customer := CustomerService.Get(customerID)
|
||||
if customer == nil || customer.Status == enums.StatusDeleted {
|
||||
return nil, errorsx.InvalidParam("客户不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
token, expiresAt, err := s.Sign(channel, customer, externalUser)
|
||||
if err != nil {
|
||||
@@ -93,7 +93,7 @@ func (s *customerSessionService) Sign(channel *models.Channel, customer *models.
|
||||
return "", time.Time{}, errorsx.BusinessError(1, "客服会话密钥未配置")
|
||||
}
|
||||
if channel == nil || customer == nil {
|
||||
return "", time.Time{}, errorsx.InvalidParam("客服会话参数不完整")
|
||||
return "", time.Time{}, errorsx.InvalidParamI18n("error.e0158")
|
||||
}
|
||||
now := time.Now()
|
||||
expiresAt := now.Add(time.Duration(cfg.TTL()) * time.Minute)
|
||||
@@ -119,21 +119,21 @@ func (s *customerSessionService) Sign(channel *models.Channel, customer *models.
|
||||
func (s *customerSessionService) VerifyRequest(ctx *gin.Context, channel *models.Channel) (*CustomerSessionVerifyResult, error) {
|
||||
token := s.getCustomerSessionToken(ctx)
|
||||
if token == "" {
|
||||
return nil, errorsx.Unauthorized("客服会话不能为空")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0157")
|
||||
}
|
||||
claims, err := s.verifyToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if channel == nil || channel.Status != enums.StatusOk {
|
||||
return nil, errorsx.InvalidParam("接入渠道不存在或已停用")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0209")
|
||||
}
|
||||
if claims.ChannelID != channel.ID || strings.TrimSpace(claims.ChannelCode) != strings.TrimSpace(channel.ChannelID) {
|
||||
return nil, errorsx.Unauthorized("客服会话校验失败")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0161")
|
||||
}
|
||||
customer := CustomerService.Get(claims.CustomerID)
|
||||
if customer == nil || customer.Status == enums.StatusDeleted {
|
||||
return nil, errorsx.Unauthorized("客服会话校验失败")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0161")
|
||||
}
|
||||
external, err := s.externalUserFromClaims(claims, customer)
|
||||
if err != nil {
|
||||
@@ -183,15 +183,15 @@ func (s *customerSessionService) verifyToken(rawToken string) (*customerSessionC
|
||||
}))
|
||||
if err != nil {
|
||||
if errors.Is(err, jwt.ErrTokenExpired) {
|
||||
return nil, errorsx.Unauthorized("客服会话已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0160")
|
||||
}
|
||||
return nil, errorsx.Unauthorized("客服会话校验失败")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0161")
|
||||
}
|
||||
if token == nil || !token.Valid || claims.TokenType != customerSessionTokenType || claims.ExpiresAt == nil {
|
||||
return nil, errorsx.Unauthorized("客服会话校验失败")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0161")
|
||||
}
|
||||
if claims.ChannelID <= 0 || strings.TrimSpace(claims.ChannelCode) == "" || claims.CustomerID <= 0 || strings.TrimSpace(claims.IdentityKey) == "" {
|
||||
return nil, errorsx.Unauthorized("客服会话校验失败")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0161")
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
@@ -200,7 +200,7 @@ func (s *customerSessionService) externalUserFromClaims(claims *customerSessionC
|
||||
identityKey := strings.TrimSpace(claims.IdentityKey)
|
||||
parts := strings.SplitN(identityKey, ":", 2)
|
||||
if len(parts) != 2 || strings.TrimSpace(parts[1]) == "" {
|
||||
return nil, errorsx.Unauthorized("客服会话校验失败")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0161")
|
||||
}
|
||||
var source enums.ExternalSource
|
||||
switch parts[0] {
|
||||
@@ -209,11 +209,11 @@ func (s *customerSessionService) externalUserFromClaims(claims *customerSessionC
|
||||
case "guest":
|
||||
source = enums.ExternalSourceGuest
|
||||
default:
|
||||
return nil, errorsx.Unauthorized("客服会话校验失败")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0161")
|
||||
}
|
||||
identity := repositories.CustomerIdentityRepository.GetBy(sqls.DB(), source, parts[1])
|
||||
if identity == nil || identity.CustomerID != claims.CustomerID {
|
||||
return nil, errorsx.Unauthorized("客服会话校验失败")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0161")
|
||||
}
|
||||
name := strings.TrimSpace(claims.CustomerName)
|
||||
if customer != nil && strings.TrimSpace(customer.Name) != "" {
|
||||
|
||||
@@ -22,24 +22,24 @@ type imMessageAssetPayload struct {
|
||||
func parseIMMessageAssetPayload(payload string) (*imMessageAssetPayload, error) {
|
||||
payload = strings.TrimSpace(payload)
|
||||
if payload == "" {
|
||||
return nil, errorsx.InvalidParam("附件消息缺少 payload")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0346")
|
||||
}
|
||||
ret := &imMessageAssetPayload{}
|
||||
if err := json.Unmarshal([]byte(payload), ret); err != nil {
|
||||
return nil, errorsx.InvalidParam("附件消息 payload 格式错误")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0344")
|
||||
}
|
||||
ret.AssetID = strings.TrimSpace(ret.AssetID)
|
||||
ret.Provider = enums.AssetProvider(strings.TrimSpace(string(ret.Provider)))
|
||||
ret.StorageKey = strings.TrimSpace(ret.StorageKey)
|
||||
if ret.AssetID == "" {
|
||||
return nil, errorsx.InvalidParam("附件消息缺少 assetId")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0345")
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func buildIMMessageAssetPayload(asset *models.Asset) (string, error) {
|
||||
if asset == nil {
|
||||
return "", errorsx.InvalidParam("附件不存在")
|
||||
return "", errorsx.InvalidParamI18n("error.e0342")
|
||||
}
|
||||
payload, err := json.Marshal(imMessageAssetPayload{
|
||||
AssetID: asset.AssetID,
|
||||
@@ -107,10 +107,10 @@ func hydrateIMMessageAssetPayload(payload *imMessageAssetPayload) *imMessageAsse
|
||||
|
||||
func validateConversationAsset(asset *models.Asset, conversationID int64, messageType enums.IMMessageType) error {
|
||||
if asset == nil {
|
||||
return errorsx.InvalidParam("附件不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0342")
|
||||
}
|
||||
if asset.Status != enums.AssetStatusSuccess {
|
||||
return errorsx.InvalidParam("附件尚未上传完成")
|
||||
return errorsx.InvalidParamI18n("error.e0343")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"agent-desk/internal/ai/rag"
|
||||
@@ -78,7 +77,7 @@ func (s *knowledgeBaseService) Delete(id int64) {
|
||||
|
||||
func (s *knowledgeBaseService) CreateKnowledgeBase(req request.CreateKnowledgeBaseRequest, operator *dto.AuthPrincipal) (*models.KnowledgeBase, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item, err := s.buildKnowledgeBaseModel(req)
|
||||
if err != nil {
|
||||
@@ -94,11 +93,11 @@ func (s *knowledgeBaseService) CreateKnowledgeBase(req request.CreateKnowledgeBa
|
||||
|
||||
func (s *knowledgeBaseService) UpdateKnowledgeBase(req request.UpdateKnowledgeBaseRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(req.ID)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("知识库不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0283")
|
||||
}
|
||||
item, err := s.buildKnowledgeBaseModel(req.CreateKnowledgeBaseRequest)
|
||||
if err != nil {
|
||||
@@ -126,15 +125,15 @@ func (s *knowledgeBaseService) UpdateKnowledgeBase(req request.UpdateKnowledgeBa
|
||||
func (s *knowledgeBaseService) DeleteKnowledgeBase(id int64) error {
|
||||
current := s.Get(id)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("知识库不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0283")
|
||||
}
|
||||
|
||||
referencingAgents := repositories.AIAgentRepository.FindByKnowledgeBaseID(sqls.DB(), id)
|
||||
if len(referencingAgents) > 0 {
|
||||
if len(referencingAgents) == 1 {
|
||||
return errorsx.Forbidden(fmt.Sprintf("知识库已被 AI Agent「%s」引用,请先解除绑定", referencingAgents[0].Name))
|
||||
return errorsx.ForbiddenI18n("error.knowledgeBase.referencedByAgent", referencingAgents[0].Name)
|
||||
}
|
||||
return errorsx.Forbidden(fmt.Sprintf("知识库已被 %d 个 AI Agent 引用,请先解除绑定", len(referencingAgents)))
|
||||
return errorsx.ForbiddenI18n("error.knowledgeBase.referencedByAgents", len(referencingAgents))
|
||||
}
|
||||
|
||||
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
@@ -185,7 +184,7 @@ func (s *knowledgeBaseService) buildKnowledgeBaseModel(req request.CreateKnowled
|
||||
item.KnowledgeType = string(enums.KnowledgeBaseTypeDocument)
|
||||
}
|
||||
if !isValidKnowledgeType(item.KnowledgeType) {
|
||||
return nil, errorsx.InvalidParam("知识库类型不支持")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0290")
|
||||
}
|
||||
if item.DefaultScoreThreshold == 0 {
|
||||
item.DefaultScoreThreshold = 0.2
|
||||
@@ -202,10 +201,10 @@ func (s *knowledgeBaseService) buildKnowledgeBaseModel(req request.CreateKnowled
|
||||
item.ChunkMaxTokens = 0
|
||||
item.ChunkOverlapTokens = 0
|
||||
} else if item.ChunkProvider == string(enums.KnowledgeChunkProviderFAQ) {
|
||||
return nil, errorsx.InvalidParam("文档知识库不能使用FAQ分块策略")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0219")
|
||||
}
|
||||
if !isValidChunkProvider(item.ChunkProvider) {
|
||||
return nil, errorsx.InvalidParam("分块策略不支持")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0130")
|
||||
}
|
||||
if item.KnowledgeType != string(enums.KnowledgeBaseTypeFAQ) && item.ChunkTargetTokens == 0 {
|
||||
item.ChunkTargetTokens = 300
|
||||
|
||||
@@ -42,20 +42,20 @@ func (s *knowledgeDirectoryService) Count(cnd *sqls.Cnd) int64 {
|
||||
|
||||
func (s *knowledgeDirectoryService) CreateDirectory(req request.CreateKnowledgeDirectoryRequest, operator *dto.AuthPrincipal) (*models.KnowledgeDirectory, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("目录名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0275")
|
||||
}
|
||||
if req.KnowledgeBaseID <= 0 || KnowledgeBaseService.Get(req.KnowledgeBaseID) == nil {
|
||||
return nil, errorsx.InvalidParam("知识库不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0283")
|
||||
}
|
||||
if err := s.validateParent(req.KnowledgeBaseID, req.ParentID, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existing := s.findByName(req.KnowledgeBaseID, req.ParentID, name); existing != nil {
|
||||
return nil, errorsx.InvalidParam("同级下已存在相同名称的目录")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0142")
|
||||
}
|
||||
item := &models.KnowledgeDirectory{
|
||||
KnowledgeBaseID: req.KnowledgeBaseID,
|
||||
@@ -74,30 +74,30 @@ func (s *knowledgeDirectoryService) CreateDirectory(req request.CreateKnowledgeD
|
||||
|
||||
func (s *knowledgeDirectoryService) UpdateDirectory(req request.UpdateKnowledgeDirectoryRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := s.Get(req.ID)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("目录不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0273")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return errorsx.InvalidParam("目录名称不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0275")
|
||||
}
|
||||
if req.KnowledgeBaseID <= 0 {
|
||||
req.KnowledgeBaseID = item.KnowledgeBaseID
|
||||
}
|
||||
if req.KnowledgeBaseID != item.KnowledgeBaseID {
|
||||
return errorsx.InvalidParam("目录不能移动到其他知识库")
|
||||
return errorsx.InvalidParamI18n("error.e0274")
|
||||
}
|
||||
if err := s.validateParent(item.KnowledgeBaseID, req.ParentID, req.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.ParentID > 0 && s.Count(sqls.NewCnd().Eq("parent_id", req.ID)) > 0 {
|
||||
return errorsx.InvalidParam("存在子目录的目录不能移动到二级目录")
|
||||
return errorsx.InvalidParamI18n("error.e0152")
|
||||
}
|
||||
if existing := s.findByName(item.KnowledgeBaseID, req.ParentID, name); existing != nil && existing.ID != req.ID {
|
||||
return errorsx.InvalidParam("同级下已存在相同名称的目录")
|
||||
return errorsx.InvalidParamI18n("error.e0142")
|
||||
}
|
||||
return repositories.KnowledgeDirectoryRepository.Updates(sqls.DB(), req.ID, map[string]any{
|
||||
"parent_id": req.ParentID,
|
||||
@@ -112,16 +112,16 @@ func (s *knowledgeDirectoryService) UpdateDirectory(req request.UpdateKnowledgeD
|
||||
func (s *knowledgeDirectoryService) DeleteDirectory(id int64) error {
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("目录不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0273")
|
||||
}
|
||||
if s.Count(sqls.NewCnd().Eq("parent_id", id)) > 0 {
|
||||
return errorsx.InvalidParam("该目录下存在子目录,无法删除")
|
||||
return errorsx.InvalidParamI18n("error.e0316")
|
||||
}
|
||||
if KnowledgeDocumentService.Count(sqls.NewCnd().Eq("directory_id", id)) > 0 {
|
||||
return errorsx.InvalidParam("该目录下存在文档,无法删除")
|
||||
return errorsx.InvalidParamI18n("error.e0317")
|
||||
}
|
||||
if KnowledgeFAQService.Count(sqls.NewCnd().Eq("directory_id", id)) > 0 {
|
||||
return errorsx.InvalidParam("该目录下存在FAQ,无法删除")
|
||||
return errorsx.InvalidParamI18n("error.e0315")
|
||||
}
|
||||
return repositories.KnowledgeDirectoryRepository.Delete(sqls.DB(), id)
|
||||
}
|
||||
@@ -131,10 +131,10 @@ func (s *knowledgeDirectoryService) UpdateSort(knowledgeBaseID int64, parentID i
|
||||
for i, id := range ids {
|
||||
item := repositories.KnowledgeDirectoryRepository.Get(ctx.Tx, id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("目录不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0273")
|
||||
}
|
||||
if item.KnowledgeBaseID != knowledgeBaseID || item.ParentID != parentID {
|
||||
return errorsx.InvalidParam("只能调整同知识库同级目录排序")
|
||||
return errorsx.InvalidParamI18n("error.e0140")
|
||||
}
|
||||
if err := repositories.KnowledgeDirectoryRepository.UpdateColumn(ctx.Tx, id, "sort_no", i+1); err != nil {
|
||||
return err
|
||||
@@ -150,13 +150,13 @@ func (s *knowledgeDirectoryService) RequireUsableDirectory(knowledgeBaseID int64
|
||||
}
|
||||
item := s.Get(directoryID)
|
||||
if item == nil {
|
||||
return nil, errorsx.InvalidParam("知识库目录不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0287")
|
||||
}
|
||||
if item.KnowledgeBaseID != knowledgeBaseID {
|
||||
return nil, errorsx.InvalidParam("知识库目录不属于当前知识库")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0288")
|
||||
}
|
||||
if item.Status != enums.StatusOk {
|
||||
return nil, errorsx.InvalidParam("知识库目录不可用")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0286")
|
||||
}
|
||||
return item, nil
|
||||
}
|
||||
@@ -192,17 +192,17 @@ func (s *knowledgeDirectoryService) validateParent(knowledgeBaseID int64, parent
|
||||
return nil
|
||||
}
|
||||
if parentID == selfID {
|
||||
return errorsx.InvalidParam("不能将目录设为自己的子目录")
|
||||
return errorsx.InvalidParamI18n("error.e0084")
|
||||
}
|
||||
parent := s.Get(parentID)
|
||||
if parent == nil {
|
||||
return errorsx.InvalidParam("父目录不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0252")
|
||||
}
|
||||
if parent.KnowledgeBaseID != knowledgeBaseID {
|
||||
return errorsx.InvalidParam("父目录不属于当前知识库")
|
||||
return errorsx.InvalidParamI18n("error.e0253")
|
||||
}
|
||||
if parent.ParentID > 0 {
|
||||
return errorsx.InvalidParam("知识库目录最多支持二级")
|
||||
return errorsx.InvalidParamI18n("error.e0289")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -85,14 +85,14 @@ func (s *knowledgeDocumentService) Delete(id int64) {
|
||||
|
||||
func (s *knowledgeDocumentService) CreateKnowledgeDocument(req request.CreateKnowledgeDocumentRequest, operator *dto.AuthPrincipal) (*models.KnowledgeDocument, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
kb := KnowledgeBaseService.Get(req.KnowledgeBaseID)
|
||||
if kb == nil {
|
||||
return nil, errorsx.InvalidParam("知识库不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0283")
|
||||
}
|
||||
if kb.KnowledgeType == string(enums.KnowledgeBaseTypeFAQ) {
|
||||
return nil, errorsx.InvalidParam("FAQ知识库不支持文档")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0026")
|
||||
}
|
||||
if _, err := KnowledgeDirectoryService.RequireUsableDirectory(req.KnowledgeBaseID, req.DirectoryID); err != nil {
|
||||
return nil, err
|
||||
@@ -123,18 +123,18 @@ func (s *knowledgeDocumentService) CreateKnowledgeDocument(req request.CreateKno
|
||||
|
||||
func (s *knowledgeDocumentService) UpdateKnowledgeDocument(req request.UpdateKnowledgeDocumentRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(req.ID)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("文档不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0218")
|
||||
}
|
||||
kb := KnowledgeBaseService.Get(req.KnowledgeBaseID)
|
||||
if kb == nil {
|
||||
return errorsx.InvalidParam("知识库不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0283")
|
||||
}
|
||||
if kb.KnowledgeType == string(enums.KnowledgeBaseTypeFAQ) {
|
||||
return errorsx.InvalidParam("FAQ知识库不支持文档")
|
||||
return errorsx.InvalidParamI18n("error.e0026")
|
||||
}
|
||||
if _, err := KnowledgeDirectoryService.RequireUsableDirectory(req.KnowledgeBaseID, req.DirectoryID); err != nil {
|
||||
return err
|
||||
@@ -185,18 +185,18 @@ func (s *knowledgeDocumentService) DeleteKnowledgeDocument(id int64) error {
|
||||
|
||||
func (s *knowledgeDocumentService) BatchMoveKnowledgeDocuments(req request.BatchMoveKnowledgeDocumentRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
ids := uniquePositiveIDs(req.IDs)
|
||||
if len(ids) == 0 {
|
||||
return errorsx.InvalidParam("请选择要移动的文档")
|
||||
return errorsx.InvalidParamI18n("error.e0333")
|
||||
}
|
||||
kb := KnowledgeBaseService.Get(req.KnowledgeBaseID)
|
||||
if kb == nil {
|
||||
return errorsx.InvalidParam("知识库不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0283")
|
||||
}
|
||||
if kb.KnowledgeType == string(enums.KnowledgeBaseTypeFAQ) {
|
||||
return errorsx.InvalidParam("FAQ知识库不支持文档")
|
||||
return errorsx.InvalidParamI18n("error.e0026")
|
||||
}
|
||||
if _, err := KnowledgeDirectoryService.RequireUsableDirectory(req.KnowledgeBaseID, req.DirectoryID); err != nil {
|
||||
return err
|
||||
@@ -204,10 +204,10 @@ func (s *knowledgeDocumentService) BatchMoveKnowledgeDocuments(req request.Batch
|
||||
for _, id := range ids {
|
||||
current := s.Get(id)
|
||||
if current == nil || current.Status == enums.StatusDeleted {
|
||||
return errorsx.InvalidParam("文档不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0218")
|
||||
}
|
||||
if current.KnowledgeBaseID != req.KnowledgeBaseID {
|
||||
return errorsx.InvalidParam("只能移动当前知识库下的文档")
|
||||
return errorsx.InvalidParamI18n("error.e0139")
|
||||
}
|
||||
}
|
||||
now := time.Now()
|
||||
@@ -240,7 +240,7 @@ func (s *knowledgeDocumentService) BatchMoveKnowledgeDocuments(req request.Batch
|
||||
func (s *knowledgeDocumentService) BatchDeleteKnowledgeDocuments(req request.BatchDeleteKnowledgeDocumentRequest) error {
|
||||
ids := uniquePositiveIDs(req.IDs)
|
||||
if len(ids) == 0 {
|
||||
return errorsx.InvalidParam("请选择要删除的文档")
|
||||
return errorsx.InvalidParamI18n("error.e0331")
|
||||
}
|
||||
for _, id := range ids {
|
||||
if err := s.DeleteKnowledgeDocument(id); err != nil {
|
||||
@@ -255,7 +255,7 @@ func (s *knowledgeDocumentService) buildKnowledgeDocumentModel(req request.Creat
|
||||
req.ContentType = enums.KnowledgeDocumentContentTypeHTML
|
||||
}
|
||||
if req.ContentType != enums.KnowledgeDocumentContentTypeHTML && req.ContentType != enums.KnowledgeDocumentContentTypeMarkdown {
|
||||
return nil, errorsx.InvalidParam("内容类型不支持")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0129")
|
||||
}
|
||||
|
||||
plainText := rag.ExtractPlainText(req.Content, req.ContentType)
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"agent-desk/internal/pkg/dto/response"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/pkg/errorsx"
|
||||
"agent-desk/internal/pkg/i18nx"
|
||||
"agent-desk/internal/pkg/utils"
|
||||
"agent-desk/internal/repositories"
|
||||
|
||||
@@ -78,22 +79,22 @@ func (s *knowledgeFAQService) ExportKnowledgeFAQs(knowledgeBaseID int64) (*respo
|
||||
|
||||
func (s *knowledgeFAQService) ImportKnowledgeFAQs(req request.ImportKnowledgeFAQRequest, operator *dto.AuthPrincipal) (*response.KnowledgeFAQImportResult, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if req.Mode != request.KnowledgeFAQImportModeAppend && req.Mode != request.KnowledgeFAQImportModeOverwrite {
|
||||
return nil, errorsx.InvalidParam("导入模式不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0177")
|
||||
}
|
||||
if req.Reader == nil {
|
||||
return nil, errorsx.InvalidParam("请选择导入文件")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0327")
|
||||
}
|
||||
if strings.ToLower(filepath.Ext(req.Filename)) != ".xlsx" {
|
||||
return nil, errorsx.InvalidParam("仅支持.xlsx文件")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0089")
|
||||
}
|
||||
kb, err := s.requireFAQKnowledgeBase(req.KnowledgeBaseID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, result, err := parseKnowledgeFAQImportRows(req.Reader)
|
||||
rows, result, err := parseKnowledgeFAQImportRows(req.Reader, req.Locale)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -119,14 +120,14 @@ func (s *knowledgeFAQService) ImportKnowledgeFAQs(req request.ImportKnowledgeFAQ
|
||||
existing, exists := existingMap[row.Question]
|
||||
if exists && req.Mode == request.KnowledgeFAQImportModeAppend {
|
||||
result.Skipped++
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: row.RowNo, Message: "标准问题已存在,已跳过"})
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: row.RowNo, Message: i18nx.Getf(req.Locale, "error.e0237")})
|
||||
continue
|
||||
}
|
||||
|
||||
similarQuestions, marshalErr := json.Marshal(row.SimilarQuestions)
|
||||
if marshalErr != nil {
|
||||
result.Failed++
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: row.RowNo, Message: "相似问格式不合法"})
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: row.RowNo, Message: i18nx.Getf(req.Locale, "error.e0280")})
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -227,33 +228,33 @@ func buildKnowledgeFAQExcelFile(filename string, workbook *excelize.File) (*resp
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseKnowledgeFAQImportRows(reader io.Reader) ([]knowledgeFAQImportRow, *response.KnowledgeFAQImportResult, error) {
|
||||
func parseKnowledgeFAQImportRows(reader io.Reader, locale string) ([]knowledgeFAQImportRow, *response.KnowledgeFAQImportResult, error) {
|
||||
workbook, err := excelize.OpenReader(reader)
|
||||
if err != nil {
|
||||
return nil, nil, errorsx.InvalidParam("Excel文件解析失败")
|
||||
return nil, nil, errorsx.InvalidParamI18n("error.e0023")
|
||||
}
|
||||
defer workbook.Close()
|
||||
sheet := knowledgeFAQExcelSheetName
|
||||
if index, _ := workbook.GetSheetIndex(sheet); index < 0 {
|
||||
sheets := workbook.GetSheetList()
|
||||
if len(sheets) == 0 {
|
||||
return nil, nil, errorsx.InvalidParam("Excel文件为空")
|
||||
return nil, nil, errorsx.InvalidParamI18n("error.e0022")
|
||||
}
|
||||
sheet = sheets[0]
|
||||
}
|
||||
table, err := workbook.GetRows(sheet)
|
||||
if err != nil {
|
||||
return nil, nil, errorsx.InvalidParam("Excel文件读取失败")
|
||||
return nil, nil, errorsx.InvalidParamI18n("error.e0024")
|
||||
}
|
||||
if len(table) == 0 {
|
||||
return nil, nil, errorsx.InvalidParam("Excel文件为空")
|
||||
return nil, nil, errorsx.InvalidParamI18n("error.e0022")
|
||||
}
|
||||
headerMap := buildKnowledgeFAQHeaderMap(table[0])
|
||||
if _, ok := headerMap["question"]; !ok {
|
||||
return nil, nil, errorsx.InvalidParam("缺少标准问题列")
|
||||
return nil, nil, errorsx.InvalidParamI18n("error.e0297")
|
||||
}
|
||||
if _, ok := headerMap["answer"]; !ok {
|
||||
return nil, nil, errorsx.InvalidParam("缺少答案列")
|
||||
return nil, nil, errorsx.InvalidParamI18n("error.e0298")
|
||||
}
|
||||
|
||||
result := &response.KnowledgeFAQImportResult{Errors: make([]response.KnowledgeFAQImportError, 0)}
|
||||
@@ -275,22 +276,22 @@ func parseKnowledgeFAQImportRows(reader io.Reader) ([]knowledgeFAQImportRow, *re
|
||||
}
|
||||
if row.Question == "" {
|
||||
result.Failed++
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: "问题不能为空"})
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: i18nx.Getf(locale, "error.e0340")})
|
||||
continue
|
||||
}
|
||||
if len([]rune(row.Question)) > 500 {
|
||||
result.Failed++
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: "问题不能超过500字"})
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: i18nx.Getf(locale, "error.e0341")})
|
||||
continue
|
||||
}
|
||||
if row.Answer == "" {
|
||||
result.Failed++
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: "答案不能为空"})
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: i18nx.Getf(locale, "error.e0292")})
|
||||
continue
|
||||
}
|
||||
if firstRow, exists := seen[row.Question]; exists {
|
||||
result.Failed++
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: fmt.Sprintf("同一文件中标准问题重复,首次出现于第%d行", firstRow)})
|
||||
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: i18nx.Getf(locale, "error.knowledgeFAQImport.duplicateQuestionInFile", firstRow)})
|
||||
continue
|
||||
}
|
||||
seen[row.Question] = rowNo
|
||||
|
||||
@@ -46,7 +46,7 @@ func (s *knowledgeFAQService) Count(cnd *sqls.Cnd) int64 {
|
||||
|
||||
func (s *knowledgeFAQService) CreateKnowledgeFAQ(req request.CreateKnowledgeFAQRequest, operator *dto.AuthPrincipal) (*models.KnowledgeFAQ, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
kb, err := s.requireFAQKnowledgeBase(req.KnowledgeBaseID)
|
||||
if err != nil {
|
||||
@@ -76,11 +76,11 @@ func (s *knowledgeFAQService) CreateKnowledgeFAQ(req request.CreateKnowledgeFAQR
|
||||
|
||||
func (s *knowledgeFAQService) UpdateKnowledgeFAQ(req request.UpdateKnowledgeFAQRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
current := s.Get(req.ID)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("FAQ不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0025")
|
||||
}
|
||||
if _, err := s.requireFAQKnowledgeBase(req.KnowledgeBaseID); err != nil {
|
||||
return err
|
||||
@@ -114,7 +114,7 @@ func (s *knowledgeFAQService) UpdateKnowledgeFAQ(req request.UpdateKnowledgeFAQR
|
||||
func (s *knowledgeFAQService) DeleteKnowledgeFAQ(id int64) error {
|
||||
current := s.Get(id)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("FAQ不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0025")
|
||||
}
|
||||
if err := repositories.KnowledgeFAQRepository.Delete(sqls.DB(), id); err != nil {
|
||||
return err
|
||||
@@ -124,11 +124,11 @@ func (s *knowledgeFAQService) DeleteKnowledgeFAQ(id int64) error {
|
||||
|
||||
func (s *knowledgeFAQService) BatchMoveKnowledgeFAQs(req request.BatchMoveKnowledgeFAQRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
ids := uniquePositiveIDs(req.IDs)
|
||||
if len(ids) == 0 {
|
||||
return errorsx.InvalidParam("请选择要移动的FAQ")
|
||||
return errorsx.InvalidParamI18n("error.e0332")
|
||||
}
|
||||
if _, err := s.requireFAQKnowledgeBase(req.KnowledgeBaseID); err != nil {
|
||||
return err
|
||||
@@ -139,10 +139,10 @@ func (s *knowledgeFAQService) BatchMoveKnowledgeFAQs(req request.BatchMoveKnowle
|
||||
for _, id := range ids {
|
||||
current := s.Get(id)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("FAQ不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0025")
|
||||
}
|
||||
if current.KnowledgeBaseID != req.KnowledgeBaseID {
|
||||
return errorsx.InvalidParam("只能移动当前知识库下的FAQ")
|
||||
return errorsx.InvalidParamI18n("error.e0138")
|
||||
}
|
||||
}
|
||||
now := time.Now()
|
||||
@@ -175,7 +175,7 @@ func (s *knowledgeFAQService) BatchMoveKnowledgeFAQs(req request.BatchMoveKnowle
|
||||
func (s *knowledgeFAQService) BatchDeleteKnowledgeFAQs(req request.BatchDeleteKnowledgeFAQRequest) error {
|
||||
ids := uniquePositiveIDs(req.IDs)
|
||||
if len(ids) == 0 {
|
||||
return errorsx.InvalidParam("请选择要删除的FAQ")
|
||||
return errorsx.InvalidParamI18n("error.e0330")
|
||||
}
|
||||
for _, id := range ids {
|
||||
if err := s.DeleteKnowledgeFAQ(id); err != nil {
|
||||
@@ -187,17 +187,17 @@ func (s *knowledgeFAQService) BatchDeleteKnowledgeFAQs(req request.BatchDeleteKn
|
||||
|
||||
func (s *knowledgeFAQService) buildKnowledgeFAQModel(req request.CreateKnowledgeFAQRequest) (*models.KnowledgeFAQ, error) {
|
||||
if req.KnowledgeBaseID <= 0 {
|
||||
return nil, errorsx.InvalidParam("知识库不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0283")
|
||||
}
|
||||
if req.Question == "" {
|
||||
return nil, errorsx.InvalidParam("问题不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0340")
|
||||
}
|
||||
if req.Answer == "" {
|
||||
return nil, errorsx.InvalidParam("答案不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0292")
|
||||
}
|
||||
similarQuestions, err := json.Marshal(normalizeSimilarQuestions(req.SimilarQuestions))
|
||||
if err != nil {
|
||||
return nil, errorsx.InvalidParam("相似问格式不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0280")
|
||||
}
|
||||
return &models.KnowledgeFAQ{
|
||||
KnowledgeBaseID: req.KnowledgeBaseID,
|
||||
@@ -212,10 +212,10 @@ func (s *knowledgeFAQService) buildKnowledgeFAQModel(req request.CreateKnowledge
|
||||
func (s *knowledgeFAQService) requireFAQKnowledgeBase(knowledgeBaseID int64) (*models.KnowledgeBase, error) {
|
||||
kb := KnowledgeBaseService.Get(knowledgeBaseID)
|
||||
if kb == nil {
|
||||
return nil, errorsx.InvalidParam("知识库不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0283")
|
||||
}
|
||||
if kb.KnowledgeType != "faq" {
|
||||
return nil, errorsx.InvalidParam("当前知识库不是FAQ知识库")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0199")
|
||||
}
|
||||
return kb, nil
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func (s *loginSessionService) Delete(id int64) {
|
||||
func (s *loginSessionService) Revoke(id int64, operatorID int64, operatorName string) error {
|
||||
session := s.Get(id)
|
||||
if session == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
now := time.Now()
|
||||
return s.Updates(id, map[string]any{
|
||||
|
||||
@@ -95,18 +95,18 @@ func (s *mCPDebugService) CallTool(ctx context.Context, serverCode string, toolN
|
||||
func (s *mCPDebugService) resolveServer(serverCode string) (mcps.ServerConfig, error) {
|
||||
cfg := config.Current()
|
||||
if !cfg.MCP.Enabled {
|
||||
return mcps.ServerConfig{}, errorsx.InvalidParam("MCP未启用")
|
||||
return mcps.ServerConfig{}, errorsx.InvalidParamI18n("error.e0035")
|
||||
}
|
||||
serverCode = strings.TrimSpace(serverCode)
|
||||
if serverCode == "" {
|
||||
return mcps.ServerConfig{}, errorsx.InvalidParam("serverCode不能为空")
|
||||
return mcps.ServerConfig{}, errorsx.InvalidParamI18n("error.e0070")
|
||||
}
|
||||
server, ok := cfg.MCP.Servers[serverCode]
|
||||
if !ok {
|
||||
return mcps.ServerConfig{}, errorsx.InvalidParam("MCP服务配置不存在")
|
||||
return mcps.ServerConfig{}, errorsx.InvalidParamI18n("error.e0034")
|
||||
}
|
||||
if !server.Enabled {
|
||||
return mcps.ServerConfig{}, errorsx.InvalidParam("MCP服务未启用")
|
||||
return mcps.ServerConfig{}, errorsx.InvalidParamI18n("error.e0033")
|
||||
}
|
||||
return mcps.ServerConfig{
|
||||
Code: serverCode,
|
||||
|
||||
@@ -122,7 +122,7 @@ func (s *messageService) GetConversationReadTarget(conversationID, messageID int
|
||||
if messageID > 0 {
|
||||
message := s.Get(messageID)
|
||||
if message == nil || message.ConversationID != conversationID {
|
||||
return nil, errorsx.InvalidParam("消息不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0244")
|
||||
}
|
||||
return message, nil
|
||||
}
|
||||
@@ -138,7 +138,7 @@ func (s *messageService) SendMessage(conversationID int64, senderType enums.IMSe
|
||||
case enums.IMSenderTypeCustomer:
|
||||
return s.sendMessage(conversationID, enums.IMSenderTypeCustomer, 0, clientMsgID, messageType, content, payload, nil, external, "")
|
||||
default:
|
||||
return nil, errorsx.InvalidParam("不支持的发送人类型")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0080")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,24 +152,24 @@ func (s *messageService) SendAgentMessageWithRequestID(conversationID int64, req
|
||||
|
||||
func (s *messageService) RecallAgentMessage(messageID int64, operator *dto.AuthPrincipal) (*models.Message, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if messageID <= 0 {
|
||||
return nil, errorsx.InvalidParam("消息不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0244")
|
||||
}
|
||||
|
||||
message := s.Get(messageID)
|
||||
if message == nil {
|
||||
return nil, errorsx.InvalidParam("消息不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0244")
|
||||
}
|
||||
if message.SenderType != enums.IMSenderTypeAgent {
|
||||
return nil, errorsx.InvalidParam("仅支持撤回客服消息")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0091")
|
||||
}
|
||||
if message.SenderID != operator.UserID {
|
||||
return nil, errorsx.Forbidden("仅允许撤回自己发送的消息")
|
||||
return nil, errorsx.ForbiddenI18n("error.e0087")
|
||||
}
|
||||
if message.RecalledAt != nil || message.SendStatus == enums.IMMessageStatusRecalled {
|
||||
return nil, errorsx.InvalidParam("消息已撤回")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0246")
|
||||
}
|
||||
|
||||
conversation, err := s.ValidateConversationSender(message.ConversationID, enums.IMSenderTypeAgent, operator, nil)
|
||||
@@ -260,10 +260,10 @@ func (s *messageService) SendAIServiceNotice(conversationID int64, aiAgentID int
|
||||
func (s *messageService) SendAIServiceNoticeWithRequestID(conversationID int64, aiAgentID int64, content string, requestID string) (*models.Message, error) {
|
||||
conversation := ConversationService.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return nil, errorsx.InvalidParam("会话不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if conversation.Status == enums.IMConversationStatusClosed {
|
||||
return nil, errorsx.InvalidParam("会话已关闭")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0119")
|
||||
}
|
||||
return s.sendValidatedMessage(conversation, enums.IMSenderTypeAI, aiAgentID, strs.UUID(), enums.IMMessageTypeText, content, "", &dto.AuthPrincipal{
|
||||
UserID: 0,
|
||||
@@ -378,10 +378,10 @@ func (s *messageService) sendMessage(conversationID int64, senderType enums.IMSe
|
||||
|
||||
if senderType == enums.IMSenderTypeCustomer {
|
||||
if external == nil || strings.TrimSpace(external.ExternalID) == "" {
|
||||
return nil, errorsx.Unauthorized("外部用户标识不能为空")
|
||||
return nil, errorsx.UnauthorizedI18n("error.e0149")
|
||||
}
|
||||
} else if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
|
||||
if strs.IsBlank(string(messageType)) {
|
||||
@@ -404,7 +404,7 @@ func (s *messageService) sendValidatedMessage(conversation *models.Conversation,
|
||||
return nil, err
|
||||
}
|
||||
if strs.IsBlank(content) && strs.IsBlank(payload) {
|
||||
return nil, errorsx.InvalidParam("消息内容不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0245")
|
||||
}
|
||||
|
||||
// 防抖,消息存在就不再发送了
|
||||
@@ -610,11 +610,11 @@ func (s *messageService) normalizeMessageContent(conversationID int64, messageTy
|
||||
sanitized := utils.SanitizeMessageHTML(content)
|
||||
normalized, err := utils.NormalizeMessageHTMLAssets(sanitized)
|
||||
if err != nil {
|
||||
return "", "", "", errorsx.InvalidParam("HTML消息中的图片必须使用已上传文件")
|
||||
return "", "", "", errorsx.InvalidParamI18n("error.e0030")
|
||||
}
|
||||
summary := utils.BuildHTMLSummary(normalized)
|
||||
if summary == "" {
|
||||
return "", "", "", errorsx.InvalidParam("消息内容不能为空")
|
||||
return "", "", "", errorsx.InvalidParamI18n("error.e0245")
|
||||
}
|
||||
return normalized, "", summary, nil
|
||||
case enums.IMMessageTypeImage, enums.IMMessageTypeAttachment:
|
||||
@@ -639,7 +639,7 @@ func (s *messageService) normalizeMessageContent(conversationID int64, messageTy
|
||||
default:
|
||||
content = strings.TrimSpace(content)
|
||||
if content == "" && strings.TrimSpace(payload) == "" {
|
||||
return "", "", "", errorsx.InvalidParam("消息内容不能为空")
|
||||
return "", "", "", errorsx.InvalidParamI18n("error.e0245")
|
||||
}
|
||||
return content, strings.TrimSpace(payload), buildMessageSummary(messageType, content), nil
|
||||
}
|
||||
@@ -648,38 +648,38 @@ func (s *messageService) normalizeMessageContent(conversationID int64, messageTy
|
||||
func (s *messageService) ValidateConversationSender(conversationID int64, senderType enums.IMSenderType, operator *dto.AuthPrincipal, external *openidentity.ExternalUser) (*models.Conversation, error) {
|
||||
conversation := ConversationService.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return nil, errorsx.InvalidParam("会话不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if conversation.Status == enums.IMConversationStatusClosed {
|
||||
return nil, errorsx.InvalidParam("会话已关闭")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0119")
|
||||
}
|
||||
switch senderType {
|
||||
case enums.IMSenderTypeAgent:
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if conversation.Status != enums.IMConversationStatusActive || conversation.CurrentAssigneeID == 0 {
|
||||
return nil, errorsx.InvalidParam("会话未分配客服,暂不允许发送消息")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0120")
|
||||
}
|
||||
if conversation.CurrentAssigneeID != operator.UserID {
|
||||
return nil, errorsx.Forbidden("当前会话已分配给其他客服")
|
||||
return nil, errorsx.ForbiddenI18n("error.e0191")
|
||||
}
|
||||
case enums.IMSenderTypeAI:
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if conversation.Status != enums.IMConversationStatusAIServing && !s.allowAIMessageOnPendingHandoff(conversation) {
|
||||
return nil, errorsx.Forbidden("当前会话不处于 AI 接待状态")
|
||||
return nil, errorsx.ForbiddenI18n("error.e0189")
|
||||
}
|
||||
if conversation.CurrentAssigneeID != 0 {
|
||||
return nil, errorsx.Forbidden("当前会话已由人工客服接管")
|
||||
return nil, errorsx.ForbiddenI18n("error.e0192")
|
||||
}
|
||||
case enums.IMSenderTypeCustomer:
|
||||
if external == nil || !ConversationService.IsCustomerConversationOwner(conversation, *external) {
|
||||
return nil, errorsx.Forbidden("无权访问该会话")
|
||||
return nil, errorsx.ForbiddenI18n("error.e0222")
|
||||
}
|
||||
default:
|
||||
return nil, errorsx.InvalidParam("不支持的发送人类型")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0080")
|
||||
}
|
||||
return conversation, nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type notificationService struct {
|
||||
|
||||
func (s *notificationService) Create(req request.CreateNotificationRequest) (*models.Notification, error) {
|
||||
if req.RecipientUserID <= 0 {
|
||||
return nil, errorsx.InvalidParam("接收人不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0212")
|
||||
}
|
||||
now := time.Now()
|
||||
item := &models.Notification{
|
||||
@@ -82,11 +82,11 @@ func (s *notificationService) CountUnread(userID int64) int64 {
|
||||
|
||||
func (s *notificationService) MarkRead(id int64, userID int64) error {
|
||||
if id <= 0 {
|
||||
return errorsx.InvalidParam("通知不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0337")
|
||||
}
|
||||
item := repositories.NotificationRepository.Get(sqls.DB(), id)
|
||||
if item == nil || item.RecipientUserID != userID {
|
||||
return errorsx.InvalidParam("通知不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0337")
|
||||
}
|
||||
if item.ReadAt != nil {
|
||||
return nil
|
||||
@@ -99,7 +99,7 @@ func (s *notificationService) MarkRead(id int64, userID int64) error {
|
||||
|
||||
func (s *notificationService) MarkAllRead(userID int64) error {
|
||||
if userID <= 0 {
|
||||
return errorsx.InvalidParam("接收人不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0212")
|
||||
}
|
||||
return repositories.NotificationRepository.MarkAllRead(sqls.DB(), userID, time.Now())
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func (s *oidcLoginService) loginWithOIDCProfile(profile *oidcLoginProfile, authC
|
||||
}
|
||||
|
||||
if user.Status != enums.StatusOk {
|
||||
return errorsx.Unauthorized("当前系统账号已被禁用")
|
||||
return errorsx.UnauthorizedI18n("error.e0200")
|
||||
}
|
||||
|
||||
if err = repositories.UserRepository.Updates(ctx.Tx, user.ID, map[string]any{
|
||||
|
||||
@@ -74,12 +74,12 @@ func (s *quickReplyService) Delete(id int64) {
|
||||
|
||||
func (s *quickReplyService) CreateQuickReply(req request.CreateQuickReplyRequest, operator *dto.AuthPrincipal) (*models.QuickReply, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
title := strings.TrimSpace(req.Title)
|
||||
content := strings.TrimSpace(req.Content)
|
||||
if title == "" || content == "" {
|
||||
return nil, errorsx.InvalidParam("标题和内容不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0240")
|
||||
}
|
||||
item := &models.QuickReply{
|
||||
GroupName: strings.TrimSpace(req.GroupName),
|
||||
@@ -97,11 +97,11 @@ func (s *quickReplyService) CreateQuickReply(req request.CreateQuickReplyRequest
|
||||
|
||||
func (s *quickReplyService) UpdateQuickReply(req request.UpdateQuickReplyRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := s.Get(req.ID)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("快捷回复不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0203")
|
||||
}
|
||||
return s.Updates(req.ID, map[string]any{
|
||||
"group_name": strings.TrimSpace(req.GroupName),
|
||||
@@ -118,7 +118,7 @@ func (s *quickReplyService) UpdateQuickReply(req request.UpdateQuickReplyRequest
|
||||
func (s *quickReplyService) DeleteQuickReply(id int64) error {
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("快捷回复不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0203")
|
||||
}
|
||||
s.Delete(id)
|
||||
return nil
|
||||
|
||||
@@ -78,10 +78,10 @@ func (s *roleService) CreateRole(req request.CreateRoleRequest, operator *dto.Au
|
||||
name := strings.TrimSpace(req.Name)
|
||||
code := strings.TrimSpace(req.Code)
|
||||
if name == "" || code == "" {
|
||||
return nil, errorsx.InvalidParam("角色名称和编码不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0306")
|
||||
}
|
||||
if s.Take("code = ?", code) != nil {
|
||||
return nil, errorsx.InvalidParam("角色编码已存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0308")
|
||||
}
|
||||
|
||||
role := &models.Role{
|
||||
@@ -102,7 +102,7 @@ func (s *roleService) CreateRole(req request.CreateRoleRequest, operator *dto.Au
|
||||
func (s *roleService) UpdateRole(req request.UpdateRoleRequest, operator *dto.AuthPrincipal) error {
|
||||
role := s.Get(req.ID)
|
||||
if role == nil {
|
||||
return errorsx.InvalidParam("角色不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0305")
|
||||
}
|
||||
now := time.Now()
|
||||
return s.Updates(req.ID, map[string]any{
|
||||
@@ -136,13 +136,13 @@ func (s *roleService) UpdateSort(ids []int64) error {
|
||||
func (s *roleService) DeleteRole(id int64) error {
|
||||
role := s.Get(id)
|
||||
if role == nil {
|
||||
return errorsx.InvalidParam("角色不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0305")
|
||||
}
|
||||
if role.IsSystem {
|
||||
return errorsx.Forbidden("系统内置角色不允许删除")
|
||||
return errorsx.ForbiddenI18n("error.e0293")
|
||||
}
|
||||
if UserRoleService.Take("role_id = ?", id) != nil {
|
||||
return errorsx.Forbidden("角色已被用户使用,无法删除")
|
||||
return errorsx.ForbiddenI18n("error.e0307")
|
||||
}
|
||||
s.Delete(id)
|
||||
return nil
|
||||
@@ -151,10 +151,10 @@ func (s *roleService) DeleteRole(id int64) error {
|
||||
func (s *roleService) UpdateStatus(id int64, status enums.Status, operator *dto.AuthPrincipal) error {
|
||||
role := s.Get(id)
|
||||
if role == nil {
|
||||
return errorsx.InvalidParam("角色不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0305")
|
||||
}
|
||||
if !slices.Contains(enums.StatusValues, status) {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
if err := s.Updates(id, map[string]any{
|
||||
"status": status,
|
||||
@@ -170,7 +170,7 @@ func (s *roleService) UpdateStatus(id int64, status enums.Status, operator *dto.
|
||||
func (s *roleService) AssignPermissions(roleID int64, permissionIDs []int64, operator *dto.AuthPrincipal) error {
|
||||
role := s.Get(roleID)
|
||||
if role == nil {
|
||||
return errorsx.InvalidParam("角色不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0305")
|
||||
}
|
||||
|
||||
return s.replaceRolePermissions(roleID, permissionIDs, operator)
|
||||
@@ -184,7 +184,7 @@ func (s *roleService) replaceRolePermissions(roleID int64, permissionIDs []int64
|
||||
for _, permissionID := range permissionIDs {
|
||||
permission := PermissionService.Get(permissionID)
|
||||
if permission == nil {
|
||||
return errorsx.InvalidParam("权限不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0236")
|
||||
}
|
||||
relation := &models.RolePermission{
|
||||
RoleID: roleID,
|
||||
|
||||
@@ -86,14 +86,14 @@ func (s *skillDefinitionService) GetByIDs(ids []int64) map[int64]models.SkillDef
|
||||
|
||||
func (s *skillDefinitionService) CreateSkillDefinition(req request.CreateSkillDefinitionRequest, operator *dto.AuthPrincipal) (*models.SkillDefinition, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
normalized, err := s.normalizeSkillDefinitionRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.Take("code = ?", normalized.Code) != nil {
|
||||
return nil, errorsx.InvalidParam("Skill 编码已存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0058")
|
||||
}
|
||||
item := &models.SkillDefinition{
|
||||
Code: normalized.Code,
|
||||
@@ -114,21 +114,21 @@ func (s *skillDefinitionService) CreateSkillDefinition(req request.CreateSkillDe
|
||||
|
||||
func (s *skillDefinitionService) UpdateSkillDefinition(req request.UpdateSkillDefinitionRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
if req.ID <= 0 {
|
||||
return errorsx.InvalidParam("Skill ID 不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0052")
|
||||
}
|
||||
current := s.Get(req.ID)
|
||||
if current == nil {
|
||||
return errorsx.InvalidParam("Skill 不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0053")
|
||||
}
|
||||
normalized, err := s.normalizeSkillDefinitionRequest(req.CreateSkillDefinitionRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists := s.Take("code = ? AND id <> ?", normalized.Code, req.ID); exists != nil {
|
||||
return errorsx.InvalidParam("Skill 编码已存在")
|
||||
return errorsx.InvalidParamI18n("error.e0058")
|
||||
}
|
||||
return repositories.SkillDefinitionRepository.Updates(sqls.DB(), req.ID, map[string]any{
|
||||
"code": normalized.Code,
|
||||
@@ -153,13 +153,13 @@ func (s *skillDefinitionService) normalizeSkillDefinitionRequest(req request.Cre
|
||||
Remark: strings.TrimSpace(req.Remark),
|
||||
}
|
||||
if normalized.Code == "" {
|
||||
return nil, errorsx.InvalidParam("Skill 编码不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0057")
|
||||
}
|
||||
if normalized.Name == "" {
|
||||
return nil, errorsx.InvalidParam("Skill 名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0055")
|
||||
}
|
||||
if normalized.Instruction == "" {
|
||||
return nil, errorsx.InvalidParam("技能说明不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0207")
|
||||
}
|
||||
examples, err := normalizeSkillStringArray(req.Examples)
|
||||
if err != nil {
|
||||
@@ -182,11 +182,11 @@ func (s *skillDefinitionService) normalizeSkillDefinitionRequest(req request.Cre
|
||||
func normalizeSkillStringArray(input []string) ([]string, error) {
|
||||
buf, err := json.Marshal(input)
|
||||
if err != nil {
|
||||
return nil, errorsx.InvalidParam("JSON 数组格式不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0031")
|
||||
}
|
||||
var ret []string
|
||||
if err := json.Unmarshal(buf, &ret); err != nil {
|
||||
return nil, errorsx.InvalidParam("JSON 数组格式不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0031")
|
||||
}
|
||||
normalized := make([]string, 0, len(ret))
|
||||
seen := make(map[string]struct{}, len(ret))
|
||||
|
||||
@@ -22,13 +22,13 @@ type skillRuntimeService struct{}
|
||||
|
||||
func (s *skillRuntimeService) DebugRun(ctx context.Context, req request.SkillDebugRunRequest) (*response.SkillDebugRunResponse, error) {
|
||||
if req.AIAgentID <= 0 {
|
||||
return nil, errorsx.InvalidParam("aiAgentId不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0061")
|
||||
}
|
||||
if strings.TrimSpace(req.SkillCode) == "" {
|
||||
return nil, errorsx.InvalidParam("skillCode不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0071")
|
||||
}
|
||||
if strings.TrimSpace(req.UserMessage) == "" {
|
||||
return nil, errorsx.InvalidParam("userMessage不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0078")
|
||||
}
|
||||
if SkillDebugRunHook == nil {
|
||||
return nil, fmt.Errorf("skill debug runner is not initialized")
|
||||
@@ -38,13 +38,13 @@ func (s *skillRuntimeService) DebugRun(ctx context.Context, req request.SkillDeb
|
||||
|
||||
func (s *skillRuntimeService) DebugResume(ctx context.Context, req request.SkillDebugResumeRequest) (*response.SkillDebugRunResponse, error) {
|
||||
if req.AIAgentID <= 0 {
|
||||
return nil, errorsx.InvalidParam("aiAgentId不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0061")
|
||||
}
|
||||
if strings.TrimSpace(req.CheckPointID) == "" {
|
||||
return nil, errorsx.InvalidParam("checkPointId不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0063")
|
||||
}
|
||||
if strings.TrimSpace(req.UserMessage) == "" {
|
||||
return nil, errorsx.InvalidParam("userMessage不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0078")
|
||||
}
|
||||
if SkillDebugResumeHook == nil {
|
||||
return nil, fmt.Errorf("skill debug resume runner is not initialized")
|
||||
|
||||
@@ -132,16 +132,16 @@ func (s *OSSStorage) getBucket() (*oss.Bucket, error) {
|
||||
|
||||
func (s *OSSStorage) validate() error {
|
||||
if strings.TrimSpace(s.cfg.Endpoint) == "" {
|
||||
return errorsx.InvalidParam("OSS endpoint 未配置")
|
||||
return errorsx.InvalidParamI18n("error.e0051")
|
||||
}
|
||||
if strings.TrimSpace(s.cfg.Bucket) == "" {
|
||||
return errorsx.InvalidParam("OSS bucket 未配置")
|
||||
return errorsx.InvalidParamI18n("error.e0050")
|
||||
}
|
||||
if strings.TrimSpace(s.cfg.AccessKeyID) == "" {
|
||||
return errorsx.InvalidParam("OSS accessKeyId 未配置")
|
||||
return errorsx.InvalidParamI18n("error.e0048")
|
||||
}
|
||||
if strings.TrimSpace(s.cfg.AccessKeySecret) == "" {
|
||||
return errorsx.InvalidParam("OSS accessKeySecret 未配置")
|
||||
return errorsx.InvalidParamI18n("error.e0049")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -45,6 +45,6 @@ func NewProvider(provider enums.AssetProvider) (FileStorageProvider, error) {
|
||||
case enums.AssetProviderOSS:
|
||||
return NewOSSStorage(cfg.OSS), nil
|
||||
default:
|
||||
return nil, errorsx.InvalidParam("不支持的文件存储类型")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0082")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,24 +87,24 @@ func (s *tagService) FindByNameAndParentID(name string, parentID int64) *models.
|
||||
|
||||
func (s *tagService) CreateTag(req request.CreateTagRequest, operator *dto.AuthPrincipal) (*models.Tag, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("标签名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0239")
|
||||
}
|
||||
|
||||
if req.ParentID > 0 {
|
||||
parent := s.Get(req.ParentID)
|
||||
if parent == nil {
|
||||
return nil, errorsx.InvalidParam("父标签不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0251")
|
||||
}
|
||||
}
|
||||
|
||||
existing := s.FindByNameAndParentID(name, req.ParentID)
|
||||
if existing != nil {
|
||||
return nil, errorsx.InvalidParam("同级下已存在相同名称的标签")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0141")
|
||||
}
|
||||
|
||||
item := &models.Tag{
|
||||
@@ -132,32 +132,32 @@ func (s *tagService) NextSortNo(parentID int64) int {
|
||||
|
||||
func (s *tagService) UpdateTag(req request.UpdateTagRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
|
||||
item := s.Get(req.ID)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("标签不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0238")
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return errorsx.InvalidParam("标签名称不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0239")
|
||||
}
|
||||
|
||||
if req.ParentID > 0 {
|
||||
if req.ParentID == req.ID {
|
||||
return errorsx.InvalidParam("不能将标签设为自己的子标签")
|
||||
return errorsx.InvalidParamI18n("error.e0083")
|
||||
}
|
||||
parent := s.Get(req.ParentID)
|
||||
if parent == nil {
|
||||
return errorsx.InvalidParam("父标签不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0251")
|
||||
}
|
||||
}
|
||||
|
||||
existing := s.FindByNameAndParentID(name, req.ParentID)
|
||||
if existing != nil && existing.ID != req.ID {
|
||||
return errorsx.InvalidParam("同级下已存在相同名称的标签")
|
||||
return errorsx.InvalidParamI18n("error.e0141")
|
||||
}
|
||||
|
||||
return s.Updates(req.ID, map[string]any{
|
||||
@@ -184,17 +184,17 @@ func (s *tagService) UpdateSort(ids []int64) error {
|
||||
func (s *tagService) DeleteTag(id int64) error {
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("标签不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0238")
|
||||
}
|
||||
|
||||
if s.HasChildren(id) {
|
||||
return errorsx.InvalidParam("该标签下存在子标签,无法删除")
|
||||
return errorsx.InvalidParamI18n("error.e0310")
|
||||
}
|
||||
if ConversationTagService.Take("tag_id = ?", id) != nil {
|
||||
return errorsx.InvalidParam("该标签已关联会话,无法删除")
|
||||
return errorsx.InvalidParamI18n("error.e0311")
|
||||
}
|
||||
if TicketTagService.Take("tag_id = ?", id) != nil {
|
||||
return errorsx.InvalidParam("该标签已关联工单,无法删除")
|
||||
return errorsx.InvalidParamI18n("error.e0312")
|
||||
}
|
||||
|
||||
s.Delete(id)
|
||||
@@ -247,16 +247,16 @@ func (s *tagService) GetSelfAndDescendantIDs(tagID int64) []int64 {
|
||||
|
||||
func (s *tagService) UpdateStatus(id int64, status int, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
|
||||
item := s.Get(id)
|
||||
if item == nil {
|
||||
return errorsx.InvalidParam("标签不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0238")
|
||||
}
|
||||
|
||||
if status != int(enums.StatusOk) && status != int(enums.StatusDisabled) {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
@@ -185,22 +185,22 @@ func (s *ticketService) GetTags(ticketID int64) []models.Tag {
|
||||
|
||||
func (s *ticketService) CreateTicket(req request.CreateTicketRequest, operator *dto.AuthPrincipal) (*models.Ticket, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
title := strings.TrimSpace(req.Title)
|
||||
description := strings.TrimSpace(req.Description)
|
||||
if title == "" {
|
||||
return nil, errorsx.InvalidParam("工单标题不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0181")
|
||||
}
|
||||
if description == "" {
|
||||
return nil, errorsx.InvalidParam("工单描述不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0179")
|
||||
}
|
||||
source := enums.TicketSource(strings.TrimSpace(req.Source))
|
||||
if source == "" {
|
||||
source = enums.TicketSourceManual
|
||||
}
|
||||
if !enums.IsValidTicketSource(string(source)) {
|
||||
return nil, errorsx.InvalidParam("工单来源不合法")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0180")
|
||||
}
|
||||
if err := s.validateTicketRefs(req.CustomerID, req.ConversationID, req.CurrentAssigneeID); err != nil {
|
||||
return nil, err
|
||||
@@ -254,11 +254,11 @@ func (s *ticketService) CreateTicket(req request.CreateTicketRequest, operator *
|
||||
|
||||
func (s *ticketService) CreateFromConversation(req request.CreateTicketFromConversationRequest, operator *dto.AuthPrincipal) (*models.Ticket, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
conversation := ConversationService.Get(req.ConversationID)
|
||||
if conversation == nil {
|
||||
return nil, errorsx.InvalidParam("会话不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
title := strings.TrimSpace(req.Title)
|
||||
if title == "" {
|
||||
@@ -288,19 +288,19 @@ func (s *ticketService) CreateFromConversation(req request.CreateTicketFromConve
|
||||
|
||||
func (s *ticketService) UpdateTicket(req request.UpdateTicketRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
title := strings.TrimSpace(req.Title)
|
||||
description := strings.TrimSpace(req.Description)
|
||||
if title == "" {
|
||||
return errorsx.InvalidParam("工单标题不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0181")
|
||||
}
|
||||
if description == "" {
|
||||
return errorsx.InvalidParam("工单描述不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0179")
|
||||
}
|
||||
ticket := s.Get(req.TicketID)
|
||||
if ticket == nil {
|
||||
return errorsx.InvalidParam("工单不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0178")
|
||||
}
|
||||
if err := s.validateAssignee(req.CurrentAssigneeID); err != nil {
|
||||
return err
|
||||
@@ -327,22 +327,22 @@ func (s *ticketService) UpdateTicket(req request.UpdateTicketRequest, operator *
|
||||
|
||||
func (s *ticketService) LinkCustomer(ticketID int64, customerID int64, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
ticket := s.Get(ticketID)
|
||||
if ticket == nil {
|
||||
return errorsx.InvalidParam("工单不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0178")
|
||||
}
|
||||
if customerID <= 0 || CustomerService.Get(customerID) == nil {
|
||||
return errorsx.InvalidParam("客户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
if ticket.ConversationID > 0 {
|
||||
conversation := ConversationService.Get(ticket.ConversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if conversation.CustomerID > 0 && conversation.CustomerID != customerID {
|
||||
return errorsx.InvalidParam("会话与客户不匹配")
|
||||
return errorsx.InvalidParamI18n("error.e0118")
|
||||
}
|
||||
}
|
||||
now := time.Now()
|
||||
@@ -356,7 +356,7 @@ func (s *ticketService) LinkCustomer(ticketID int64, customerID int64, operator
|
||||
|
||||
func (s *ticketService) AssignTicket(req request.AssignTicketRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
var assignedEvent *events.TicketAssignedEvent
|
||||
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
@@ -377,15 +377,15 @@ func (s *ticketService) AssignTicket(req request.AssignTicketRequest, operator *
|
||||
|
||||
func (s *ticketService) ChangeStatus(req request.ChangeTicketStatusRequest, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
status := strings.TrimSpace(req.Status)
|
||||
if !enums.IsValidTicketStatus(status) {
|
||||
return errorsx.InvalidParam("工单状态不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0182")
|
||||
}
|
||||
ticket := s.Get(req.TicketID)
|
||||
if ticket == nil {
|
||||
return errorsx.InvalidParam("工单不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0178")
|
||||
}
|
||||
now := time.Now()
|
||||
var handledAt *time.Time
|
||||
@@ -403,15 +403,15 @@ func (s *ticketService) ChangeStatus(req request.ChangeTicketStatusRequest, oper
|
||||
|
||||
func (s *ticketService) AddProgress(req request.CreateTicketProgressRequest, operator *dto.AuthPrincipal) (*models.TicketProgress, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
content := strings.TrimSpace(req.Content)
|
||||
if content == "" {
|
||||
return nil, errorsx.InvalidParam("处理进展不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0148")
|
||||
}
|
||||
ticket := s.Get(req.TicketID)
|
||||
if ticket == nil {
|
||||
return nil, errorsx.InvalidParam("工单不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0178")
|
||||
}
|
||||
now := time.Now()
|
||||
progress := &models.TicketProgress{
|
||||
@@ -438,7 +438,7 @@ func (s *ticketService) AddProgress(req request.CreateTicketProgressRequest, ope
|
||||
func (s *ticketService) GetDetail(id int64) (*TicketDetailAggregate, error) {
|
||||
ticket := s.Get(id)
|
||||
if ticket == nil {
|
||||
return nil, errorsx.InvalidParam("工单不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0178")
|
||||
}
|
||||
aggregate := &TicketDetailAggregate{
|
||||
Ticket: ticket,
|
||||
@@ -497,14 +497,14 @@ func (s *ticketService) GetSummary(operator *dto.AuthPrincipal, staleHours ...in
|
||||
func (s *ticketService) assignTicketTx(tx *gorm.DB, req request.AssignTicketRequest, operator *dto.AuthPrincipal) (*events.TicketAssignedEvent, error) {
|
||||
ticket := repositories.TicketRepository.Get(tx, req.TicketID)
|
||||
if ticket == nil {
|
||||
return nil, errorsx.InvalidParam("工单不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0178")
|
||||
}
|
||||
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("负责人不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0334")
|
||||
}
|
||||
var fromUser *models.User
|
||||
if ticket.CurrentAssigneeID > 0 {
|
||||
@@ -627,15 +627,15 @@ func (s *ticketService) enrichTicketTags(db *gorm.DB, aggregate *TicketListAggre
|
||||
|
||||
func (s *ticketService) validateTicketRefs(customerID, conversationID, assigneeID int64) error {
|
||||
if customerID > 0 && CustomerService.Get(customerID) == nil {
|
||||
return errorsx.InvalidParam("客户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0155")
|
||||
}
|
||||
if conversationID > 0 {
|
||||
conversation := ConversationService.Get(conversationID)
|
||||
if conversation == nil {
|
||||
return errorsx.InvalidParam("会话不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0116")
|
||||
}
|
||||
if customerID > 0 && conversation.CustomerID != customerID {
|
||||
return errorsx.InvalidParam("会话与客户不匹配")
|
||||
return errorsx.InvalidParamI18n("error.e0118")
|
||||
}
|
||||
}
|
||||
return s.validateAssignee(assigneeID)
|
||||
@@ -650,11 +650,11 @@ func (s *ticketService) validateAssignee(userID int64) error {
|
||||
|
||||
func (s *ticketService) validateRequiredAssignee(userID int64) error {
|
||||
if userID <= 0 {
|
||||
return errorsx.InvalidParam("负责人不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0334")
|
||||
}
|
||||
user := UserService.Get(userID)
|
||||
if user == nil || user.Status != enums.StatusOk {
|
||||
return errorsx.InvalidParam("负责人不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0334")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -66,11 +66,11 @@ func (s *ticketTagService) ValidateTagIDs(tagIDs []int64) ([]int64, error) {
|
||||
}
|
||||
tags := repositories.TagRepository.Find(sqls.DB(), sqls.NewCnd().In("id", normalized))
|
||||
if len(tags) != len(normalized) {
|
||||
return nil, errorsx.InvalidParam("存在无效工单标签")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0153")
|
||||
}
|
||||
for i := range tags {
|
||||
if tags[i].Status != enums.StatusOk {
|
||||
return nil, errorsx.InvalidParam("存在未启用的工单标签")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0154")
|
||||
}
|
||||
}
|
||||
return normalized, nil
|
||||
|
||||
@@ -41,21 +41,21 @@ func (s *ticketViewService) ListByUser(userID int64) []models.TicketView {
|
||||
|
||||
func (s *ticketViewService) Save(req request.SaveTicketViewRequest, operator *dto.AuthPrincipal) (*models.TicketView, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
return nil, errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return nil, errorsx.InvalidParam("视图名称不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0303")
|
||||
}
|
||||
filtersJSON, err := json.Marshal(req.Filters)
|
||||
if err != nil {
|
||||
return nil, errorsx.InvalidParam("视图筛选条件格式不正确")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0304")
|
||||
}
|
||||
now := time.Now()
|
||||
if req.ID > 0 {
|
||||
item := repositories.TicketViewRepository.Get(sqls.DB(), req.ID)
|
||||
if item == nil || item.UserID != operator.UserID {
|
||||
return nil, errorsx.InvalidParam("视图不存在")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0302")
|
||||
}
|
||||
if err := repositories.TicketViewRepository.Updates(sqls.DB(), req.ID, map[string]any{
|
||||
"name": name,
|
||||
@@ -82,11 +82,11 @@ func (s *ticketViewService) Save(req request.SaveTicketViewRequest, operator *dt
|
||||
|
||||
func (s *ticketViewService) Delete(id int64, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
item := repositories.TicketViewRepository.Get(sqls.DB(), id)
|
||||
if item == nil || item.UserID != operator.UserID {
|
||||
return errorsx.InvalidParam("视图不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0302")
|
||||
}
|
||||
return repositories.TicketViewRepository.Delete(sqls.DB(), id)
|
||||
}
|
||||
|
||||
@@ -95,21 +95,21 @@ func (s *toolCatalogService) ValidateToolCode(toolCode string) error {
|
||||
cfg := config.Current()
|
||||
toolCode = strings.TrimSpace(toolCode)
|
||||
if toolCode == "" {
|
||||
return errorsx.InvalidParam("toolCode不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0074")
|
||||
}
|
||||
if toolx.IsAgentDirectToolCode(toolCode) {
|
||||
return nil
|
||||
}
|
||||
serverCode, toolName := toolx.SplitMCPToolCode(toolCode)
|
||||
if serverCode == "" || toolName == "" {
|
||||
return errorsx.InvalidParam("toolCode格式不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0075")
|
||||
}
|
||||
if !cfg.MCP.Enabled {
|
||||
return errorsx.InvalidParam("MCP未启用")
|
||||
return errorsx.InvalidParamI18n("error.e0035")
|
||||
}
|
||||
server, ok := cfg.MCP.Servers[serverCode]
|
||||
if !ok || !server.Enabled {
|
||||
return errorsx.InvalidParam("toolCode 绑定的 MCP 服务不存在或未启用")
|
||||
return errorsx.InvalidParamI18n("error.e0073")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -91,19 +91,19 @@ func (s *userService) GetByEmail(email string) *models.User {
|
||||
func (s *userService) CreateUser(req request.CreateUserRequest, operator *dto.AuthPrincipal) (*models.User, string, error) {
|
||||
username := strings.TrimSpace(req.Username)
|
||||
if username == "" {
|
||||
return nil, "", errorsx.InvalidParam("用户名不能为空")
|
||||
return nil, "", errorsx.InvalidParamI18n("error.e0257")
|
||||
}
|
||||
if s.GetByUsername(username) != nil {
|
||||
return nil, "", errorsx.InvalidParam("用户名已存在")
|
||||
return nil, "", errorsx.InvalidParamI18n("error.e0259")
|
||||
}
|
||||
|
||||
mobile := utils.NormalizeNullableString(req.Mobile)
|
||||
email := utils.NormalizeNullableString(req.Email)
|
||||
if mobile != nil && s.GetByMobile(*mobile) != nil {
|
||||
return nil, "", errorsx.InvalidParam("手机号已存在")
|
||||
return nil, "", errorsx.InvalidParamI18n("error.e0206")
|
||||
}
|
||||
if email != nil && s.GetByEmail(*email) != nil {
|
||||
return nil, "", errorsx.InvalidParam("邮箱已存在")
|
||||
return nil, "", errorsx.InvalidParamI18n("error.e0338")
|
||||
}
|
||||
|
||||
plain, err := utils.GenerateRandomPassword(12)
|
||||
@@ -146,19 +146,19 @@ func (s *userService) CreateUser(req request.CreateUserRequest, operator *dto.Au
|
||||
func (s *userService) UpdateUser(req request.UpdateUserRequest, operator *dto.AuthPrincipal) error {
|
||||
user := s.Get(req.ID)
|
||||
if user == nil || user.DeletedAt != nil {
|
||||
return errorsx.InvalidParam("用户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0255")
|
||||
}
|
||||
|
||||
mobile := utils.NormalizeNullableString(req.Mobile)
|
||||
email := utils.NormalizeNullableString(req.Email)
|
||||
if mobile != nil {
|
||||
if existed := s.GetByMobile(*mobile); existed != nil && existed.ID != req.ID {
|
||||
return errorsx.InvalidParam("手机号已存在")
|
||||
return errorsx.InvalidParamI18n("error.e0206")
|
||||
}
|
||||
}
|
||||
if email != nil {
|
||||
if existed := s.GetByEmail(*email); existed != nil && existed.ID != req.ID {
|
||||
return errorsx.InvalidParam("邮箱已存在")
|
||||
return errorsx.InvalidParamI18n("error.e0338")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ func (s *userService) UpdateUser(req request.UpdateUserRequest, operator *dto.Au
|
||||
func (s *userService) DeleteUser(id int64, operator *dto.AuthPrincipal) error {
|
||||
user := s.Get(id)
|
||||
if user == nil {
|
||||
return errorsx.InvalidParam("用户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0255")
|
||||
}
|
||||
|
||||
if err := s.Updates(id, map[string]any{
|
||||
@@ -195,10 +195,10 @@ func (s *userService) DeleteUser(id int64, operator *dto.AuthPrincipal) error {
|
||||
func (s *userService) UpdateStatus(id int64, status int, operator *dto.AuthPrincipal) error {
|
||||
user := s.Get(id)
|
||||
if user == nil {
|
||||
return errorsx.InvalidParam("用户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0255")
|
||||
}
|
||||
if !slices.Contains(enums.StatusValues, enums.Status(status)) {
|
||||
return errorsx.InvalidParam("状态值不合法")
|
||||
return errorsx.InvalidParamI18n("error.e0254")
|
||||
}
|
||||
if err := s.Updates(id, map[string]any{
|
||||
"status": status,
|
||||
@@ -227,7 +227,7 @@ func (s *userService) ResetPassword(userID int64, operator *dto.AuthPrincipal) (
|
||||
|
||||
func (s *userService) ChangeOwnPassword(password string, operator *dto.AuthPrincipal) error {
|
||||
if operator == nil || operator.UserID <= 0 {
|
||||
return errorsx.Unauthorized("未登录或登录已过期")
|
||||
return errorsx.UnauthorizedI18n("error.auth.expired")
|
||||
}
|
||||
return s.changePassword(operator.UserID, password, operator)
|
||||
}
|
||||
@@ -235,7 +235,7 @@ func (s *userService) ChangeOwnPassword(password string, operator *dto.AuthPrinc
|
||||
func (s *userService) AssignRoles(userID int64, roleIDs []int64, operator *dto.AuthPrincipal) error {
|
||||
user := s.Get(userID)
|
||||
if user == nil || user.DeletedAt != nil {
|
||||
return errorsx.InvalidParam("用户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0255")
|
||||
}
|
||||
if err := s.replaceUserRoles(userID, roleIDs, operator); err != nil {
|
||||
return err
|
||||
@@ -256,10 +256,10 @@ func (s *userService) replaceUserRolesDB(db *gorm.DB, userID int64, roleIDs []in
|
||||
for _, roleID := range roleIDs {
|
||||
role := RoleService.Get(roleID)
|
||||
if role == nil {
|
||||
return errorsx.InvalidParam("角色不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0305")
|
||||
}
|
||||
if role.Status != enums.StatusOk {
|
||||
return errorsx.InvalidParam("禁用角色不允许分配")
|
||||
return errorsx.InvalidParamI18n("error.e0291")
|
||||
}
|
||||
relation := &models.UserRole{
|
||||
UserID: userID,
|
||||
@@ -276,10 +276,10 @@ func (s *userService) replaceUserRolesDB(db *gorm.DB, userID int64, roleIDs []in
|
||||
func (s *userService) changePassword(userID int64, password string, operator *dto.AuthPrincipal) error {
|
||||
user := s.Get(userID)
|
||||
if user == nil || user.DeletedAt != nil {
|
||||
return errorsx.InvalidParam("用户不存在")
|
||||
return errorsx.InvalidParamI18n("error.e0255")
|
||||
}
|
||||
if strings.TrimSpace(password) == "" {
|
||||
return errorsx.InvalidParam("新密码不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0220")
|
||||
}
|
||||
|
||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"agent-desk/internal/pkg/dto/response"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/pkg/errorsx"
|
||||
"agent-desk/internal/pkg/i18nx"
|
||||
"agent-desk/internal/pkg/openidentity"
|
||||
"agent-desk/internal/pkg/utils"
|
||||
"encoding/json"
|
||||
@@ -44,7 +45,7 @@ func newWsService() *wsService {
|
||||
func (s *wsService) HandleDashboardWS(ctx *gin.Context) {
|
||||
principal := AuthService.GetAuthPrincipal(ctx)
|
||||
if principal == nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, web.JsonError(errorsx.Unauthorized("未登录或登录已过期")))
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, web.JsonErrorCode(errorsx.CodeAuthUnauthorized, i18nx.T(ctx, "error.auth.expired")))
|
||||
return
|
||||
}
|
||||
if err := s.upgradeConnection(ctx, principal, nil, realtimeRoleAdmin); err != nil {
|
||||
@@ -57,7 +58,7 @@ func (s *wsService) HandleDashboardWS(ctx *gin.Context) {
|
||||
func (s *wsService) HandleDashboardNotificationWS(ctx *gin.Context) {
|
||||
principal := AuthService.GetAuthPrincipal(ctx)
|
||||
if principal == nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, web.JsonError(errorsx.Unauthorized("未登录或登录已过期")))
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, web.JsonErrorCode(errorsx.CodeAuthUnauthorized, i18nx.T(ctx, "error.auth.expired")))
|
||||
return
|
||||
}
|
||||
if err := s.upgradeConnection(ctx, principal, nil, realtimeRoleNotification); err != nil {
|
||||
@@ -70,7 +71,7 @@ func (s *wsService) HandleDashboardNotificationWS(ctx *gin.Context) {
|
||||
func (s *wsService) HandleOpenWS(ctx *gin.Context) {
|
||||
channel := ChannelService.GetEnabledChannel(ctx)
|
||||
if channel == nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, web.JsonErrorMsg("接入渠道不存在或已停用"))
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, web.JsonErrorCode(errorsx.CodeInvalidParam, i18nx.T(ctx, "error.e0209")))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ func (s *wxWorkKFInboundService) SyncCallbackMessages(message kf.CallbackMessage
|
||||
func (s *wxWorkKFInboundService) consumeSyncMessage(item syncmsg.Message) error {
|
||||
msgID := strings.TrimSpace(item.MsgID)
|
||||
if msgID == "" {
|
||||
return errorsx.InvalidParam("企业微信消息ID不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0101")
|
||||
}
|
||||
if WxWorkKFMessageRefService.Take("wx_msg_id = ?", msgID) != nil {
|
||||
return nil
|
||||
@@ -369,7 +369,7 @@ func (s *wxWorkKFInboundService) recordOrphanEvent(item syncmsg.Message, content
|
||||
func (s *wxWorkKFInboundService) ensureConversation(base syncmsg.BaseMessage, profile map[string]any) (*models.Conversation, error) {
|
||||
externalID := strings.TrimSpace(base.ExternalUserID)
|
||||
if externalID == "" {
|
||||
return nil, errorsx.InvalidParam("企业微信客户ID不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0096")
|
||||
}
|
||||
channel, err := s.getChannelByOpenKfID(base.OpenKFID)
|
||||
if err != nil {
|
||||
@@ -474,7 +474,7 @@ func (s *wxWorkKFInboundService) createMessageRef(conversationID, messageID int6
|
||||
func (s *wxWorkKFInboundService) saveNextCursor(openKfID, nextCursor string) error {
|
||||
openKfID = strings.TrimSpace(openKfID)
|
||||
if openKfID == "" {
|
||||
return errorsx.InvalidParam("openKfID不能为空")
|
||||
return errorsx.InvalidParamI18n("error.e0068")
|
||||
}
|
||||
now := time.Now()
|
||||
state := WxWorkKFSyncStateService.Take("open_kf_id = ?", openKfID)
|
||||
@@ -517,7 +517,7 @@ func (s *wxWorkKFInboundService) appendConversationEvent(conversationID int64, c
|
||||
func (s *wxWorkKFInboundService) buildInboundAssetPayload(conversationID int64, mediaID string) (string, string, error) {
|
||||
mediaID = strings.TrimSpace(mediaID)
|
||||
if mediaID == "" {
|
||||
return "", "", errorsx.InvalidParam("企业微信媒体ID不能为空")
|
||||
return "", "", errorsx.InvalidParamI18n("error.e0095")
|
||||
}
|
||||
materialCli := wxwork.GetWorkCli().GetMaterial()
|
||||
data, err := materialCli.GetTempFile(mediaID)
|
||||
@@ -542,18 +542,18 @@ func (s *wxWorkKFInboundService) buildInboundAssetPayload(conversationID int64,
|
||||
func (s *wxWorkKFInboundService) getChannelByOpenKfID(openKfID string) (*models.Channel, error) {
|
||||
openKfID = strings.TrimSpace(openKfID)
|
||||
if openKfID == "" {
|
||||
return nil, errorsx.InvalidParam("企业微信 openKfID 不能为空")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0094")
|
||||
}
|
||||
channel := ChannelService.GetEnabledWxWorkKFChannelByOpenKfID(openKfID)
|
||||
if channel == nil {
|
||||
return nil, errorsx.InvalidParam("未找到匹配的企业微信接入渠道")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0231")
|
||||
}
|
||||
if channel.AIAgentID <= 0 {
|
||||
return nil, errorsx.InvalidParam("企业微信接入渠道未绑定AI Agent")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0098")
|
||||
}
|
||||
agent := AIAgentService.Get(channel.AIAgentID)
|
||||
if agent == nil || agent.Status != enums.StatusOk {
|
||||
return nil, errorsx.InvalidParam("企业微信接入渠道绑定的AI Agent不存在或已禁用")
|
||||
return nil, errorsx.InvalidParamI18n("error.e0099")
|
||||
}
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"agent-desk/internal/models"
|
||||
"agent-desk/internal/pkg/enums"
|
||||
"agent-desk/internal/pkg/i18nx"
|
||||
"agent-desk/internal/pkg/utils"
|
||||
"agent-desk/internal/repositories"
|
||||
"agent-desk/internal/wxwork"
|
||||
@@ -233,7 +234,7 @@ func (s *wxWorkKFOutboundService) sendOutboundChunk(mapping *models.WxWorkKFConv
|
||||
case enums.IMMessageTypeImage:
|
||||
return s.sendImageMessage(mapping, message, chunk, chunkIndex)
|
||||
default:
|
||||
return "", fmt.Errorf("不支持的企业微信下行消息类型: %s", chunk.MessageType)
|
||||
return "", i18nx.Errorf("error.wxwork.unsupportedOutboundMessageType", chunk.MessageType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +266,7 @@ func (s *wxWorkKFOutboundService) sendTextMessage(mapping *models.WxWorkKFConver
|
||||
return "", err
|
||||
}
|
||||
if strings.TrimSpace(resp.MsgID) == "" {
|
||||
return "", fmt.Errorf("企业微信返回的消息ID为空")
|
||||
return "", i18nx.Errorf("error.e0114")
|
||||
}
|
||||
slog.Info("wxwork text message accepted",
|
||||
"conversation_id", message.ConversationID,
|
||||
@@ -281,12 +282,12 @@ func (s *wxWorkKFOutboundService) sendTextMessage(mapping *models.WxWorkKFConver
|
||||
|
||||
func (s *wxWorkKFOutboundService) sendImageMessage(mapping *models.WxWorkKFConversation, message *models.Message, chunk wxWorkKFOutboundChunk, chunkIndex int) (string, error) {
|
||||
if strings.TrimSpace(chunk.AssetID) == "" {
|
||||
return "", fmt.Errorf("图片消息缺少 assetId")
|
||||
return "", i18nx.Errorf("error.e0145")
|
||||
}
|
||||
|
||||
asset := AssetService.GetByAssetID(chunk.AssetID)
|
||||
if asset == nil {
|
||||
return "", fmt.Errorf("图片资源不存在")
|
||||
return "", i18nx.Errorf("error.e0146")
|
||||
}
|
||||
fileReader, err := AssetService.OpenReader(asset)
|
||||
if err != nil {
|
||||
@@ -315,7 +316,7 @@ func (s *wxWorkKFOutboundService) sendImageMessage(mapping *models.WxWorkKFConve
|
||||
return "", err
|
||||
}
|
||||
if strings.TrimSpace(uploadResp.MediaID) == "" {
|
||||
return "", fmt.Errorf("企业微信返回的图片 media_id 为空")
|
||||
return "", i18nx.Errorf("error.e0113")
|
||||
}
|
||||
|
||||
kfCli, err := wxwork.GetWorkCli().GetKF()
|
||||
@@ -337,7 +338,7 @@ func (s *wxWorkKFOutboundService) sendImageMessage(mapping *models.WxWorkKFConve
|
||||
return "", err
|
||||
}
|
||||
if strings.TrimSpace(resp.MsgID) == "" {
|
||||
return "", fmt.Errorf("企业微信返回的消息ID为空")
|
||||
return "", i18nx.Errorf("error.e0114")
|
||||
}
|
||||
slog.Info("wxwork image message accepted",
|
||||
"conversation_id", message.ConversationID,
|
||||
@@ -421,19 +422,19 @@ func (s *wxWorkKFOutboundService) parseOutboxPayload(raw string) (*wxWorkKFOutbo
|
||||
|
||||
func (s *wxWorkKFOutboundService) buildOutboundChunks(message *models.Message) ([]wxWorkKFOutboundChunk, error) {
|
||||
if message == nil {
|
||||
return nil, fmt.Errorf("平台消息不存在")
|
||||
return nil, i18nx.Errorf("error.e0186")
|
||||
}
|
||||
switch message.MessageType {
|
||||
case enums.IMMessageTypeText:
|
||||
content := strings.TrimSpace(message.Content)
|
||||
if content == "" {
|
||||
return nil, fmt.Errorf("文本消息内容为空")
|
||||
return nil, i18nx.Errorf("error.e0217")
|
||||
}
|
||||
return []wxWorkKFOutboundChunk{{MessageType: enums.IMMessageTypeText, Content: content}}, nil
|
||||
case enums.IMMessageTypeHTML:
|
||||
return s.buildHTMLChunks(message.Content)
|
||||
default:
|
||||
return nil, fmt.Errorf("当前暂不支持企业微信下行消息类型: %s", message.MessageType)
|
||||
return nil, i18nx.Errorf("error.wxwork.currentUnsupportedOutboundMessageType", message.MessageType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,7 +468,7 @@ func (s *wxWorkKFOutboundService) buildHTMLChunks(content string) ([]wxWorkKFOut
|
||||
}
|
||||
}
|
||||
if len(chunks) == 0 {
|
||||
return nil, fmt.Errorf("HTML 消息内容为空")
|
||||
return nil, i18nx.Errorf("error.e0029")
|
||||
}
|
||||
return chunks, nil
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func (s *wxWorkLoginService) BuildWxWorkQRCodeLoginURL(next string) (string, err
|
||||
func (s *wxWorkLoginService) LoginByWxWork(code, state string, authCfg config.AuthConfig, clientIP, userAgent string) (string, string, error) {
|
||||
next, err := wxwork.ParseState(state)
|
||||
if err != nil {
|
||||
return "", "", errorsx.Unauthorized("企业微信登录状态无效或已过期")
|
||||
return "", "", errorsx.UnauthorizedI18n("error.e0111")
|
||||
}
|
||||
profile, err := wxwork.GetUserDetail(code)
|
||||
if err != nil {
|
||||
@@ -96,7 +96,7 @@ func (s *wxWorkLoginService) loginWithWxWorkProfile(profile *wxwork.LoginUser, a
|
||||
}
|
||||
|
||||
if user.Status != enums.StatusOk {
|
||||
return errorsx.Unauthorized("当前系统账号已被禁用")
|
||||
return errorsx.UnauthorizedI18n("error.e0200")
|
||||
}
|
||||
|
||||
if err = repositories.UserRepository.Updates(ctx.Tx, user.ID, map[string]any{
|
||||
|
||||
Reference in New Issue
Block a user