1. 项目概述与整体架构思考
1.1 家校联系场景里的真实痛点
我前阵子接了一个家校通平台的项目,技术栈直接锁定了微信小程序、PHP、Vue这三件套。为什么这么选,后面细说,先把业务场景讲明白。传统家校联系基本靠微信群和电话,老师发个通知要挨个确认家长有没有看到,作业布置了还要担心刷屏被顶掉;家长这边想知道孩子今天有没有按时到校、作业有没有交,只能主动去问老师。这个项目要解决的就是这些具体问题:通知公告的精准触达、作业的布置与提交、学生请假审批、每日考勤记录、成绩发布留痕。
我服务的客户是一所民办学校,他们之前用过某款第三方家校App,抱怨最多的是两件事:一是App下载安装成本高,家长不愿意装,激活率一直上不去;二是后台功能太死板,班主任想要的一些自定义表单根本没法改。换到微信小程序之后,家长扫个码就能用,天然免安装,触达率一下就上来了。管理端用Vue做Web后台,老师在学校电脑上就能完成所有操作,灵活度也高,想加什么功能直接改前端再对接PHP接口就行。
1.2 三方终端的分工与数据流转
整个系统拆成三个端:家长端是微信小程序,核心使用场景是查通知、看作业、请假、看考勤;教师端是Vue单页后台,核心使用场景是发通知、布置批改作业、处理请假审批、录入成绩;服务端是PHP提供的RESTful API,统一处理业务逻辑和数据存储。
很多人在设计这类系统时会纠结一个问题:小程序端和管理后台是不是要分别做两套接口?我的做法是共用同一套PHP接口,通过不同的鉴权方式区分角色。小程序端用微信登录态换取身份凭证,Web后台则用传统的账号密码加JWT令牌,两端调用同一个业务接口,服务端根据令牌解析出用户角色,再做数据权限控制。这样做的最大好处是业务逻辑只维护一份,后面改需求时不会出现两套接口对不上的情况。
数据流转大概是这样的:老师在Vue后台发布一条班级通知,Vue前端把表单内容通过POST请求提交给PHP接口,PHP把数据写入MySQL的notice表,同时往班级学生的关联表里写入待读记录;家长打开小程序,页面加载时调用PHP的通知列表接口,服务端根据当前用户的学生身份查出该班级的所有通知,返回给小程序渲染;家长看完通知后点击确认收到,小程序再提交一个已读回执。整个过程看起来简单,但里面每个环节都需要处理细节:分页加载、登录态过期、图片上传、消息触达,这些我后面都会展开讲。
1.3 为什么是“小程序 + Vue + PHP”这个组合
先聊聊技术选型的逻辑。微信小程序作为C端载体几乎是这个场景下的最优解,家长不需要下载安装,微信扫一扫就能进入,而且微信生态内的订阅消息可以直接触达用户,这在App体系里要复杂得多。教师管理后台选Vue,是因为后端的典型形态就是大量表单、表格、弹窗交互,Vue的组件化开发方式在这种场景下效率极高,配合Element UI组件库,一个通知发布页面半天时间就能写完,这在传统的jQuery时代是不可想象的。
PHP在这个项目里看中的倒不是性能,而是交付速度和部署便利。家校通这类项目通常对并发要求不高,一个学校几千个家长,PHP配合MySQL完全能扛住。而且很多中小型服务商的服务器本身就是LNMP架构,PHP部署上去几乎没有额外成本,出问题也好排查。我在项目里用的是ThinkPHP 8这个框架,路由、ORM、验证器都是现成的,开发效率和代码规范都有保障。如果团队更熟悉Laravel也可以,思路完全一样。
在实际开发中我建议把接口设计当成一个独立的环节来做,先用文档把所有接口的请求参数和返回结构定下来,再让小程序端和Vue端并行开发。我做这个项目时就是先写了一份接口文档,包含登录、通知、作业、请假、考勤、统计六大模块,大约40个接口,前后端并行开发两周,联调阶段几乎没有出现接口理解不一致的问题。
2. 技术选型依据与核心细节拆解
2.1 PHP服务端的框架选择与接口规范
PHP端我最终定了ThinkPHP 8,主要原因是它内置了validate验证器、JWT扩展支持、以及非常方便的路由分组能力,比较适合这类多模块业务系统。项目里我把接口按模块拆成了几个路由分组:auth(登录鉴权)、notice(通知)、homework(作业)、leave(请假)、attendance(考勤)、user(用户管理),每一组下面再挂具体的资源路由。
接口规范方面,我统一约定返回格式为JSON结构:
{ "code": 0, "msg": "success", "data": { "list": [], "total": 120, "page": 1, "page_size": 10 } }code为0代表成功,非0代表业务异常,msg携带具体的错误信息。分页接口统一接收page和page_size两个参数,列表数据放在data.list里。这个约定在实践中有个明显的好处:小程序端无论对接哪个模块,处理逻辑都是一套,前端只根据code判断结果,根据data渲染内容,开发效率高很多,也基本不会出现后端返回结构不一致导致的联调问题。
鉴权方案用JWT。小程序登录成功后,PHP端下发一个token,小程序每次请求在header里带上Authorization: Bearer token。服务端中间件统一解析token并获取当前用户信息,然后注入到请求上下文里。对于Web后台,登录成功后发同样的token,前端存在localStorage里,每次请求通过axios拦截器自动带上。角色鉴权我用了一个简单的注解方式,在控制器方法上用注释声明允许的角色类型,中间件读取后校验,实现起来干净利落。
2.2 Vue管理后台的路由、权限与状态管理
Vue端用的是Vue 3 + Vite + Element Plus这套组合。管理后台的页面不外乎登录页面、首页看板、班级管理、通知管理、作业管理、请假审批、考勤记录、成绩管理这些模块。我把每个模块都拆成独立的视图组件,通过vue-router配置路由。
权限控制这里有几个细节值得展开。家校通平台的用户角色至少分三种:系统管理员、教师、班主任。班主任比普通教师多出了班级管理和成绩发布权限。我在路由守卫里做了动态权限判断,路由meta字段里声明该页面允许的角色数组,beforeEach守卫里判断当前用户角色是否在允许列表中。比如请假审批页面meta配置为roles: ['admin', 'head_teacher'],普通教师访问时直接重定向到首页。
Vue端在接口交互上有个容易踩的坑:跨域配置。开发环境下Vite的proxy配置把/api前缀代理到PHP服务地址,生产环境则由nginx把/api路径转发给PHP服务,这样前后端代码里都只用相对路径和统一前缀。我在项目里就是前后端分开部署,前端静态文件放在nginx的wwwroot,PHP服务跑在同一台服务器的9000端口上,nginx配置了两条location规则,一个指向前端静态目录,一个以 /api 开头反向代理到PHP进程,整个部署结构非常清晰。
2.3 小程序端的高频细节清单
小程序端的开发工具用的是微信开发者工具,框架原生语法,没有引入uniapp。虽然uniapp在跨端上有优势,但这个项目明确只做微信生态,用原生语法访问微信接口是最直接的,比如订阅消息、手机号快速验证、小程序码生成这些能力,原生组件调用起来延迟最低,也没有兼容性损耗。实际写下来,原生开发的页面结构反而更清晰,wxml、wxss、js三个文件对应一个页面,逻辑直观。
项目里高频用到的几个小程序能力,我列一个实际操作清单:
- wx.login 获取code,后端换取openid和session_key,再生成自定义登录态token。
- wx.request 统一封装Promise请求函数,所有接口都走这个封装,自动携带登录token,自动处理code为401时的登录过期跳转。
- wx.pageScrollTo 和 onReachBottom 配合做列表触底加载更多。
- wx.uploadFile 配合chooseMedia实现图片上传。
- wx.requestSubscribeMessage 实现订阅消息授权,授权后后端通过接口触发订阅消息推送。
- wx.getMenuButtonBoundingClientRect 获取胶囊按钮位置,配合导航栏的自适应适配。
关于顶部导航栏高度适配,这里有一个我从实践中总结的经验。微信小程序的导航栏在不同机型上高度不一样,状态栏高度用wx.getSystemInfoSync().statusBarHeight获取,但导航栏的真实高度不能简单认为是44px或48px,因为它是胶囊按钮所在区域决定的。最稳妥的做法是用wx.getMenuButtonBoundingClientRect()拿到胶囊按钮的top和height,然后通过这个值动态计算导航栏高度和内容布局的top位置。特别是自定义导航栏的场景,这个适配做不好,在全面屏手机上会出现按钮错位的情况,我在项目里就是封装了一个nav-bar自定义组件,内部动态计算高度,所有页面统一使用。
单选框在处理性别、状态选择时,用原生radio-group组件加上自定义样式,比第三方组件少很多兼容性问题。这个项目里用在请假表单里选择请假类型,病假、事假、公假,加上一个调休选项,学生选完后直接提交,逻辑很轻。
2.4 数据库设计与模型关系
数据库层面我设计了这样几张核心表:users(用户账号)、user_profiles(用户扩展信息,手机号、头像、角色)、classes(班级)、students(学生档案)、class_members(班级成员关联)、notices(通知公告)、notice_reads(通知阅读回执)、assignments(作业)、assignment_submissions(作业提交)、leave_requests(请假申请)、attendance_records(考勤记录)、scores(成绩记录)。
用户、学生、家长之间的关系是这个系统的关键。一个家长账号可以关联多个孩子,一个孩子属于一个班级,一个班级有一个班主任和若干任课老师。我在users表里存了role字段区分管理员、教师、家长,在user_profiles里通过student_id字段把家长账号和孩子关联起来。这样设计的好处是,一个家庭如果有两个孩子在不同班级,家长小程序里可以切换当前查看的孩子,系统根据student_id切换数据范围,这个需求在真实学校场景里非常常见。
数据库建表时有个容易忽略的细节:几乎所有业务表都要冗余一个school_id或class_id字段,用于数据隔离和查询加速。家校通平台后期如果要做多学校版本,这些冗余字段会非常关键,按学校维度做数据筛选就是一条where条件的事,不需要去反查关联表。这个项目我已经按多学校可扩展的模型设计了,每个用户在users表里有school_id,每条业务数据也有school_id,虽然当前只需要单学校,但扩展性预留好了。
3. 核心功能模块的实现过程
3.1 微信登录与身份绑定流程
小程序端登录流程是:前端调用wx.login获取临时code,把code发送给后端login接口;后端拿着code去微信官方接口请求openid和session_key,然后根据openid查询用户,如果第一次使用则自动创建账号,生成JWT token返回给前端。整个过程中,小程序端不需要自己保存微信的session_key,也不需要知道openid,这些敏感信息统一保存在服务端。
登录之后有一个关键的绑定步骤:家长需要绑定孩子。实际场景是家长首次进入小程序时,会看到一个绑定孩子的引导页,输入孩子姓名和班级,后端根据这两个条件匹配学生档案,匹配成功后建立家长与学生的关联关系。这个设计比手机号验证码绑定更简单,而且不需要老师参与维护。我在验证阶段增加了班主任审核机制,家长发起绑定请求后,对应班级的班主任在Vue后台会看到一条待审核记录,确认无误后点击通过,这样能够避免误绑或者有人恶意关联他人孩子信息的情况。虽然多了一步审核流程,但确实更安全,家长和老师对系统的信任度都更高。
如果后续接入手机号快速验证,在小程序里用button组件的open-type="getPhoneNumber"就能拿到微信提供的手机号,提交到后端保存到user_profiles表。这个能力对老用户补全信息很有用,我留了接口,后面迭代时随时可以接上。
3.2 列表页加载更多的完整实现
列表页加载更多是“页面列表加载更多”这个搜索热词背后指向的核心功能,在小程序里几乎每个业务页面都会用到:通知列表、作业列表、请假记录、考勤历史,全都是分页列表。如果实现不严谨,会出现数据重复、错位、loading闪烁、反复触发加载等问题。
我封装了一个通用的分页加载逻辑,核心实现是这样的:
// 通用分页逻辑,page.js中 const DEFAULT_PAGE_SIZE = 10 Page({ data: { list: [], page: 1, page_size: DEFAULT_PAGE_SIZE, has_more: true, loading: false, loading_more: false, }, async fetchList(reset = false) { const page = reset ? 1 : this.data.page if (!reset && (!this.data.has_more || this.data.loading_more)) return this.setData(reset ? { loading: true } : { loading_more: true }) try { const res = await api.getNoticeList({ page, page_size: this.data.page_size, }) const list = reset ? res.list : this.data.list.concat(res.list) const has_more = list.length < res.total this.setData({ list, page: page + 1, has_more, loading: false, loading_more: false, }) } catch (e) { this.setData({ loading: false, loading_more: false }) wx.showToast({ title: '加载失败', icon: 'none' }) } }, onPullDownRefresh() { this.fetchList(true).then(() => wx.stopPullDownRefresh()) }, onReachBottom() { this.fetchList() }, })这段代码里有几个关键点。第一,has_more的判定用的是list长度小于total,这个比判断返回条数是否等于page_size更可靠,因为最后一页可能刚好等于page_size,用条数判断会导致多请求一次空数据。第二,loading_more和loading是两个独立状态,首屏加载显示loading,触底加载显示“加载中”提示,不能混用。第三,fetchList开始的if条件避免了并发重复请求,这对触底加载特别重要,因为onReachBottom在快速滚动时可能连续触发多次,如果没有这一层防抖,会出现同一页数据被重复拼接的情况。
下拉刷新用onPullDownRefresh配合enablePullDownRefresh页面配置,数据重置后重新加载第一页。还有一个小细节,列表页从详情页返回时,小程序默认会保留之前的页面状态,但通知列表这种场景用户可能希望返回时看到最新数据。我在onShow里加了一个简单的对比策略,如果页面数据不是正在加载中,就静默重新拉取第一页数据,这样既不会闪烁又能保证数据新鲜度。
3.3 通知公告与作业模块的设计要点
通知发布这个功能我花了不少心思。老师发一条通知,不仅要通知到人,还要确认家长是否看到,所以业务上我设计了阅读回执机制。notices表里存通知标题、正文、富文本内容、附件URL、发布人ID、目标班级ID、发布时间。当老师选择目标班级并发布后,PHP接口启动一个事务,先把notice数据写入,然后批量往notice_reads表里插入该班级所有学生的待读记录,is_read默认0。这条批量插入在主数据量不大的时候性能没有问题,几千条记录一次insert就能完成。
小程序端通知详情页加载时,调用已读接口,把当前用户的notice_read记录标记为1。老师在后台的通知列表里可以看到每个学生是否已读,如果超过24小时没有已读,可以一键生成订阅消息提醒,这是后面要讲的订阅消息能力。
作业模块和通知模块在数据模型上有区别。通知是单向的,老师发家长看;作业则有提交反馈环节。assignments表存作业内容、截止时间、发布班级、附件;assignment_submissions表存学生提交的内容和图片。学生在小程序里提交作业时可以上传多张图片,Vue后台看到提交列表后老师可以打分或者写评语反馈。这个闭环设计完整覆盖了“布置作业-学生提交-老师批改”这条主线,在真实使用中很顺畅。
3.4 请假审批的状态机设计
请假审批算是家校通里比较典型的业务流程。status字段我定义为几个状态常量:0待审批、1已通过、2已驳回、3已撤销。学生在小程序端提交请假申请,填写时间范围、事由类型、详细说明,可以上传医院诊断书照片。提交后状态为0,对应班级班主任在Vue后台的待办事项里看到这条记录,点进去查看详情,可以选择通过或者驳回,驳回时必须填写驳回原因。
这个状态机的核心是每一步都要留痕。leave_requests表里除了当前状态字段,我还设计了operation_log字段,存JSON格式的审批记录,包括谁操作的、什么时间、操作结果、备注内容。这样做的好处是后续如果家长和老师之间产生分歧,随时可以调出完整的审批轨迹,这在真实学校场景的纠纷处理中非常有用,我见过太多没有留痕导致扯皮的例子了。
审批结束后,系统通过订阅消息通知家长审批结果。这里要注意订阅消息的单次有效机制,用户每次授权只能收到一次模板消息,所以小程序端在发起请假时会同时引导用户勾选“审批结果通知”的订阅授权,用户允许后,后端在审批完成时才能推送。这个引导要在业务关键时刻做,不能一进小程序就弹窗要授权,那样用户大概率拒绝。
3.5 考勤记录与数据看板
考勤模块是打卡机之外的一个补充方案。学生到校后,在小程序里点击“签到”,记录当前时间和位置。我在这个功能里使用wx.getLocation获取经纬度,PHP端通过学校坐标判断距离是否在合理范围内,比如200米内判定有效签到,这样既实现了考勤功能,也能有效防止代打卡和虚假签到的情况。定位授权失败时会提示手动补签,补签记录进入待审核状态,由班主任确认是否有效。
另一个家长很关心的功能是成绩发布。老师在后台录入成绩,系统自动生成得分和班级排名数据,家长在小程序里可以查看到自己孩子的成绩和评语。这里在设计上有一个重要的隐私考虑:家长只能看到自己孩子的成绩,不能看到其他同学的成绩。
Vue后台的首页我做了一个数据看板,用ECharts展示今日出勤率、待审批数量、近期通知阅读率、作业提交率这些核心指标。这些数据对学校管理者来说价值很高,以前这些数据都靠手工汇总日报,现在系统一打开,所有关键数字一目了然。管理人员还可以按班级对比这些指标,在学期末做班级评估时有数据支撑,比凭印象说话有说服力得多。
3.6 文件上传处理的注意事项
家校通里用到上传的场景很多:作业图片、头像、通知附件、请假诊断书。小程序端用wx.uploadFile上传文件到PHP端,PHP端接收后做校验和存储。这里有一个安全细节容易忽略:前端做了文件类型和大小限制还不够,后端必须再次校验,否则绕过前端直接发请求会把各种文件上传上来。
PHP端的具体逻辑,我用一段代码示例展示:
public function upload(Request $request) { $file = $request->file('file'); if (!$file) { return json(['code' => 1, 'msg' => '未接收到文件']); } if ($file->getSize() > 5 * 1024 * 1024) { return json(['code' => 1, 'msg' => '文件大小不能超过5M']); } $ext = strtolower($file->getExtension()); $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf', 'doc', 'docx']; if (!in_array($ext, $allowed)) { return json(['code' => 1, 'msg' => '文件类型不允许']); } $filename = md5(uniqid() . $file->getRealPath()) . '.' . $ext; $file->move(public_path('uploads/' . date('Ymd')), $filename); return json([ 'code' => 0, 'msg' => 'success', 'data' => [ 'url' => '/uploads/' . date('Ymd') . '/' . $filename ] ]); }这段代码里mime类型和扩展名都要校验,只校验扩展名是不安全的。我遇到过有人把脚本改名成jpg上传的情况,虽然PHP服务器上默认不会执行这种文件,但为了稳妥,校验mime类型这一步不能省。文件名用md5重命名,避免中文文件名和路径穿越问题。按日期分目录存储,文件管理也方便,一个月一个目录,后面清理过期文件容易操作。
4. 常见问题与排查技巧实录
4.1 域名校验和开发环境的跨域问题
微信小程序的request请求有域名白名单机制,正式发布版本只能请求在mp后台配置过的合法域名。开发调试时可以在开发者工具里勾选“不校验合法域名”,但真机预览时这个选项不生效,必须把接口域名配置到后台或者用调试模式绕过。这里的实操建议是:开发初期直接用IP + 端口调试,同时把开发者工具里的域名校验关掉;等后端代码稳定了,再绑定正式域名并完成ICP备案,在微信公众平台配置服务器域名。这个过程要留足时间,因为域名备案通常需要几周到一个月,不要等到要发布时才去备案,项目排期要把这个时间算进去。
跨域问题主要出现在Vue后台开发环境。Vite的proxy配置如下:
// vite.config.js export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })生产环境nginx配置类似这样:
location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }PHP端如果遇到跨域请求,还需要在返回的响应头里加上跨域允许配置,包括允许的Origin、Method、Headers。如果有人直接请求PHP接口而不是走nginx代理,响应头里没有跨域配置一样会报错。
4.2 中文乱码和JSON返回格式的坑
PHP接口返回中文乱码,多半是编码不一致导致的。数据库统一用utf8mb4,PHP文件本身用UTF-8无BOM模式保存,接口返回JSON时设置Content-Type: application/json; charset=utf-8。我在项目里遇到过一个问题:某个表字段存emoji表情时插入失败,后来排查发现是数据库的utf8编码不支持四字节字符,改成utf8mb4后解决了。由于家长在填写信息时经常会输入emoji,这个坑几乎一定会遇到。
另一个编码相关的坑是Vue端显示中文正常但小程序端乱码。这个通常是接口返回的JSON没有正确设置charset,小程序端的wx.request默认按UTF-8解析,如果PHP端没有给Content-Type响应头,部分托管环境下会导致乱码。排查思路很简单,浏览器里直接访问接口URL看返回是否正常,如果浏览器正常而小程序乱码,基本就是响应头的问题。
4.3 体验版分享与试用反馈收集
微信开发者工具里做好的小程序,想发给别人试用,一个常用路径是:点击工具栏的“预览”按钮,会生成一个预览二维码,手机微信扫码就能打开开发版小程序。如果要把版本正式给别人测试,更好的做法是上传代码到微信后台,然后在管理后台把它设为体验版,生成体验版二维码。
体验版和正式版的区别是体验版可以绑定体验成员,只有绑定过的微信账号才能打开。我在项目里是让客户的所有老师都加入了体验成员名单,这样他们每个人扫码都能进入小程序试用。收集试用反馈时,我在后台加了一个反馈入口,测试人员可以提交文字描述和使用截图,数据落到feedback表里。我每隔两天导出一批反馈记录,按“Bug”“体验建议”“新需求”分类整理,然后排进迭代计划。有问题的地方要有具体的操作步骤和设备型号信息,否则很难复现问题。如果测试阶段发现问题,修改后重新上传版本,体验版二维码不变,测试人员刷新即可。
4.4 通过抓包工具调试小程序的网络请求
小程序开发过程中经常需要查看真实的网络请求参数和响应数据,特别是排查登录异常、接口参数错误时,光靠console.log和调试面板不够直观。我自己习惯用Charles做代理抓包调试,整理一下要点,供参考。
抓包小程序的基本配置思路是在电脑上打开Charles,设置SSL代理监听端口,然后把手机WiFi的代理指向电脑IP和端口,安装Charles的CA证书到手机上。之后小程序发起的HTTPS请求就能在Charles里明文查看,包括请求URL、请求头、参数和返回结果。连接后如果请求显示红锁图标,说明证书没有正确信任,需要检查证书安装步骤。
这个抓包方式在真机上尤其有用,因为开发者工具的模拟环境有时候和真机行为不完全一致,比如手机网络环境下接口超时、UA不同导致的兼容问题,不抓真机包很难发现。Vue后台的请求抓包更简单,直接用浏览器开发者工具的Network面板就行,不需要额外配置。
4.5 Vue后台部署后的路由404问题
Vue是单页应用,生产部署时如果用的是history模式路由,用户直接访问某个深层链接(比如手动输入/admin/notices或在后台刷新页面),nginx找不到对应的静态文件就会返回404。解决方法是nginx里配置try_files规则,把所有请求都指向index.html:
location / { try_files $uri $uri/ /index.html; }这个配置在SPA部署里是必选项,我第一次部署时忘了加,结果用户在后台点刷新就直接白屏,排查了半天才发现是路由重定向的问题。另外,Vue打包后的静态资源路径要注意,如果部署在域名根路径下,base配置用默认值即可;如果部署在子目录里,需要设置base为对应路径,并把nginx的location路径改成对应前缀。
4.6 Session过期与登录态失效的处理
小程序端登录态过期,请求接口时返回401。处理策略是全局统一拦截:在请求封装的响应拦截器里判断code,如果是401,清除本地存储的token和用户信息,跳转到登录引导页,提示用户需要重新登录或自动静默登录。这里有个体验细节需要注意,wx.login是静默的,不需要用户操作,用户重新进入小程序后可以直接自动登录,不应该要求用户再输入账号密码,否则体验会很差。
后端JWT过期时间我设置为30天,这样家长在一段时间内不需要反复登录。Web后台则设置短一些,12小时过期,因为教师端数据敏感度高,而且在学校场景使用频率高,每次上班时重新登录一次完全可以接受。Vue端的axios拦截器在遇到401时自动跳转登录页,并带上redirect参数,登录成功后回到之前的页面。
4.7 PHP并发安全和性能优化提示
家校通平台的并发量通常在几十到几百,PHP横向扩展和MySQL性能储备都有富余,真正的瓶颈多数出在慢查询和文件上传带宽上。我做优化时主要看两条线:一是给高频查询的字段加索引,比如notices的class_id和created_at、leave_requests的student_id和status、notice_reads的notice_id和user_id,查询性能提升明显;二是对耗时操作改用异步处理,比如批量发送订阅消息,PHP端接收到请求后把发送任务丢给队列进程异步执行,前端不用一直等待响应。
真遇到高并发加载的话,合理的缓存策略也能化解一部分压力。比如通知列表的首页数据可以缓存30秒,作业列表缓存1分钟,数据一致性要求不高的场景用缓存完全没问题。PHP端用Redis做缓存在这类项目里非常实用,苹果和核微信授权信息、热点数据都可以放Redis,比直接每次都查MySQL快好几倍的。
5. 一些个人体会
这个项目做完交付,我最大的感受是,家校通这类平台的技术实现并不是最难的,真正难的是把家校沟通的需求理顺。家长要的不是一个功能堆砌的App,而是“我打开就能看到老师说了什么、孩子今天表现怎么样”;老师要的不是复杂的系统设置,而是“我发一条通知,全班家长都能收到”的流畅体验。技术选型上,微信小程序、PHP、Vue这个组合足够稳定、开发速度快、维护成本低,非常适合这种中小体量但业务流程完整的业务系统。
最后分享一点关于这段经历的心得。做这种带有明确场景边界的系统,前期设计角色权限和业务数据模型时多花时间,后面开发会非常顺畅。我在这个项目里前期花了大概三天时间梳理角色关系、业务状态和数据表结构,虽然当时觉得进度慢了,但后面两周多的开发期几乎没有返工,每个模块顺着数据模型往下写就完了,联调阶段也很顺利。反过来,如果表结构设计得粗糙,后面会发现这里改一下、那里补一个字段,整个开发节奏都会被拖垮。所以我的经验是:项目前期,先想清楚数据模型和权限模型,再动手敲代码。这个原则在类似的管理系统项目中,基本是通用的。