接入流程
开放平台提供简历文件/内容解析、简历编辑、AI 和 PDF 生成能力,不负责永久保存第三方简历。每次草稿自动保存成功后,iframe 通过 saved 事件把完整简历 JSON 交给父页面,接入方自行保存。
两种接入方式
- 嵌入编辑器:创建会话后把 editorUrl 放进 iframe,用户在官方编辑器里改简历、用 AI、导出 PDF。
- 直接调用 API:先解析简历得到
resumeData,再按需打开编辑器或生成 PDF。
完整流程
用户点击“编辑简历”
→ 接入方后端拉取模板列表,用户选择模板
→ 接入方后端用 appSecret 创建临时会话
→ 创建会话时把选中模板写入 resumeData.setData.tplId
→ 后端将 editorUrl 返回给自己的前端
→ 前端立即将 editorUrl 放进 iframe
→ 用户编辑,草稿临时保存在平台(最长 24 小时)
→ 编辑器自动保存草稿
→ iframe 向已校验的父页面发送 saved 事件和完整 resumeData
→ 若 JSON 里有平台临时照片地址,接入方转存并替换成自己的 URL
→ 父页面自行保存、转换或丢弃 JSON
第一步:创建应用
- 登录开发者中心,进入“应用管理”。
- 创建应用,填写应用名称和允许嵌入域名。
- 允许域名必须是完整 Origin,例如
https://hr.example.com。不要填写路径,例如/resume/edit。 - 保存创建后只显示一次的
appSecret。它只能放在接入方后端。
应用配置的允许域名、创建会话传入的 parentOrigin、实际父页面 Origin 三者必须完全一致,包括协议和端口。
第二步:获取简历模板列表
生成新简历前,接入方后端可以先拉取可用模板,让用户选择。详见 获取简历模板。该接口需要使用 appSecret,不要在浏览器中直接调用:
GET https://www.qmjianli.com/api_open/v1/templates
Authorization: Bearer {appSecret}成功返回:
{
"code": 1,
"data": [
{
"templateId": 1013,
"tags": "通用,简洁",
"skin": "#1575bf",
"coverUrl": "https://www.qmjianli.com/img/resume/template/online/1013.png"
}
]
}前端展示 coverUrl,并把用户选中的 templateId 提交给接入方后端。模板列表可以短期缓存,但创建会话仍应在用户开始编辑时进行。
第三步:后端创建会话
用户点击编辑时,由接入方后端调用:
POST https://www.qmjianli.com/api_open/v1/editor-sessions
Authorization: Bearer {appSecret}
Content-Type: application/json
{
"externalResumeId": "R10001",
"parentOrigin": "https://hr.example.com",
"resumeData": {
"setData": { "tplId": 1013 }
}
}parentOrigin 和 externalResumeId 是必填项。externalResumeId 是接入方的简历 ID,最长 128 个字符且必须在当前应用内唯一。新简历把选中模板写入 resumeData.setData.tplId;不传则使用默认模板 1013。已有简历首次创建会话时,把上次 saved 事件拿到的 JSON 放进 resumeData(照片须已换成接入方自己的 URL)。
成功返回:
{
"code": 1,
"data": {
"sessionId": "S...",
"editorUrl": "https://www.qmjianli.com/open/edit?code=C...",
"expiresAt": "2026-08-15T02:00:00.000Z"
}
}editorUrl 内的一次性 code 只有 60 秒有效且只能使用一次。拿到后应立即打开 iframe。iframe 自身刷新会自动恢复;父页面刷新后,使用相同 externalResumeId 再次调用本接口即可恢复 24 小时内的原会话并获得新的 editorUrl。第四步:前端嵌入 iframe
<iframe
id="qm-resume-editor"
src="后端返回的 editorUrl"
style="width: 100%; height: 100vh; border: 0;"
></iframe>第五步:监听自动保存事件
父页面必须同时校验 event.origin 和 event.source,不能只判断事件名:
var iframe = document.getElementById("qm-resume-editor");
window.addEventListener("message", function (event) {
if (event.origin !== "https://www.qmjianli.com") return;
if (event.source !== iframe.contentWindow) return;
var message = event.data;
if (!message || message.source !== "QM_RESUME_EDITOR") return;
if (message.event === "saved") {
// 完整简历 JSON。basicInfo.photo 若是平台临时地址,须转存后替换再保存。
saveToYourOwnServer(message.externalResumeId, message.resumeData);
}
});编辑器发送消息时也不会使用通配符 *,只会向创建会话时已经通过白名单校验的 parentOrigin 发送。
第六步(可选):生成 PDF
用户可在编辑器中点击“导出 PDF”,也可由接入方后端直接提交 resumeData 调用生成 PDF。两种方式都同步返回临时下载地址。
{
"source": "QM_RESUME_EDITOR",
"event": "exported",
"externalResumeId": "R10001",
"downloadUrl": "https://www.qmjianli.com/api_open/v1/files/...",
"expiresAt": "..."
}该地址只服务本次下载,过期后不保证再次打开。需要时让用户重新导出即可。
接入方最小后端示例
app.post("/api/resume/editor-url", async function (req, res) {
var response = await fetch(
"https://www.qmjianli.com/api_open/v1/editor-sessions",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + process.env.QM_APP_SECRET
},
body: JSON.stringify({
externalResumeId: String(req.body.resumeId || ""),
parentOrigin: "https://hr.example.com",
resumeData: req.body.resumeData || { setData: { tplId: 1013 } }
})
}
);
var result = await response.json();
if (result.code !== 1) {
return res.status(400).json({ message: result.data });
}
res.json({ editorUrl: result.data.editorUrl });
});平台不负责什么
- 不负责接入方简历的永久存储。
- 不负责简历照片的永久存储。编辑器上传后给出的是临时地址,接入方须转存并替换。
- 不判断父页面是否已经把 saved 事件写入数据库。
- 不提供 PDF 历史文件管理。
- 不要求最终用户登录全民简历个人账号。