毕业设计实战:工作量统计系统设计与实现(SpringBoot+Vue精简版)
在开发“工作量统计系统”时,曾因“工作量计算公式未与教工类型关联”踩过关键坑——初期所有教工采用统一计算标准,未区分教学、科研、行政等不同类型,导致计算结果严重失真,耗费2天重构计算逻辑才解决问题📊。本文将系统拆解为精简实用的实施指南。
一、需求分析:聚焦核心统计场景
工作量统计系统最忌“大而全”。明确“系主任-教师-管理员”三方核心需求是关键。
1. 核心业务流程
工作量统计流程:
教师提交工作记录 → 系主任审核 → 系统自动计算 → 生成统计报表 → 管理员汇总
核心统计维度:
1. 按教工类型:教学型、科研型、行政型、混合型
2. 按时间周期:周统计、月统计、学期统计、年度统计
3. 按工作性质:教学工作量、科研工作量、社会工作、行政工作
2. 核心功能设计(精简版)
| 角色 | 核心功能 | 关键要点 |
|---|---|---|
| 教师 | 提交工作记录、查看个人统计、下载报表 | 记录需包含:类型、时长、内容、附件 |
| 系主任 | 审核工作记录、查看部门统计、批量操作 | 需设置审核流程,支持批量通过/驳回 |
| 管理员 | 系统配置、数据汇总、报表生成、权限管理 | 支持工作量计算公式自定义 |
3. 核心计算公式设计
// 工作量计算公式(简化版)
总工作量 = 教学工作量 + 科研工作量 + 行政工作量
// 教学工作量 = 标准课时 × 课程系数 × 学生系数
教学工作量 = 课时数 × 1.0 × (1 + 学生人数/100 × 0.1)
// 科研工作量 = 项目级别 × 参与排名 × 经费系数
科研工作量 = 国家级(10分) × 排名系数(主持人1.0) × 经费系数(每万0.1)
// 行政工作量 = 岗位系数 × 服务时长
行政工作量 = 岗位级别(系主任1.5) × 服务时长(小时)
二、技术选型:精简实用的技术栈
| 技术 | 选型理由 | 精简说明 |
|---|---|---|
| Spring Boot 2.7 | 快速启动,自动配置 | 无需复杂XML配置 |
| MySQL 8.0 | 稳定可靠,支持事务 | 核心数据存储 |
| MyBatis Plus | 简化CRUD操作 | 减少SQL编写 |
| Vue 2 + ElementUI | 组件丰富,上手快 | 管理后台开发 |
| EasyExcel | 报表导出性能好 | 替代传统POI |
三、数据库设计:核心表结构(精简版)
-- 教工信息表
CREATE TABLE teacher (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
teacher_no VARCHAR(20) UNIQUE NOT NULL COMMENT '工号',
name VARCHAR(50) NOT NULL COMMENT '姓名',
department_id BIGINT NOT NULL COMMENT '部门ID',
teacher_type TINYINT NOT NULL COMMENT '类型:1教学 2科研 3行政 4混合',
base_coefficient DECIMAL(4,2) DEFAULT 1.0 COMMENT '基础系数',
status TINYINT DEFAULT 1 COMMENT '状态:1正常 2离职'
) COMMENT='教工信息表';
-- 工作量记录表(核心表)
CREATE TABLE workload_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
record_no VARCHAR(30) UNIQUE NOT NULL COMMENT '记录编号',
teacher_id BIGINT NOT NULL COMMENT '教工ID',
work_type TINYINT NOT NULL COMMENT '工作类型:1教学 2科研 3行政',
-- 工作详情
work_date DATE NOT NULL COMMENT '工作日期',
work_hours DECIMAL(5,2) NOT NULL COMMENT '工作时长',
work_content VARCHAR(500) COMMENT '工作内容',
attachment VARCHAR(500) COMMENT '附件路径',
-- 计算参数
coefficient DECIMAL(4,2) DEFAULT 1.0 COMMENT '工作系数',
calculated_score DECIMAL(8,2) COMMENT '计算得分',
-- 审核状态
status TINYINT DEFAULT 1 COMMENT '状态:1待提交 2待审核 3已通过 4已驳回',
approver_id BIGINT COMMENT '审核人',
approval_time DATETIME COMMENT '审核时间',
approval_remark VARCHAR(200) COMMENT '审核意见',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_teacher_date (teacher_id, work_date),
INDEX idx_status (status)
) COMMENT='工作量记录表';
-- 工作量统计表(汇总结果)
CREATE TABLE workload_statistic (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
teacher_id BIGINT NOT NULL,
statistic_month VARCHAR(7) NOT NULL COMMENT '统计月份YYYY-MM',
-- 各类工作量汇总
teaching_score DECIMAL(8,2) DEFAULT 0 COMMENT '教学工作量',
research_score DECIMAL(8,2) DEFAULT 0 COMMENT '科研工作量',
admin_score DECIMAL(8,2) DEFAULT 0 COMMENT '行政工作量',
total_score DECIMAL(8,2) DEFAULT 0 COMMENT '总工作量',
-- 排名信息
department_rank INT COMMENT '部门排名',
total_rank INT COMMENT '全校排名',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_teacher_month (teacher_id, statistic_month)
) COMMENT='工作量月度统计表';
-- 计算公式配置表
CREATE TABLE formula_config (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
formula_type VARCHAR(20) NOT NULL COMMENT '公式类型:teaching/research/admin',
formula_expression VARCHAR(500) NOT NULL COMMENT '公式表达式',
variables_desc TEXT COMMENT '变量说明',
is_active BOOLEAN DEFAULT TRUE COMMENT '是否生效',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP
) COMMENT='计算公式配置表';
四、核心业务实现(精简代码)
1. 工作量计算服务
@Service
public class WorkloadCalculateService {
@Autowired
private FormulaConfigMapper formulaMapper;
/**
* 计算单条记录工作量
*/
public BigDecimal calculateRecordScore(WorkloadRecord record) {
// 根据工作类型获取计算公式
FormulaConfig formula = formulaMapper.selectByType(
getFormulaType(record.getWorkType()));
if (formula == null) {
throw new BusinessException("未找到计算公式");
}
// 构建计算参数
Map<String, Object> params = new HashMap<>();
params.put("hours", record.getWorkHours());
params.put("coefficient", record.getCoefficient());
params.put("teacherType", record.getTeacher().getTeacherType());
// 解析并计算公式(简化实现)
return evaluateFormula(formula.getFormulaExpression(), params);
}
/**
* 月度工作量统计
*/
@Transactional
public void calculateMonthlyStatistic(Long teacherId, String month) {
// 查询该月所有已通过记录
List<WorkloadRecord> records = recordMapper.selectMonthlyRecords(
teacherId, month, RecordStatus.APPROVED);
WorkloadStatistic statistic = new WorkloadStatistic();
statistic.setTeacherId(teacherId);
statistic.setStatisticMonth(month);
// 分类统计
for (WorkloadRecord record : records) {
BigDecimal score = calculateRecordScore(record);
switch (record.getWorkType()) {
case 1: // 教学
statistic.setTeachingScore(
statistic.getTeachingScore().add(score));
break;
case 2: // 科研
statistic.setResearchScore(
statistic.getResearchScore().add(score));
break;
case 3: // 行政
statistic.setAdminScore(
statistic.getAdminScore().add(score));
break;
}
}
// 计算总分
BigDecimal total = statistic.getTeachingScore()
.add(statistic.getResearchScore())
.add(statistic.getAdminScore());
statistic.setTotalScore(total);
// 保存统计结果
statisticMapper.insertOrUpdate(statistic);
}
}
2. 批量审核功能
@Service
public class WorkloadApproveService {
/**
* 批量审核工作记录
*/
@Transactional
public ApiResult batchApprove(List<Long> recordIds, Long approverId,
boolean approved, String remark) {
if (recordIds.isEmpty()) {
return ApiResult.error("请选择要审核的记录");
}
List<WorkloadRecord> records = recordMapper.selectBatchIds(recordIds);
Date now = new Date();
for (WorkloadRecord record : records) {
// 状态校验
if (record.getStatus() != RecordStatus.PENDING) {
continue;
}
record.setApproverId(approverId);
record.setApprovalTime(now);
record.setApprovalRemark(remark);
record.setStatus(approved ?
RecordStatus.APPROVED : RecordStatus.REJECTED);
recordMapper.updateById(record);
// 记录操作日志
logService.addApproveLog(record, approverId, approved);
}
return ApiResult.success(String.format("已处理 %d 条记录", records.size()));
}
}
3. 统计报表生成
@Service
public class ReportService {
@Autowired
private EasyExcel easyExcel;
/**
* 生成部门工作量报表
*/
public void exportDepartmentReport(Long departmentId, String month,
HttpServletResponse response) {
// 查询部门统计数据
List<WorkloadStatistic> statistics = statisticMapper
.selectDepartmentStatistics(departmentId, month);
// 设置响应头
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode(
String.format("工作量统计_%s.xlsx", month), "UTF-8");
response.setHeader("Content-disposition",
"attachment;filename=" + fileName);
// 导出Excel
try (ExcelWriter excelWriter = easyExcel.write(response.getOutputStream())) {
WriteSheet writeSheet = easyExcel.writerSheet("工作量统计").build();
excelWriter.write(statistics, writeSheet);
} catch (IOException e) {
throw new BusinessException("导出失败");
}
}
}
五、前端核心页面(Vue精简版)
1. 工作记录提交页面
<template>
<div class="record-submit">
<el-form :model="recordForm" :rules="rules" ref="formRef">
<el-form-item label="工作类型" prop="workType">
<el-select v-model="recordForm.workType" placeholder="请选择">
<el-option label="教学工作" :value="1" />
<el-option label="科研工作" :value="2" />
<el-option label="行政工作" :value="3" />
</el-select>
</el-form-item>
<el-form-item label="工作日期" prop="workDate">
<el-date-picker v-model="recordForm.workDate" type="date" />
</el-form-item>
<el-form-item label="工作时长" prop="workHours">
<el-input-number v-model="recordForm.workHours" :min="0.5" :step="0.5" />
<span class="tip">单位:小时</span>
</el-form-item>
<el-form-item label="工作内容" prop="workContent">
<el-input v-model="recordForm.workContent" type="textarea" :rows="4" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitRecord">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
recordForm: {
workType: null,
workDate: null,
workHours: 1,
workContent: ''
},
rules: {
workType: [{ required: true, message: '请选择工作类型' }],
workDate: [{ required: true, message: '请选择工作日期' }],
workContent: [{ required: true, message: '请填写工作内容' }]
}
}
},
methods: {
async submitRecord() {
try {
await this.$refs.formRef.validate()
await this.$api.workload.submitRecord(this.recordForm)
this.$message.success('提交成功')
this.resetForm()
} catch (error) {
this.$message.error('提交失败')
}
},
resetForm() {
this.$refs.formRef.resetFields()
}
}
}
</script>
2. 工作量统计页面
<template>
<div class="statistic-page">
<!-- 筛选条件 -->
<el-card class="filter-card">
<el-form :inline="true">
<el-form-item label="统计月份">
<el-date-picker v-model="query.month" type="month" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search">查询</el-button>
<el-button @click="exportExcel">导出Excel</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 统计表格 -->
<el-table :data="statisticList" border>
<el-table-column prop="teacherName" label="姓名" width="100" />
<el-table-column prop="departmentName" label="部门" width="120" />
<el-table-column prop="teachingScore" label="教学工作量" width="120" />
<el-table-column prop="researchScore" label="科研工作量" width="120" />
<el-table-column prop="adminScore" label="行政工作量" width="120" />
<el-table-column prop="totalScore" label="总工作量" width="120">
<template slot-scope="scope">
<strong class="total-score">{{ scope.row.totalScore }}</strong>
</template>
</el-table-column>
<el-table-column prop="departmentRank" label="部门排名" width="100" />
</el-table>
</div>
</template>
六、系统测试要点
1. 核心功能测试
| 测试场景 | 测试要点 | 预期结果 |
|---|---|---|
| 工作量计算 | 不同类型、不同参数的工作记录 | 计算结果符合公式定义 |
| 批量审核 | 选择多条记录进行批量操作 | 所有选中记录状态更新 |
| 月度统计 | 统计指定月份数据 | 生成准确的汇总报表 |
| 公式配置 | 修改计算公式表达式 | 新记录使用新公式计算 |
2. 数据准确性验证
- 边界值测试:0小时、超大时长、负数时长
- 类型转换测试:教学、科研、行政类型切换
- 并发测试:多人同时提交、审核、统计
七、答辩准备要点
1. 演示流程(5分钟)
-
教师端演示(1分钟)
- 提交教学工作记录
- 查看个人统计报表
-
系主任端演示(2分钟)
- 批量审核待审记录
- 查看部门统计排名
-
管理员端演示(2分钟)
- 修改计算公式
- 导出全校统计报表
2. 重点问题准备
Q: 工作量计算公式如何适应不同学院的需求? A: 采用公式配置化设计:
- 每类工作可独立配置计算公式
- 支持按部门设置不同的计算参数
- 公式修改后,历史数据保持不变
Q: 如何确保统计数据的准确性? A: 三层校验机制:
- 提交时:基础数据校验
- 审核时:内容真实性审核
- 统计时:数据完整性检查
Q: 系统如何处理大量数据统计? A: 优化策略:
- 月度统计结果缓存
- 分页加载大数据量
- 异步生成报表文件
3. 创新点展示
- 公式配置化:支持动态调整计算规则
- 批量处理:大幅提升审核效率
- 实时统计:数据变更立即更新排名
结语
工作量统计系统的核心在于计算准确和流程高效。开发时需重点关注: