1. 项目概述
这个基于微信小程序的绘画学习平台是一个面向绘画爱好者的在线教育系统。作为一名有十年开发经验的Java工程师,我最近完成了这个项目的开发工作。平台采用微信小程序作为前端入口,后端使用Java+SSM框架开发,数据库选用MySQL,实现了课程学习、作业提交、社区交流等核心功能。
微信小程序的轻量化特性非常适合绘画学习这种场景。用户无需下载安装,打开微信就能随时随地进行学习。平台整合了视频课程、图文教程、作业批改、社区互动等多种功能,为绘画爱好者提供了一站式的学习解决方案。
2. 技术选型与架构设计
2.1 技术栈选择
2.1.1 前端技术选型
选择微信小程序作为前端主要基于以下考虑:
- 用户覆盖广:微信月活用户超过12亿,无需额外安装
- 开发成本低:小程序开发工具完善,文档齐全
- 性能优秀:小程序运行在微信原生环境中,流畅度高
- 跨平台:一套代码可同时运行在iOS和Android系统
小程序端主要使用了以下技术:
- WXML/WXSS:小程序特有的标记语言和样式表
- JavaScript:实现页面逻辑和交互
- Canvas API:用于实现绘画功能
- 微信云开发:部分功能直接调用云数据库和云存储
2.1.2 后端技术选型
后端采用Java+SSM框架组合,主要考虑因素:
- 成熟稳定:SSM框架经过多年发展,社区资源丰富
- 开发效率:MyBatis简化了数据库操作,Spring提供了完善的IoC和AOP支持
- 性能可靠:Java虚拟机优化成熟,适合高并发场景
- 团队熟悉:团队成员对Java技术栈掌握程度高
具体技术组件:
- Spring 5.x:IoC容器和AOP支持
- Spring MVC:Web层框架
- MyBatis 3.x:ORM框架
- Redis:缓存和会话管理
- MySQL 8.0:关系型数据库
2.2 系统架构设计
系统采用典型的三层架构:
code复制小程序端 → API网关 → 业务服务层 → 数据访问层 → MySQL数据库
↑
↓
Redis缓存
2.2.1 小程序端架构
小程序端采用模块化设计:
- 基础模块:用户认证、权限控制、异常处理
- 业务模块:课程学习、作业提交、社区交流
- 工具模块:文件上传、支付对接、消息通知
页面路由设计遵循微信小程序规范,主要页面包括:
- 首页:推荐课程、热门文章
- 课程中心:分类展示所有课程
- 学习中心:我的课程、作业记录
- 社区:论坛交流、作品展示
- 个人中心:个人信息、学习记录
2.2.2 后端服务架构
后端采用微服务架构设计,主要服务包括:
- 用户服务:注册登录、个人信息管理
- 课程服务:课程CRUD、分类管理
- 学习服务:学习记录、作业提交
- 社区服务:帖子管理、评论互动
- 支付服务:课程购买、VIP订阅
服务间通信采用RESTful API,通过API网关统一对外暴露接口。网关负责:
- 请求路由
- 权限验证
- 限流熔断
- 日志记录
3. 核心功能实现
3.1 课程学习模块
3.1.1 课程展示与筛选
课程列表页实现了多种筛选方式:
- 按分类筛选:素描、水彩、油画等
- 按难度筛选:初级、中级、高级
- 按价格筛选:免费、付费
- 综合排序:最新、最热、评分
后端接口设计:
java复制@GetMapping("/courses")
public Result listCourses(
@RequestParam(required = false) Integer categoryId,
@RequestParam(required = false) Integer level,
@RequestParam(required = false) Integer priceType,
@RequestParam(defaultValue = "new") String sort,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
// 构建查询条件
CourseQuery query = new CourseQuery();
query.setCategoryId(categoryId);
query.setLevel(level);
query.setPriceType(priceType);
query.setSort(sort);
// 分页查询
PageInfo<CourseVO> pageInfo = courseService.queryCourses(query, page, size);
return Result.success(pageInfo);
}
3.1.2 课程详情页
课程详情页包含以下核心信息:
- 课程封面和简介
- 讲师介绍
- 课程目录
- 学习人数和评价
- 价格信息(免费或付费)
关键技术点:
- 使用微信小程序的scroll-view实现目录滚动
- 通过rich-text组件渲染课程详情HTML内容
- 调用微信支付接口实现课程购买
- 使用缓存减少详情页接口调用
3.2 作业提交与批改
3.2.1 作业提交功能
学生可以通过小程序提交绘画作业:
- 选择课程对应的作业
- 上传作品图片(支持多张)
- 填写作品描述
- 提交给讲师批改
后端接口示例:
java复制@PostMapping("/homeworks/submit")
public Result submitHomework(
@RequestHeader("token") String token,
@RequestBody HomeworkSubmitDTO dto) {
// 验证用户身份
User user = userService.getUserByToken(token);
if (user == null) {
return Result.error(401, "未登录");
}
// 保存作业记录
homeworkService.submitHomework(user.getId(), dto);
return Result.success();
}
3.2.2 作业批改功能
讲师端可以:
- 查看待批改作业列表
- 对每份作业进行评分
- 添加文字评语
- 使用画板工具直接在作品上标注修改建议
关键技术实现:
- 使用微信小程序的canvas组件实现画板批注功能
- 将批注信息保存为JSON格式
- 前端解析JSON还原批注位置和内容
- 使用WebSocket实现实时通知学生批改结果
3.3 社区交流模块
3.3.1 论坛功能
社区论坛支持:
- 发帖:图文混排,支持@用户
- 回帖:嵌套评论,点赞功能
- 话题分类:绘画技巧、作品展示等
- 热门排序:按热度展示内容
数据库设计:
sql复制CREATE TABLE `post` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`title` varchar(100) NOT NULL,
`content` text NOT NULL,
`category_id` int NOT NULL,
`view_count` int DEFAULT '0',
`like_count` int DEFAULT '0',
`comment_count` int DEFAULT '0',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_category_id` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.3.2 作品展示功能
用户可以:
- 上传自己的绘画作品
- 添加作品描述和标签
- 查看他人作品并评论
- 收藏喜欢的作品
小程序端实现技巧:
- 使用微信的chooseImage API选择图片
- 通过uploadFile API上传到服务器
- 使用waterfall布局展示作品列表
- 实现图片懒加载提升性能
4. 数据库设计与优化
4.1 核心表结构设计
4.1.1 课程相关表
- 课程表(course):
sql复制CREATE TABLE `course` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`cover_url` varchar(255) NOT NULL,
`teacher_id` bigint NOT NULL,
`category_id` int NOT NULL,
`level` tinyint NOT NULL COMMENT '1-初级 2-中级 3-高级',
`price` decimal(10,2) NOT NULL DEFAULT '0.00',
`description` text,
`status` tinyint NOT NULL DEFAULT '1' COMMENT '0-下架 1-上架',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_teacher_id` (`teacher_id`),
KEY `idx_category_id` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 课程章节表(course_chapter):
sql复制CREATE TABLE `course_chapter` (
`id` bigint NOT NULL AUTO_INCREMENT,
`course_id` bigint NOT NULL,
`title` varchar(100) NOT NULL,
`sort` int NOT NULL DEFAULT '0',
`video_url` varchar(255) DEFAULT NULL,
`duration` int DEFAULT NULL COMMENT '视频时长(秒)',
`free` tinyint NOT NULL DEFAULT '0' COMMENT '0-收费 1-免费',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_course_id` (`course_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 数据库优化实践
4.2.1 索引优化
针对高频查询场景添加了以下索引:
- 课程表的复合索引:(category_id, level, status)
- 用户学习记录的索引:(user_id, course_id)
- 作业表的索引:(user_id, course_id, status)
使用EXPLAIN分析查询性能:
sql复制EXPLAIN SELECT * FROM course
WHERE category_id = 1 AND level = 2 AND status = 1
ORDER BY create_time DESC LIMIT 10;
4.2.2 分表策略
对于可能产生大量数据的表采用了分表策略:
- 用户学习记录表按用户ID哈希分表
- 作业提交表按月分表
- 论坛帖子评论表按帖子ID哈希分表
分表路由逻辑示例:
java复制public String getTableName(Long userId, String baseName) {
int hash = Math.abs(userId.hashCode());
int tableNum = hash % 10; // 分为10张表
return baseName + "_" + tableNum;
}
4.2.3 缓存策略
使用Redis缓存热点数据:
- 课程详情信息:缓存30分钟
- 首页推荐数据:缓存1小时
- 用户权限信息:缓存2小时
- 热门帖子列表:缓存15分钟
缓存更新策略:
- 读操作:先查缓存,命中则返回,未命中查DB并写入缓存
- 写操作:先更新DB,再删除缓存(Cache Aside Pattern)
5. 性能优化与问题排查
5.1 小程序端性能优化
5.1.1 图片优化
- 使用CDN加速图片加载
- 根据设备DPI返回不同尺寸图片
- 重要图片预加载
- 使用WebP格式减小体积
图片加载代码示例:
javascript复制function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = url + '?imageView2/2/w/300/q/75'
img.onload = () => resolve(img)
img.onerror = reject
})
}
5.1.2 数据预取
- 在页面onLoad时预取下一页数据
- 使用微信的preloadPage功能预加载关键页面
- 本地缓存高频访问数据
5.2 服务端性能优化
5.2.1 接口优化
- 合并细粒度接口为粗粒度接口
- 使用GraphQL实现按需查询
- 接口响应时间监控与优化
- 启用HTTP/2提升并发性能
接口响应监控代码:
java复制@Around("execution(* com.artstudy.controller..*.*(..))")
public Object monitorApiPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long cost = System.currentTimeMillis() - start;
String method = joinPoint.getSignature().toShortString();
metricsService.recordApiPerformance(method, cost);
}
}
5.2.2 数据库连接池优化
- 使用HikariCP替代DBCP
- 根据压测结果调整连接数
- 配置合理的超时时间
- 监控连接泄漏
配置示例:
properties复制spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.connection-timeout=10000
5.3 常见问题与解决方案
5.3.1 微信登录失败
问题现象:部分用户微信登录失败,返回"code无效"
排查过程:
- 检查code是否过期(微信code有效期5分钟)
- 检查服务器时间是否同步
- 检查微信开放平台配置的域名是否正确
解决方案:
- 确保服务器时间同步
- 前端获取code后立即调用登录接口
- 添加重试机制
5.3.2 作业图片上传失败
问题现象:部分用户上传作业图片失败
排查过程:
- 检查图片大小限制(后端限制10MB)
- 检查图片格式限制(仅支持jpg/png)
- 检查服务器存储空间
解决方案:
- 前端上传前检查图片大小和格式
- 后端添加更友好的错误提示
- 使用OSS存储替代本地存储
5.3.3 高并发下的性能问题
问题现象:新课程发布时系统响应变慢
排查过程:
- 监控发现数据库CPU飙升
- 慢查询日志显示课程详情查询变慢
- 缓存命中率下降
解决方案:
- 优化课程详情查询SQL
- 增加缓存过期时间
- 使用读写分离减轻主库压力
- 对热门课程接口添加限流
6. 项目部署与运维
6.1 生产环境部署
6.1.1 服务器配置
生产环境采用阿里云ECS:
- CPU:4核
- 内存:8GB
- 系统:CentOS 7.9
- JDK:OpenJDK 11
- MySQL:8.0 主从架构
- Redis:6.x 哨兵模式
部署架构:
code复制负载均衡(Nginx)
↓
Web服务器(Spring Boot) ×2
↓
MySQL主库 → MySQL从库
↓
Redis哨兵集群
6.1.2 部署流程
- 代码打包:
bash复制mvn clean package -DskipTests
- 部署脚本:
bash复制#!/bin/bash
APP_NAME=artstudy-backend
APP_PORT=8080
JAR_FILE=target/$APP_NAME.jar
# 停止现有服务
PID=$(ps -ef | grep $APP_NAME.jar | grep -v grep | awk '{print $2}')
if [ -n "$PID" ]; then
kill -9 $PID
fi
# 启动新服务
nohup java -jar $JAR_FILE --server.port=$APP_PORT > logs/$APP_NAME.log 2>&1 &
6.2 监控与告警
6.2.1 系统监控
- 使用Prometheus收集指标
- Grafana展示监控数据
- 监控项包括:
- CPU/内存/磁盘使用率
- 接口响应时间
- 数据库查询性能
- JVM内存和GC情况
6.2.2 业务监控
- 每日新增用户数
- 课程购买转化率
- 作业提交数量
- 社区活跃度
监控代码示例:
java复制@Slf4j
@Component
public class BusinessMetrics {
private final MeterRegistry meterRegistry;
public BusinessMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
public void recordCoursePurchase(Long userId, Long courseId) {
meterRegistry.counter("course.purchase",
"userId", userId.toString(),
"courseId", courseId.toString())
.increment();
log.info("User {} purchased course {}", userId, courseId);
}
}
6.3 持续集成与交付
6.3.1 CI/CD流程
- 代码提交触发GitHub Actions
- 运行单元测试和集成测试
- 构建Docker镜像
- 推送镜像到阿里云容器镜像服务
- 自动部署到测试环境
- 人工确认后部署到生产环境
6.3.2 自动化测试
- 单元测试:JUnit 5 + Mockito
- 接口测试:RestAssured
- 前端测试:小程序自动化测试工具
- 性能测试:JMeter
测试代码示例:
java复制@SpringBootTest
class CourseControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@BeforeEach
void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
void testListCourses() throws Exception {
mockMvc.perform(get("/api/courses")
.param("categoryId", "1")
.param("page", "1")
.param("size", "10"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.list").isArray());
}
}
7. 项目总结与展望
这个绘画学习平台小程序项目从需求分析到上线历时4个月,期间遇到了不少技术挑战,也积累了很多宝贵的经验。
7.1 技术收获
- 微信小程序开发更加熟练,特别是Canvas绘图API的使用
- 对SSM框架的理解更加深入,能够根据业务特点进行定制化开发
- 掌握了MySQL性能优化的多种手段
- 积累了高并发场景下的系统设计经验
7.2 业务思考
- 在线教育类产品需要特别关注学习体验的流畅性
- 社区互动功能显著提升了用户粘性
- 作业批改功能的实时性要求比预期高
- 移动端用户更倾向于碎片化学习
7.3 未来优化方向
- 引入AI辅助批改功能,减轻讲师负担
- 增加直播教学功能,提升互动性
- 开发配套的PC端管理后台
- 优化推荐算法,提供个性化学习路径
在实际开发过程中,有几个特别值得注意的经验点:
- 微信小程序审核比较严格,特别是涉及用户生成内容的板块,需要提前做好内容审核机制
- Canvas绘图API在不同机型上的表现有差异,需要充分测试
- 支付功能一定要做好异常处理和状态同步
- 数据库设计要预留足够的扩展空间,在线教育业务的变化往往比较快