1. 项目背景与核心价值
在传统办公环境中,信息孤岛、流程繁琐、响应迟缓等问题长期困扰着企业运营。我曾参与过某制造企业的信息化改造项目,亲眼目睹他们用Excel表格管理200多名员工的考勤和报销,每月底财务部门需要手动合并40多张表格,错误率高达15%。这正是我们决定开发这套OA系统的初衷。
SpringBoot+Vue的前后端分离架构为现代OA系统提供了理想的技术栈。后端采用Java生态的成熟解决方案,前端使用灵活的Vue框架,两者通过RESTful API无缝对接。这种架构的优势在疫情期间尤为明显——当企业突然需要支持远程办公时,我们的系统可以零改造直接适配移动端访问。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构深度解析
2.1 后端技术选型决策
选择SpringBoot 2.7.x版本而非最新的3.0系列,这是经过严格技术评估的决策。在兼容性测试中我们发现:
- JDK17的LTS支持周期与客户现有基础设施更匹配
- SpringBoot 2.7对MyBatis-Plus 3.5.x的支持更稳定
- 企业客户的生产环境通常滞后主流版本1-2个迭代周期
数据库设计采用了分库分表策略:
sql复制# 核心表结构示例
CREATE TABLE `oa_employee` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '雪花算法ID',
`dept_id` int(11) NOT NULL COMMENT '部门ID',
`employee_no` varchar(32) NOT NULL COMMENT '工号',
`name` varchar(64) NOT NULL COMMENT '姓名',
`position_level` tinyint(4) DEFAULT '1' COMMENT '职级',
`status` tinyint(4) DEFAULT '1' COMMENT '状态(1在职 0离职)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_employee_no` (`employee_no`),
KEY `idx_dept` (`dept_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工主表';
2.2 前端工程化实践
Vue3组合式API大幅提升了代码可维护性。我们特别设计了以下工程规范:
- 按功能模块划分的store管理
- 基于axios的请求拦截器统一处理401/403状态码
- 动态路由表配合后端权限体系
- 采用svg-sprite-loader实现图标按需加载
javascript复制// 典型API调用示例
export const getEmployeeList = (params) => {
return request({
url: '/api/employee/list',
method: 'get',
params,
// 特殊业务标记
_businessType: 'HR_MANAGEMENT'
})
}
3. 核心功能实现细节
3.1 权限系统设计
RBAC模型在实际落地时进行了业务适配:
- 引入数据权限概念(部门隔离)
- 操作日志与权限变更联动
- 前端路由权限与按钮级权限分离
权限校验的核心逻辑:
java复制@Aspect
@Component
public class PermissionAspect {
@Before("@annotation(requiresPermission)")
public void checkPermission(JoinPoint jp, RequiresPermission requiresPermission) {
String permissionKey = requiresPermission.value();
UserContext user = SecurityUtils.getCurrentUser();
if (!permissionService.hasPermission(user, permissionKey)) {
throw new BusinessException(ErrorCode.PERMISSION_DENIED);
}
// 数据权限过滤
if (jp.getArgs() != null) {
Arrays.stream(jp
