fix: 优化资源重复的错误提示
Some checks failed
continuous-integration/drone/push Build is failing

1. 后端 course_service.py:
   - 课程名重复时返回 existing_id 和 existing_name
   - 成长路径名重复时返回详细信息

2. 前端 edit-course.vue:
   - 处理409冲突错误,提供跳转到已存在课程的选项

3. 前端 errorHandler.ts:
   - 添加409错误的处理逻辑
   - 添加冲突错误工具函数

4. 前端 position-management.vue, user-management.vue:
   - 改进错误消息提取,显示更详细的错误信息
This commit is contained in:
yuliang_guo
2026-01-29 17:04:15 +08:00
parent 7998b32080
commit 696b48e97a
5 changed files with 117 additions and 11 deletions

View File

@@ -195,8 +195,12 @@ class CourseService(BaseService[Course]):
and_(Course.name == course_in.name, Course.is_deleted == False) and_(Course.name == course_in.name, Course.is_deleted == False)
) )
) )
if existing.scalar_one_or_none(): existing_course = existing.scalar_one_or_none()
raise ConflictError(f"课程名称 '{course_in.name}' 已存在") if existing_course:
raise ConflictError(
f"课程名称 '{course_in.name}' 已存在",
detail={"existing_id": existing_course.id, "existing_name": existing_course.name}
)
# 创建课程 # 创建课程
course_data = course_in.model_dump() course_data = course_in.model_dump()
@@ -260,8 +264,12 @@ class CourseService(BaseService[Course]):
) )
) )
) )
if existing.scalar_one_or_none(): existing_course = existing.scalar_one_or_none()
raise ConflictError(f"课程名称 '{course_in.name}' 已存在") if existing_course:
raise ConflictError(
f"课程名称 '{course_in.name}' 已存在",
detail={"existing_id": existing_course.id, "existing_name": existing_course.name}
)
# 记录状态变更 # 记录状态变更
old_status = course.status old_status = course.status
@@ -800,8 +808,12 @@ class GrowthPathService(BaseService[GrowthPath]):
and_(GrowthPath.name == path_in.name, GrowthPath.is_deleted == False) and_(GrowthPath.name == path_in.name, GrowthPath.is_deleted == False)
) )
) )
if existing.scalar_one_or_none(): existing_path = existing.scalar_one_or_none()
raise ConflictError(f"成长路径名称 '{path_in.name}' 已存在") if existing_path:
raise ConflictError(
f"成长路径名称 '{path_in.name}' 已存在",
detail={"existing_id": existing_path.id, "existing_name": existing_path.name, "type": "growth_path"}
)
# 验证课程是否存在 # 验证课程是否存在
if path_in.courses: if path_in.courses:

View File

@@ -365,6 +365,16 @@ export const handleHttpError = (error: any): ErrorInfo => {
type = ErrorType.API type = ErrorType.API
message = '请求的资源不存在' message = '请求的资源不存在'
break break
case 409:
type = ErrorType.VALIDATION
level = ErrorLevel.MEDIUM
// 处理冲突错误,通常是资源重复
if (typeof data?.detail === 'object') {
message = data.detail.message || '资源已存在'
} else {
message = data?.detail || data?.message || '资源已存在'
}
break
case 429: case 429:
type = ErrorType.API type = ErrorType.API
level = ErrorLevel.HIGH level = ErrorLevel.HIGH
@@ -487,4 +497,48 @@ export const handleWebSocketError = (error: Event, url: string): void => {
errorHandler.handleError(errorInfo) errorHandler.handleError(errorInfo)
} }
// 冲突错误详情接口
export interface ConflictDetail {
existing_id?: number
existing_name?: string
type?: string // 'course', 'growth_path', 'user' 等
}
// 检查是否为冲突错误
export const isConflictError = (error: any): boolean => {
const status = error?.status || error?.response?.status
return status === 409
}
// 获取冲突错误详情
export const getConflictDetail = (error: any): ConflictDetail | null => {
if (!isConflictError(error)) return null
// 尝试从不同位置获取详情
const detail = error?.detail?.detail
|| error?.response?.data?.detail?.detail
|| error?.response?.data?.detail
|| null
if (detail && typeof detail === 'object') {
return {
existing_id: detail.existing_id,
existing_name: detail.existing_name,
type: detail.type
}
}
return null
}
// 获取冲突错误消息
export const getConflictMessage = (error: any): string => {
const detail = error?.detail?.message
|| error?.response?.data?.detail?.message
|| error?.message
|| '资源已存在'
return typeof detail === 'string' ? detail : '资源已存在'
}
// 导出类型定义 // 导出类型定义

View File

@@ -640,9 +640,14 @@ const handleSubmit = async () => {
loadPositionList() loadPositionList()
} }
} }
} catch (error) { } catch (error: any) {
console.error('提交失败:', error) console.error('提交失败:', error)
ElMessage.error(isEdit.value ? '岗位编辑失败' : '岗位创建失败') // 提取详细错误信息
const message = error?.detail?.message
|| error?.response?.data?.detail?.message
|| error?.message
|| (isEdit.value ? '岗位编辑失败' : '岗位创建失败')
ElMessage.error(message)
} finally { } finally {
submitLoading.value = false submitLoading.value = false
} }

View File

@@ -1026,9 +1026,18 @@ const handleEdit = async () => {
ElMessage.success('用户信息修改成功') ElMessage.success('用户信息修改成功')
loadUserList() loadUserList()
} }
} catch (error) { } catch (error: any) {
console.error('编辑用户失败:', error) console.error('编辑用户失败:', error)
ElMessage.error('编辑用户失败') // 提取详细错误信息
let errorMsg = '编辑用户失败'
if (error?.detail?.message) {
errorMsg = error.detail.message
} else if (error?.response?.data?.detail?.message) {
errorMsg = error.response.data.detail.message
} else if (error.message) {
errorMsg = error.message
}
ElMessage.error(errorMsg)
} finally { } finally {
editLoading.value = false editLoading.value = false
} }

View File

@@ -988,7 +988,33 @@ const handleSave = async () => {
router.push('/manager/course-management') router.push('/manager/course-management')
} catch (error: any) { } catch (error: any) {
console.error('保存课程失败:', error) console.error('保存课程失败:', error)
ElMessage.error(error.message || '保存课程失败')
// 处理课程名重复的409冲突错误
const status = error?.status || error?.response?.status
const detail = error?.detail?.detail || error?.response?.data?.detail?.detail
if (status === 409 && detail?.existing_id) {
// 课程名重复,提供跳转选项
ElMessageBox.confirm(
`课程名称"${courseForm.name}"已存在,您可以点击下方按钮查看已有课程。`,
'课程名称重复',
{
confirmButtonText: '查看已有课程',
cancelButtonText: '修改名称',
type: 'warning'
}
).then(() => {
// 跳转到已存在的课程
router.push(`/manager/edit-course/${detail.existing_id}`)
}).catch(() => {
// 用户选择修改名称,聚焦到名称输入框
activeTab.value = 'basic'
})
} else {
// 其他错误
const message = error?.detail?.message || error?.message || '保存课程失败'
ElMessage.error(message)
}
} }
} }