简历优化和岗位简历
This commit is contained in:
+253
@@ -1,5 +1,7 @@
|
||||
import request from '@/utils/request'
|
||||
import aiService from '@/utils/aiRequest'
|
||||
import type { ApiResult } from '@/api/auth'
|
||||
import type { AiResult } from '@/utils/aiRequest'
|
||||
|
||||
// ==================== 匹配详情 ====================
|
||||
|
||||
@@ -363,3 +365,254 @@ export function fetchJobDetail(jobId: string) {
|
||||
params: { jobId },
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== 技能差距分析(AI 接口) ====================
|
||||
|
||||
/** 技能差距分析 — 岗位信息 */
|
||||
export interface SkillGapJob {
|
||||
/** 岗位 ID */
|
||||
jobId: string
|
||||
/** 岗位标题 */
|
||||
title: string
|
||||
/** 技能标签列表 */
|
||||
skillTags: string[]
|
||||
}
|
||||
|
||||
/** 技能差距分析 — 简历信息 */
|
||||
export interface SkillGapResume {
|
||||
/** 简历 ID */
|
||||
resumeId: string
|
||||
/** 简历名称 */
|
||||
resumeName: string
|
||||
/** 目标岗位 */
|
||||
targetPosition: string
|
||||
}
|
||||
|
||||
/** 技能差距分析返回数据 */
|
||||
export interface SkillGapData {
|
||||
/** 匹配度分数 */
|
||||
score: number
|
||||
/** 岗位信息 */
|
||||
job: SkillGapJob
|
||||
/** 简历信息 */
|
||||
resume: SkillGapResume
|
||||
/** 缺少的技能列表 */
|
||||
missingSkills: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 技能差距分析(AI 接口)
|
||||
* POST /job/skill-gap
|
||||
* @param jobId 岗位 ID(字符串,避免大整数精度丢失)
|
||||
*/
|
||||
export function fetchSkillGap(jobId: string) {
|
||||
// jobId 作为字符串发送,避免 JS 大整数精度丢失
|
||||
return aiService.post<any, { data: AiResult<SkillGapData> }>('/job/skill-gap', { jobId }, {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
// ==================== 定制简历(AI 接口) ====================
|
||||
|
||||
/** 定制简历接口返回的简历数据 */
|
||||
export interface CustomizeResumeData {
|
||||
/** 简历基本信息 */
|
||||
resume: {
|
||||
avatarUrl?: string
|
||||
name?: string
|
||||
email?: string
|
||||
mobileNumber?: string
|
||||
city?: string
|
||||
wechatNumber?: string
|
||||
portfolioUrl?: string
|
||||
skills?: string[]
|
||||
certificates?: string[]
|
||||
summary?: string
|
||||
}
|
||||
/** 教育经历 */
|
||||
education?: Array<{
|
||||
id?: string
|
||||
school?: string
|
||||
major?: string
|
||||
degree?: string
|
||||
studyType?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
description?: Array<{ id?: string; text?: string }>
|
||||
}>
|
||||
/** 工作经历 */
|
||||
work?: Array<{
|
||||
id?: string
|
||||
companyName?: string
|
||||
position?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
description?: Array<{ id?: string; text?: string }>
|
||||
}>
|
||||
/** 实习经历 */
|
||||
internship?: Array<{
|
||||
id?: string
|
||||
companyName?: string
|
||||
position?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
description?: Array<{ id?: string; text?: string }>
|
||||
}>
|
||||
/** 项目经历 */
|
||||
project?: Array<{
|
||||
id?: string
|
||||
projectName?: string
|
||||
companyName?: string
|
||||
role?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
description?: Array<{ id?: string; text?: string }>
|
||||
}>
|
||||
/** 竞赛经历 */
|
||||
competition?: Array<{
|
||||
id?: string
|
||||
competitionName?: string
|
||||
award?: string
|
||||
awardDate?: string
|
||||
description?: Array<{ id?: string; text?: string }>
|
||||
}>
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询定制简历结果(AI 接口)
|
||||
* GET /job/customize-resume
|
||||
*/
|
||||
export function fetchCustomizeResume() {
|
||||
return aiService.get<any, { data: AiResult<CustomizeResumeData | null> }>('/job/customize-resume', {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
/** 生成定制简历请求参数 */
|
||||
export interface GenerateCustomizeResumeParams {
|
||||
/** 岗位 ID(字符串,避免大整数精度丢失) */
|
||||
jobId: string
|
||||
/** 简历 ID(字符串,避免大整数精度丢失) */
|
||||
resumeId: string
|
||||
/** 要优化的模块列表 */
|
||||
optimizeModules: string[]
|
||||
/** 要新增的技能关键词 */
|
||||
addSkills?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成定制简历(AI 接口)
|
||||
* POST /job/customize-resume
|
||||
* @param params 生成参数
|
||||
*/
|
||||
export function generateCustomizeResume(params: GenerateCustomizeResumeParams) {
|
||||
return aiService.post<any, { data: AiResult<{ success: boolean }> }>('/job/customize-resume', params, {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
|
||||
// ==================== AI对话编辑简历(AI 接口) ====================
|
||||
|
||||
/** AI对话编辑简历的聊天记录项 */
|
||||
export interface AiEditChatMessage {
|
||||
/** 角色:user-用户 assistant-AI助手 */
|
||||
role: 'user' | 'assistant'
|
||||
/** 消息内容 */
|
||||
content: string
|
||||
}
|
||||
|
||||
/** AI对话编辑简历请求参数 */
|
||||
export interface AiEditResumeParams {
|
||||
/** 岗位 ID(字符串,避免大整数精度丢失) */
|
||||
jobId: string
|
||||
/** 用户输入的指令 */
|
||||
instruction: string
|
||||
/** 对话历史记录 */
|
||||
chatHistory?: AiEditChatMessage[]
|
||||
}
|
||||
|
||||
/** AI对话编辑简历返回数据 */
|
||||
export interface AiEditResumeResponse {
|
||||
/** 返回类型:message-对话消息 updated-简历已更新 */
|
||||
type: 'message' | 'updated'
|
||||
/** AI回复的消息内容 */
|
||||
message: string
|
||||
}
|
||||
|
||||
/**
|
||||
* AI对话编辑简历(AI 接口)
|
||||
* POST /job/customize-resume/ai-edit
|
||||
* @param params 请求参数
|
||||
*/
|
||||
export function aiEditResume(params: AiEditResumeParams) {
|
||||
// 强制确保 jobId 为字符串,避免大整数精度丢失
|
||||
const safeParams = { ...params, jobId: String(params.jobId) }
|
||||
return aiService.post<any, { data: AiResult<AiEditResumeResponse> }>('/job/customize-resume/ai-edit', safeParams, {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销AI对话编辑简历的修改(AI 接口)
|
||||
* POST /job/customize-resume/rollback
|
||||
*/
|
||||
export function rollbackCustomizeResume() {
|
||||
return aiService.post<any, { data: AiResult<null> }>('/job/customize-resume/rollback', null, {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改定制简历(AI 接口)
|
||||
* PUT /job/customize-resume
|
||||
* 输入框失焦或选择器选中后自动调用
|
||||
* @param data 定制简历完整数据
|
||||
*/
|
||||
export function updateCustomizeResume(data: CustomizeResumeData) {
|
||||
return aiService.put<any, { data: AiResult<null> }>('/job/customize-resume', data, {
|
||||
transformResponse: [(raw: string) => {
|
||||
try {
|
||||
const processed = raw.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return raw
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
+2
-2
@@ -27,9 +27,9 @@ export interface MenuItemRaw {
|
||||
export async function fetchUserRoutes(): Promise<MenuItemRaw[]> {
|
||||
// TODO: 替换为真实接口 → return axios.get('/api/user/menus').then(res => res.data)
|
||||
return [
|
||||
{ path: '/agent', name: 'Agent', component: 'Agent', meta: { label: 'AI助手', icon: 'nav-agent-icon', badge: 'NEW' } },
|
||||
{ path: '/profile', name: 'Profile', component: 'Profile', meta: { label: '个人资料', icon: 'nav-profile-icon' } },
|
||||
{ path: '/resume', name: 'Resume', component: 'Resume', meta: { label: '简历', icon: 'nav-resume-icon' } },
|
||||
{ path: '/profile', name: 'Profile', component: 'Profile', meta: { label: '个人资料', icon: 'nav-profile-icon' } },
|
||||
{ path: '/agent', name: 'Agent', component: 'Agent', meta: { label: 'AI助手', icon: 'nav-agent-icon', badge: 'NEW' } },
|
||||
{ path: '/settings', name: 'Settings', component: 'Settings', meta: { label: '设置', icon: 'nav-setting-icon', position: 'footer' } },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import request from '@/utils/request'
|
||||
import aiService from '@/utils/aiRequest'
|
||||
import type { ApiResult } from '@/api/auth'
|
||||
import type { AiResult } from '@/utils/aiRequest'
|
||||
|
||||
// ==================== 简历列表相关 ====================
|
||||
|
||||
@@ -370,3 +372,185 @@ export interface SaveResumeCompetitionItem {
|
||||
export function saveResumeCompetition(resumeId: string, data: SaveResumeCompetitionItem[]) {
|
||||
return request.post<any, ApiResult>('/resume/competition', { resumeId, items: data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除简历
|
||||
* POST /resume/delete
|
||||
*/
|
||||
export function deleteResume(resumeId: string) {
|
||||
return request.post<any, ApiResult>('/resume/delete', null, {
|
||||
params: { resumeId },
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== 简历诊断相关 ====================
|
||||
|
||||
/** 诊断报告数据 */
|
||||
export interface DiagnosisReport {
|
||||
/** 报告 ID */
|
||||
id?: string
|
||||
/** 简历 ID */
|
||||
resumeId?: string
|
||||
/** 评级 A/B/C/D */
|
||||
grade?: string
|
||||
/** AI 整体评价 */
|
||||
summary?: string
|
||||
/** 紧急修复总数 */
|
||||
urgentTotal?: number
|
||||
/** 重点优化总数 */
|
||||
importantTotal?: number
|
||||
/** 表达提升总数 */
|
||||
expressionTotal?: number
|
||||
/** 创建时间 */
|
||||
createTime?: string
|
||||
}
|
||||
|
||||
/** 诊断问题子类型计数 */
|
||||
export interface IssueSubCounts {
|
||||
[key: string]: number
|
||||
}
|
||||
|
||||
/** 诊断问题项 */
|
||||
export interface DiagnosisIssue {
|
||||
/** 问题 ID */
|
||||
id?: string
|
||||
/** 模块类型: summary/education/work/internship/project/competition */
|
||||
moduleType?: string
|
||||
/** 模块记录 ID,summary 时为 resume_id */
|
||||
moduleRecordId?: string
|
||||
/** 诊断发现 */
|
||||
finding?: string
|
||||
/** 为什么重要 */
|
||||
importance?: string
|
||||
/** 改进建议 */
|
||||
suggestion?: string
|
||||
/** 紧急修复子类型计数 */
|
||||
urgentIssues?: IssueSubCounts
|
||||
/** 重点优化子类型计数 */
|
||||
importantIssues?: IssueSubCounts
|
||||
/** 表达提升子类型计数 */
|
||||
expressionIssues?: IssueSubCounts
|
||||
/** AI 改写后的内容 */
|
||||
optimizedContent?: any
|
||||
/** 0=待处理 1=已处理 */
|
||||
status?: number
|
||||
/** 0=未评价 1=符合 2=不符合 */
|
||||
userFeedback?: number
|
||||
}
|
||||
|
||||
/** 诊断接口返回的 data 结构 */
|
||||
export interface DiagnosisData {
|
||||
/** 诊断报告 */
|
||||
report: DiagnosisReport
|
||||
/** 诊断问题列表 */
|
||||
issues: DiagnosisIssue[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询简历诊断报告(AI 接口)
|
||||
* GET /resume/diagnose/{resume_id}
|
||||
*/
|
||||
export function fetchResumeDiagnosis(resumeId: string) {
|
||||
return aiService.get<AiResult<DiagnosisData | null>>(`/resume/diagnose/${resumeId}`, {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
// 大整数安全解析
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
/** 触发诊断返回的 data 结构 */
|
||||
export interface TriggerDiagnosisData {
|
||||
/** 诊断报告 ID */
|
||||
reportId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发简历诊断(AI 接口)
|
||||
* POST /resume/diagnose
|
||||
*/
|
||||
export function triggerResumeDiagnosis(resumeId: string) {
|
||||
return aiService.post<AiResult<TriggerDiagnosisData>>('/resume/diagnose', { resumeId }, {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
// ==================== 问题润色(AI 优化用户改写内容) ====================
|
||||
|
||||
/** 润色接口返回的 data 结构 */
|
||||
export interface PolishIssueData {
|
||||
/** AI 润色后的文本段落数组 */
|
||||
content: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 对诊断问题进行 AI 润色(AI 接口)
|
||||
* POST /resume/diagnose/issue/{issue_id}/polish
|
||||
* @param issueId 问题 ID
|
||||
* @param content 用户编辑后的文本段落数组
|
||||
*/
|
||||
export function polishDiagnosisIssue(issueId: string, content: string[]) {
|
||||
return aiService.post<AiResult<PolishIssueData>>(`/resume/diagnose/issue/${issueId}/polish`, { content }, {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
// ==================== 问题用户评价 ====================
|
||||
|
||||
/**
|
||||
* 提交诊断问题的用户评价(AI 接口)
|
||||
* PUT /resume/diagnose/issue/{issue_id}/feedback
|
||||
* @param issueId 问题 ID
|
||||
* @param userFeedback 1=符合(有用) 2=不符合(无用)
|
||||
*/
|
||||
export function feedbackDiagnosisIssue(issueId: string, userFeedback: number) {
|
||||
return aiService.put<AiResult<null>>(`/resume/diagnose/issue/${issueId}/feedback`, { userFeedback }, {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
// ==================== 标记问题已处理 ====================
|
||||
|
||||
/**
|
||||
* 标记诊断问题为已处理(AI 接口)
|
||||
* PUT /resume/diagnose/issue/{issue_id}/resolve
|
||||
* @param issueId 问题 ID
|
||||
*/
|
||||
export function resolveDiagnosisIssue(issueId: string) {
|
||||
return aiService.put<AiResult<null>>(`/resume/diagnose/issue/${issueId}/resolve`, null, {
|
||||
transformResponse: [(data: string) => {
|
||||
try {
|
||||
const processed = data.replace(/:\s*(\d{16,})/g, ':"$1"')
|
||||
return JSON.parse(processed)
|
||||
} catch {
|
||||
return data
|
||||
}
|
||||
}],
|
||||
}).then(res => res.data)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user