PHP+uniapp心理健康测评系统开发实战:从架构到排坑
2026/9/15 6:36:41 网站建设 项目流程

开题先说一个我最近做完的项目:一个面向大学生心理健康场景的PHP后端 + 小程序/APP双端系统。这个项目前后花了大概两个月,从需求梳理、原型设计、后端接口开发到小程序端和APP端联调,踩了不少坑,也沉淀了一些比较完整的经验,今天整理出来分享给正在做类似方向的朋友。

如果你也在做心理健康类应用、校园服务类小程序,或者准备用PHP作为后端给小程序/APP提供接口,这篇文章会比较对胃口。我不打算讲太多空泛的概念,直接把它拆成几个能落地的模块来说:设计思路、功能拆解、技术架构、核心代码实现和排坑记录,全程保持工程化视角。

1. 项目整体设计与思路拆解

1.1 为什么选PHP做后端,而不是Java或Node

很多做小程序后端的人一上来就选Java Spring Boot,或者Node.js,但在这个项目里我坚持用了PHP,理由是实际项目约束下的最优解。

第一是开发效率。这个系统的核心业务并不复杂,主要是用户登录、测评量表管理、咨询预约、文章资讯和简单的社区互动,这类业务用PHP的成熟框架(我用的ThinkPHP 8)写起来非常快,Model层、验证器、中间件这些都有现成方案,不太需要从零造轮子。对于单人开发或者小团队来说,PHP能把“从0到上线”的周期压缩得很短。

第二是部署成本低。大学校园里的项目或者个人练习项目,服务器资源往往有限,PHP + Nginx + MySQL的经典组合,一台2核4G的云服务器就能跑得很稳,运维成本比Java那一套低很多。而且PHP的生态环境对虚拟主机、宝塔面板这类轻量运维方式支持很好,即使你不会复杂运维,也能很快把项目跑起来。

第三是团队技术栈。如果是课程设计或者实验室项目,PHP往往是很多人学过的一门语言,学习曲线低,接手的人也基本都能看懂。

但我也要说一下PHP在这个项目里的边界:它更适合做API接口层和业务逻辑层,不适合做实时通信。如果后面要上“在线即时倾诉”这种强实时功能,建议用WebSocket服务单独做,让PHP专注处理业务接口。我在这个项目里用的方案是:业务API用PHP,实时聊天用小程序自带的WebSocket能力对接独立服务,两者通过消息队列解耦。

1.2 小程序+APP双端的实现路线:uniapp一套代码跑两端

这个项目要求同时覆盖微信小程序和安卓/iOS APP,如果分别用原生写,开发量直接翻倍,而且还要维护两套代码逻辑,出Bug的概率也高。我的选择是使用uniapp做前端跨端框架,一套Vue语法代码,编译成小程序和APP两种形态。

实际开发下来,uniapp在90%的场景下是够用的,但有几个地方需要提前做条件编译:

  • 支付逻辑:小程序端走微信支付,APP端可能需要接入支付宝或微信APP支付;
  • 登录逻辑:小程序用微信授权登录,APP端用账号密码或手机号验证码登录;
  • 推送能力:小程序用订阅消息,APP端需要接入个推或极光推送;
  • 文件上传:小程序选择图片走uni.chooseImage,APP端可能需要考虑拍照权限。

除此之外,页面布局、接口请求、状态管理都可以共用一套代码。在项目里我用uni.getSystemInfo去区分不同端,再用#ifdef MP-WEIXIN#ifdef APP-PLUS做局部差异处理,整体维护成本比两套原生省了至少60%。

1.3 心理健康场景的功能边界:不是“功能越多越好”

做心理健康类APP,第一个要考虑的不是功能多少,而是边界。这类产品关乎用户的隐私和心理状态,设计不好容易产生误导,甚至带来二次伤害。

我参考了市面上几款成熟的心理健康产品,把功能收敛成了四大块:

  1. 心理测评:包含SDS抑郁自评量表、SAS焦虑自评量表、SCL-90症状自评量表等标准化量表,用户在线做完后生成测评报告;
  2. 咨询预约:展示可预约的心理咨询师列表,按时间段预约,支持取消;
  3. 知识科普:心理文章、减压技巧、正念练习等图文内容;
  4. 我的记录:查看历史测评报告、预约记录、个人资料。

