网站打不开?部署自己的SBTI人格测试(附源码链接)

最近在B站刷到了一个很火的SBTI人格测试,觉得挺有意思的。作为一名开发者,我决定自己动手实现一个可以本地部署的版本。本文将带你从零开始,用Flask框架搭建一个完整的人格测试网站。

技术栈: Flask + 原生HTML/CSS/JavaScript

项目地址: 部署自己的SBTI


一、SBTI测试原理解析

1.1 什么是SBTI?

SBTI人格测试采用"量表打分 + 向量匹配"的混合机制,通过30道常规题目从5个模型的15个维度分析用户的人格特征。

1.2 五大模型与15个维度

模型 维度 说明
自我模型(S) S1-自尊自信
S2-自我清晰度
S3-核心价值
评估自我认知和价值观
情感模型(E) E1-依恋安全感
E2-情感投入度
E3-边界与依赖
评估情感模式和亲密关系
态度模型(A) A1-世界观倾向
A2-规则与灵活度
A3-人生意义感
评估对世界的态度
行动驱力(Ac) Ac1-动机导向
Ac2-决策风格
Ac3-执行模式
评估行动力和决策方式
社交模型(So) So1-社交主动性
So2-人际边界感
So3-表达与真实度
评估社交风格

1.3 赋分机制

选项分值:

  • A选项 = 1分(低分)
  • B选项 = 2分(中分)
  • C选项 = 3分(高分)

维度等级转换:
每个维度由2道题组成,总分转换为等级:

  • 2-3分 → L(低)
  • 4分 → M(中)
  • 5-6分 → H(高)

人格匹配:
将用户的15维度等级向量与25种预定义人格模式进行匹配,选出最接近的类型。


二、项目架构设计

2.1 项目结构

sbti/
├── app.py                 # Flask后端主文件
├── templates/
│   └── index.html        # 前端页面
├── requirements.txt      # Python依赖
└── README.md            # 项目文档

2.2 技术选型

  • 后端框架: Flask 3.0.0(轻量、简单、易上手)
  • 前端技术: 原生HTML/CSS/JavaScript(无需打包,开箱即用)
  • 数据存储: 内存存储(无需数据库,适合小型项目)

三、后端实现详解

3.1 Flask应用初始化

from flask import Flask, render_template, request, jsonify
import random

app = Flask(__name__)

3.2 数据结构设计

人格类型定义
PERSONALITIES = {
    "HHHH": {"name": "HHHH", "emoji": "🌟", "desc": "全维度高分,罕见的全能型人格"},
    "SEALH": {"name": "SEALH", "emoji": "🦭", "desc": "自信稳定,情感投入,乐观行动派"},
    "SEAHH": {"name": "SEAHH", "emoji": "🌊", "desc": "自信独立,情感充沛,高执行力"},
    "DRUNK": {"name": "DRUNK", "emoji": "🍺", "desc": "保温杯里泡枸杞的养生朋克"},
    # ... 共25种人格类型
}
题目数据结构
QUESTIONS = [
    {
        "id": 1,
        "dim": "S1",  # 对应维度
        "text": "别人夸你的时候,你的第一反应是?",
        "options": [
            "A. 先怀疑是不是在讽刺",
            "B. 表面谦虚,心里还挺受用",
            "C. 坦然接受,觉得理所当然"
        ]
    },
    # ... 共30道题
]
特殊题(彩蛋)
SPECIAL_QUESTIONS = [
    {
        "id": 31,
        "type": "drunk",
        "text": "保温杯里泡枸杞,你觉得?",
        "options": ["A. 养生达人", "B. 朋克养生", "C. 保温杯里泡白酒"],
        "trigger": "C"  # 触发条件
    }
]

3.3 核心API实现

API 1:获取题目列表
@app.route('/api/questions', methods=['GET'])
def get_questions():
    """获取随机打乱的题目"""
    questions = QUESTIONS.copy()
    random.shuffle(questions)  # 随机打乱顺序

    # 随机插入1道特殊题
    special_q = random.choice(SPECIAL_QUESTIONS)
    insert_pos = random.randint(10, 25)
    questions.insert(insert_pos, special_q)

    return jsonify(questions)
