1. 项目背景与核心需求
这个项目源于高校日常教学管理中的一个实际痛点:传统纸质点名效率低下且难以统计。作为一名长期从事教育信息化开发的工程师,我经常收到老师们关于考勤系统的改进需求。基于此,我们决定开发一套支持网页、小程序和APP三端的智能点名系统,并实现与学校信息门户的数据对接。
点名系统的核心诉求可以归纳为三点:
- 多终端覆盖:满足教师在不同场景下的使用习惯(教室电脑用网页版、移动办公用小程/APP)
- 实时数据同步:自动获取教务系统的课程学生名单,避免手动导入
- 考勤数据可视化:生成多维度的出勤统计报表
2. 技术选型与架构设计
2.1 跨平台方案对比
我们对比了三种主流跨平台方案:
- React Native:性能优异但学习成本高
- Flutter:UI表现力强但生态不够成熟
- UniApp:基于Vue.js语法,一次开发多端发布
最终选择UniApp主要基于:
- 开发团队已有Vue.js技术积累
- 完善的插件市场(如uView UI)
- 对微信小程序原生API的良好支持
2.2 系统架构图解
code复制[前端]
UniApp → 编译为 → Web/小程序/APP
[后端]
Node.js + Express
├─ 教务数据爬虫(Puppeteer)
├─ 考勤业务逻辑
└─ MySQL数据存储
2.3 关键技术栈说明
- 前端:UniApp + Vue3 + TypeScript
- UI框架:uView UI 3.0
- 后端:Node.js + Express
- 数据库:MySQL 8.0
- 爬虫工具:Puppeteer + Cheerio
3. 爬虫模块实现细节
3.1 教务系统逆向分析
以某高校信息门户为例,登录流程需要处理:
- 动态token验证
- 滑块验证码
- 加密表单提交
通过Chrome开发者工具抓包发现:
- 登录请求需要携带
__RequestVerificationToken - 密码采用RSA加密传输
- 课表数据接口返回JSONP格式
3.2 Puppeteer自动化方案
javascript复制const puppeteer = require('puppeteer');
async function getCourseList(teacherId) {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
});
try {
const page = await browser.newPage();
await page.goto('https://jwxt.example.edu.cn');
// 处理滑块验证
await handleSlider(page);
// 模拟登录
await page.type('#username', teacherId);
await page.type('#password', encryptPassword(pwd));
await page.click('#login-btn');
// 获取课表数据
const courses = await page.evaluate(() => {
return JSON.parse(document.querySelector('pre').innerText);
});
return courses;
} finally {
await browser.close();
}
}
3.3 数据清洗与缓存策略
原始数据需要经过:
- 字段映射(如"教师名称"→"teacherName")
- 时间格式标准化("第1-2节"→"08:00-09:40")
- 学生名单去重
采用Redis缓存机制:
- 课程数据缓存2小时
- 学生名单缓存1天
- 使用MD5生成缓存键
4. UniApp多端开发实践
4.1 项目初始化配置
bash复制# 创建项目
vue create -p dcloudio/uni-preset-vue my-project
# 重要依赖
"dependencies": {
"@dcloudio/uni-ui": "^1.4.20",
"uview-ui": "^3.1.31",
"dayjs": "^1.11.7"
}
4.2 多端适配方案
4.2.1 条件编译
javascript复制// #ifdef H5
console.log('网页端特有逻辑');
// #endif
// #ifdef MP-WEIXIN
console.log('微信小程序特有逻辑');
// #endif
4.2.2 样式适配技巧
css复制/* 通用样式 */
.attendance-btn {
/* #ifdef H5 */
padding: 12px 24px;
/* #endif */
/* #ifdef APP */
padding: 10px 20px;
/* #endif */
}
4.3 核心功能实现
4.3.1 点名列表渲染
vue复制<template>
<u-list @scrolltolower="loadMore">
<u-cell
v-for="student in students"
:key="student.id"
:title="student.name"
:label="student.id"
>
<u-switch
slot="right-icon"
v-model="student.attended"
@change="updateAttendance(student)"
/>
</u-cell>
</u-list>
</template>
4.3.2 考勤数据统计
javascript复制computed: {
attendanceRate() {
const total = this.students.length;
const attended = this.students.filter(s => s.attended).length;
return (attended / total * 100).toFixed(1);
}
}
5. 性能优化与踩坑记录
5.1 列表渲染性能问题
初期直接渲染500+学生导致卡顿,解决方案:
- 使用uList虚拟滚动
- 分页加载(每页50条)
- 避免在v-for中使用复杂表达式
5.2 小程序包体积超标
通过以下措施将包体积从3M降到1.5M:
- 启用分包加载
- 使用微信开发者工具的"代码瘦身"
- 动态加载非必要组件
5.3 Android后台服务保活
采用WorkManager实现定时同步:
java复制// Android原生代码
val request = PeriodicWorkRequestBuilder<SyncWorker>(
4, TimeUnit.HOURS
).build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"attendanceSync",
ExistingPeriodicWorkPolicy.KEEP,
request
)
6. 安全防护方案
6.1 接口防刷策略
- 请求签名:HMAC-SHA256
- 频率限制:Redis令牌桶算法
- 敏感操作二次验证
6.2 数据加密方案
- 传输层:HTTPS + 双向证书
- 存储加密:AES-256-GCM
- 日志脱敏:手机号显示为138****1234
6.3 爬虫伦理规范
- 遵守robots.txt协议
- 请求间隔≥3秒
- 仅采集必要数据
- 提供数据删除接口
7. 部署与运维实践
7.1 多环境配置
javascript复制// config.js
module.exports = {
development: {
baseURL: 'http://localhost:3000'
},
production: {
baseURL: 'https://api.attendance.example.com'
}
}
7.2 监控告警体系
- ELK日志收集
- Prometheus监控指标
- 企业微信机器人告警
7.3 持续集成流程
yaml复制# GitHub Actions配置
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install
- run: npm run build:h5
- run: npm run build:mp-weixin
8. 扩展功能展望
- 人脸识别签到:集成百度AI开放平台
- 蓝牙信标定位:防止代签到
- 微信消息提醒:缺勤自动通知
- 学习分析报告:出勤率与成绩关联分析
在实际开发中,我们发现UniApp的picker组件在Android端存在渲染性能问题,最终通过自定义虚拟滚动组件解决。这个项目让我深刻体会到,跨平台开发不是简单的"一次编写到处运行",而是需要针对各平台特性做精细化适配。
