开放 API 接口文档
为「拾棠心理测试」提供的开放接口:合作方通过 App Key/Secret 换取访问令牌 后,可拉取可用的心理测评量表列表与介绍、获取全部题目、提交答题并即时获得计分结果,随后凭报告编号随时调取完整测评报告。本文档为正式接口规范,作为对接开发与联调验收的依据。
一、概述
为「拾棠心理测试」提供的开放接口:合作方通过 App Key/Secret 换取访问令牌后,可拉取可用的心理测评量表列表与介绍、获取全部题目、提交答题并即时获得计分结果,随后凭报告编号随时调取完整测评报告。
API 请求统一采用 POST 方法,参数以 UTF-8 编码的 application/json 提交,响应同样为 application/json。
一次完整测评的调用流程:
基础信息
| 项目 | 说明 |
|---|---|
Base URL | https://api.xpsy.cc/openapi/ |
协议 | 仅支持 HTTPS |
数据格式 | application/json; charset=utf-8 |
时间格式 | YYYY-MM-DD HH:mm:ss(北京时间 UTC+8) |
接口总数 | 8 个 |
二、快速开始
# ① 用 App Key / Secret 换取访问令牌(有效期 2 小时,后续接口复用同一个)
curl -X POST https://api.xpsy.cc/openapi/auth/token \
-H "Content-Type: application/json" \
-d '{"grant_type":"client_credentials","app_key":"xpsy_demo_key","app_secret":"••••••••","sign":"9f2a...","timestamp":1790740341}'
# ② 携带令牌拉取 sds 全部题目(token 放在请求体)
curl -X POST https://api.xpsy.cc/openapi/questions \
-H "Content-Type: application/json" \
-d '{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","test_id":"sds"}'
# ③ 提交 20 题答案,即时返回计分结果与报告编号
curl -X POST https://api.xpsy.cc/openapi/submissions \
-H "Content-Type: application/json" \
-d '{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","test_id":"sds","answers":"[1,3,1,1,2,2,4,3,1,2,3,2,3,1,1,2,3,2,1,2]","duration_seconds":392}'
# ④ 凭报告编号随时调取完整报告
curl -X POST https://api.xpsy.cc/openapi/reports \
-H "Content-Type: application/json" \
-d '{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","test_id":"sds","report_no":"a1b2c3d4e5f6g7h8"}'
# ⑤ 查询账号到期状态与剩余测试额度
curl -X POST https://api.xpsy.cc/openapi/account \
-H "Content-Type: application/json" \
-d '{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'
三、通用约定
3.1 认证方式
App Key/Secret 换 JWT 令牌(类似 OAuth2 client_credentials):
- 合作方在拾棠后台申请
app_key与app_secret(secret 仅存服务端,严禁放入前端); - 调用
POST https://api.xpsy.cc/openapi/auth/token换取access_token(JWT,有效期 7200 秒),有效期内缓存复用; - 其余接口统一为 POST,在请求体中以
access_token字段携带访问令牌;业务参数以 JSON 放在同一请求体中,不使用请求头鉴权。
sign 与 timestamp 做请求验签,sign = sha256(app_key + app_secret + timestamp)(三者直接拼接为字符串,取小写十六进制)。服务端校验时间偏差(默认 ±300 秒),验签失败返回 40104。3.2 统一响应结构
| 字段 | 类型 | 说明 |
|---|---|---|
code | int | 业务状态码,10001 成功 |
message | string | 状态描述 |
data | object / null | 业务数据,失败时为 null |
timestamp | int | 服务器返回时间戳(秒级) |
// 成功
{ "code": 10001, "message": "success", "data": { }, "timestamp": 1790740341 }
// 失败(token 过期)
{ "code": 40102, "message": "访问令牌无效或已过期,请重新获取", "data": null, "timestamp": 1790740352 }
3.3 错误码表
| 业务码 | 含义 | 处理建议 |
|---|---|---|
| 30001 | 账号已过期或未开通 | 联系平台续期或开通 |
| 30002 | 账号剩余测试额度不足 | 联系平台充值或调整额度 |
| 40001 | 参数缺失或格式错误 | 按 message 修正 |
| 40101 | app_key 或 app_secret 错误 | 检查密钥与环境 |
| 40102 | access_token 无效或过期 | 重新获取令牌 |
| 40103 | 应用已禁用或欠费 | 联系平台 |
| 40104 | 请求验证不通过 | 获取令牌时 sign 生成错误,检查 sign 的生成方法 |
| 40301 | 无权使用该功能 | 联系平台开通 |
| 40302 | 触发频率限制 | 按 Retry-After 等待 |
| 40401 | 测试不存在或已下架 | 重新拉取列表 |
| 40402 | 报告编号不存在 | 核对编号 |
| 40901 | 答案数量与题目总数不符 | 核对 answers 长度后重新提交 |
| 50001 | 服务端内部错误 | 携 timestamp 与请求参数联系平台 |
| 50301 | 服务维护中 | 稍后重试 |
code 为 10001 表示成功,其余为对应错误。30001(账号已过期或未开通)为账号级状态,每个接口均可能返回,请在统一响应处理中优先判断。30002(账号剩余测试额度不足)仅接口 5(获取全部题目)与接口 6(提交测试)可能返回。四、接口详情
接口 1 · 验证身份,获取访问令牌
https://api.xpsy.cc/openapi/auth/token无需令牌用 app_key + app_secret 换取访问令牌。请求体须同时携带 sign 与 timestamp 完成验签(timestamp 为请求发起时的秒级时间戳,与服务端时间偏差不得超过 ±300 秒),sign = sha256(app_key + app_secret + timestamp)。令牌在有效期内可缓存复用,请勿频繁调用。
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
grant_type | string | 必填 | 授权类型,固定为 client_credentials |
app_key | string | 必填 | 平台分配的应用标识(App Key) |
app_secret | string | 必填 | 应用密钥(App Secret),仅可在服务端保存,严禁下发到客户端 |
sign | string | 必填 | 请求签名,生成方式见下方「sign 生成方法」 |
timestamp | int | 必填 | 请求发起时的秒级时间戳,服务端校验与当前时间的偏差,超出 ±300 秒视为过期 |
请求示例
{
"grant_type": "client_credentials",
"app_key": "xpsy_demo_key",
"app_secret": "9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c",
"sign": "3f8a1c9e7b2d4a6f0e5c8b1d9a3f7e2c4b6d8a0f1e3c5b7d9a1f2e4c6b8d0a2f",
"timestamp": 1790740341
}
sign 生成方法:sign = sha256(app_key + app_secret + timestamp),三者按顺序直接拼接为字符串(无分隔符)后取哈希,输出小写十六进制。例如 sha256("xpsy_demo_key" + "9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c" + "1790740341")。响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
access_token | string | JWT 访问令牌,后续接口在请求体中提交该值 |
token_type | string | 固定 Bearer |
expires_in | int | 有效期(秒),默认 7200 |
timestamp | int | 签发时间戳(秒级) |
响应示例
{
"code": 10001,
"message": "success",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 7200,
"timestamp": 1790740341
},
"timestamp": 1790740341
}
常见错误:30001 账号已过期或未开通、40001 参数非法、40101 密钥错误、40103 应用被禁用、40104 验签不通过。
接口 2 · 获取可用测试列表
https://api.xpsy.cc/openapi/tests需请求体携带 access_token返回当前应用可用的完整量表列表,已下架的量表不会返回。
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
access_token | string | 必填 | 访问令牌,取自接口 1 返回的 access_token |
请求示例
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
version | string | 测试列表整体版本号,形如 v1.0.0,内容变化时递增,与接口 3 返回值一致 |
list | array | 全部可用量表对象数组,结构见下表 |
响应 data.list[]
| 字段 | 类型 | 说明 |
|---|---|---|
test_id | string | 量表唯一标识,作为后续接口的入参,如 sds |
test_name | string | 量表名称 |
question_count | int | 题目数量 |
duration_minutes | int | 预计答题时长(分钟) |
响应示例
{
"code": 10001,
"message": "success",
"data": {
"version": "v1.0.0",
"list": [
{
"test_id": "sds",
"test_name": "抑郁自评量表(SDS)",
"question_count": 20,
"duration_minutes": 10
},
{
"test_id": "sas",
"test_name": "焦虑自评量表(SAS)",
"question_count": 20,
"duration_minutes": 10
},
{
"test_id": "scl-90",
"test_name": "症状自评量表(SCL-90)",
"question_count": 90,
"duration_minutes": 25
}
]
},
"timestamp": 1790740342
}
常见错误:30001 账号已过期或未开通、40001 参数非法、40102 令牌无效或过期。
接口 3 · 获取测试列表版本
https://api.xpsy.cc/openapi/tests/version需请求体携带 access_token轻量接口,用于快速判断测试列表是否已更新。调用方可在本地缓存量表列表,定期调用本接口比对 version:仅当返回值与本地缓存不一致时,再调用接口 2 重新拉取完整列表,避免频繁传输全量数据。
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
access_token | string | 必填 | 访问令牌,取自接口 1 返回的 access_token |
请求示例
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
version | string | 测试列表整体版本号,形如 v1.0.0,列表内容变化时递增,与接口 2 返回值一致 |
响应示例
{
"code": 10001,
"message": "success",
"data": {
"version": "v1.0.0"
},
"timestamp": 1790740342
}
常见错误:30001 账号已过期或未开通、40001 参数非法、40102 令牌无效或过期。
接口 4 · 获取测试介绍
https://api.xpsy.cc/openapi/introduction需请求体携带 access_token返回指定量表的详细介绍,用于测评前的知情说明页。
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
access_token | string | 必填 | 访问令牌,取自接口 1 返回的 access_token |
test_id | string | 必填 | 量表唯一标识,取自接口 2 返回的 test_id,如 sds |
请求示例
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"test_id": "sds"
}
响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
test_id | string | 测试标识 |
test_name | string | 量表全名 |
question_count | int | 题数 |
duration_minutes | int | 预计时长 |
test_introduction | string | 测试介绍,<div class="rich"></div> 包裹的 HTML 片段(CSS 样式另见附录 D 样式模版) |
响应示例(sds)
{
"code": 10001,
"message": "success",
"data": {
"test_id": "sds",
"test_name": "抑郁自评量表(SDS)",
"question_count": 20,
"duration_minutes": 10,
"test_introduction": "<div class=\"rich\"><p class=\"p\">抑郁自评量表(Self-Rating Depression Scale,SDS)由 Zung 于 1965 年编制,用于衡量抑郁状态的轻重程度及其在治疗中的变化。量表共 20 个条目,涵盖情绪、躯体症状、精神运动性障碍及心理障碍等维度,评定时间范围为最近一周。本量表适用于具有抑郁症状的成年人,测试结果仅供自我了解与参考,不构成医学诊断。</p></div>"
},
"timestamp": 1790740342
}
常见错误:30001 账号已过期或未开通、40401 测试不存在或已下架。
接口 5 · 获取全部题目
https://api.xpsy.cc/openapi/questions需请求体携带 access_token一次性返回指定量表的全部题目及选项,题目顺序以 questions 数组下标为准。
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
access_token | string | 必填 | 访问令牌,取自接口 1 返回的 access_token |
test_id | string | 必填 | 量表唯一标识,取自接口 2 返回的 test_id,如 sds |
请求示例
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"test_id": "sds"
}
响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
test_id | string | 测试标识 |
guide | string | 测试引导说明,以 <div class="rich"></div> 包裹的 HTML 片段(CSS 样式另见附录 D 样式模版) |
questions | array | 题目数组,按顺序排列,结构见下表 |
响应 data.questions[]
| 字段 | 类型 | 说明 |
|---|---|---|
text | string | 题干,纯文本,或 <div class="rich"></div> 包裹的 HTML(CSS 样式另见附录 D 样式模版) |
option | array | 选项组,按顺序排列,结构见下表「选项对象」 |
questions 数组的默认顺序展示题目;统计分数时同样按默认顺序将每题的作答与题目一一对应计分,不可调整顺序。选项对象(data.questions[].option[])
| 字段 | 类型 | 说明 |
|---|---|---|
text | string | 选项文案,纯文本,或 <div class="rich"></div> 包裹的 HTML(CSS 样式另见附录 D 样式模版) |
value | int/string | 选项分值,整数或字符串 |
响应示例(sds 前 2 题,实际返回全部 20 题)
{
"code": 10001,
"message": "success",
"data": {
"test_id": "sds",
"guide": "<div class=\"rich\"><p class=\"p\">请找一个安静、不受打扰的环境,根据您最近两周的真实感受作答,无需过度思考每道题,凭第一直觉选择即可。</p></div>",
"questions": [
{
"text": "我觉得闷闷不乐,情绪低沉",
"option": [
{ "text": "没有或很少时间", "value": 1 },
{ "text": "少部分时间", "value": 2 },
{ "text": "相当多时间", "value": 3 },
{ "text": "绝大部分或全部时间", "value": 4 }
]
},
{
"text": "我觉得一天之中早晨最好",
"option": [
{ "text": "没有或很少时间", "value": 4 },
{ "text": "少部分时间", "value": 3 },
{ "text": "相当多时间", "value": 2 },
{ "text": "绝大部分或全部时间", "value": 1 }
]
}
]
},
"timestamp": 1790740342
}
常见错误:30001 账号已过期或未开通、30002 账号剩余测试额度不足、40401 测试不存在。
接口 6 · 提交测试,获取计分结果
https://api.xpsy.cc/openapi/submissions需请求体携带 access_token一次性提交指定量表的全部答案,服务端完成计分、等级判定与结果生成后,同步返回报告编号。
answers 为字符串,内容为按题目顺序排列的答案数组的 JSON 序列化结果——数组中第 1 个元素对应第 1 题,第 n 个元素对应第 n 题。数组长度必须等于该量表题目总数,否则返回 40901。以 SDS 20 题为例,答案依次为 1、3、1、…、2 时,
answers 传 "[1,3,1,1,2,2,4,3,1,2,3,2,3,1,1,2,3,2,1,2]";选项值为字母的量表(如 MBTI)同理传入字母序列的字符串形式。请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
access_token | string | 必填 | 访问令牌,取自接口 1 返回的 access_token |
test_id | string | 必填 | 量表唯一标识,取自接口 2 返回的 test_id,如 sds |
answers | string | 必填 | 按题目顺序排列的答案数组,序列化为字符串后传入(第 1 个元素对应第 1 题);长度必须等于该量表题目总数,元素为题目所选选项的选项值 |
duration_seconds | int | 可选 | 本次答题用时(秒),用于结果参考,不影响计分 |
请求示例(sds,20 题完整提交)
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"test_id": "sds",
"answers": "[1,3,1,1,2,2,4,3,1,2,3,2,3,1,1,2,3,2,1,2]",
"duration_seconds": 392
}
请求示例(mbti,选项值为字母)
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"test_id": "mbti",
"answers": "[\"OK\",\"P\",\"P\",\"S\",\"E\",\"N\",\"F\",\"P\",\"I\",\"J\",\"P\",\"J\",\"E\",\"S\",\"I\",\"N\",\"F\",\"P\",\"E\",\"E\",\"P\",\"P\",\"E\",\"E\",\"N\",\"P\",\"E\",\"OK\",\"E\",\"P\",\"N\",\"T\",\"F\",\"N\",\"J\",\"E\",\"E\",\"P\",\"S\",\"F\",\"T\",\"S\",\"P\",\"E\",\"J\",\"N\",\"F\",\"T\",\"S\",\"E\",\"P\",\"N\",\"F\",\"F\",\"N\",\"E\",\"S\",\"T\",\"F\",\"N\",\"OK\",\"J\",\"E\",\"N\",\"E\",\"N\",\"T\",\"J\",\"I\",\"E\",\"J\",\"T\",\"P\",\"P\",\"E\",\"S\",\"N\",\"T\",\"P\",\"E\",\"F\",\"OK\",\"N\",\"F\",\"F\",\"N\",\"S\",\"T\",\"F\",\"N\",\"N\",\"T\",\"F\",\"N\",\"F\",\"T\",\"N\"]",
"duration_seconds": 508
}
响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
report_no | string | 报告编号,全局唯一,长度为 6–32 位字符串 |
test_id | string | 测试标识 |
响应示例
{
"code": 10001,
"message": "success",
"data": {
"report_no": "a1b2c3d4e5f6g7h8",
"test_id": "sds"
},
"timestamp": 1790740342
}
常见错误
// 40901:答案数量与题目总数不符
{
"code": 40901,
"message": "答案数量与题目总数不符",
"data": null,
"timestamp": 1790740342
}
30001:账号已过期或未开通30002:账号剩余测试额度不足40901:答案数量与题目总数不符40401:测试不存在或已下架
接口 7 · 根据报告编号获取测试报告
https://api.xpsy.cc/openapi/reports需请求体携带 access_token凭报告编号调取完整报告,报告内容生成后固定不变。响应为标准 JSON;其中的 interpretation 字段是 <div class="rich"></div> 包裹的 HTML 片段,调用方按需渲染即可——如需还原拾棠标准页面的排版,可直接引入附录 D 提供的 CSS 样式模版。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
access_token | string | 必填 | 访问令牌,取自接口 1 返回的 access_token |
test_id | string | 必填 | 量表唯一标识,需与提交时一致,用于校验报告归属 |
report_no | string | 必填 | 报告编号,取自接口 6 返回的 report_no |
请求示例
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"test_id": "sds",
"report_no": "a1b2c3d4e5f6g7h8"
}
响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
report_no | string | 报告编号 |
result_score | float | 测评得分(量表标准分);无分数的量表(如 MBTI)返回 0 |
result_text | string | 结果等级文字,如「轻度抑郁倾向」;无总体结果的量表(如大五人格)返回空字符串 "" |
score_level | array | 分数等级对照表,每项含 score(float)与 label(string);仅当 result_score 不为空时返回,为空时不返回此字段 |
interpretation | string | 结果解读,<div class="rich"></div> 包裹的 HTML 片段(CSS 样式另见附录 D 样式模版) |
test_data | string | 测试结果的结构化数据(JSON 序列化字符串),供调用方自行解析、生成报告或绘制图表;无结构化数据的量表返回空字符串 "" |
generated_at | string | 报告生成时间 |
响应示例一(SAS,无结构化数据,test_data 为空字符串)
{
"code": 10001,
"message": "success",
"data": {
"report_no": "a1b2c3d4e5f6g7h8",
"result_score": 58,
"result_text": "轻度焦虑倾向",
"score_level": [
{ "score": 25, "label": "正常" },
{ "score": 53, "label": "轻度" },
{ "score": 63, "label": "中度" },
{ "score": 72, "label": "重度" },
{ "score": 100, "label": "" }
],
"interpretation": "<div class=\"rich\"><p class=\"p\">本次测评标准分为 58 分,处于中国常模 53–62 区间,提示目前存在轻度焦虑倾向。这一水平通常表现为紧张不安、易烦躁、睡眠欠佳等主观感受,一般尚不影响正常的工作与生活功能,但值得引起关注。</p></div>",
"test_data": "",
"generated_at": "2026-09-29 10:31:02"
},
"timestamp": 1790740342
}
响应示例二(SCL-90,含结构化数据,test_data 为 JSON 字符串)
{
"code": 10001,
"message": "success",
"data": {
"report_no": "a1b2c3d4e5f6g7h8",
"result_score": 90,
"result_text": "轻度",
"score_level": [
{ "score": 90, "label": "正常" },
{ "score": 160, "label": "轻度" },
{ "score": 200, "label": "中度" },
{ "score": 250, "label": "重度" }
],
"interpretation": "<div class=\"rich\"><p class=\"p\">本次测评总分 90 分,各因子均分均处于正常范围,未见明显的心理症状群。</p></div>",
"test_data": "{\"totalScore\":90,\"averageScore\":1,\"positive\":0,\"negative\":90,\"averagePositive\":0,\"factor\":{\"敌对\":{\"total\":6,\"average\":1},\"偏执\":{\"total\":6,\"average\":1},\"恐怖\":{\"total\":7,\"average\":1},\"其他\":{\"total\":7,\"average\":1},\"人际关系敏感\":{\"total\":9,\"average\":1},\"强迫症状\":{\"total\":10,\"average\":1},\"焦虑\":{\"total\":10,\"average\":1},\"精神病性\":{\"total\":10,\"average\":1},\"躯体化\":{\"total\":12,\"average\":1},\"抑郁\":{\"total\":13,\"average\":1}}}",
"generated_at": "2026-09-29 10:31:02"
},
"timestamp": 1790740342
}
常见错误:30001 账号已过期或未开通、40001 报告编号长度非法(须为 6–32 位字符串);40402 报告编号不存在。
接口 8 · 获取账号状态与剩余测试额度
https://api.xpsy.cc/openapi/account需请求体携带 access_token查询当前应用账号是否到期、以及剩余可用的测试额度,便于在业务侧提前拦截或提示。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
access_token | string | 必填 | 访问令牌,取自接口 1 返回的 access_token |
请求示例
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
expire_at | string | 账号到期时间,格式 YYYY-MM-DD HH:mm:ss |
is_valid | boolean | 账号是否有效,true 表示有效 |
quota_total | int | 测试额度总量,-1 表示不限量 |
quota_used | int | 已使用的测试额度 |
quota_remaining | int | 剩余可用测试额度,-1 表示不限量 |
响应示例
{
"code": 10001,
"message": "success",
"data": {
"expire_at": "2026-12-31 23:59:59",
"is_valid": true,
"quota_total": 1000,
"quota_used": 328,
"quota_remaining": 672
},
"timestamp": 1790740342
}
常见错误:30001 账号已过期或未开通、40102 令牌无效或过期。
附录 A · 安全与合规
app_secret仅存合作方服务端,浏览器/小程序直连场景须由后端反向代理,严禁前端暴露;- 全链路 HTTPS;
- 心理测评属敏感个人信息,调用方须取得用户明确知情同意;
- 结果不构成医学诊断,不得以「诊断」名义对外宣传。
附录 B · 设计决策说明
本附录说明接口关键设计取舍,便于对接方理解实现约定。
| # | 事项 | 设计说明 |
|---|---|---|
| 1 | 认证方式 | App Key/Secret 换取 JWT(有效期 7200 秒),请求须携带 sign + timestamp 验签 |
| 2 | 用户体系 | 匿名测评 + 报告编号查询,不绑定 C 端账号;如需用户维度历史报告列表,另行扩展接口 |
| 3 | 答案格式 | answers 为字符串,内容是按题目顺序排列的答案数组的 JSON 序列化结果(第 1 个元素对应第 1 题),长度须等于题目总数,服务端严格校验 |
| 4 | 频率限制 | 查询 60 次/分、提交 10 次/分(按应用计),超出返回 40302 |
| 5 | 报告保留期 | 报告自生成之日起保存 2 年,到期后自动清理 |
| 6 | 部署环境 | https://api.xpsy.cc/openapi/ |
| 7 | 列表版本 | 接口 2、接口 3 均返回列表整体 version,用于比对缓存是否需要刷新 |
附录 C · 更新记录
| 日期 | 版本 | 说明 |
|---|---|---|
| 2026-09-30 | v1.0 | 正式发布 |
附录 D · 富文本 <div class="rich"></div> CSS 样式模版
接口 4 的 test_introduction(测试介绍)、接口 5 的 guide(测试引导)与 questions[].text / option[].text(题干与选项)、接口 7 的 interpretation(结果解读)等字段,均为 <div class="rich"></div> 包裹的 HTML 片段。调用方只需引入以下样式,即可还原拾棠标准页面的排版(可按自身站点主题微调):
.rich {
/* ---------- 主题变量 ---------- */
--rich-text: #454545;
--rich-text-secondary: #666666;
--rich-text-muted: #999999;
--rich-text-strong: #333333;
--rich-line: #dddddd;
--rich-primary: #0099ff;
--rich-primary-deep: #007bff;
--rich-primary-table: #0080e2;
--rich-primary-bar: #00a6e2;
--rich-bg-soft: #f9f9f9;
--rich-bg-border: #f5f5f5;
--rich-level-1: #00aaff;
--rich-level-2: #ff8800;
--rich-level-3: #ff0000;
--rich-level-4: #aa0000;
--rich-level-5: #8a0000;
--rich-radius-sm: 6px;
--rich-radius-md: 8px;
--rich-radius-lg: 10px;
--rich-radius-xl: 18px;
box-sizing: border-box;
width: 100%;
margin: 0 auto;
padding: 0;
color: var(--rich-text);
font-size: 16px;
line-height: 1.6;
}
.rich *,
.rich *::before,
.rich *::after {
box-sizing: border-box;
}
/* 富文本内图片/表格不自适应宽度时的兜底 */
.rich img {
max-width: 100%;
}
.rich table {
width: 100%;
border-collapse: collapse;
}
/* ---------- 间距工具类 ---------- */
.rich .mt20 {
margin-top: 20px;
}
.rich .mt40 {
margin-top: 40px;
}
.rich .mb20 {
margin-bottom: 20px;
}
.rich .mb40 {
margin-bottom: 40px;
}
.rich .tc {
text-align: center !important;
}
/* ---------- 分级结果色 ---------- */
.rich .result-level-1 {
color: var(--rich-level-1);
}
.rich .result-level-2 {
color: var(--rich-level-2);
}
.rich .result-level-3 {
color: var(--rich-level-3);
}
.rich .result-level-4 {
color: var(--rich-level-4);
}
.rich .result-level-5 {
color: var(--rich-level-5);
}
/* ---------- 标题 / 段落 ---------- */
.rich .h,
.rich .h-color {
font-size: 22px;
line-height: 1.5;
font-weight: bold;
margin-bottom: 10px;
}
.rich .h {
color: var(--rich-text-strong);
}
.rich .h-color {
color: var(--rich-primary-deep);
}
.rich .p,
.rich .p-n {
color: var(--rich-text-secondary);
font-size: 16px;
line-height: 1.5em;
text-align: justify;
margin-bottom: 6px;
}
.rich .p-n {
text-indent: 0;
}
.rich .p2i {
text-indent: 2em;
}
.rich .p:last-child,
.rich .p-n:last-child {
margin-bottom: 0;
}
.rich .t-color {
color: var(--rich-primary);
}
.rich .label{
color: var(--rich-primary) !important;
}
/* ---------- 图片 ---------- */
.rich .img {
display: block;
width: 100%;
max-width: 800px;
margin: 2px auto;
}
/* ---------- 信息块 ---------- */
.rich .info {
padding: 15px;
border-radius: var(--rich-radius-lg);
background-color: var(--rich-bg-soft);
color: var(--rich-text-secondary);
font-size: 13px;
line-height: 1.8;
}
/* ---------- 因素列表(卡片式) ---------- */
.rich .items,
.rich .factors-list {
width: 100%;
padding: 0;
}
.rich .factors-list .factor {
padding: 8px;
margin-bottom: 8px;
border: 3px solid var(--rich-bg-border);
border-radius: var(--rich-radius-md);
list-style-type: none;
}
.rich .factors-list .factor .factor-name {
padding: 8px;
margin-bottom: 5px;
border-bottom: 1px solid #eeeeee;
color: var(--rich-text-secondary);
font-size: 16px;
font-weight: bold;
line-height: 1.8;
}
.rich .factors-list .factor .row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 6px;
font-size: 16px;
color: var(--rich-text-secondary);
}
.rich .factors-list .factor .label{
white-space: nowrap;
}
.rich .factors-list .factor .score {
color: var(--rich-text);
font-weight: bold;
}
.rich .factors-list .factor .result {
flex: 1 1 auto;
color: var(--rich-text-secondary);
line-height: 1.4;
text-align: justify;
}
.rich .factors-list .factor .tip {
color: var(--rich-text-muted);
font-size: 12px;
line-height: 22px;
}
/* ---------- 选中内容块 ---------- */
.rich .selected-content {
padding: 10px;
border-radius: var(--rich-radius-sm);
}
/* ==========================================================================
items 列表
========================================================================== */
.rich .item-block {
padding: 2px 15px 15px;
margin-bottom: 12px;
border: 1.5px dashed var(--rich-line);
border-radius: var(--rich-radius-lg);
}
.rich .item-title {
margin-top: 20px;
margin-bottom: 9px;
border-bottom: 1px solid var(--rich-line);
color: var(--rich-primary);
font-size: 15px;
font-weight: bold;
line-height: 30px;
}
.rich .items {
padding: 0 10px;
list-style-type: circle;
}
.rich .items .item-li {
margin-left: 15px;
margin-bottom: 6px;
color: var(--rich-text-secondary);
font-size: 13px;
line-height: 22px;
list-style-position: outside;
text-align: justify;
}
.rich .items .item-li b {
color: var(--rich-text-strong);
}
.rich .items .item-li span {
color: var(--rich-primary);
}
/* ==========================================================================
score-table 评分表
========================================================================== */
.rich .score-table {
width: 100%;
height: auto;
border: 0;
border-collapse: collapse;
}
.rich .score-table .score-header {
border-bottom: 1px solid #aaaaaa;
}
.rich .score-table .score-header .item {
height: 40px;
color: var(--rich-primary-table);
font-size: 16px;
font-weight: bold;
text-align: left;
white-space: nowrap;
}
.rich .score-table .factor {
height: 40px;
color: #888888;
font-size: 14px;
}
/* 斑马纹:统一挂在单元格上,避免 tr 背景被 td 覆盖导致偶发不生效 */
.rich .score-table .factor:nth-child(odd) > * {
background-color: var(--rich-bg-soft);
}
.rich .score-table .score-header .item,
.rich .score-table .factor .factor-name,
.rich .score-table .factor .factor-score,
.rich .score-table .factor .factor-result,
.rich .score-table .factor .factor-info {
padding: 10px 6px;
}
.rich .score-table .factor .factor-name {
color: var(--rich-text-strong);
}
/* ==========================================================================
标题条
========================================================================== */
.rich .h-bar {
width: 100%;
padding: 14px 10px;
margin-bottom: 12px;
border-bottom: 2px solid #5587c3;
border-radius: var(--rich-radius-md) var(--rich-radius-md) 0 0;
background-image: linear-gradient(150deg, #e1f1ff, #fafdff);
color: var(--rich-primary-bar);
font-size: 18px;
line-height: 1;
}
/* ==========================================================================
多维结果
========================================================================== */
.rich .result-title {
margin-bottom: 10px;
color: var(--rich-text-secondary);
font-size: 28px;
line-height: 1.3;
text-align: center;
}
.rich .result-base-info {
margin-bottom: 10px;
color: var(--rich-text-muted);
font-size: 16px;
text-align: center;
}
.rich .result-title .result-info,
.rich .result-base-info .info {
color: #004eff;
}
.rich .result-row-items {
display: flex;
flex-direction: row;
align-items: stretch;
justify-content: space-around;
gap: 10px;
}
.rich .result-row-items .row-item {
display: flex;
flex: 1 1 0;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
min-width: 0;
padding: 16px 10px;
border-radius: var(--rich-radius-xl);
}
/* 四项配色:背景 + 标题色 */
.rich .result-row-items .row-item:nth-child(4n + 1) {
background-color: #f5ede0;
}
.rich .result-row-items .row-item:nth-child(4n + 2) {
background-color: #ede2f3;
}
.rich .result-row-items .row-item:nth-child(4n + 3) {
background-color: #e5f0ea;
}
.rich .result-row-items .row-item:nth-child(4n) {
background-color: #e5e8f0;
}
.rich .result-row-items .row-item:nth-child(4n + 1) .title {
color: #ff9700;
}
.rich .result-row-items .row-item:nth-child(4n + 2) .title {
color: #9c28b1;
}
.rich .result-row-items .row-item:nth-child(4n + 3) .title {
color: #4cb050;
}
.rich .result-row-items .row-item:nth-child(4n) .title {
color: #007eff;
}
.rich .result-row-items .title,
.rich .result-row-items .result,
.rich .result-row-items .score,
.rich .result-row-items .info {
width: 100%;
}
.rich .result-row-items .title,
.rich .result-row-items .result,
.rich .result-row-items .score {
text-align: center;
line-height: 1em;
}
.rich .result-row-items .title {
color: var(--rich-text-strong);
font-size: 16px;
font-weight: bold;
}
.rich .result-row-items .result {
margin-top: 8px;
color: var(--rich-text);
font-size: 14px;
line-height: 1.4;
}
.rich .result-row-items .score {
margin-top: 12px;
color: var(--rich-text-muted);
font-size: 12px;
}
.rich .result-row-items .info {
margin-top: 10px;
color: var(--rich-text-muted);
font-size: 12px;
line-height: 1.6;
text-align: justify;
}
/* ==========================================================================
响应式
========================================================================== */
@media only screen and (max-width: 768px) {
.rich .img {
width: 100%;
margin: 10px auto;
}
/* 多维结果卡片过多时换行,避免被压缩到不可读 */
.rich .result-row-items {
flex-wrap: wrap;
}
.rich .result-row-items .row-item {
flex: 1 1 calc(50% - 10px);
min-width: 140px;
}
.rich .score-table {
font-size: 13px;
}
.rich .score-table .score-header .item,
.rich .score-table .factor .factor-name,
.rich .score-table .factor .factor-score,
.rich .score-table .factor .factor-result {
padding: 8px 0;
}
.rich .result-title {
font-size: 22px;
}
}
@media print {
.rich .h-bar {
background-image: none;
background-color: #f2f8ff;
}
.rich .result-row-items .row-item {
border: 1px solid var(--rich-line);
}
}
样式源文件:https://static.xpsy.cc/css/rich.css(本页内容已同步该源文件 2026-10-10 版本,源文件更新后需同步修订本页)