本来想分享一个之前打磨过的学员管理项目,结果有同行问能不能把艺术体育培训机构的业务流程也整理一套出来。这类机构跟K12学科培训差别还挺大的,排课要看教室空闲、老师时间、学员级别是否匹配,收费又要应付课时包、试听转正、请假补课这些乱七八糟的场景,普通进销存系统根本顶不住。今天就把这套基于SpringBoot+Vue+MyBatis+MySQL的艺体培训机构业务管理系统完整拆开讲,从功能设计到表结构再到本地启动部署,一篇能直接照着落地。
项目适合三类人看:一是培训机构内部的技术或运营负责人,想找一套能覆盖报名、排课、考勤、续费全流程的内部工具;二是Java全栈学习者,想找一个业务逻辑真实、不是那种烂大街的增删改查项目的练手案例;三是接私活的技术人员,这类机构管理系统在市面上需求量大,这套架构和表设计可以直接改改复用。
1. 项目背景与核心需求拆解
1.1 艺体培训行业的管理痛点在哪里
传统艺体培训机构,尤其是舞蹈、美术、篮球、钢琴这类,管理上跟纯文化课培训有非常大的差异。我调研过几家线下机构的实际运转流程,发现它们的日常管理基本被这几个问题困住。
第一个是课程维度复杂。同样是"少儿美术班",可能按年龄分3-6岁启蒙阶段、7-12岁进阶阶段,再按画种分素描、水彩、国画方向,每个班一周上几次、每次多长时间各不一样。排课的时候如果靠人工在Excel里拉表格,教室冲突、老师时间撞车基本是家常便饭。
第二个是收费模式灵活。艺体机构几乎没有统一的收费标准。有的按学期收,有的按课时包收,还有的搞"报一年送两个月";学员请假了课时怎么算,试听课转正式课怎么抵扣,退费按什么比例扣手续费,这些规则每家在合同里写得都不一样。用通用财务软件去管,根本没法自动算清楚。
第三个是教学过程需要记录。家长付费后非常在意"这节课学了什么""老师有没有点评",舞蹈机构要记录学员的考级进度,球类机构要记录体能测试数据。这些过程性数据如果落在纸面上,很难形成对运营有用的统计分析。
总结下来,艺体培训机构需要的管理系统,核心不是"记账",而是围绕"人、课、钱、教室"四个要素做闭环管理。人指学员、教师、家长,课指课程产品和具体排课,钱指收费、退费、课销,教室指物理场地和时段资源。这四个要素相互关联,缺一个环节数据就会断裂。
1.2 这套系统的功能模块到底有哪些
这套系统在设计上按照上述痛点做了模块拆分,覆盖机构前台运营和后台管理的完整链路。功能上主要分为以下六大模块:
- 学员管理:登记学员档案、跟进试听报名、记录学员级别成长;支持批量导入导出学员信息。
- 课程管理:维护课程产品库,包括课程名称、适合年龄、课时单价、课程分类(舞蹈/美术/体育等);支持课程上下架。
- 排课中心:以周历形式展示教室课表,支持快速创建课程班次、调课、停课;自动检测教室和教师时间冲突。
- 课消与考勤:上课签到、课时扣减、请假审批、补课登记;每节课自动生成课消记录,关联到学员剩余课时。
- 收费与财务:报名收费、续费、退费,支持多种收费方式;记录每笔订单明细,生成简单经营报表。
- 系统管理:用户角色权限、操作日志、基础参数配置(如机构名称、退费规则等)。
这套系统比较适合中小型机构,单校区或几个校区都可以用。技术上做了前后端分离,后端提供RESTful接口,前端独立部署,后续要加小程序端或者家长端App,直接复用现有接口即可。
2. 技术架构与选型思路
2.1 为什么选择SpringBoot+Vue这一套前后端分离方案
很多做管理系统的团队会纠结到底用单体模板引擎(比如Thymeleaf、JSP)还是前后端分离。这套项目选SpringBoot+Vue,并不是因为技术潮流,而是基于实际维护成本和后续扩展性考虑的。
SpringBoot负责纯后端接口服务,它的优势在于自动配置和起步依赖,一个SpringBootApplication就能把Tomcat内嵌启动,不用单独部署WAR包到外部容器,这对培训机构这种业务量级来说足够了。项目里用SpringBoot 2.7.x版本,打包后一个jar直接跑起来,部署非常省事。
Vue负责前端页面渲染和交互。艺体机构的前端页面虽然不算复杂,但排课周历、课程表这种交互式组件,用传统模板引擎写起来非常痛苦。Vue的双向数据绑定加上Element UI组件库,像表格、弹窗、表单校验、日期选择器这些管理后台高频组件都能直接拿来用。选Vue 2还是Vue 3的问题,这里用Vue 2.6 + Element UI,主要考虑到稳定性和团队上手成本,如果新项目从零开始,用Vue 3 + Element Plus也完全可行,架构上不需要调整。
前后端分离还有一个隐性好处:可以独立部署、独立扩容。后期如果机构想做家长端微信小程序,小程序直接请求后端接口就可以,不用动前端管理后台代码。实际上,很多二手项目改造都是先把这个管理后台的接口复用给小程序端,效率非常高。
2.2 MyBatis和MySQL在数据持久化层面扮演的角色
MyBatis这套ORM框架在国内企业管理类项目中的地位不需要多强调。与JPA/Hibernate的"自动生成SQL"不同,MyBatis让开发者自己编写SQL,虽然代码量看起来多一些,但胜在SQL可控、优化空间大、排查问题直观。
这套系统里使用MyBatis的核心场景集中在报表统计和课消计算。比如统计某个月的营收,需要关联订单表、学员表、课程表,用多表联查加上日期过滤,如果交给JPA去自动推导,SQL会变得晦涩难懂;写在Mapper XML里,一条SQL看明白,改起来也方便。再比如课消结算,需要处理课时扣减的原子性操作,用MyBatis配合数据库事务,控制起来非常清晰。
MySQL做底层数据库没有太多争议。8.0版本在性能、窗口函数、JSON支持上都比5.7提升明显。这套系统用InnoDB存储引擎,事务隔离级别默认的REPEATABLE READ,字符集用utf8mb4(这一点非常重要,否则输入emoji表情或者生僻字会出现乱码)。
2.3 这套技术组合的上下游生态
选择这套组合还有一个现实原因:生态成熟,招聘和外包成本低。SpringBoot、Vue、MyBatis、MySQL这四样,几乎是国内Java全栈开发的基本盘。你随便找一个Java工程师,让他接手这套项目,不需要额外学习就能上手。
从部署角度看,MySQL、Redis(如果用到缓存)、后端jar包、前端静态文件,四件套部署在2核4G的云服务器上足够。培训机构一般不会为IT系统准备高性能服务器,低配硬件也能流畅运行,这一点在选型时非常重要。
3. 核心功能模块设计与实现
3.1 学员档案与报名跟进这条主线
学员管理模块是整个系统的数据底座。我的设计思路是:学员信息主导,报名试听做辅助流程,二者链路贯通。
学员表的核心字段包括姓名、性别、出生日期、家长联系方式、就读学校(或幼儿园)、报名来源(地推/转介绍/线上广告)、当前级别、备注。艺体机构特别关注学员年龄,因为课程匹配度跟年龄强相关,比如3岁半的孩子能不能上某舞蹈班的启蒙课,需要用出生日期自动计算年龄并做提示。
报名跟进功能做成一个独立的来访登记表,记录每个潜在学员的试听时间、试听课程、跟进老师、跟进状态(待联系/已试听/已报名/已流失)。这个功能被很多系统忽略,但实际上它是机构销售转化的核心抓手。管理后台里我会专门做一个"今日待跟进"列表,按照剩余跟进天数倒序排,提醒课程顾问及时回访。
代码层面,学员列表页要支持按姓名、手机号、课程分类筛选,后端用MyBatis的<where>标签动态拼接条件,避免了字符串拼接SQL注入的问题。
<select id="queryStudentPage" resultType="com.arttrain.entity.Student"> SELECT s.*, c.course_name FROM student s LEFT JOIN course c ON s.course_id = c.id <where> <if test="studentName != null and studentName != ''"> AND s.student_name LIKE CONCAT('%', #{studentName}, '%') </if> <if test="phone != null and phone != ''"> AND s.parent_phone LIKE CONCAT('%', #{phone}, '%') </if> <if test="courseId != null"> AND s.course_id = #{courseId} </if> </where> ORDER BY s.create_time DESC </select>这里有个容易踩的坑:LIKE查询如果写成#{studentName}不带CONCAT,MyBatis默认传参会被当作字符串整体匹配,导致查不出包含匹配的结果。用CONCAT('%', #{studentName}, '%')才是正确的模糊查询姿势。
3.2 排课中心的周历视图与冲突检测算法
排课是艺体机构管理系统里技术含量最高的模块。市面上很多开源系统的做法是先建班级,再往班级里塞学员,然后给班级排课。这种方式对文化课培训够用,但艺体机构的排课涉及三个维度的约束:
- 教师维度:一个老师同一时间段不能上两节课;
- 教室维度:一个教室同一时间段不能被两个班占用;
- 学员维度:一个学员同一时间段不能在不同班上课(尤其是一对一课程)。
系统里的排课表结构设计为:班级表(class_info) + 课次表(class_schedule)。班级表存固定的上课规律,比如每周三、周六的16:00-17:30;课次表是实际生成的每一节课,比如2024年11月1日16:00-17:30这节课。这种设计的好处是:班级是模板,课次是实例,后续请假、补课、调课都在实例上操作,不影响模板。
冲突检测算法写成独立工具类,核心逻辑是时间段重叠判断。判断两个时间段[start1, end1)和[start2, end2)是否冲突,条件为start1 < end2 && start2 < end1。这个判断看起来简单,但很多人会写成start1 <= end2 && start2 <= end1,边界条件不对会导致刚好相邻的课也报冲突。
public class ScheduleConflictUtil { public static boolean isTimeOverlap(LocalDateTime start1, LocalDateTime end1, LocalDateTime start2, LocalDateTime end2) { return start1.isBefore(end2) && start2.isBefore(end1); } }排课页面用Vue + FullCalendar插件渲染周历,后端提供两个接口:查询某周所有课次、创建课次时做冲突校验。FullCalendar的周视图非常直观,教室作为纵轴资源列,一天的时间段作为横轴,上课班级对应一个色块,拖动可以快速调整上课时间,保存后重新检查一遍冲突。
3.3 课消记账的设计细节
课消系统是这个项目里业务价值最高的部分。它的核心逻辑是:学员报名某个课时包后,账户里有了总课时数,每上一次课扣减一次,请假则不扣,补课上完再扣。
数据库设计上,学员账户表(student_account)保存总课时和剩余课时,课消流水表(consume_log)记录每一次扣减、充值和调整记录。这里是典型的"流水账"模式,账户表只存当前状态,流水表保存所有历史动作,两者配合可以随时回溯用户的课时变动。
关键点是扣减课时的并发安全。如果学员同时被两个老师操作(比如前台补录签到同时老师手机端扫码签到),剩余课时可能扣成负数。解决办法是在账户表上使用乐观锁版本号,更新时带上version字段:
<update id="deductLessonCount"> UPDATE student_account SET remain_lesson_count = remain_lesson_count - #{deductCount}, version = version + 1 WHERE student_id = #{studentId} AND remain_lesson_count >= #{deductCount} </update>WHERE remain_lesson_count >= #{deductCount}这个条件确保了余额充足才扣减,返回的受影响行数如果为0,说明余额不足或账号被并发修改,程序再抛出业务异常回滚整个课消事务。这种写法比先SELECT再UPDATE更安全,也是MyBatis开发里最推荐的扣库存/扣课时写法。
3.4 收费订单与退费联动
收费模块跟课消模块的数据联动需要非常小心。一次报名动作,前端提交的信息包括:学员ID、课程ID、课时包ID(比如40课时)、应付金额、实收金额、优惠金额、收费方式(现金/微信/支付宝/刷卡)。
后端事务里做三件事:插入订单主表记录、给学员账户充值课时、记录一条课时充值流水。全部成功才提交事务,任何一个环节失败则回滚,保证钱和课时不会出现不一致。
退费是机构老板最敏感也最容易出错的场景。很多机构的退费规则是"已上课时按原价扣除,不足整课时的按整课时计算,剩余课时按比例退款,同时扣除报名时赠送的课时"。这套系统把这些规则做成一个退费计算器,前端选择退费原因后自动计算应退金额,后端再校验一遍,防止前端被篡改。
退费计算逻辑示例:学员报名40课时,实付4000元,机构赠送2课时,已上10课时。剩余32课时(40 + 2 - 10),折算到已购买课时为100元/课时(4000/40),但赠送课时不能折算退款,所以实际退款 = 4000 - 10 * 100 = 3000元。如果机构政策是按剩余总课时折价退款,则会得到不同结果。这类规则差异必须配置化处理,把退费规则做成一个JSON配置项存到系统参数表,方便不同机构自行调整。
这里再次强调:交易类操作一定要有操作日志,记录操作人、操作时间、改动前后字段值。培训机构经常发生家长投诉"为什么我的课时少了一节课",日志就是排查的事实依据。
4. 数据库表结构设计实战
4.1 核心表清单与职责边界
这套系统的数据库共设计16张核心业务表,按数据域可以分成几组:
| 数据域 | 表名 | 主要职责 |
|---|---|---|
| 学员域 | student | 存储学员基本档案 |
| 学员域 | student_account | 存储课时账户总课时与剩余课时 |
| 学员域 | enroll_follow | 存储试听报名跟进记录 |
| 课程域 | course | 存储课程产品信息 |
| 课程域 | class_info | 存储班级信息(固定上课规律) |
| 课程域 | class_schedule | 存储实际课次信息 |
| 课程域 | class_student | 存储班级与学员的关联 |
| 教师域 | teacher | 存储教师档案与任教课程 |
| 财务域 | order_info | 存储收费订单主表 |
| 财务域 | consume_log | 存储课时充值与扣减流水 |
| 财务域 | refund_record | 存储退费记录 |
| 考勤域 | attendance_record | 存储每节课的签到记录 |
| 系统域 | sys_user | 存储后台登录用户 |
| 系统域 | sys_role | 存储角色信息 |
| 系统域 | sys_user_role | 用户角色关联 |
| 系统域 | sys_config | 系统参数配置 |
表设计的一个原则是:不出现"万能表"。比如学费记录不会硬塞进学员主表里,而是通过外键关联到订单表和账户表。这样数据冗余最小化,后续做统计报表时多表JOIN也足够高效。
4.2 三张关键表的字段细节说明
课程表与课次表是理解整个排课系统的钥匙。课程表定义的是一类产品,比如"少儿中国舞启蒙班-学期卡";班级表定义的是某个实际开班,比如"周六10点少儿中国舞A班";课次表定义的是某个时间点的实际课程,比如"2024-11-16 10:00-11:30周六班课次"。
course表的核心字段:course_name、course_type(舞蹈/美术/书法/篮球等)、target_age_min、target_age_max、class_hour(每节课时长)、lesson_count(标准课时包总课时)、price(标准价格)、status(上架/下架)。
class_schedule表的核心字段:class_id、teacher_id、classroom_id、start_time、end_time、schedule_date(上课日期)、status(待上课/已上/已取消/已调课)、remark。注意start_time和end_time这里只存当天的时间段字符串(如"10:00:00"、"11:30:00"),日期单独存schedule_date。这样设计的主要原因是:周历视图查询时,按日期范围过滤非常高效;而跨周重复规则通过班级表里的week_day字段存储,比如1表示周一,7表示周日。
order_info表是财务域的核心。字段包括order_no(唯一单号,格式建议为日期+随机数)、student_id、course_id、class_id、total_amount(应收)、discount_amount(优惠)、paid_amount(实收)、pay_method、status(待支付/已支付/已退款/部分退款)、operator_id(操作人)、create_time。订单状态这里没有做成复杂的支付全流程,机构线下收费大部分是当场结清,所以流程相对简化。
4.3 索引设计与查询优化建议
MySQL表设计时容易被忽略的是索引。学员表上parent_phone字段要建普通索引,因为家长咨询时前台基本都按电话号码快速检索;student_name字段如果经常做模糊查询,可以考虑前缀索引,但培训机构的学员量级在几千到几万,不建索引全表扫也能接受,所以这里只对手机号建索引。
排课表的索引要重点设计。query_schedule_by_week是最高频的查询,直接按schedule_date范围查,索引结构为idx_schedule_date(schedule_date)。同时因为要校验教室冲突,classroom_id、schedule_date、start_time、end_time四个字段的联合索引idx_classroom_time是必须的。老师冲突校验则依赖teacher_id、schedule_date的联合索引。
课消流水表增长最快,一年可能到几十万行。除了student_id索引外,我建议加create_time索引,方便按月份统计课消趋势。MySQL 8.0支持降序索引,时间字段上可以声明CREATE INDEX idx_create_time ON consume_log (create_time DESC),查询"最近30天课消记录"时性能更好。
5. 本地部署与项目启动指南
5.1 环境准备清单
项目要跑起来,先把环境准备好。我自己用的版本组合是经过多轮测试的,建议大家尽量按住这些版本:
| 软件 | 版本 | 说明 |
|---|---|---|
| JDK | 1.8(8u201+) | SpringBoot 2.7支持最高到JDK 19,但JDK 8最稳 |
| Maven | 3.6.x | 不要用3.9.x,有些仓库插件兼容性问题 |
| MySQL | 8.0.x | 5.7也能运行,但部分SQL写法建议用8.0 |
| Node.js | 14/16 | Vue 2工程的编译,Node版本太高会报OOM |
| npm/cnpm | 任意 | npm官方源慢的话配置淘宝镜像 |
| IDE | IDEA 2022+ | 社区版也可以用,装上Lombok插件 |
安装数据库时有个细节,MySQL 8.0默认的认证插件是caching_sha2_password,而很多老的数据库连接驱动不支持。这套项目里的MySQL Connector用的是mysql-connector-java 8.0.x,天然兼容,不需要改配置。如果你遇到Public Key Retrieval is not allowed异常,需要在JDBC URL上加allowPublicKeyRetrieval=true&useSSL=false。
5.2 后端配置和数据库初始化
拿到源码后,先创建数据库。项目里带了sql目录,里面有初始化SQL脚本arttrain_db.sql。在命令行或Navicat里执行:
mysql -u root -p -e "CREATE DATABASE arttrain_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -u root -p arttrain_db < arttrain_db.sql创建数据库务必指定utf8mb4字符集,不然建表默认用MySQL服务器的默认字符集,很多云服务器的MySQL默认还是latin1,中文全变乱码。
下面看最关键的配置文件application.yml。要改的是数据源、Redis(如果有)和文件上传路径:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/arttrain_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true username: root password: your_password servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.arttrain.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl logging: level: com.arttrain.mapper: debugmap-underscore-to-camel-case: true这一行配置非常核心。数据库字段名user_name会自动映射到Java实体类的userName属性,不用手写一堆resultMap。如果日志里发现查出来的对象属性全是null,百分之八九十是这行配置写错或者没写。
项目里还带了用户登录模块,默认管理员账号在sys_user表里,初始化脚本会插入一条记录,用户名admin、密码123456。上线前一定要改掉,并且建议在sys_config表里开启登录验证码功能。
5.3 前端配置和启动步骤
前端工程是标准的Vue 2项目,目录结构如下:
arttrain-web/ ├── build/ ├── config/ ├── src/ │ ├── api/ # 接口请求封装 │ ├── assets/ # 静态资源 │ ├── components/ # 公共组件 │ ├── router/ # 路由配置 │ ├── store/ # Vuex状态管理 │ ├── views/ # 页面组件 │ │ ├── student/ # 学员管理页 │ │ ├── schedule/ # 排课管理页 │ │ ├── finance/ # 财务管理页 │ │ └── system/ # 系统管理页 │ ├── App.vue │ ├── main.js │ └── utils/ # 工具函数 └── package.json运行前端前,修改src/utils/request.js里的请求统一个体路径:
const service = axios.create({ baseURL: process.env.NODE_ENV === 'development' ? 'http://localhost:8080' : '/api', timeout: 15000 })开发环境用localhost:8080指向后端接口,生产环境通过Nginx转发,baseURL设置成/api再配合反向代理。安装依赖:
npm install --registry=https://registry.npmmirror.com npm run dev启动后浏览器访问http://localhost:9527(默认配置端口)。如果端口被占用,在config/index.js里修改dev.port。
5.4 前后端联调时的跨域问题
开发模式下最常碰到的是跨域(CORS)问题。浏览器控制台会报Access-Control-Allow-Origin错误。解决办法有两个,一是后端加一个跨域过滤器,二是前端配代理。
这个项目后端已经加了统一跨域配置类CorsConfig,允许http://localhost:9527访问。如果你用IDEA跑后端,用npm run dev跑前端,基本都能正常联调。真正容易出问题的是接口路径对不上,MyBatis Mapper的命名空间、JDK代理访问路径、前端API请求路径三个要对齐,否则会报404。
6. 常见问题与调优经验实录
6.1 启动失败类问题
很多人拿到源码第一步就卡住,报错五花八门。我整理几个最高频的:
错误1:java.sql.SQLNonTransientConnectionException: Could not create connection to database server
原因是数据库地址、用户名或密码不对。先检查application.yml里的连接信息,再看MySQL服务是否启动。Ubuntu下可以systemctl status mysql,Windows下看服务管理器。
错误2:Caused by: org.xml.sax.SAXParseException: Content is not allowed in trailing section
这是MyBatis的Mapper XML文件尾部有多余内容,检查XML结尾标签是否闭合,或者有没有多出来的注释块。IDEA能自动格式校验,但有时候多了一个空格也会报错。
错误3:前端npm run dev启动后白屏
检查浏览器控制台,如果是Cannot find module 'node-sass'这类错误,需要改Dart Sass。把package.json里sass相关依赖替换成sass+sass-loader@10,然后重新npm install。
6.2 数据库连接和时区问题
MySQL 8.0连接URL里必须带serverTimezone参数,否则会报The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized的乱码异常。项目里设置的是Asia/Shanghai。
另一个容易被忽略的是MySQL的max_allowed_packet参数。导入包含大量初始数据的SQL文件时,如果脚本里某个字段的Text内容特别大,会报Packet for query is too large。在MySQL配置文件的[mysqld]下加一行:
max_allowed_packet=64M6.3 MyBatis接口返回值为null排查法
这类问题最常见,但排查思路比较固定。第一步检查application.yml里map-underscore-to-camel-case是否为true;第二步看实体类属性名是否和数据库列名对应;第三步查SQL执行日志,直接在控制台看MyBatis打印出来的预编译SQL和参数。项目在log-impl里配置了StdOutImpl,SQL会打到控制台,用idea mybatis log free插件也可以更直观地看参数绑定结果。
6.4 排课模块的边界场景调优
排课创建课次时,如果遇到一个月跨了好几个星期,需要批量生成课次。我建议用数据库存储过程生成,而不是Java循环逐条插,因为几十条甚至上百条插入在循环里会非常慢。项目里提供了一个生成学期课次的存储过程generate_semester_schedule,输入班级ID、起始日期、结束日期、周期规则,自动生成全部课次。
在此基础上还做了一个保护机制:生成的课次默认是"未确认"状态,后台管理员审核确认后,课次才参与教室和教师的冲突校验。这种设计避免了批量修改课表时大量弹冲突提示框,实际体验很好。
再提一个隐藏问题,就是排课日期跨天。舞蹈、晚辅导等课程上课时间可能在晚上,如果下课时间跨过零点,比如22:30开始、23:30结束,不会跨天;但如果有23:00开始、次日00:30结束的课,schedule_date存的是哪一天,边界必须提前定好。这套系统里的约定是:schedule_date存上课开始时间的日期,结束时间如果跨天,通过end_time的"次日凌晨"标记来处理。如果机构有深夜课程,这里需要稍微改一下逻辑。
6.5 数据权限与角色权限控制
管理系统的权限控制不能只做前端路由拦截,后端接口也要做鉴权。这套系统用Spring Security + JWT做认证和授权。登录成功后签发JWT,前端每次请求带上Authorization: Bearer <token>,后端通过拦截器解析用户信息和角色。
角色分三层:超级管理员、校区主管、普通员工(课程顾问/ 教师)。超级管理员看全部数据;校区主管只能看本校区数据(如果有多校区字段);普通员工只能看自己负责的学员和课次。实现上,在Mapper的SQL里根据当前登录用户自动追加数据权限过滤条件,比如教师角色的查询自动加上WHERE teacher_id = #{currentUserId}。
这里有一个教训:刚开始做权限只用前端Vue Router的beforeEach路由守卫控制菜单可见,结果别人直接访问后端接口还是能拿到全部数据。后来全部改成后端权限校验,前端路由守卫只负责菜单显示,安全性才真正兜住。
写在最后
这套艺体培训机构业务管理系统从需求分析到表结构设计再到前后端实现,整体链路是完整的,也经历过实际机构的业务验证。如果只是在本地跑起来,一两个小时足够;但如果要真正用到机构里,建议先梳理清楚自己的业务规则——特别是退费规则和课消规则,把系统参数配置里的JSON模板改成自己机构的版本再上线。
我个人的习惯是拿这类源码学习时,不要急着做二次开发,先把表结构和核心接口的调用链捋一遍,再根据自己理解的业务流程去修改和扩展。踩过几次坑之后最深的体会是,和业务方沟通确认每一笔课时变动的定义,比写代码本身更花时间。好在代码该有的基础都有了,后续不管是加家长端小程序、接微信公众号通知,还是加一堂课的在线点评打分,都留了足够的接口扩展空间。