这里有一个很重要的设计取舍:不提供“心理问答社区”。原因很现实,社区内容需要大量人力审核,心理健康内容的审核标准又很难量化,一旦出现不当言论、极端案例甚至教唆倾向,平台要承担很大责任。所以我个人建议,除非有专业心理团队支持,否则不要轻易上开放社区模块。如果用户有倾诉需求,可以做成“匿名留言箱”,由心理咨询师在后台回复,这种单向、可控的沟通模式安全得多。

2. 核心功能模块设计与实操拆解

2.1 测评模块:量表管理、计分逻辑、报告生成

测评模块是整个系统的核心功能,也是最容易出错的地方。先说量表数据模型怎么设计。每份量表包含:量表基本信息(名称、类型、题目总数、计分方式)、题目表、选项表、维度表。

以SDS抑郁自评量表为例,它包含20个题目,每个题目有4个选项(从“没有或很少时间”到“绝大部分或全部时间”),前10题为正向计分(1-4分),后10题为反向计分(4-1分)。总分乘以1.25取整数部分就是标准分,根据标准分来判定抑郁程度。

这个逻辑听起来不复杂,但实现时有几个细节容易被忽略:

  • 反向计分不能写死。量表可能随时被后台编辑调整,如果代码里写死“第11-20题反向计分”,一旦题目顺序调整,计分就错了。我建议在题目表里加一个reverse_score字段,标记这个题目是否反向计分。
  • 量表版本管理。同一份量表如果修改过,历史测评记录对应的是旧版的题目顺序和计分规则。所以在测评记录表里要保存当时作答的题目快照,不能只存一个总分。不然新版量表上线后,历史报告就可能对不上了。
  • 报告生成的边界判断。测评结果需要分档位(正常/轻度/中度/重度),每个档位对应不同的建议文案。建议文案需要心理专业人员的审核,不能随便写,特别要加上“本报告仅供参考,不能替代专业医疗诊断”的免责说明。

这块在实际开发中一般是这样的流程:管理员在后台维护量表内容 → 小程序端根据量表ID拉取题目列表 → 用户提交答案 → 后端计算分数并生成报告 → 报告存储到测评记录表 → 用户查看报告。

我在小程序的测评页面做了一个“中断续答”的功能:用户做题做到一半退出,下次进入还能继续。实现思路也很简单,前端组件销毁时把当前题目序号和已选答案存到uni.setStorageSync,再次进入时先检查本地缓存,有缓存就提示从上次位置继续。

2.2 咨询预约模块:时间段冲突与并发处理

咨询预约模块的难点不在页面,而在时间段冲突的处理。一个咨询师一周开放多个时间窗口,每个窗口只允许一个学生预约,而学生可以随时提交预约请求,如果后端不做控制,就会出现“同一个时间段被两个人同时预约成功”的竞态问题。

我用的方案是MySQL的悲观锁 + 事务:

// 预约时,先锁定咨询师某个时间段 $schedule = Schedule::where('id', $scheduleId) ->lock(true) // 开启悲观锁 ->find(); if ($schedule->status != 1) { return error('该时间段已被预约'); } Db::startTrans(); try { // 更新时间段状态为已预约 $schedule->status = 2; $schedule->student_id = $userId; $schedule->save(); // 插入预约记录 Appointment::create([...]); Db::commit(); } catch (\Exception $e) { Db::rollback(); return error('预约失败,请重试'); }

lock(true)在ThinkPHP中会生成FOR UPDATE语句,把这一行锁住。事务提交前,其他事务查询到这条记录会阻塞等待,从而避免超卖。实际压测下来,200个并发请求同时预约同一个时间段,只会有1个成功,其余全部返回“该时间段已被预约”。