API 2:计算测试结果
@app.route('/api/calculate', methods=['POST'])
def calculate_result():
    """计算测试结果"""
    data = request.json
    answers = data.get('answers', {})

    # 1. 检查是否触发DRUNK彩蛋
    for qid, answer in answers.items():
        q = next((q for q in SPECIAL_QUESTIONS if str(q['id']) == qid), None)
        if q and q.get('type') == 'drunk' and answer == q.get('trigger'):
            return jsonify({
                'personality': 'DRUNK',
                'emoji': '🍺',
                'name': 'DRUNK',
                'desc': '保温杯里泡枸杞的养生朋克',
                'match_rate': 100
            })

    # 2. 计算15个维度的分数
    dim_scores = {}
    for q in QUESTIONS:
        qid = str(q['id'])
        if qid in answers:
            answer = answers[qid]
            # 计算分数(A=1, B=2, C=3)
            if answer == 'A':
                score = 3 if q.get('reverse') else 1
            elif answer == 'B':
                score = 2
            else:  # C
                score = 1 if q.get('reverse') else 3

            dim = q['dim']
            if dim not in dim_scores:
                dim_scores[dim] = []
            dim_scores[dim].append(score)

    # 3. 转换为L/M/H等级
    dim_levels = {}
    for dim, scores in dim_scores.items():
        total = sum(scores)
        if total <= 3:
            level = 'L'
        elif total == 4:
            level = 'M'
        else:
            level = 'H'
        dim_levels[dim] = level

    # 4. 匹配最佳人格类型
    best_match = find_best_personality(dim_levels)
    personality_info = PERSONALITIES.get(best_match)

    return jsonify({
        'personality': best_match,
        'emoji': personality_info['emoji'],
        'name': personality_info['name'],
        'desc': personality_info['desc'],
        'match_rate': 85,
        'dimensions': dim_levels
    })
人格匹配算法
def find_best_personality(dim_levels):
    """根据维度等级匹配最佳人格"""
    # 计算各模型的平均等级
    s_avg = get_avg_level([dim_levels.get('S1'), dim_levels.get('S2'), dim_levels.get('S3')])
    e_avg = get_avg_level([dim_levels.get('E1'), dim_levels.get('E2'), dim_levels.get('E3')])
    a_avg = get_avg_level([dim_levels.get('A1'), dim_levels.get('A2'), dim_levels.get('A3')])
    ac_avg = get_avg_level([dim_levels.get('Ac1'), dim_levels.get('Ac2'), dim_levels.get('Ac3')])

    # 组合成人格模式
    pattern = s_avg + e_avg + a_avg + ac_avg

    if pattern in PERSONALITIES:
        return pattern

    return 'HHHH'  # 默认返回

def get_avg_level(levels):
    """计算平均等级"""
    level_map = {'L': 1, 'M': 2, 'H': 3}
    scores = [level_map.get(l, 2) for l in levels if l]
    if not scores:
        return 'M'
    avg = sum(scores) / len(scores)
    if avg <= 1.5:
        return 'L'
    elif avg <= 2.5:
        return 'M'
    else:
        return 'H'

四、前端实现详解

4.1 UI设计思路

参考原版网站,采用清新的绿色主题:

:root {
  --bg: #f6faf6;              /* 背景色 */
  --panel: #ffffff;           /* 卡片背景 */
  --text: #1e2a22;            /* 文字颜色 */
  --accent: #6c8d71;          /* 主题色 */
  --accent-strong: #4d6a53;   /* 深色主题 */
  --shadow: 0 16px 40px rgba(47, 73, 55, 0.08);  /* 阴影 */
  --radius: 22px;             /* 圆角 */
}

4.2 页面结构

采用三屏切换设计:

<!-- 首页 -->
<div id="home-screen" class="screen active">
  <div class="card hero">
    <h1>SBTI 人格测试</h1>
    <button onclick="startTest()">开始测试</button>
  </div>
</div>

<!-- 测试页面 -->
<div id="test-screen" class="screen">
  <div class="progress">
    <span id="progress-bar"></span>
  </div>
  <div id="question-container"></div>
  <button onclick="nextQuestion()">下一题</button>
</div>

<!-- 结果页面 -->
<div id="result-screen" class="screen">
  <div class="result-emoji">🦭</div>
  <div class="result-name">SEALH</div>
  <div class="result-desc">自信稳定,情感投入,乐观行动派</div>
</div>

4.3 核心JavaScript逻辑

