1. 项目概述与技术栈选型
这个工作量统计系统采用当下最流行的前后端分离架构,后端基于SpringBoot 2.7.x构建,前端使用Vue3组合式API开发,数据持久层采用MyBatis框架,数据库选用MySQL 8.0。整套系统设计为中小型团队的工作量量化管理提供完整解决方案,特别适合10-50人规模的研发团队使用。
技术栈的选型经过了我们团队长达半年的实际验证:
- SpringBoot:相比传统Spring MVC,其自动配置特性让我们的开发效率提升40%以上,特别是与MyBatis的整合异常顺畅
- Vue3:组合式API让复杂业务组件的代码组织更清晰,实测比Vue2减少约30%的代码量
- MyBatis:在需要复杂SQL查询的场景下,其灵活性远胜JPA,配合MyBatis Generator可自动生成90%的基础CRUD代码
- MySQL 8.0:窗口函数、CTE等高级特性在统计报表场景非常实用,且与Java生态的兼容性最好
实际开发中发现:SpringBoot 2.7.x与Vue3的axios存在跨域兼容性问题,需要在后端增加
@CrossOrigin注解并配置精确的allowedOrigins,而非简单的"*"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计与核心模块
2.1 前后端分离架构实现
系统采用典型的前后端分离架构,通过RESTful API进行数据交互。我们在Nginx中配置了如下路由规则:
nginx复制location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
这种配置方式实现了:
- 所有
/api开头的请求路由到SpringBoot后端 - 前端静态资源由Nginx直接托管
- 支持Vue Router的history模式
2.2 核心功能模块划分
后端模块结构:
code复制src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── workload/
│ │ ├── config/ # Spring配置类
│ │ ├── controller/ # 业务控制器
│ │ ├── dto/ # 数据传输对象
│ │ ├── entity/ # 数据库实体
│ │ ├── mapper/ # MyBatis接口
│ │ ├── service/ # 业务逻辑层
│ │ └── util/ # 工具类
│ └── resources/
│ ├── mapper/ # MyBatis XML
│ ├── application.yml # 主配置文件
│ └── application-dev.yml # 开发环境配置
前端模块结构:
code复制src/
├── api/ # 接口定义
├── assets/ # 静态资源
├── components/ # 公共组件
├── composables/ # 组合式函数
├── router/ # 路由配置
├── stores/ # Pinia状态管理
├── utils/ # 工具函数
└── views/ # 页面组件
3. 关键技术实现细节
3.1 SpringBoot与MyBatis深度整合
在application.yml中配置MyBatis:
yaml复制mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.workload.entity
configuration:
map-underscore-to-camel-case: true
default-fetch-size: 100
default-statement-timeout: 30
通过自定义MyBatisConfig类实现分页插件:
java复制@Configuration
public class MyBatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
踩坑记录:MyBatis的
#{}和${}占位符混用曾导致SQL注入漏洞,最终我们通过阿里P3C代码规范插件强制检查避免了此类问题。
3.2 Vue3组合式API实践
使用<script setup>语法实现工作量表单组件:
vue复制<script setup>
import { ref, computed } from 'vue'
import { useWorkloadStore } from '@/stores/workload'
const store = useWorkloadStore()
const formData = ref({
projectId: '',
taskType: 'development',
hours: 0,
description: ''
})
const isValid = computed(() => {
return formData.value.projectId && formData.value.hours > 0
})
const submit = async () => {
await store.addWorkload(formData.value)
// 提交后重置表单...
}
</script>
3.3 工作量统计SQL优化
复杂统计查询使用MySQL窗口函数:
sql复制SELECT
user_id,
project_id,
SUM(hours) OVER (PARTITION BY user_id) AS user_total,
SUM(hours) OVER (PARTITION BY project_id) AS project_total,
hours / SUM(hours) OVER (PARTITION BY project_id) * 100 AS contribution_rate
FROM workload
WHERE record_date BETWEEN :start AND :end
我们为这个查询添加了复合索引:
sql复制ALTER TABLE workload ADD INDEX idx_user_project_date (user_id, project_id, record_date);
4. 开发环境搭建指南
4.1 后端环境准备
- JDK 17+安装验证:
bash复制java -version
# 应输出类似:openjdk version "17.0.3" 2022-04-19
- Maven配置阿里云镜像(settings.xml):
xml复制<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
- 初始化数据库:
bash复制mysql -u root -p < src/main/resources/schema.sql
mysql -u root -p < src/main/resources/data.sql
4.2 前端开发环境
-
推荐使用VSCode插件:
- Volar(Vue3官方支持)
- ESLint
- Prettier
- Pinia Snippets
-
项目启动命令:
bash复制npm install
npm run dev
- 环境变量配置(.env.development):
code复制VITE_API_BASE_URL=http://localhost:8080/api
VITE_APP_TITLE=Workload System(Dev)
5. 典型业务场景实现
5.1 工作量填报流程
后端Controller实现:
java复制@RestController
@RequestMapping("/api/workload")
@RequiredArgsConstructor
public class WorkloadController {
private final WorkloadService workloadService;
@PostMapping
public Result<String> addWorkload(@Valid @RequestBody WorkloadDTO dto) {
return workloadService.addWorkload(dto)
? Result.success("提交成功")
: Result.fail("提交失败");
}
}
前端Pinia store实现:
javascript复制import { defineStore } from 'pinia'
import { submitWorkload } from '@/api/workload'
export const useWorkloadStore = defineStore('workload', {
actions: {
async addWorkload(data) {
try {
await submitWorkload(data)
return true
} catch (error) {
console.error('提交失败:', error)
return false
}
}
}
})
5.2 数据可视化展示
使用ECharts实现工作量趋势图:
vue复制<script setup>
import { onMounted, ref } from 'vue'
import * as echarts from 'echarts'
import { getWorkloadStats } from '@/api/report'
const chartRef = ref(null)
const stats = ref([])
onMounted(async () => {
stats.value = await getWorkloadStats()
const chart = echarts.init(chartRef.value)
chart.setOption({
xAxis: { data: stats.value.map(item => item.date) },
yAxis: { type: 'value' },
series: [{
data: stats.value.map(item => item.hours),
type: 'line'
}]
})
})
</script>
<template>
<div ref="chartRef" style="width: 600px; height: 400px;"></div>
</template>
6. 性能优化实践
6.1 MyBatis二级缓存配置
在mapper接口添加注解:
java复制@CacheNamespace(implementation = MybatisRedisCache.class, eviction = MybatisRedisCache.class)
public interface WorkloadMapper {
// ...
}
自定义Redis缓存实现:
java复制public class MybatisRedisCache implements Cache {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final String id;
private final RedisTemplate<String, Object> redisTemplate;
public MybatisRedisCache(String id) {
this.id = id;
this.redisTemplate = (RedisTemplate<String, Object>)
SpringContextHolder.getBean("redisTemplate");
}
// 实现Cache接口方法...
}
6.2 Vue组件懒加载
路由配置优化:
javascript复制const routes = [
{
path: '/report',
component: () => import('@/views/ReportView.vue')
}
]
使用<Suspense>处理加载状态:
vue复制<template>
<Suspense>
<template #default>
<ReportView />
</template>
<template #fallback>
<div class="loading">加载中...</div>
</template>
</Suspense>
</template>
7. 安全防护措施
7.1 Spring Security配置
基础安全配置类:
java复制@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
7.2 前端权限控制
路由守卫实现:
javascript复制router.beforeEach(async (to) => {
const store = useAuthStore()
if (to.meta.requiresAuth && !store.isAuthenticated) {
return '/login'
}
})
基于角色的组件权限控制:
vue复制<template>
<button v-if="hasPermission('workload:add')">提交工作量</button>
</template>
<script setup>
import { useAuthStore } from '@/stores/auth'
const hasPermission = (permission) => {
return useAuthStore().permissions.includes(permission)
}
</script>
8. 部署与监控方案
8.1 Docker集成部署
后端Dockerfile示例:
dockerfile复制FROM openjdk:17-jdk-slim
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
前端Dockerfile示例:
dockerfile复制FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
使用docker-compose编排:
yaml复制version: '3'
services:
backend:
build: ./backend
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
frontend:
build: ./frontend
ports:
- "80:80"
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=workload
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
8.2 Prometheus监控配置
SpringBoot Actuator集成:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
tags:
application: workload-system
前端性能监控(使用web-vitals):
javascript复制import { getCLS, getFID, getLCP } from 'web-vitals'
function sendToAnalytics(metric) {
const body = JSON.stringify(metric)
navigator.sendBeacon('/analytics', body)
}
getCLS(sendToAnalytics)
getFID(sendToAnalytics)
getLCP(sendToAnalytics)
9. 项目演进方向
9.1 技术债清理计划
当前待优化项:
- 部分复杂查询需要重构为存储过程
- Vue组件需要增加单元测试覆盖率(目标80%+)
- 引入GraalVM原生镜像编译支持
9.2 功能扩展路线
近期规划:
- 移动端适配(基于Vant组件库)
- 工时预测功能(使用TensorFlow.js)
- 与企业微信/钉钉集成
长期规划:
- 多维度数据分析看板
- 自动化报告生成(PDF/Excel)
- 项目成本核算功能
在三个月的实际运行中,我们发现用户最需要的其实是更灵活的自定义报表功能,这将成为下个迭代周期的重点。同时建议新上手的开发者特别注意MyBatis的N+1查询问题,我们在中期评审时发现这导致了约30%的慢查询。