有一个体验细节也值得提:预约状态的可视化。咨询师后台排好班后,小程序端要实时看到哪些时间段是空的、哪些已经被约满了。这里我做了接口缓存,预约成功后主动清理对应咨询师有日期段的缓存,而不是被动等缓存过期,这样数据一致性体验好很多。

2.3 资讯与内容安全:人工智能过滤 + 人工审核

心理健康类的文章内容比较特殊,要避免出现“自我诊断”“药物治疗建议”等引导性过强的表述。我在内容管理后台加了两道审核:

第一道是敏感词和风险词过滤,包含心理危机、自伤、自杀等关键词的内容会自动进入“人工待审”状态,不会直接发布。这里要注意,过滤规则不能只做“包含即删”,因为用户可能是在分享自己如何度过低谷期,内容本身是正向的。所以敏感词命中的内容一律转人工审核,由管理员判断。

第二道是发布后举报处理。用户看到不当内容可以举报,举报数量超过阈值后内容自动下架并通知管理员复核。

技术上就是一张文章表加一个check_status字段,0=草稿、1=待审核、2=已发布、3=已下架。发布接口只返回已审核通过的列表,管理后台单独看待审核列表。

2.4 紧急求助功能:不硬编码,数据可配置

“紧急求助”是心理健康类APP比较有特色的功能。我做的设计很简单:首页固定位置放一个“我需要帮助”按钮,点击后显示心理援助热线电话、校内心理咨询中心预约入口和一段安抚文案。

这里涉及一个合规细节:热线电话和线下求助渠道必须可配置,不能写死在代码里。因为学校每年的值班电话可能会变,如果写死了,换号码意味着要重新发版审核。我把它放到后台“系统设置”表里,通过接口下发,这样管理员直接在后台改就能生效。

3. 技术架构与关键实现细节

3.1 后端API接口设计:统一返回、统一异常

整个项目用的是前后端分离的接口模式,前端是小程序/APP,后端只提供JSON格式的API。接口设计了统一的返回结构,方便前端做统一处理:

{ "code": 0, "message": "success", "data": {} }
  • code=0表示成功;
  • code非0表示业务错误,比如10001表示未登录,10002表示参数错误;
  • message是对错误的中文描述,前端会直接弹出提示。

我封装了一个ApiResponse类来统一生成这个结构,控制器里只要返回业务数据,响应格式自动套好。

还有全局异常处理,实现思路是:在应用初始化阶段注册一个异常处理器,所有未捕获的异常统一转成上面的JSON格式。这样即使代码里某个同学没写try-catch,也不会把PHP的报错信息直接暴露给前端,提高安全性的同时,也让联调时排查问题更方便。

// 全局异常处理逻辑 public function render($request, \Throwable $e) { if ($e instanceof ValidateException) { return json(['code' => 10002, 'message' => $e->getError(), 'data' => null]); } if ($e instanceof \Exception) { return json(['code' => 500, 'message' => '服务器繁忙,请稍后重试', 'data' => null]); } return json(['code' => 500, 'message' => $e->getMessage(), 'data' => null]); }

这样做还有一个好处:前端对接时只需要在请求封装里统一判断code,等于全项目只维护一套错误处理逻辑,不用每个页面单独写异常分支。

3.2 数据库设计:从用户到测评记录的完整表结构

数据库设计这块我直接给出核心表结构和设计理由,你可以根据自己的需求做增减。

用户表user

字段名类型说明
idint主键
openidvarchar(64)微信小程序端唯一标识
phonevarchar(20)手机号(APP端登录用)
nicknamevarchar(50)昵称
avatarvarchar(255)头像URL
roletinyint0=学生,1=咨询师,2=管理员
create_timedatetime创建时间

微信小程序和APP端用户登录方式不一样,所以这里我预留了两个字段:openid对应小程序,phone对应APP的账号密码/短信验证码登录。如果同一个用户在小程序和APP都登录,这里的处理逻辑需要做一个绑定,但因为是校园场景,学生一般只会用一种端,我没有做太复杂的账号打通,而是让两种登录方式各自生成独立的用户记录。

量表相关表

  • scale:量表ID、名称、描述、类型(SDS/SAS/SCL-90);
  • scale_question:题目ID、量表ID、题目内容、计分方向(正向/反向);
  • scale_option:选项ID、题目ID、选项文本、选项分数;
  • scale_report_template:模板ID、量表ID、分数区间、建议内容。

测评记录表assessment_record

字段名类型说明
idint主键
user_idint用户ID
scale_idint量表ID
total_scoredecimal原始总分
standard_scoredecimal标准分
leveltinyint档位:1正常,2轻度,3中度,4重度
answer_snapshottext作答快照JSON
create_timedatetime测评时间

answer_snapshot存储的是用户对每个题目的选项ID和选项分数,存的是JSON字符串,方便报表展示和日志追溯。即使以后量表内容改版,历史记录仍然可以按照当时的快照生成完整报告。

预约相关表

  • schedule:排班ID、咨询师ID、预约日期、开始时间、结束时间、状态(1空闲,2已约,3已被管理员锁定);
  • appointment:预约ID、学生ID、排班ID、预约状态(1待确认,2已确认,3已完成,4已取消)、备注。

3.3 小程序端微信登录:从code到session_key完整流程

微信小程序的登录流程是每个做小程序的人都要过的第一关。整体链路是这样:

  1. 前端调用uni.login()获取临时code
  2. 前端把code通过后端API传给PHP;
  3. PHP调用微信jscode2session接口,传入appidsecretcode,换取用户唯一的openid和会话密钥session_key
  4. PHP用openiduser表里查询用户是否存在,不存在则创建新用户;
  5. PHP生成一个自定义的登录凭证token返回给前端,后续所有请求都带这个token来识别用户身份。

这里有几个坑必须提醒:

  • 不要在前端把appidsecret打包进代码里,secret尤其敏感,必须在后端保存和调用;
  • code只能用一次,而且有效期很短(大概几分钟),所以前端每次登录都要重新uni.login()获取新code,不能缓存;
  • 小程序端的openid在不同小程序之间是不通用的,如果以后要换小程序主体,用户数据会隔离;
  • 后端生成token时,建议用random_bytes生成随机字符串,并设置有效期(我设的是7天),不要用单纯的user_id直接当token,容易被伪造。

PHP端核心代码逻辑:

public function login(Request $request) { $code = $request->param('code'); if (empty($code)) { return error('code不能为空'); } $appid = config('wx.appid'); $secret = config('wx.secret'); $url = "https://api.weixin.qq.com/sns/jscode2session?appid={$appid}&secret={$secret}&js_code={$code}&grant_type=authorization_code"; $result = json_decode(file_get_contents($url), true); if (isset($result['errcode'])) { return error('登录失败:' . $result['errmsg']); } $openid = $result['openid']; $user = User::where('openid', $openid)->find(); if (!$user) { $user = User::create([ 'openid' => $openid, 'nickname' => '微信用户', 'create_time' => date('Y-m-d H:i:s') ]); } $token = bin2hex(random_bytes(32)); // 将token存入缓存或数据库,并设置过期时间 cache('user_token_' . $token, $user->id, 604800); return success(['token' => $token, 'user_id' => $user->id]); }

3.4 前端请求封装:统一携带token、统一处理状态码

小程序端我做了一个request.js统一封装,核心思想就是所有网络请求都走同一个函数,自动带上token、统一处理响应码、统一弹出错误提示:

const request = (url, method, data) => { return new Promise((resolve, reject) => { uni.request({ url: BASE_URL + url, method: method || 'GET', data: data || {}, header: { 'Content-Type': 'application/json', 'Authorization': uni.getStorageSync('token') || '' }, success: (res) => { if (res.data.code === 0) { resolve(res.data.data) } else if (res.data.code === 10001) { // 未登录,跳转登录页 uni.navigateTo({ url: '/pages/login/login' }) reject(res.data) } else { uni.showToast({ title: res.data.message, icon: 'none' }) reject(res.data) } }, fail: (err) => { uni.showToast({ title: '网络异常', icon: 'none' }) reject(err) } }) }) }

这里有一个实际踩坑的地方:PHP端接收Authorization头时,Nginx默认会把它转发给PHP-FPM,但在某些环境里,HTTP_AUTHORIZATION变量没有正确传递,导致后端取不到token。解决办法是在Nginx配置里加一行:

fastcgi_param HTTP_AUTHORIZATION $http_authorization;

如果不加这一行,你会发现小程序端明明刚登录成功,下一次请求却被判定为未登录,排查起来非常隐蔽。

3.5 PHP接口跨域处理

小程序/APP端调用接口不涉及浏览器跨域问题,但如果是H5端,或者后端需要在本地调试时用Vue开发服务器,就绕不开跨域。

PHP端处理跨域最省事的方式是写一个中间件:

public function handle($request, \Closure $next) { header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, Authorization'); if ($request->isOptions()) { exit; } return $next($request); }

这个中间件放在应用全局中间件列表里,所有接口自动带上跨域头。注意预检请求OPTIONS要直接返回,不进入业务逻辑,否则前端会报“CORS错误”。

4. 实操记录:从零到一搭建核心业务流程

4.1 环境准备:PHP版本、Composer依赖、数据库初始化

这个项目我用的PHP 8.1 + ThinkPHP 8.0 + MySQL 8.0,本地开发用PHP内置服务器,上线部署用Nginx + PHP-FPM。

环境准备阶段,我先建好了数据库,然后按照之前设计好的表结构建表。这里建议直接用数据库迁移工具,或者至少把SQL文件放到项目版本管理里,不要只在本地手动建,方便其他人快速把项目跑起来。

依赖安装用Composer:

composer create-project topthink/think tp

ThinkPHP 8的目录结构和之前的版本有些差异,app目录下按模块分:controller、model、validate、middleware,每个模块按业务领域拆。

4.2 测评计分接口实现:从提交答案到生成报告全流程

测评提交流程是整个项目里逻辑最复杂的一块。我按步骤拆解:

前端把用户的答案拼成JSON格式提交:

{ "scale_id": 1, "answers": [ {"question_id": 1, "option_id": 2}, {"question_id": 2, "option_id": 1} ] }

后端接收到之后,按这样的顺序处理:

  1. 校验题目数量是否完整(防止用户漏题);
  2. 查询当前量表所有题目,根据reverse_score字段确定每个题目的正反向;
  3. 根据选项ID查出每个选项对应的分数,换算成最终分数;
  4. 累加所有题目得分得到总粗分,总粗分乘以1.25取整数部分得到标准分;
  5. 根据标准分所在区间,匹配scale_report_template表中对应的报告模板;
  6. 把总分、标准分、档位、作答快照存到assessment_record表;
  7. 返回报告内容给前端展示。

这里有个容易被忽略的业务点:同一用户对同一量表不能无限次测。虽然心理健康测评不是考试,不需要限制次数,但为了防止某些人恶意刷接口、或者在情绪不稳定时频繁测试导致数据异常,我设置了一个间隔限制,同一用户对同一量表24小时内只能测一次,后台可以配置是否开启限制。

核心计分代码:

public function submit(Request $request) { $params = $request->param(); $this->validate($params, [ 'scale_id' => 'require|integer', 'answers' => 'require|array' ]); $scaleId = $params['scale_id']; $answers = $params['answers']; // 查询量表所有题目 $questions = ScaleQuestion::where('scale_id', $scaleId) ->order('sort_order', 'asc') ->select() ->toArray(); $questionMap = []; foreach ($questions as $q) { $questionMap[$q['id']] = $q; } if (count($answers) != count($questions)) { return error('题目未作答完整'); } $totalScore = 0; $snapshot = []; foreach ($answers as $answer) { $questionId = $answer['question_id']; $optionId = $answer['option_id']; $question = $questionMap[$questionId] ?? null; if (!$question) { return error('包含无效题目'); } $option = ScaleOption::where('id', $optionId) ->where('question_id', $questionId) ->find(); if (!$option) { return error('包含无效选项'); } $score = $option['score']; // 反向计分题目:4 - 原分数 + 1 if ($question['reverse_score'] == 1) { $score = 5 - $score; } $totalScore += $score; $snapshot[] = [ 'question_id' => $questionId, 'question' => $question['content'], 'option_id' => $optionId, 'option' => $option['content'], 'score' => $score ]; } $standardScore = floor($totalScore * 1.25); // 匹配报告模板 $template = ScaleReportTemplate::where('scale_id', $scaleId) ->where('min_score', '<=', $standardScore) ->where('max_score', '>=', $standardScore) ->find(); $level = $template['level'] ?? 1; $record = AssessmentRecord::create([ 'user_id' => $request->userId, 'scale_id' => $scaleId, 'total_score' => $totalScore, 'standard_score' => $standardScore, 'level' => $level, 'answer_snapshot' => json_encode($snapshot), 'create_time' => date('Y-m-d H:i:s') ]); return success([ 'record_id' => $record->id, 'standard_score' => $standardScore, 'level' => $level, 'report' => $template['content'] ?? '' ]); }

4.3 咨询预约超时处理:怎么样防“占着茅坑不拉屎”

预约场景里还有一个常见问题:学生预约了某个时间段,但因为有事没来,也不取消,导致咨询师的时间被浪费。

我做了两个机制来缓解:

一个是预约前要先做心理测评,系统会在用户没有近期测评记录时提示“建议先完成一次测评,以便咨询师了解你的情况”,但不强制。这样一方面保证用户真的有咨询需求,另一方面也降低了随意预约的概率。

另一个是后台定时任务:预约时间开始前2小时,系统会通过小程序订阅消息提醒用户;如果用户多次“预约未到”,系统自动限制该用户两周内不能再次预约。

定时任务在ThinkPHP里可以用命令行方式跑:php think reminder,配合服务器crontab每天凌晨或每小时执行一次。这在部署时比较容易遗漏,建议在项目部署文档里单独写一节说明。

4.4 咨询师后台:排班、报表和隐私保护

咨询师端我做了独立的登录入口和页面,主要功能是排班和查看预约记录。排班操作很简单:选择日期、选择时间段、点击“开放预约”,一分钟搞定一周的班表。

咨询师查看用户资料时,有一个设计很关键:看不到用户的完整姓名和学号,只显示脱敏后的用户编号,比如“S20250001”。用户的心理测评报告也只显示分数区间和建议内容,不显示原始题目和答案。这是心理健康相关系统非常重要的隐私设计,从产品层面就尽量降低敏感数据暴露面。

4.5 APP端能力扩展:通知推送与本地存储

小程序端的消息触达用的是订阅消息,但APP端没有微信订阅消息可用。我自己实装的是uni-push方案,因为uniapp内置了uni-push的支持,对接个推服务,可以做到APP端的推送通知。后端PHP想要推送消息给用户时,调用一个统一的消息推送服务封装,该封装内部根据用户的设备类型分别调用微信订阅消息接口或个推API。

本地存储方面,小程序有wx.setStorageSync,APP端在uniapp里统一用uni.setStorageSync,这个API在两端都能正常工作。用户上次测评做到一半的缓存、首页是否看过“使用须知”之类的标记,都直接存本地,后端不需要额外的接口维护。

5. 常见问题与排查技巧实录

5.1 PHP接口偶现返回502,Nginx日志显示连接被重置

这个问题是我做联调阶段遇到的第一个比较诡异的Bug。小程序端偶尔请求失败,后台Nginx错误日志显示“upstream prematurely closed connection while reading response header from upstream”。

排查思路一步步来:

  • 先看PHP错误日志,没有明显的Fatal Error;
  • 再检查PHP-FPM状态,发现高峰期进程数打满,新请求排不上队,导致Nginx超时断开连接;
  • 最后定位到一台1核2G的服务器上,PHP-FPM默认的pm.max_children是5,明显不够用。

解决办法:调整PHP-FPM配置,把pm.max_children从5调到30,pm.start_servers从2调到5,同时开启pm.status_path方便以后监控。调完之后,再压测200并发接口测试,稳定了很多。

如果你是本地开发环境遇到502,大概率是端口占用或者PHP内置服务器崩了,先重启再排查。

5.2 小程序登录流程:reset code后再登录就报错

有一个被问得非常多的问题:为什么uni.login()获取的code传给后端后,后调用微信接口返回40029错误?

这个错误码对应的含义是invalid code。常见原因:

  1. code被重复使用:同一个code只能用一次,第一次用完之后再次使用就会报错;
  2. code过期:微信的code有效期很短,前端如果隔了很久才把它传到后端,后端再调微信接口,就可能已经过期;
  3. 小程序AppID与Secret不匹配:这个属于配置问题,检查后端配置文件里的appidsecret是否跟小程序后台一致。

后来我还在前端做了一个优化:把登录态判断提前。进入小程序时先检查本地有没有token,有且未过期就默认已登录,只有接口返回10001时才触发重新登录,避免每次冷启动都走一遍微信登录流程。

5.3 测评报告结果和用户预期不符:反向计分Bug排查

还有一次测试反馈说“SDS量表测出来分数很高,但感觉不对”。排查之后发现,反向计分的规则被我写反了。SDS的10个反向题目应该用“4 - 原始分数”,但做的时候被“5 - 分数”的逻辑弄混了,导致反向题的得分全部加错,整体分数偏高。

这个问题说明:正向和反向计分题混在一起时,一定要拿一个真实的人工测评案例来验证分数。不要只做单元测试,要实际跑一次完整流程,把输出和已知标准比对。后来我专门建了一个测试用户,用一套固定答案反复测试,把每次计算的原始分和标准分打出来核对。

5.4 小程序端图片上传失败:域名白名单限制

项目里用户可以在“个人资料”页上传头像。本地调试时一切正常,但用真机测试时发现上传失败,报错信息是“不在以下uploadFile合法域名列表中”。

这是因为微信小程序要求所有请求的域名都必须在小程序后台配置合法域名,本地开发环境使用了http://localhost,不属于合法域名。解决办法是在微信开发者工具中勾选“不校验合法域名”,真机预览时需要把后端代码部署到配置了HTTPS域名的服务器上,并在小程序后台添加对应的uploadFile合法域名。

如果买不起证书,可以先用云开发环境或者内网穿透工具做临时调试,但上线时必须有合法HTTPS域名。

5.5 心理健康类内容审核:防止敏感词误伤

我建了一个敏感词表,但第一次上线后就收到用户投诉,说正常的“抑郁情绪自我调节”文章被拦截了。因为我把“抑郁”直接加进了敏感词库,导致所有提到“抑郁”的文章都被误伤。

后来调整规则:把词库分成两类,一类是“强制拦截词”(如明确的危机求助信号、违法违规词),另一类是“人工审核词”(如“抑郁”“焦虑”“自杀”等心理健康相关的常见词),遇到第二类词不直接拦截,而是自动标记为待审,由运营人员判断。这样既保证内容安全,又避免误伤正常科普内容。

后记:一点个人体会

这个项目做下来,我最大的感受是:心理健康类应用对后端开发者的要求不只是写出能跑的接口,还要理解业务场景里的分寸感。哪些数据该脱敏、哪些内容该审核、哪些功能不能只考虑“实现出来”而更要考虑“会不会造成误导”,这些都是普通CRUD项目里不会遇到的。

技术层面,PHP + uniapp这套组合做校园类业务系统,确实是性价比很高的选择,开发速度快、部署成本低、维护也简单。如果你也是一个人要搞定前后端,可以参考这个方案。但如果你做的系统对实时性要求很高、并发量预期很大,那还是要认真评估PHP的定位,或者在架构上引入更合适的组件。

关于这个项目的扩展方向,我觉得最有价值的是把测评数据做成趋势分析,用户可以按周/月维度查看自己心理状态的变化曲线,这对心理健康的持续关注会很有帮助。另外一个方向是和企业微信打通,让辅导员在合规前提下看到学生的预警信息,但这个涉及比较复杂的授权和隐私审批流程,暂时没有在这个版本里做。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询