定制简历浏览器记录缓存加IndexDB存储工具封装,修复同一个页面的不同协议切换问题
This commit is contained in:
@@ -23,7 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch, nextTick } from 'vue'
|
||||||
import { fetchAgreement } from '@/api/common'
|
import { fetchAgreement } from '@/api/common'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import markdownit from 'markdown-it'
|
import markdownit from 'markdown-it'
|
||||||
@@ -48,19 +48,32 @@ const agreementName = ref('')
|
|||||||
const contentHtml = ref('')
|
const contentHtml = ref('')
|
||||||
/** 加载状态 */
|
/** 加载状态 */
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
/** 是否已加载过(避免重复请求) */
|
/** 已加载过的协议缓存(key: code, value: { name, html }) */
|
||||||
const loaded = ref(false)
|
const cache = new Map<string, { name: string; html: string }>()
|
||||||
|
|
||||||
/** 加载协议内容 */
|
/** 加载协议内容 */
|
||||||
async function loadAgreement() {
|
async function loadAgreement() {
|
||||||
if (loaded.value) return
|
const code = props.code
|
||||||
|
if (!code) return
|
||||||
|
|
||||||
|
// 有缓存直接使用
|
||||||
|
const cached = cache.get(code)
|
||||||
|
if (cached) {
|
||||||
|
agreementName.value = cached.name
|
||||||
|
contentHtml.value = cached.html
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
contentHtml.value = ''
|
||||||
try {
|
try {
|
||||||
const res = await fetchAgreement(props.code)
|
const res = await fetchAgreement(code)
|
||||||
if (res.data?.content) {
|
if (res.data?.content) {
|
||||||
agreementName.value = res.data.agreementName || '协议内容'
|
const name = res.data.agreementName || '协议内容'
|
||||||
contentHtml.value = md.render(res.data.content)
|
const html = md.render(res.data.content)
|
||||||
loaded.value = true
|
agreementName.value = name
|
||||||
|
contentHtml.value = html
|
||||||
|
cache.set(code, { name, html })
|
||||||
} else {
|
} else {
|
||||||
contentHtml.value = '<p>暂无协议内容</p>'
|
contentHtml.value = '<p>暂无协议内容</p>'
|
||||||
}
|
}
|
||||||
@@ -71,20 +84,13 @@ async function loadAgreement() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 监听弹窗打开 — 触发加载 */
|
/** 监听弹窗打开 — 触发加载(使用 nextTick 确保 code 已更新) */
|
||||||
watch(() => props.modelValue, (val) => {
|
watch(() => props.modelValue, (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
loadAgreement()
|
nextTick(() => loadAgreement())
|
||||||
document.body.style.overflow = 'hidden'
|
document.body.style.overflow = 'hidden'
|
||||||
} else {
|
} else {
|
||||||
document.body.style.overflow = ''
|
document.body.style.overflow = ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 监听 code 变化 — 重置已加载状态 */
|
|
||||||
watch(() => props.code, () => {
|
|
||||||
loaded.value = false
|
|
||||||
contentHtml.value = ''
|
|
||||||
agreementName.value = ''
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -283,3 +283,267 @@ export function generateResumeWordFile(element: HTMLElement, fileName: string):
|
|||||||
const blob = new Blob([fullHtml], { type: 'application/msword' })
|
const blob = new Blob([fullHtml], { type: 'application/msword' })
|
||||||
return new File([blob], `${fileName}.doc`, { type: 'application/msword' })
|
return new File([blob], `${fileName}.doc`, { type: 'application/msword' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 定制简历本地缓存(IndexedDB) ====================
|
||||||
|
/**
|
||||||
|
* 【定制简历缓存机制说明】
|
||||||
|
*
|
||||||
|
* 由于服务器资源有限,投递流程中生成的岗位专属简历PDF不直接上传到OSS,
|
||||||
|
* 而是以 Blob 形式缓存到浏览器 IndexedDB 中(空间可达几百MB,远大于 localStorage 的 5~10MB)。
|
||||||
|
*
|
||||||
|
* 存储结构:
|
||||||
|
* - 索引记录数组:localStorage key = "CUSTOM_RESUME_CACHE_INDEX"
|
||||||
|
* 存储 CachedResumeRecord[] 数组的 JSON 字符串(几百字节,不占空间),每条记录包含:
|
||||||
|
* userId(用户ID)、jobId(岗位ID)、fileName(雪花ID文件名)、
|
||||||
|
* storageKey(简历文件在 IndexedDB 中的 key)、localDateTime(存储时间)
|
||||||
|
*
|
||||||
|
* - 简历文件数据:IndexedDB 数据库名 = "ResumeFileCache",对象仓库名 = "files"
|
||||||
|
* 以 storageKey 为主键,直接存储 PDF 的 Blob 对象(无需 Base64 编码,不膨胀体积)
|
||||||
|
*
|
||||||
|
* 使用方式:
|
||||||
|
* 1. 存储:调用 cacheResumePdfToLocal(element, userId, jobId) 生成PDF并缓存到IndexedDB
|
||||||
|
* 2. 查询:调用 getCachedResumeRecord(userId, jobId) 通过用户ID+岗位ID获取缓存记录
|
||||||
|
* 3. 取文件:调用 getCachedResumeFile(storageKey) 通过记录中的 storageKey 从IndexedDB获取 File 对象
|
||||||
|
* 4. 清理:调用 clearExpiredResumeCache(maxAgeDays) 清理过期缓存
|
||||||
|
*
|
||||||
|
* 优势:
|
||||||
|
* - IndexedDB 直接存 Blob,不需要 Base64 编码,文件体积不膨胀
|
||||||
|
* - 空间充足(几百MB~GB级),不会像 localStorage 那样容易满
|
||||||
|
* - 取出来就是 Blob,直接 new File([blob], name) 即可当文件使用
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 定制简历缓存索引记录 */
|
||||||
|
export interface CachedResumeRecord {
|
||||||
|
/** 系统用户ID */
|
||||||
|
userId: string
|
||||||
|
/** 对应岗位ID */
|
||||||
|
jobId: string
|
||||||
|
/** 缓存的简历文件名(雪花ID格式,不含扩展名) */
|
||||||
|
fileName: string
|
||||||
|
/** 简历文件在 IndexedDB 中的存储 key */
|
||||||
|
storageKey: string
|
||||||
|
/** 存储时间(ISO 8601 格式) */
|
||||||
|
localDateTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 缓存索引在 localStorage 中的 key(仅存索引JSON,体积极小) */
|
||||||
|
const CACHE_INDEX_KEY = 'CUSTOM_RESUME_CACHE_INDEX'
|
||||||
|
|
||||||
|
/** IndexedDB 数据库名 */
|
||||||
|
const IDB_NAME = 'ResumeFileCache'
|
||||||
|
|
||||||
|
/** IndexedDB 对象仓库名 */
|
||||||
|
const IDB_STORE = 'files'
|
||||||
|
|
||||||
|
/** IndexedDB 版本号 */
|
||||||
|
const IDB_VERSION = 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开 IndexedDB 数据库连接
|
||||||
|
* @returns IDBDatabase 实例
|
||||||
|
*/
|
||||||
|
function openResumeDB(): Promise<IDBDatabase> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const request = indexedDB.open(IDB_NAME, IDB_VERSION)
|
||||||
|
request.onupgradeneeded = () => {
|
||||||
|
const db = request.result
|
||||||
|
// 创建对象仓库(如果不存在)
|
||||||
|
if (!db.objectStoreNames.contains(IDB_STORE)) {
|
||||||
|
db.createObjectStore(IDB_STORE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request.onsuccess = () => resolve(request.result)
|
||||||
|
request.onerror = () => reject(request.error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向 IndexedDB 写入 Blob 数据
|
||||||
|
* @param key 存储键
|
||||||
|
* @param blob 文件 Blob
|
||||||
|
*/
|
||||||
|
async function idbPut(key: string, blob: Blob): Promise<void> {
|
||||||
|
const db = await openResumeDB()
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const tx = db.transaction(IDB_STORE, 'readwrite')
|
||||||
|
const store = tx.objectStore(IDB_STORE)
|
||||||
|
const request = store.put(blob, key)
|
||||||
|
request.onsuccess = () => resolve()
|
||||||
|
request.onerror = () => reject(request.error)
|
||||||
|
tx.oncomplete = () => db.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 IndexedDB 读取 Blob 数据
|
||||||
|
* @param key 存储键
|
||||||
|
* @returns Blob 对象,未找到返回 null
|
||||||
|
*/
|
||||||
|
async function idbGet(key: string): Promise<Blob | null> {
|
||||||
|
const db = await openResumeDB()
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const tx = db.transaction(IDB_STORE, 'readonly')
|
||||||
|
const store = tx.objectStore(IDB_STORE)
|
||||||
|
const request = store.get(key)
|
||||||
|
request.onsuccess = () => resolve(request.result || null)
|
||||||
|
request.onerror = () => reject(request.error)
|
||||||
|
tx.oncomplete = () => db.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 IndexedDB 删除指定 key 的数据
|
||||||
|
* @param key 存储键
|
||||||
|
*/
|
||||||
|
async function idbDelete(key: string): Promise<void> {
|
||||||
|
const db = await openResumeDB()
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const tx = db.transaction(IDB_STORE, 'readwrite')
|
||||||
|
const store = tx.objectStore(IDB_STORE)
|
||||||
|
const request = store.delete(key)
|
||||||
|
request.onsuccess = () => resolve()
|
||||||
|
request.onerror = () => reject(request.error)
|
||||||
|
tx.oncomplete = () => db.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成简单的雪花ID(基于时间戳 + 随机数,保证不重复)
|
||||||
|
* @returns 雪花ID字符串
|
||||||
|
*/
|
||||||
|
function generateSnowflakeId(): string {
|
||||||
|
const timestamp = Date.now().toString(36)
|
||||||
|
const random = Math.random().toString(36).substring(2, 10)
|
||||||
|
return `${timestamp}${random}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存索引记录数组(从 localStorage 读取)
|
||||||
|
* @returns 缓存记录数组
|
||||||
|
*/
|
||||||
|
function getCacheIndex(): CachedResumeRecord[] {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(CACHE_INDEX_KEY)
|
||||||
|
return raw ? JSON.parse(raw) : []
|
||||||
|
} catch {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存缓存索引记录数组(写入 localStorage)
|
||||||
|
* @param records 缓存记录数组
|
||||||
|
*/
|
||||||
|
function saveCacheIndex(records: CachedResumeRecord[]) {
|
||||||
|
localStorage.setItem(CACHE_INDEX_KEY, JSON.stringify(records))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将简历PDF生成并缓存到浏览器 IndexedDB
|
||||||
|
* @param element 简历DOM元素
|
||||||
|
* @param userId 当前用户ID
|
||||||
|
* @param jobId 对应岗位ID
|
||||||
|
* @returns 缓存记录(包含 storageKey 等信息),存储失败返回 null
|
||||||
|
*/
|
||||||
|
export async function cacheResumePdfToLocal(
|
||||||
|
element: HTMLElement,
|
||||||
|
userId: string,
|
||||||
|
jobId: string,
|
||||||
|
): Promise<CachedResumeRecord | null> {
|
||||||
|
try {
|
||||||
|
// 生成雪花ID作为文件名
|
||||||
|
const snowflakeId = generateSnowflakeId()
|
||||||
|
const storageKey = `resume_${snowflakeId}`
|
||||||
|
|
||||||
|
// 生成PDF Blob
|
||||||
|
const options = {
|
||||||
|
margin: [10, 10, 10, 10] as [number, number, number, number],
|
||||||
|
filename: `${snowflakeId}.pdf`,
|
||||||
|
image: { type: 'jpeg' as const, quality: 0.98 },
|
||||||
|
html2canvas: { scale: 2, useCORS: true, logging: false },
|
||||||
|
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' as const },
|
||||||
|
pagebreak: { mode: ['css', 'legacy'] },
|
||||||
|
}
|
||||||
|
const blob: Blob = await html2pdf().set(options).from(element).outputPdf('blob')
|
||||||
|
|
||||||
|
// 将 Blob 直接存入 IndexedDB(无需 Base64 编码)
|
||||||
|
await idbPut(storageKey, blob)
|
||||||
|
|
||||||
|
// 构建缓存记录
|
||||||
|
const record: CachedResumeRecord = {
|
||||||
|
userId,
|
||||||
|
jobId,
|
||||||
|
fileName: snowflakeId,
|
||||||
|
storageKey,
|
||||||
|
localDateTime: new Date().toISOString(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新索引(如果同一用户+岗位已有记录,先移除旧的)
|
||||||
|
const index = getCacheIndex()
|
||||||
|
const existIdx = index.findIndex(r => r.userId === userId && r.jobId === jobId)
|
||||||
|
if (existIdx !== -1) {
|
||||||
|
// 删除旧的 IndexedDB 文件缓存
|
||||||
|
await idbDelete(index[existIdx].storageKey)
|
||||||
|
index.splice(existIdx, 1)
|
||||||
|
}
|
||||||
|
index.push(record)
|
||||||
|
saveCacheIndex(index)
|
||||||
|
|
||||||
|
return record
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[resumeExport] 缓存简历到IndexedDB失败', e)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过用户ID和岗位ID查询缓存记录
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param jobId 岗位ID
|
||||||
|
* @returns 缓存记录,未找到返回 null
|
||||||
|
*/
|
||||||
|
export function getCachedResumeRecord(userId: string, jobId: string): CachedResumeRecord | null {
|
||||||
|
const index = getCacheIndex()
|
||||||
|
return index.find(r => r.userId === userId && r.jobId === jobId) || null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过 storageKey 从 IndexedDB 获取缓存的简历 File 对象
|
||||||
|
* @param storageKey 缓存记录中的 storageKey
|
||||||
|
* @param fileName 可选,指定输出文件名(不含扩展名),默认用 storageKey 中的雪花ID
|
||||||
|
* @returns PDF格式的 File 对象,未找到返回 null
|
||||||
|
*/
|
||||||
|
export async function getCachedResumeFile(storageKey: string, fileName?: string): Promise<File | null> {
|
||||||
|
try {
|
||||||
|
const blob = await idbGet(storageKey)
|
||||||
|
if (!blob) return null
|
||||||
|
|
||||||
|
const name = fileName || storageKey.replace('resume_', '')
|
||||||
|
return new File([blob], `${name}.pdf`, { type: 'application/pdf' })
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期的简历缓存
|
||||||
|
* @param maxAgeDays 最大保留天数,默认7天
|
||||||
|
*/
|
||||||
|
export async function clearExpiredResumeCache(maxAgeDays = 7) {
|
||||||
|
const index = getCacheIndex()
|
||||||
|
const now = Date.now()
|
||||||
|
const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1000
|
||||||
|
const validRecords: CachedResumeRecord[] = []
|
||||||
|
|
||||||
|
for (const record of index) {
|
||||||
|
const recordTime = new Date(record.localDateTime).getTime()
|
||||||
|
if (now - recordTime > maxAgeMs) {
|
||||||
|
// 过期,删除 IndexedDB 中的文件缓存
|
||||||
|
await idbDelete(record.storageKey)
|
||||||
|
} else {
|
||||||
|
validRecords.push(record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveCacheIndex(validRecords)
|
||||||
|
}
|
||||||
|
|||||||
+55
-4
@@ -152,7 +152,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 模式3:简历预览 -->
|
<!-- 模式3:简历预览 -->
|
||||||
<div v-else-if="rightPanelMode === 'resume'" class="agent-main__right-resume">
|
<div v-else-if="rightPanelMode === 'resume'" class="agent-main__right-resume">
|
||||||
<JobResumeTemplate :resume-data="applyResumeData" />
|
<JobResumeTemplate ref="applyResumeTemplateRef" :resume-data="applyResumeData" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 模式4:岗位预览 -->
|
<!-- 模式4:岗位预览 -->
|
||||||
<AgentJobPreviewPanel
|
<AgentJobPreviewPanel
|
||||||
@@ -179,7 +179,6 @@
|
|||||||
v-else-if="rightPanelMode === 'settings'"
|
v-else-if="rightPanelMode === 'settings'"
|
||||||
@close="handleCloseSettingsPanel"
|
@close="handleCloseSettingsPanel"
|
||||||
/>
|
/>
|
||||||
<!-- 模式7:AI助手设置 -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -209,6 +208,8 @@ import { fetchAgentTaskList } from '@/api/jobs'
|
|||||||
import type { JobListItem } from '@/api/jobs'
|
import type { JobListItem } from '@/api/jobs'
|
||||||
import { fetchResumeList } from '@/api/resume'
|
import { fetchResumeList } from '@/api/resume'
|
||||||
import { getIntentionCategoryNames, getIntentionRegionNames, getIntentionIndustryNames } from '@/utils/intention'
|
import { getIntentionCategoryNames, getIntentionRegionNames, getIntentionIndustryNames } from '@/utils/intention'
|
||||||
|
import { cacheResumePdfToLocal, getCachedResumeRecord, getCachedResumeFile } from '@/utils/resumeExport'
|
||||||
|
import store from '@/stores'
|
||||||
import AiThinkingIndicator from '@/components/tools/AiThinkingIndicator.vue'
|
import AiThinkingIndicator from '@/components/tools/AiThinkingIndicator.vue'
|
||||||
import JobGoalDialog from '@/components/JobGoalDialog.vue'
|
import JobGoalDialog from '@/components/JobGoalDialog.vue'
|
||||||
|
|
||||||
@@ -796,6 +797,9 @@ const generateProgress = ref(0)
|
|||||||
/** 当前投递流程的简历数据 */
|
/** 当前投递流程的简历数据 */
|
||||||
const applyResumeData = ref<ResumeTemplateData>({} as ResumeTemplateData)
|
const applyResumeData = ref<ResumeTemplateData>({} as ResumeTemplateData)
|
||||||
|
|
||||||
|
/** 右侧面板简历模板组件引用(用于生成PDF上传) */
|
||||||
|
const applyResumeTemplateRef = ref<InstanceType<typeof JobResumeTemplate> | null>(null)
|
||||||
|
|
||||||
/** 当前投递流程的简历名称 */
|
/** 当前投递流程的简历名称 */
|
||||||
const applyResumeName = ref('')
|
const applyResumeName = ref('')
|
||||||
|
|
||||||
@@ -1098,13 +1102,60 @@ function handleCancelApply(msg: AgentChatMessage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 第2步:确认简历 */
|
/** 第2步:确认简历 */
|
||||||
function handleConfirmResumeStep(msg: AgentChatMessage) {
|
async function handleConfirmResumeStep(msg: AgentChatMessage) {
|
||||||
try {
|
try {
|
||||||
const extraData = JSON.parse(msg.extra)
|
const extraData = JSON.parse(msg.extra)
|
||||||
// 勾选第2步,进入第3步
|
// 勾选第2步,进入第3步
|
||||||
extraData.step = 3
|
extraData.step = 3
|
||||||
msg.extra = JSON.stringify(extraData)
|
msg.extra = JSON.stringify(extraData)
|
||||||
} catch { /* 忽略 */ }
|
|
||||||
|
// 确认简历后,自动生成PDF并缓存到浏览器localStorage
|
||||||
|
await nextTick()
|
||||||
|
const element = applyResumeTemplateRef.value?.resumeRef
|
||||||
|
if (element) {
|
||||||
|
const userId = String(store.state.userInfo?.id || '')
|
||||||
|
const jobId = String(extraData.jobInfo?.id || '')
|
||||||
|
if (userId && jobId) {
|
||||||
|
const cacheRecord = await cacheResumePdfToLocal(element, userId, jobId)
|
||||||
|
if (cacheRecord) {
|
||||||
|
// 将缓存记录的 storageKey 存入 extraData,方便后续读取
|
||||||
|
extraData.resumeCacheKey = cacheRecord.storageKey
|
||||||
|
msg.extra = JSON.stringify(extraData)
|
||||||
|
|
||||||
|
//测试一下通过userId和jobId拿到storageKey,再用storageKey拿到简历文件并下载到浏览器测试一下(测试成功,先注释)
|
||||||
|
const testRecord = getCachedResumeRecord(userId, jobId)
|
||||||
|
// if (testRecord) {
|
||||||
|
// const testFile = await getCachedResumeFile(testRecord.storageKey, '测试缓存简历')
|
||||||
|
// if (testFile) {
|
||||||
|
// const url = URL.createObjectURL(testFile)
|
||||||
|
// const a = document.createElement('a')
|
||||||
|
// a.href = url
|
||||||
|
// a.download = testFile.name
|
||||||
|
// document.body.appendChild(a)
|
||||||
|
// a.click()
|
||||||
|
// document.body.removeChild(a)
|
||||||
|
// URL.revokeObjectURL(url)
|
||||||
|
// console.log('[Agent] 测试:从IndexedDB取出简历并下载成功', testRecord)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 【暂时注释】上传到服务器方案(服务器资源压力大,暂用localStorage缓存替代)
|
||||||
|
// const element = applyResumeTemplateRef.value?.resumeRef
|
||||||
|
// if (element) {
|
||||||
|
// const fileName = applyResumeName.value || '岗位专属简历'
|
||||||
|
// const pdfFile = await generateResumePdfFile(element, fileName)
|
||||||
|
// const uploadRes = await uploadFileToOss(pdfFile, 'ResumeFile')
|
||||||
|
// if (uploadRes.code === '0' && uploadRes.data) {
|
||||||
|
// extraData.resumeFileUrl = uploadRes.data.downloadUrl
|
||||||
|
// msg.extra = JSON.stringify(extraData)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Agent] 简历PDF缓存失败', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 第3步:跳过投递 */
|
/** 第3步:跳过投递 */
|
||||||
|
|||||||
Reference in New Issue
Block a user