Files
offerpai_web/src/api/jobs.ts
T
2026-07-13 23:01:49 +08:00

695 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import request from '@/utils/request'
import aiService from '@/utils/aiRequest'
import type { ApiResult } from '@/api/auth'
import type { AiResult } from '@/utils/aiRequest'
// ==================== 岗位总数 ====================
/**
* 获取全网实时岗位总数
* GET /job/count
*/
export function fetchJobCount() {
return request.get<any, ApiResult<number>>('/job/count')
}
// ==================== 匹配详情 ====================
/** 匹配度详情 */
export interface MatchDetail {
/** 行业匹配分 */
educationScore: number
/** 技能匹配分 */
skillScore: number
/** 经验匹配分 */
experienceScore: number
}
// ==================== 岗位列表项 ====================
/** 岗位列表项(接口返回结构) */
export interface JobListItem {
/** 岗位 ID */
id: string
/** 岗位标题 */
title: string
/** 公司全称 */
companyName: string
/** 公司简称 */
companyShortName: string
/** 公司类型(如"上市企业" */
companyType: string
/** 公司标签列表(如 ["新能源汽车", "乘用车制造", "10000人以上"] */
companyTags: string[]
/** 地区名称 */
regionName: string
/** 岗位分类名称 */
categoryName: string
/** 标签列表 */
tags: string[]
/** 原始招聘链接 */
sourceUrl: string
/** 是否已收藏 */
isFavorite: boolean
/** 岗位状态:0=有效 1=已下架 2=已过期 */
status: number
/** 综合匹配分(0-100 */
matchScore: number
/** 匹配度详情 */
matchDetail: MatchDetail
/** 招聘分类:0=社招 1=校招 2=实习 */
recruitCategory?: number
/** 学历要求 0=不限 1=大专 2=本科 3=硕士 4=博士 */
education?: number
}
// ==================== 分页结构 ====================
/** 分页数据结构 */
export interface JobPageData {
/** 当前页码 */
pageNum: string
/** 每页条数 */
pageSize: string
/** 总记录数 */
total: string
/** 岗位列表 */
list: JobListItem[]
}
// ==================== 请求参数 ====================
/** 岗位列表请求参数 */
export interface JobListParams {
/** 当前页码,从1开始,默认1 */
pageNum?: number
/** 每页条数,默认15 */
pageSize?: number
/** 地区编码列表 */
regionCodes?: string[]
/** 岗位类型 ID 列表 */
categoryIds?: number[]
/** 行业 ID 列表 */
industryIds?: number[]
/** 指定岗位 ID 列表(用于收藏列表) */
jobIds?: number[]
/** 岗位状态过滤(0=有效 1=已下架 2=已过期,可多选,null或空=查所有) */
statusFilter?: number[]
/** 搜索关键词 */
keyword?: string
/** 招聘分类 0=校招 1=实习 2=社招 3=其他 */
recruitCategory?: number
/** 排除岗位ID列表(用于推荐时排除已推荐过的) */
excludeJobIds?: number[]
}
// ==================== 求职意向 ====================
/** 求职意向出参/入参 */
export interface JobIntention {
/** 期望岗位分类 ID 列表 */
categoryIds?: number[]
/** 期望地区编码列表 */
regionCodes?: string[]
/** 期望行业 ID 列表 */
industryIds?: number[]
/** 就业类型:0=校招,1=实习,2=社招 */
employmentType?: number | null
/** 招聘分类:0=社招,1=校招,2=实习,3=其他 */
recruitCategory?: number | null
/** AI 推荐的岗位分类 ID 列表 */
aiCategoryIds?: string[]
/** AI 推荐的行业 ID 列表 */
aiIndustryIds?: string[]
}
/**
* 查询求职意向
* GET /job-intention
*/
export function fetchJobIntention() {
return request.get<any, ApiResult<JobIntention>>('/job-intention')
}
/**
* 保存求职意向
* POST /job-intention
*/
export function saveJobIntention(data: JobIntention) {
return request.post<any, ApiResult<any>>('/job-intention', data)
}
// ==================== 接口方法 ====================
/**
* 获取岗位列表
* POST /job/list
* @param params 岗位列表查询参数
*/
export function fetchJobList(params: JobListParams = {}) {
return request.post<any, ApiResult<JobPageData>>('/job/list', {
pageNum: params.pageNum ?? 1,
pageSize: params.pageSize ?? 15,
...params,
})
}
// ==================== 收藏列表 ====================
/** 收藏列表请求参数 */
export interface FavoriteListParams {
/** 当前页码,从1开始,默认1 */
pageNum?: number
/** 每页条数,默认10 */
pageSize?: number
/** 是否只查询有效收藏(true=只查有效岗位,false=只查失效岗位,null=查所有) */
valid?: boolean | null
}
/**
* 获取收藏列表
* POST /job/favorite/list
*/
export function fetchFavoriteList(params: FavoriteListParams = {}) {
return request.post<any, ApiResult<JobPageData>>('/job/favorite/list', {
pageNum: params.pageNum ?? 1,
pageSize: params.pageSize ?? 10,
...params,
})
}
/**
* 收藏/取消收藏岗位
* POST /job/favorite?jobId=xxx
* @param jobId 岗位 ID
*/
export function toggleJobFavorite(jobId: string) {
return request.post<any, ApiResult<any>>('/job/favorite', null, {
params: { jobId },
})
}
/**
* 取消收藏岗位
* DELETE /job/favorite?jobId=xxx
* @param jobId 岗位 ID
*/
export function removeJobFavorite(jobId: string) {
return request.delete<any, ApiResult<any>>('/job/favorite', {
params: { jobId },
})
}
// ==================== 收藏统计 ====================
/** 收藏统计结果 */
export interface FavoriteCountData {
/** 收藏总数 */
totalCount: number
/** 有效收藏数(岗位status=0 */
validCount: number
/** 失效收藏数(岗位status!=0或已删除) */
invalidCount: number
}
/**
* 获取收藏统计
* GET /job/favorite/count
*/
export function fetchFavoriteCount() {
return request.get<any, ApiResult<FavoriteCountData>>('/job/favorite/count')
}
// ==================== 投递列表 ====================
/** 投递列表请求参数 */
export interface ApplyListParams {
/** 当前页码,从1开始,默认1 */
pageNum?: number
/** 每页条数,默认10 */
pageSize?: number
/** 投递状态筛选(0=已投递 1=面试中 2=有Offer 3=未通过 4=已结束) */
status?: number | null
/** 搜索关键词 */
keyword?: string
}
/**
* 获取投递列表
* POST /job/apply/list
*/
export function fetchApplyList(params: ApplyListParams = {}) {
return request.post<any, ApiResult<JobPageData>>('/job/apply/list', {
pageNum: params.pageNum ?? 1,
pageSize: params.pageSize ?? 10,
...(params.status !== null && params.status !== undefined ? { status: params.status } : {}),
...(params.keyword ? { keyword: params.keyword } : {}),
})
}
// ==================== 求职助手任务列表 ====================
/** 求职助手任务列表请求参数 */
export interface AgentTaskListParams {
/** 当前页码,从1开始,默认1 */
pageNum?: number
/** 每页条数,默认10 */
pageSize?: number
/** 搜索关键字 */
keyword?: string
/** tab类型 1=进行中(待投递) 2=已完成(已投递及之后状态),默认1 */
tab?: number
}
/**
* 获取求职助手任务列表
* POST /job/agent/task/list
*/
export function fetchAgentTaskList(params: AgentTaskListParams = {}) {
return request.post<any, ApiResult<JobPageData>>('/job/agent/task/list', {
pageNum: params.pageNum ?? 1,
pageSize: params.pageSize ?? 10,
tab: params.tab ?? 1,
...(params.keyword ? { keyword: params.keyword } : {}),
})
}
// ==================== 投递统计 ====================
/** 投递统计结果 */
export interface ApplyCountData {
/** 投递总数 */
totalCount: number
/** 已投递数(status=0 */
appliedCount: number
/** 面试中数(status=1 */
interviewingCount: number
/** 有Offer数(status=2 */
offerCount: number
/** 未通过数(status=3 */
rejectedCount: number
/** 已结束数(status=4 */
closedCount: number
}
/**
* 获取投递统计
* GET /job/apply/count
*/
export function fetchApplyCount() {
return request.get<any, ApiResult<ApplyCountData>>('/job/apply/count')
}
// ==================== 岗位详情 ====================
/** 匹配度详情(岗位详情用) */
export interface JobMatchScoreDto {
/** 行业得分(0-100 */
educationScore: number
/** 技能得分(0-100 */
skillScore: number
/** 经验得分(0-100 */
experienceScore: number
}
/** 岗位详情出参 */
export interface JobDetailData {
/** 岗位 ID */
jobId: string
/** 岗位标题 */
jobTitle: string
/** 薪资描述 */
salary: string
/** 工作类型 0=全职 1=兼职 */
employmentType: number
/** 招聘分类 0=社招 1=校招 2=实习 3=其他 */
recruitCategory: number
/** 学历要求 0=不限 1=大专 2=本科 3=硕士 4=博士 */
education: number
/** 最低工作年限 */
minExperience: number
/** 岗位职责 */
description: string
/** 任职要求 */
requirement: string
/** 加分项 */
bonus: string
/** 岗位标签 */
tags: string[]
/** 技能标签 */
skillTags: string[]
/** 来源链接 */
sourceUrl: string
/** 岗位类型名称 */
categoryName: string
/** 要求的行业经验名称 */
requiredIndustryName: string
/** 公司 ID */
companyId: string
/** 公司名称 */
companyName: string
/** 公司简称 */
companyShortName: string
/** 公司 Logo URL */
companyLogoUrl: string
/** 公司类型 */
companyType: string
/** 公司所属行业名称 */
companyIndustryName: string
/** 公司标签 */
companyTags: string[]
/** 公司简介 */
companySummary: string
/** 公司描述 */
companyDescription: string
/** 成立时间 */
companyFoundedYear: string
/** 公司地址 */
companyAddress: string
/** 公司规模 */
companyScale: string
/** 公司官网 */
companyWebsite: string
/** 融资状态 */
companyFinancingStage: string
/** 最新估值 */
companyLatestValuation: string
/** 公司新闻 */
companyNews: string[]
/** 地区名称 */
regionName: string
/** 匹配总分 */
matchScore: number
/** 匹配度详情 */
matchDetail: JobMatchScoreDto
/** 是否已收藏 */
isFavorite: boolean
}
/**
* 从推荐列表中移除岗位
* TODO: 接口待后端提供,当前仅做前端列表移除
* POST /job/remove?jobId=xxx
* @param jobId 岗位 ID
*/
export function removeJobFromList(jobId: string) {
// TODO: 接口待对接,暂时返回模拟成功结果
return Promise.resolve({ code: '0', msg: '', data: null, timestamp: '', uuid: '' } as ApiResult<any>)
// 对接时替换为:
// return request.post<any, ApiResult<any>>('/job/remove', null, { params: { jobId } })
}
/**
* 不感兴趣
* POST /job/dislike?jobId=xxx
* @param jobId 岗位 ID
* @param reason 不感兴趣原因 0-5
*/
export function dislikeJob(jobId: string, reason: number) {
return request.post<any, ApiResult<any>>('/job/dislike', { reason }, {
params: { jobId },
})
}
/**
* 获取岗位详情
* GET /job/detail?jobId=xxx
* @param jobId 岗位 ID
*/
export function fetchJobDetail(jobId: string) {
return request.get<any, ApiResult<JobDetailData>>('/job/detail', {
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 SkillGapItem {
/** 关键词 */
keyword: string
/** 差距标题 */
title: string
/** 差距描述 */
description: string
}
/** 技能差距分析返回数据 */
export interface SkillGapData {
/** 匹配度分数 */
score: number
/** 岗位信息 */
job: SkillGapJob
/** 简历信息 */
resume: SkillGapResume
/** 缺少的技能列表(兼容旧版) */
missingSkills?: string[]
/** 差距分析列表(新版) */
gaps?: SkillGapItem[]
}
/**
* 技能差距分析(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 {
/** 简历基本信息 */
id?: string
resumeId?: string
resumeName?: string
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
* @param jobId 岗位ID(必需)
*/
export function fetchCustomizeResume(jobId: string) {
return aiService.get<any, { data: AiResult<CustomizeResumeData | null> }>('/job/customize-resume', {
params: { job_id: 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)
}
/** 生成定制简历请求参数 */
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(jobId: string) {
return aiService.post<any, { data: AiResult<null> }>('/job/customize-resume/rollback', null, {
params: { job_id: 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 接口)
* PUT /job/customize-resume
* 输入框失焦或选择器选中后自动调用
* @param data 定制简历完整数据
*/
export function updateCustomizeResume(data: CustomizeResumeData,jobId: string) {
return aiService.put<any, { data: AiResult<null> }>('/job/customize-resume', data, {
params: { job_id: jobId },
transformResponse: [(raw: string) => {
try {
const processed = raw.replace(/:\s*(\d{16,})/g, ':"$1"')
return JSON.parse(processed)
} catch {
return raw
}
}],
}).then(res => res.data)
}