开始测试
async function startTest() {
  const response = await fetch('/api/questions');
  questions = await response.json();
  showScreen('test-screen');
  renderQuestion();
}
渲染题目
function renderQuestion() {
  const q = questions[currentIndex];
  const container = document.getElementById('question-container');

  container.innerHTML = `
    <div class="question-card">
      <div class="question-text">${currentIndex + 1}. ${q.text}</div>
      <div class="options">
        ${q.options.map((opt, i) => `
          <div class="option ${answers[q.id] === opt[0] ? 'selected' : ''}"
               onclick="selectOption(${q.id}, '${opt[0]}')">
            ${opt}
          </div>
        `).join('')}
      </div>
    </div>
  `;

  updateProgress();
}
提交测试
async function submitTest() {
  const response = await fetch('/api/calculate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ answers })
  });

  const result = await response.json();
  showResult(result);
}
显示结果
function showResult(result) {
  document.getElementById('result-emoji').textContent = result.emoji;
  document.getElementById('result-name').textContent = result.name;
  document.getElementById('result-desc').textContent = result.desc;
  document.getElementById('result-match').textContent = `匹配度: ${result.match_rate}%`;

  // 显示维度分析
  const dimLabels = {
    'S1': '自尊自信', 'S2': '自我清晰度', 'S3': '核心价值',
    'E1': '依恋安全感', 'E2': '情感投入度', 'E3': '边界与依赖',
    // ... 其他维度
  };

  // 渲染维度网格
  // ...

  showScreen('result-screen');
}

五、项目亮点与优化

5.1 题目随机化

每次测试时,题目顺序都会随机打乱,避免记忆答案:

questions = QUESTIONS.copy()
random.shuffle(questions)

5.2 隐藏彩蛋机制

在题目中随机插入特殊题,选择特定答案触发彩蛋:

# 检查是否触发DRUNK彩蛋
if answer == "C":  # 选择"保温杯里泡白酒"
    return DRUNK人格

5.3 反转题处理

部分题目(Q14、Q27)的选项分值顺序相反:

if answer == 'A':
    score = 3 if q.get('reverse') else 1  # 反转题A=3分

5.4 响应式设计

使用CSS媒体查询适配移动端:

@media (max-width: 640px) {
  .hero h1 { font-size: 32px; }
  .result-emoji { font-size: 60px; }
}

5.5 进度条动画

实时显示测试进度,提升用户体验:

function updateProgress() {
  const progress = ((currentIndex + 1) / questions.length) * 100;
  document.getElementById('progress-bar').style.width = progress + '%';
}

六、部署与运行

6.1 本地运行

# 1. 克隆项目
git clone https://github.com/yourusername/sbti-test.git
cd sbti-test

# 2. 安装依赖
pip install Flask==3.0.0

# 3. 运行项目
python app.py

# 4. 访问网站
# 打开浏览器访问 http://localhost:5000

6.2 生产环境部署

使用Gunicorn部署:

pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 app:app

6.3 Docker部署

FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]

七、效果展示

首页

在这里插入图片描述

测试页面

在这里插入图片描述
在这里插入图片描述

结果页面

在这里插入图片描述


八、常见问题

Q1: 为什么选择Flask而不是Django?

A: Flask更轻量,适合小型项目。本项目只需要几个API接口,不需要Django的复杂功能。

Q2: 为什么不使用数据库?

A: 题目和人格类型数据是固定的,不需要动态修改,使用内存存储即可。如果需要保存用户测试记录,可以后续添加数据库。

Q3: 匹配算法准确吗?

A: 本项目采用简化的匹配算法,主要用于学习和娱乐。如需更精确的匹配,可以使用曼哈顿距离或欧氏距离计算相似度。

Q4: 可以商用吗?

A: 本项目仅供学习交流使用,原始测试设计版权归原作者所有。


九、总结

这是一个非常适合初学者的Flask实战项目,代码简洁易懂,功能完整。你可以在此基础上进行扩展,添加更多有趣的功能。

项目源码:
SBTI

如果觉得有帮助,欢迎Star⭐和Fork!
在这里插入图片描述

需要注意:

仅供娱乐:SBTI不是专业心理测评,结果不具备心理学依据

半随机性:多次测试可能得到不同结果,这很正常

勿当真:不要将测试结果用于诊断、面试、相亲等严肃场合

作者初衷:B站UP主@蛆肉儿串儿最初是为了劝朋友戒酒而设计

参考资料

  • Flask官方文档:https://flask.palletsprojects.com/
  • 原版SBTI测试:https://sbti.unun.dev/

💡 提示: 本文所有代码均已在GitHub开源,欢迎下载学习!如有问题,欢迎在评论区讨论。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