鸿蒙app 心理健康测评(压力/焦虑指数评估)
【摘要】 引言心理健康已成为现代健康管理的重要维度,压力、焦虑等情绪问题需及时评估与干预。鸿蒙系统的分布式能力、本地数据存储及隐私安全特性,为心理健康测评App提供了高效开发基础,支持标准化量表测评、情绪指数可视化与个性化干预建议,助力用户科学认知心理状态。技术背景鸿蒙框架:基于Stage模型,@Component构建UI,Preferences存储测评历史,关系型数据库记录量表结果与干预建议。心理测...
引言
技术背景
-
鸿蒙框架:基于Stage模型, @Component构建UI,Preferences存储测评历史,关系型数据库记录量表结果与干预建议。 -
心理测评原理:采用国际通用量表(如GAD-7焦虑量表、PSS压力感知量表),通过问卷答题计分,结合常模换算为0-100指数。 -
数据可视化:通过图表库(如 @ohos/chart)展示指数趋势,日历组件标记测评日期与情绪等级。 -
隐私保护:测评数据本地加密存储,支持生物识别(指纹/人脸)解锁敏感记录,符合鸿蒙TEE可信执行环境规范。
应用使用场景
-
日常情绪监测:用户每周进行一次压力测评,App生成指数曲线,提示情绪波动趋势。 -
心理咨询辅助:咨询师通过分布式数据共享,查看用户历史测评结果,辅助诊断。 -
职场/校园心理筛查:企业HR或学校心理老师批量分发测评,统计群体压力分布。
核心特性
-
多维度量表支持:内置GAD-7(焦虑)、PSS(压力)、PHQ-9(抑郁)等权威量表,支持自定义量表导入。 -
指数可视化:折线图展示指数变化趋势,热力图标记高频压力时段,雷达图对比多维度心理状态。 -
智能干预建议:根据指数等级推荐呼吸训练、正念冥想等音频引导(本地音频文件播放)。 -
隐私安全:测评记录加密存储,支持设置“仅自己可见”模式,防止数据泄露。
原理流程图与原理解释
流程图
graph TD
A[用户选择量表] --> B[加载问卷题目与选项]
B --> C[用户逐题作答]
C --> D[提交答案并计分]
D --> E[换算为压力/焦虑指数(0-100)]
E --> F[生成测评报告与干预建议]
F --> G[存储记录并可视化展示]
原理解释
-
量表计分:每个量表预设题目权重与计分规则(如GAD-7共7题,每题0-3分,总分0-21分,换算为指数=总分/21×100)。 -
指数分级: -
压力指数:0-30(轻度)、31-60(中度)、61-100(重度); -
焦虑指数:0-25(正常范围)、26-50(轻度)、51-75(中度)、76-100(重度)。
-
-
干预建议匹配:根据指数等级从预设建议库中匹配内容(如重度焦虑推荐“立即联系心理咨询师”)。
环境准备
-
开发工具:DevEco Studio 4.0+ -
SDK版本:API 9+(支持数据库、图表库、生物识别) -
权限配置:在 module.json5中声明权限:"requestPermissions": [ { "name": "ohos.permission.USE_BIOMETRIC" }, // 生物识别(可选) { "name": "ohos.permission.READ_MEDIA" } // 读取本地音频(干预建议) ] -
资源准备:在 entry/src/main/resources/rawfile下存放:-
量表数据JSON(如 scales.json); -
干预建议音频(如 breathing_guide.mp3); -
图标资源(如情绪等级图标)。
-
代码实现(完整示例)
1. 数据模型(Model/PsychologyModel.ts)
// 量表题目
export class ScaleQuestion {
id: number;
content: string; // 题目内容
options: { text: string; score: number }[]; // 选项与得分
constructor(id: number, content: string, options: { text: string; score: number }[]) {
this.id = id;
this.content = content;
this.options = options;
}
}
// 量表信息
export class Scale {
id: string; // 量表ID(如"GAD-7")
name: string; // 量表名称(如"广泛性焦虑障碍量表")
description: string; // 量表简介
questions: ScaleQuestion[]; // 题目列表
maxScore: number; // 最高得分(用于换算指数)
constructor(id: string, name: string, description: string, questions: ScaleQuestion[], maxScore: number) {
this.id = id;
this.name = name;
this.description = description;
this.questions = questions;
this.maxScore = maxScore;
}
}
// 测评记录
export class AssessmentRecord {
id: number;
scaleId: string; // 量表ID
scaleName: string; // 量表名称
totalScore: number; // 原始总分
index: number; // 换算指数(0-100)
level: string; // 等级(如"轻度焦虑")
suggestion: string; // 干预建议
recordTime: string; // 测评时间(yyyy-MM-dd HH:mm)
constructor(id: number, scaleId: string, scaleName: string, totalScore: number, index: number, level: string, suggestion: string) {
this.id = id;
this.scaleId = scaleId;
this.scaleName = scaleName;
this.totalScore = totalScore;
this.index = index;
this.level = level;
this.suggestion = suggestion;
this.recordTime = new Date().toISOString().replace('T', ' ').substring(0, 16);
}
}
2. 量表数据库(Database/ScaleDB.ts)
import relationalStore from '@ohos.data.relationalStore';
import { Scale, ScaleQuestion } from '../Model/PsychologyModel';
import fs from '@ohos.file.fs';
export class ScaleDB {
private rdbStore: relationalStore.RdbStore | null = null;
private context: Context | null = null;
async init(context: Context) {
this.context = context;
this.rdbStore = await relationalStore.getRdbStore(context, {
name: 'scale.db',
securityLevel: relationalStore.SecurityLevel.S1
});
await this.createTables();
await this.importInitialScales(); // 首次启动导入量表数据
}
// 创建量表相关表
private async createTables() {
// 量表表
const scaleSql = `CREATE TABLE IF NOT EXISTS scale (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
max_score INTEGER
)`;
await this.rdbStore!.executeSql(scaleSql);
// 题目表
const questionSql = `CREATE TABLE IF NOT EXISTS question (
id INTEGER PRIMARY KEY AUTOINCREMENT,
scale_id TEXT,
content TEXT NOT NULL,
options TEXT, -- JSON数组字符串:[{"text":"选项1","score":0},...]
FOREIGN KEY(scale_id) REFERENCES scale(id)
)`;
await this.rdbStore!.executeSql(questionSql);
}
// 从rawfile导入初始量表(示例:GAD-7焦虑量表)
private async importInitialScales() {
const file = fs.openSync(this.context!.resourceManager.getRawFd('scales.json'), fs.OpenMode.READ_ONLY);
const buffer = new ArrayBuffer(file.length);
fs.readSync(file.fd, buffer);
const jsonStr = String.fromCharCode.apply(null, new Uint8Array(buffer));
const scales: Scale[] = JSON.parse(jsonStr);
// 检查是否已导入
const count = await this.getScaleCount();
if (count === 0) {
for (const scale of scales) {
// 插入量表
await this.rdbStore!.insert('scale', {
'id': scale.id,
'name': scale.name,
'description': scale.description,
'max_score': scale.maxScore
});
// 插入题目
for (const question of scale.questions) {
await this.rdbStore!.insert('question', {
'scale_id': scale.id,
'content': question.content,
'options': JSON.stringify(question.options)
});
}
}
}
fs.closeSync(file);
}
private async getScaleCount(): Promise<number> {
const resultSet = await this.rdbStore!.query(new relationalStore.RdbPredicates('scale'), ['COUNT(*) as count']);
resultSet.goToNextRow();
const count = resultSet.getLong(resultSet.getColumnIndex('count'));
resultSet.close();
return count;
}
// 获取所有量表
async getAllScales(): Promise<Scale[]> {
const scales: Scale[] = [];
const scaleResultSet = await this.rdbStore!.query(new relationalStore.RdbPredicates('scale'), ['*']);
while (scaleResultSet.goToNextRow()) {
const scaleId = scaleResultSet.getString(scaleResultSet.getColumnIndex('id'));
// 获取该量表的题目
const questionPredicates = new relationalStore.RdbPredicates('question').equalTo('scale_id', scaleId);
const questionResultSet = await this.rdbStore!.query(questionPredicates, ['*']);
const questions: ScaleQuestion[] = [];
while (questionResultSet.goToNextRow()) {
const optionsStr = questionResultSet.getString(questionResultSet.getColumnIndex('options'));
questions.push(new ScaleQuestion(
questionResultSet.getLong(questionResultSet.getColumnIndex('id')),
questionResultSet.getString(questionResultSet.getColumnIndex('content')),
JSON.parse(optionsStr || '[]')
));
}
questionResultSet.close();
scales.push(new Scale(
scaleId,
scaleResultSet.getString(scaleResultSet.getColumnIndex('name')),
scaleResultSet.getString(scaleResultSet.getColumnIndex('description')),
questions,
scaleResultSet.getLong(scaleResultSet.getColumnIndex('max_score'))
));
}
scaleResultSet.close();
return scales;
}
// 根据ID获取量表
async getScaleById(id: string): Promise<Scale | null> {
const scaleResultSet = await this.rdbStore!.query(
new relationalStore.RdbPredicates('scale').equalTo('id', id),
['*']
);
let scale: Scale | null = null;
if (scaleResultSet.goToNextRow()) {
const scaleId = scaleResultSet.getString(scaleResultSet.getColumnIndex('id'));
const questionPredicates = new relationalStore.RdbPredicates('question').equalTo('scale_id', scaleId);
const questionResultSet = await this.rdbStore!.query(questionPredicates, ['*']);
const questions: ScaleQuestion[] = [];
while (questionResultSet.goToNextRow()) {
const optionsStr = questionResultSet.getString(questionResultSet.getColumnIndex('options'));
questions.push(new ScaleQuestion(
questionResultSet.getLong(questionResultSet.getColumnIndex('id')),
questionResultSet.getString(questionResultSet.getColumnIndex('content')),
JSON.parse(optionsStr || '[]')
));
}
questionResultSet.close();
scale = new Scale(
scaleId,
scaleResultSet.getString(scaleResultSet.getColumnIndex('name')),
scaleResultSet.getString(scaleResultSet.getColumnIndex('description')),
questions,
scaleResultSet.getLong(scaleResultSet.getColumnIndex('max_score'))
);
}
scaleResultSet.close();
return scale;
}
}
3. 测评记录数据库(Database/AssessmentDB.ts)
import relationalStore from '@ohos.data.relationalStore';
import { AssessmentRecord } from '../Model/PsychologyModel';
export class AssessmentDB {
private rdbStore: relationalStore.RdbStore | null = null;
async init(context: Context) {
this.rdbStore = await relationalStore.getRdbStore(context, {
name: 'assessment.db',
securityLevel: relationalStore.SecurityLevel.S1
});
const sql = `CREATE TABLE IF NOT EXISTS assessment_record (
id INTEGER PRIMARY KEY AUTOINCREMENT,
scale_id TEXT,
scale_name TEXT,
total_score INTEGER,
index_value INTEGER, -- 指数(0-100)
level TEXT,
suggestion TEXT,
record_time TEXT
)`;
await this.rdbStore.executeSql(sql);
}
// 添加测评记录
async addRecord(record: Omit<AssessmentRecord, 'id' | 'recordTime'>): Promise<boolean> {
if (!this.rdbStore) return false;
const valueBucket = {
'scale_id': record.scaleId,
'scale_name': record.scaleName,
'total_score': record.totalScore,
'index_value': record.index,
'level': record.level,
'suggestion': record.suggestion,
'record_time': new Date().toISOString().replace('T', ' ').substring(0, 16)
};
try {
await this.rdbStore.insert('assessment_record', valueBucket);
return true;
} catch (err) {
console.error(`Add assessment record failed: ${err}`);
return false;
}
}
// 获取历史记录(按时间倒序)
async getHistoryRecords(): Promise<AssessmentRecord[]> {
if (!this.rdbStore) return [];
const predicates = new relationalStore.RdbPredicates('assessment_record').orderByDesc('record_time');
const resultSet = await this.rdbStore.query(predicates, ['*']);
const records: AssessmentRecord[] = [];
while (resultSet.goToNextRow()) {
records.push(new AssessmentRecord(
resultSet.getLong(resultSet.getColumnIndex('id')),
resultSet.getString(resultSet.getColumnIndex('scale_id')),
resultSet.getString(resultSet.getColumnIndex('scale_name')),
resultSet.getLong(resultSet.getColumnIndex('total_score')),
resultSet.getLong(resultSet.getColumnIndex('index_value')),
resultSet.getString(resultSet.getColumnIndex('level')),
resultSet.getString(resultSet.getColumnIndex('suggestion'))
));
}
resultSet.close();
return records;
}
}
4. UI界面(pages/Index.ets)
import { ScaleDB } from '../Database/ScaleDB';
import { AssessmentDB } from '../Database/AssessmentDB';
import { Scale, AssessmentRecord } from '../Model/PsychologyModel';
import promptAction from '@ohos.promptAction';
@Entry
@Component
struct MentalHealthPage {
@State scales: Scale[] = [];
@State selectedScale: Scale | null = null;
@State currentQuestionIndex: number = 0;
@State answers: number[] = []; // 每题得分
@State showResult: boolean = false;
@State assessmentResult: AssessmentRecord | null = null;
@State historyRecords: AssessmentRecord[] = [];
private scaleDB: ScaleDB = new ScaleDB();
private assessmentDB: AssessmentDB = new AssessmentDB();
aboutToAppear() {
this.initDatabases();
this.loadScales();
this.loadHistory();
}
async initDatabases() {
await this.scaleDB.init(getContext());
await this.assessmentDB.init(getContext());
}
async loadScales() {
this.scales = await this.scaleDB.getAllScales();
}
async loadHistory() {
this.historyRecords = await this.assessmentDB.getHistoryRecords();
}
// 开始测评
startAssessment(scale: Scale) {
this.selectedScale = scale;
this.currentQuestionIndex = 0;
this.answers = new Array(scale.questions.length).fill(-1); // -1表示未答
this.showResult = false;
}
// 选择答案
selectAnswer(optionScore: number) {
if (this.selectedScale) {
this.answers[this.currentQuestionIndex] = optionScore;
// 自动进入下一题或提交
if (this.currentQuestionIndex < this.selectedScale.questions.length - 1) {
this.currentQuestionIndex++;
} else {
this.submitAssessment();
}
}
}
// 提交测评
async submitAssessment() {
if (!this.selectedScale) return;
const totalScore = this.answers.reduce((sum, score) => sum + score, 0);
const index = Math.round((totalScore / this.selectedScale.maxScore) * 100); // 换算为0-100指数
const { level, suggestion } = this.getLevelAndSuggestion(this.selectedScale.id, index);
const record = new AssessmentRecord(
0,
this.selectedScale.id,
this.selectedScale.name,
totalScore,
index,
level,
suggestion
);
const success = await this.assessmentDB.addRecord(record);
if (success) {
this.assessmentResult = record;
this.showResult = true;
this.loadHistory(); // 刷新历史记录
promptAction.showToast({ message: '测评完成!' });
} else {
promptAction.showToast({ message: '保存失败,请重试' });
}
}
// 根据量表ID和指数获取等级与建议
getLevelAndSuggestion(scaleId: string, index: number): { level: string; suggestion: string } {
// 示例逻辑(实际需根据量表定义调整)
if (scaleId.includes('GAD')) { // 焦虑量表
if (index <= 25) return { level: '正常范围', suggestion: '保持良好心态,规律作息。' };
if (index <= 50) return { level: '轻度焦虑', suggestion: '尝试深呼吸练习,每天10分钟正念冥想。' };
if (index <= 75) return { level: '中度焦虑', suggestion: '建议咨询心理医生,学习认知行为疗法技巧。' };
return { level: '重度焦虑', suggestion: '立即联系专业心理咨询师或精神科医生。' };
} else { // 压力量表(PSS)
if (index <= 30) return { level: '轻度压力', suggestion: '适当运动(如散步)缓解压力。' };
if (index <= 60) return { level: '中度压力', suggestion: '减少咖啡因摄入,保证7小时睡眠。' };
return { level: '重度压力', suggestion: '暂停高强度工作,寻求家人或专业人士支持。' };
}
}
// 重置测评
resetAssessment() {
this.selectedScale = null;
this.showResult = false;
this.assessmentResult = null;
}
build() {
Column({ space: 20 }) {
// 标题
Text("心理健康测评").fontSize(24).fontWeight(FontWeight.Bold).margin(16);
// 量表选择
if (!this.selectedScale && !this.showResult) {
Text("选择测评量表").fontSize(18).fontWeight(FontWeight.Medium);
List() {
ForEach(this.scales, (scale: Scale) => {
ListItem() {
Column() {
Text(scale.name).fontSize(16);
Text(scale.description).fontSize(14).fontColor(Color.Gray);
}.width('100%').padding(10)
Button("开始测评").onClick(() => this.startAssessment(scale))
.margin({ left: 10 })
}
})
}.width('100%').flexGrow(1)
}
// 测评进行中
if (this.selectedScale && !this.showResult) {
Column({ space: 15 }) {
Text(`${this.selectedScale.name}(${this.currentQuestionIndex + 1}/${this.selectedScale.questions.length})`)
.fontSize(18).fontWeight(FontWeight.Medium);
Text(this.selectedScale.questions[this.currentQuestionIndex].content)
.fontSize(16).width('100%').textAlign(TextAlign.Center);
Column({ space: 10 }) {
ForEach(this.selectedScale.questions[this.currentQuestionIndex].options, (option) => {
Button(option.text)
.width('80%')
.onClick(() => this.selectAnswer(option.score));
})
}.alignItems(HorizontalAlign.Center)
}.width('100%').flexGrow(1)
}
// 测评结果
if (this.showResult && this.assessmentResult) {
Column({ space: 15 }) {
Text("测评结果").fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.Blue);
Text(`量表:${this.assessmentResult.scaleName}`).fontSize(16);
Text(`指数:${this.assessmentResult.index} 分`).fontSize(16);
Text(`等级:${this.assessmentResult.level}`).fontSize(16)
.fontColor(this.assessmentResult.level.includes('重度') ? Color.Red : Color.Orange);
Text(`建议:${this.assessmentResult.suggestion}`).fontSize(16).padding(10).backgroundColor('#F5F5F5');
Row() {
Button("重新测评").onClick(() => this.resetAssessment());
Button("查看历史").onClick(() => this.resetAssessment()); // 实际可跳转历史页
}.justifyContent(FlexAlign.SpaceAround).width('100%').margin({ top: 10 });
}.width('100%').padding(10).backgroundColor('#FFFFFF').borderRadius(8);
}
// 历史记录
if (this.historyRecords.length > 0) {
Column({ space: 10 }) {
Text("测评历史").fontSize(18).fontWeight(FontWeight.Medium);
List() {
ForEach(this.historyRecords, (record: AssessmentRecord) => {
ListItem() {
Row() {
Column() {
Text(`${record.scaleName}(${record.recordTime.split(' ')[0]})`).fontSize(14);
Text(`指数:${record.index} 分 | 等级:${record.level}`).fontSize(12).fontColor(Color.Gray);
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
}.width('100%').padding(8)
}
})
}.width('100%').height(200)
}.width('100%').margin({ top: 20 });
}
}
.width('100%').height('100%').padding(16)
}
}
运行结果与测试步骤
运行结果
-
量表选择:首页显示GAD-7、PSS等量表列表,点击“开始测评”进入答题页。 -
答题流程:逐题显示题目与选项,选择后自动进入下一题,全部答完后显示结果(如“焦虑指数:42分,轻度焦虑”)。 -
历史记录:测评历史区按时间倒序显示过往记录,包含量表名称、日期、指数与等级。
测试步骤
-
环境配置:创建鸿蒙工程,添加权限与代码文件,将 scales.json(含GAD-7题目)放入resources/rawfile。 -
模拟器运行:使用API 9+模拟器,运行App,验证量表列表加载正常。 -
测评测试:选择GAD-7量表,依次作答(如每题选“偶尔”得1分),提交后确认指数=总分/21×100,等级与建议匹配。 -
历史查看:完成2次测评,检查历史记录是否按时间倒序显示。
部署场景
-
个人手机:作为日常情绪监测工具,支持离线测评与隐私保护。 -
学校心理中心:批量分发量表,通过鸿蒙分布式数据共享查看学生群体测评统计。 -
企业EAP服务:集成至企业健康管理平台,员工匿名测评后可获取定制化心理支持资源。
疑难解答
-
量表加载失败:检查 scales.json格式是否正确(题目options为JSON数组字符串),确认数据库初始化成功。 -
指数计算错误:验证量表 maxScore是否正确(如GAD-7为21分),确认计分公式总分/maxScore×100无误。 -
历史记录不显示:检查 AssessmentDB的getHistoryRecords方法是否正确按时间倒序查询。
未来展望与技术趋势与挑战
未来展望
-
AI情绪识别:集成麦克风语音分析与前置摄像头表情识别,自动触发测评(如检测到语气低落时推送焦虑量表)。 -
VR干预场景:结合鸿蒙VR能力,提供沉浸式放松场景(如森林冥想、海浪声景)。 -
群体心理画像:通过分布式数据分析,生成部门/班级压力热力图,辅助管理者干预。
技术挑战
-
量表版权合规:需获得量表版权方授权(如GAD-7需经APA许可),避免法律风险。 -
跨文化适应性:不同文化背景下量表常模需本地化校准,提升测评准确性。
总结
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)