基于 JeecgBoot 低代码平台构建请假审批系统实战
引言:低代码开发模式与架构优势
在当今快速变化的商业环境中,企业应用开发的传统瓶颈日益凸显:开发周期长、成本高、对专业开发人员依赖度强、难以快速响应业务变化。低代码开发平台正成为解决这一困境的关键技术。
JeecgBoot 作为主流的 Java 低代码开发平台,凭借其强大的代码生成能力和可视化开发工具,让开发者能够以高效的速度构建企业级应用。本文将通过一个完整的'请假审批系统'实战案例,深入解析 JeecgBoot 的低代码核心原理,并展示如何完成一个功能完备的后台系统开发。
一、JeecgBoot 低代码架构深度解析
1.1 JeecgBoot 整体架构概览
JeecgBoot 采用经典的前后端分离架构,但其核心价值在于提供了一系列可视化低代码工具,大幅降低了开发门槛。下面的架构图展示了 JeecgBoot 的核心组件及其协作关系:

这种架构设计的核心优势在于分层解耦和工具集成。JeecgBoot 不仅提供了技术框架,更重要的是集成了完整的低代码开发工具链,使得开发者可以专注于业务逻辑,而非重复的技术实现。
1.2 低代码核心组件原理解析
1.2.1 在线表单设计器:可视化 UI 构建
JeecgBoot 的在线表单设计器是其低代码能力的核心体现。它通过 JSON Schema 来描述表单结构和行为,实现了完全可视化的表单构建。其工作原理如下:
{
"schemas": [
{
"field": "leaveType",
"label": "请假类型",
"component": "JRadioButton",
"componentProps": {
"options": [
{"label": "年假", "value": "1"},
{"label": "病假", "value": "2"},
{"label": "事假", "value": "3"},
{"label": "调休", "value": "4"}
]
},
"rules": [{"required": true, "message": "请选择请假类型"}]
},
{
"field": "startTime",
"label": "开始时间",
"component": "JDatePicker",
"componentProps": {"showTime": true, "format": "YYYY-MM-DD HH:mm:ss"},
"rules": [{"required": true, "message": "请选择开始时间"}]
}
]
}
表单设计器的技术实现基于以下关键机制:
- 组件注册中心:维护所有可用表单组件的元数据
- Schema 解析引擎:将 JSON Schema 转换为实际的 Vue 组件
- 双向数据绑定:实时同步表单数据与 UI 状态
- 验证规则引擎:动态应用表单验证规则
1.2.2 代码生成器:自动化 CRUD 开发
JeecgBoot 的代码生成器是其'快速开发'的基石。它采用模板驱动的代码生成策略,支持高度定制化。代码生成的工作流程如下:
- 用户选择数据表
- 解析表结构
- 生成基础代码模板
- 选择生成模式(单表模式、树表模式、一对多模式、ERP 模式)
- 应用 Velocity 模板
- 生成源代码文件、SQL 文件、前端页面
- 输出到项目目录
- 用户二次开发
代码生成器的核心技术包括:
- 数据库元数据解析:通过 JDBC DatabaseMetaData 接口获取表结构信息
- 模板引擎:使用 Velocity 模板引擎进行代码生成
- 类型映射系统:将数据库字段类型映射为 Java 类型和前端组件
- 路径计算算法:根据命名规范自动计算包路径和文件路径
1.2.3 流程设计器:可视化工作流配置
JeecgBoot 集成了 Activiti 工作流引擎,并提供可视化流程设计器。这使得非技术人员也能设计和修改业务流程:
@Entity
@Table(name = "act_re_procdef")
public class ProcessDefinition {
@Id
private String id;
@Column(name = "category_")
private String category; // 流程分类
@Column(name = "name_")
private String name; // 流程名称
@Column(name = "key_")
private String key; // 流程 KEY
@Column(name = "version_")
private Integer version; // 版本
@Column(name = "deployment_id_")
private String deploymentId; // 部署 ID
@Column(name = "resource_name_")
private String resourceName; // 资源名称
@Column(name = "diagram_resource_name_")
private String diagramResourceName; // 流程图资源
@Column(name = "suspension_state_")
private Integer suspensionState; // 挂起状态
}
流程设计器的核心特性:
- BPMN 2.0 兼容:支持标准 BPMN 流程图设计
- 节点类型丰富:包括用户任务、服务任务、网关、事件等
- 表单绑定:支持将流程节点与动态表单关联
- 权限配置:可视化配置任务处理人和候选组
1.2.4 报表设计器:可视化数据分析
JeecgBoot 的报表设计器支持拖拽式报表设计,集成了多种图表类型和数据源:
# 报表配置示例
report:
id: leave_statistics
name: 请假统计报表
type: chart
dataSource:
type: sql
query: |
SELECT DATE_FORMAT(start_time, '%Y-%m') as month, leave_type, COUNT(*) as count, SUM(DATEDIFF(end_time, start_time)) as total_days FROM sys_leave WHERE status = '2' -- 已批准 GROUP BY DATE_FORMAT(start_time, '%Y-%m'), leave_type
charts:
- type: bar
title: 月度请假统计
xAxis: month
yAxis: count
series: leave_type
- type: pie
title: 请假类型分布
data: leave_type
value: count
二、实战演练:一天构建请假审批系统
2.1 需求分析与系统设计
2.1.1 业务需求
我们要构建的请假审批系统需要满足以下核心需求:
- 员工功能:提交请假申请(包括类型、时间、事由等)、查看请假申请状态和历史记录、撤销未审批的申请
- 审批功能:部门经理审批(1-3 天请假)、人事部门审批(3 天以上请假或特殊类型)、总经理审批(7 天以上请假)
- 管理功能:请假类型管理、审批流程配置、请假统计报表
- 系统功能:消息通知(邮件、站内信)、日历视图展示、导出请假记录
2.1.2 数据库设计
-- 请假申请表
CREATE TABLE `sys_leave` (
`id` varchar(32) NOT NULL COMMENT '主键 ID',
`user_id` varchar(32) NOT NULL COMMENT '申请人 ID',
`user_name` varchar(50) NOT NULL COMMENT '申请人姓名',
`dept_id` varchar(32) DEFAULT NULL COMMENT '部门 ID',
`dept_name` varchar(50) DEFAULT NULL COMMENT '部门名称',
`leave_type` varchar(2) NOT NULL COMMENT '请假类型 1 年假 2 病假 3 事假 4 调休',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`leave_days` decimal(10,1) NOT NULL COMMENT '请假天数',
`reason` varchar(500) NOT NULL COMMENT '请假事由',
`emergency_contact` varchar(50) DEFAULT NULL COMMENT '紧急联系人',
`emergency_phone` varchar(20) DEFAULT NULL COMMENT '紧急联系电话',
`attachment` varchar(500) DEFAULT NULL COMMENT '附件',
`status` varchar(2) NOT NULL DEFAULT '0' COMMENT '状态 0 草稿 1 审批中 2 已批准 3 已拒绝 4 已撤销',
`process_instance_id` varchar(64) DEFAULT NULL COMMENT '流程实例 ID',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='请假申请表';
-- 请假审批记录表
CREATE TABLE `sys_leave_approval` (
`id` varchar(32) NOT NULL COMMENT '主键 ID',
`leave_id` varchar(32) NOT NULL COMMENT '请假 ID',
`approval_user_id` varchar(32) NOT NULL COMMENT '审批人 ID',
`approval_user_name` varchar(50) NOT NULL COMMENT '审批人姓名',
`approval_result` varchar(2) NOT NULL COMMENT '审批结果 1 通过 2 拒绝',
`approval_comment` varchar(500) DEFAULT NULL COMMENT '审批意见',
`approval_time` datetime NOT NULL COMMENT '审批时间',
`approval_node` varchar(50) NOT NULL COMMENT '审批节点',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `idx_leave_id` (`leave_id`),
KEY `idx_approval_user_id` (`approval_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='请假审批记录表';
-- 请假类型表
CREATE TABLE `sys_leave_type` (
`id` varchar(32) NOT NULL COMMENT '主键 ID',
`type_code` varchar(20) NOT NULL COMMENT '类型编码',
`type_name` varchar(50) NOT NULL COMMENT '类型名称',
`max_days` int(11) DEFAULT NULL COMMENT '最大天数',
`need_approval` tinyint(1) DEFAULT '1' COMMENT '是否需要审批',
`description` varchar(200) DEFAULT NULL COMMENT '描述',
`sort_no` int(11) DEFAULT '0' COMMENT '排序号',
`status` varchar(2) DEFAULT '1' COMMENT '状态 1 启用 0 停用',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_type_code` (`type_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='请假类型表';
2.2 步骤一:使用在线开发创建请假模块
2.2.1 创建数据表并导入
首先在数据库中创建上述表结构,然后进入 JeecgBoot 的在线开发功能模块:
- 登录 JeecgBoot 系统,进入'系统管理' → '在线开发'
- 点击'导入数据库表',选择
sys_leave表 - 系统自动解析表结构,生成字段配置
2.2.2 配置表单字段
在字段配置界面,我们可以对每个字段进行详细配置:
| 字段名 | 字段描述 | 表单组件 | 校验规则 | 列表显示 | 查询条件 |
|---|---|---|---|---|---|
| leave_type | 请假类型 | 下拉框 | 必填 | 是 | 是 |
| start_time | 开始时间 | 时间选择 | 必填 | 是 | 范围查询 |
| end_time | 结束时间 | 时间选择 | 必填 | 是 | - |
| leave_days | 请假天数 | 输入框 | 数字校验 | 是 | - |
| reason | 请假事由 | 文本域 | 必填,最大 500 字 | 是 | 模糊查询 |
| status | 状态 | 下拉框 | - | 是 | 是 |
特殊字段配置示例:
{
"field": "leave_type",
"title": "请假类型",
"component": "JSearchSelect",
"options": [
{"text": "年假", "value": "1"},
{"text": "病假", "value": "2"},
{"text": "事假", "value": "3"},
{"text": "调休", "value": "4"}
],
"dictCode": "leave_type",
"validateRules": [{"required": true}],
"tableShow": true,
"queryShow": true
}
2.2.3 生成代码并导入项目
配置完成后,点击'生成代码'按钮,系统将生成以下文件:
- 后端代码:
SysLeaveController.java- 控制器ISysLeaveService.java- 服务接口SysLeaveServiceImpl.java- 服务实现SysLeaveMapper.java- 数据访问接口SysLeaveMapper.xml- MyBatis 映射文件SysLeave.java- 实体类
- 前端代码:
SysLeaveList.vue- 列表页面SysLeaveModal.vue- 表单弹窗SysLeave.js- API 接口文件
- SQL 脚本:
- 初始化数据脚本
- 菜单权限脚本
下载生成的代码包,解压并按照说明导入到 JeecgBoot 项目中。
2.3 步骤二:使用流程设计器配置审批流程
2.3.1 设计请假审批流程
进入'流程设计'模块,新建一个名为'请假审批流程'的流程:
- 开始:提交请假申请
- 判断:请假天数
- 1-3 天:部门经理审批 -> 结束
- 3-7 天:部门经理审批 -> 人事审批 -> 结束
- 7 天以上:部门经理审批 -> 人事审批 -> 总经理审批 -> 结束
- 结束:更新请假状态,通知申请人
2.3.2 配置流程节点
每个审批节点都需要配置以下信息:
- 节点基本信息:名称、处理人/组、表单
- 表单绑定:关联请假申请表单
- 处理规则:自动通过、手动审批等
- 通知配置:邮件、站内信通知
部门经理审批节点配置示例:
<userTask id="deptManagerApprove" name="部门经理审批">
<extensionElements>
<activiti:formProperty id="approvalResult" name="审批结果" type="enum" required="true">
<activiti:value id="1" name="通过"/>
<activiti:value id="2" name="拒绝"/>
</activiti:formProperty>
<activiti:formProperty id="approvalComment" name="审批意见" type="string"/>
<activiti:taskListener event="create" class="com.jeecg.listener.LeaveTaskListener"/>
</extensionElements>
<documentation>部门经理审批请假申请</documentation>
</userTask>
2.3.3 配置流程网关
流程中的决策网关需要配置条件表达式:
<sequenceFlow id="flow1" sourceRef="start" targetRef="judgeDays">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${leave != null}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow2" sourceRef="judgeDays" targetRef="deptManagerOnly">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${leave.leaveDays <= 3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow3" sourceRef="judgeDays" targetRef="deptManagerFirst">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${leave.leaveDays > 3 && leave.leaveDays <= 7}]]></conditionExpression>
</sequenceFlow>
2.4 步骤三:自定义业务逻辑开发
虽然 JeecgBoot 的在线开发可以生成大部分 CRUD 代码,但复杂的业务逻辑仍需要手动开发。
2.4.1 请假天数自动计算
在 SysLeaveServiceImpl.java 中添加业务逻辑:
@Service
public class SysLeaveServiceImpl extends ServiceImpl<SysLeaveMapper, SysLeave> implements ISysLeaveService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean saveLeave(SysLeave sysLeave) {
// 自动计算请假天数
sysLeave.setLeaveDays(calculateLeaveDays(sysLeave.getStartTime(), sysLeave.getEndTime()));
// 设置默认状态
if (sysLeave.getStatus() == null) {
sysLeave.setStatus("0"); // 草稿状态
}
// 设置申请人信息
LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
sysLeave.setUserId(loginUser.getId());
sysLeave.setUserName(loginUser.getRealname());
// 保存请假申请
boolean result = this.save(sysLeave);
// 如果状态是提交审批,则启动流程
if ("1".equals(sysLeave.getStatus())) {
startLeaveProcess(sysLeave);
}
return result;
}
/**
* 计算请假天数(考虑工作日)
*/
private BigDecimal calculateLeaveDays(Date startTime, Date endTime) {
// 简单的天数计算,实际应排除周末和节假日
long diff = endTime.getTime() - startTime.getTime();
double days = (double) diff / (1000 * 60 * 60 * 24);
// 向上取整,半天按 0.5 天计算
if (days % 1 > 0) {
days = Math.ceil(days * 2) / 2;
}
return BigDecimal.valueOf(days);
}
/**
* 启动请假审批流程
*/
private void startLeaveProcess(SysLeave leave) {
try {
// 获取流程定义
ProcessDefinition processDefinition = repositoryService
.createProcessDefinitionQuery()
.processDefinitionKey("leave_approval_process")
.latestVersion()
.singleResult();
// 设置流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("leave", leave);
variables.put("applicant", leave.getUserId());
variables.put("deptManager", getDeptManager(leave.getDeptId()));
// 启动流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceById(
processDefinition.getId(), leave.getId(), variables);
// 更新请假申请的流程实例 ID
leave.setProcessInstanceId(processInstance.getId());
this.updateById(leave);
// 发送通知
sendProcessStartNotification(leave);
} catch (Exception e) {
log.error("启动请假流程失败", e);
throw new JeecgBootException("启动审批流程失败");
}
}
}
2.4.2 审批结果处理
创建审批服务类处理流程任务:
@Component
public class LeaveApprovalService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private ISysLeaveService leaveService;
@Autowired
private ISysLeaveApprovalService leaveApprovalService;
/**
* 处理审批任务
*/
@Transactional(rollbackFor = Exception.class)
public void handleApprovalTask(String taskId, String approvalResult, String comment, String userId) {
// 获取当前任务
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
throw new JeecgBootException("任务不存在或已完成");
}
// 获取流程变量
String leaveId = task.getBusinessKey();
SysLeave leave = leaveService.getById(leaveId);
// 保存审批记录
SysLeaveApproval approval = new SysLeaveApproval();
approval.setLeaveId(leaveId);
approval.setApprovalUserId(userId);
approval.setApprovalResult(approvalResult);
approval.setApprovalComment(comment);
approval.setApprovalNode(task.getTaskDefinitionKey());
approval.setApprovalTime(new Date());
leaveApprovalService.save(approval);
// 设置任务变量
Map<String, Object> variables = new HashMap<>();
variables.put("approvalResult", approvalResult);
variables.put("approvalComment", comment);
variables.put("approvalUser", userId);
// 完成任务
taskService.complete(taskId, variables);
// 如果流程结束,更新请假状态
ProcessInstance processInstance = runtimeService
.createProcessInstanceQuery()
.processInstanceId(task.getProcessInstanceId())
.singleResult();
if (processInstance == null) {
// 流程已结束
String finalStatus = "1".equals(approvalResult) ? "2" : "3"; // 2-已批准 3-已拒绝
leave.setStatus(finalStatus);
leaveService.updateById(leave);
// 发送最终通知
sendFinalNotification(leave, finalStatus);
}
}
}
2.5 步骤四:使用报表设计器创建统计图表
2.5.1 设计请假统计报表
进入'报表设计'模块,创建请假统计报表:
- 数据源配置:连接
sys_leave表 - SQL 查询设计:
SELECT DATE_FORMAT(start_time,'%Y-%m') as 月份,leave_type as 请假类型,COUNT(*) as 申请次数,SUM(leave_days) as 总天数,AVG(leave_days) as 平均天数
FROM sys_leave
WHERE status='2' -- 仅统计已批准的
GROUP BY DATE_FORMAT(start_time,'%Y-%m'), leave_type
ORDER BY 月份 DESC
- 图表设计:
- 柱状图:每月各类型请假天数对比
- 饼图:请假类型分布
- 折线图:请假趋势分析
2.5.2 报表页面集成
将报表集成到请假管理模块中:
<template>
<div>
<a-card :bordered="false">
<!-- 报表筛选条件 -->
<div>
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item label="统计月份">
<a-month-picker v-model="queryParam.month" format="YYYY-MM" placeholder="请选择月份" />
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="部门">
<j-select-depart v-model="queryParam.departId" :multi="false" />
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<span>
<a-button type="primary" @click="searchQuery">查询</a-button>
<a-button @click="searchReset">重置</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- 图表展示 -->
<a-row :gutter="24">
<a-col :span="12">
<a-card title="月度请假统计" size="small">
<div id="monthChart"></div>
</a-card>
</a-col>
<a-col :span="12">
<a-card title="请假类型分布" size="small">
<div id="typeChart"></div>
</a-card>
</a-col>
</a-row>
<!-- 数据表格 -->
<div>
<a-table ref="table" size="middle" :columns="columns" :dataSource="dataSource" :pagination="ipagination" @change="handleTableChange" rowKey="id">
</a-table>
</div>
</a-card>
</div>
</template>
<script>
import { getLeaveStatistics } from '@/api/system/leave'
import { Chart } from '@antv/g2'
export default {
name: 'LeaveStatistics',
data() {
return {
queryParam: {},
ipagination: {
current: 1,
pageSize: 10,
total: 0
},
columns: [
{ title: '月份', dataIndex: 'month', align: 'center' },
{ title: '请假类型', dataIndex: 'leaveType', align: 'center' },
{ title: '申请次数', dataIndex: 'applyCount', align: 'center' },
{ title: '总天数', dataIndex: 'totalDays', align: 'center' },
{ title: '平均天数', dataIndex: 'avgDays', align: 'center' }
],
dataSource: [],
monthChart: null,
typeChart: null
}
},
mounted() {
this.loadData()
this.initCharts()
},
methods: {
loadData() {
const params = { ...this.queryParam, pageNo: this.ipagination.current, pageSize: this.ipagination.pageSize }
getLeaveStatistics(params).then(res => {
if (res.success) {
this.dataSource = res.result.records || []
this.ipagination.total = res.result.total
// 更新图表数据
this.updateCharts(res.result.records)
}
})
},
initCharts() {
// 初始化月度统计图表
this.monthChart = new Chart({ container: 'monthChart', autoFit: true, height: 300 })
// 初始化类型分布图表
this.typeChart = new Chart({ container: 'typeChart', autoFit: true, height: 300 })
},
updateCharts(data) {
// 更新月度统计
const monthData = this.processMonthData(data)
this.monthChart.data(monthData)
this.monthChart.scale({ month: { alias: '月份' }, value: { alias: '请假天数' }, type: { alias: '请假类型' } })
this.monthChart.interval().position('month*value').color('type')
this.monthChart.render()
// 更新类型分布
const typeData = this.processTypeData(data)
this.typeChart.data(typeData)
this.typeChart.coordinate('theta', { radius: 0.75 })
this.typeChart.tooltip({ showTitle: false, showMarkers: false })
this.typeChart.interval()
.position('value')
.color('type')
.label('type', { content: (data) => {
return `${data.type}: ${data.value}`
}})
this.typeChart.render()
},
processMonthData(data) {
// 处理月度数据逻辑
return []
},
processTypeData(data) {
// 处理类型数据逻辑
return []
}
}
}
</script>
三、JeecgBoot 前后端分离架构剖析
3.1 前端架构:Ant Design Vue 深度集成
JeecgBoot 前端基于 Ant Design Vue,并进行了深度定制和扩展:
3.1.1 组件封装体系
JeecgBoot 封装了大量业务组件,提高开发效率:
// 自定义业务组件示例
import JDate from '@/components/jeecg/JDate'
import JSelectUser from '@/components/jeecg/JSelectUser'
import JSelectDepart from '@/components/jeecg/JSelectDepart'
import JSearchSelect from '@/components/jeecg/JSearchSelect'
import JEditor from '@/components/jeecg/JEditor'
import JUpload from '@/components/jeecg/JUpload'
import JTreeSelect from '@/components/jeecg/JTreeSelect'
// 组件全局注册
const components = [JDate, JSelectUser, JSelectDepart, JSearchSelect, JEditor, JUpload, JTreeSelect]
const install = function(Vue) {
components.forEach(component => {
Vue.component(component.name, component)
})
}
export default { install }
3.1.2 表单渲染引擎
JeecgBoot 的表单渲染引擎是其低代码能力的关键:
// 表单渲染引擎核心逻辑
export default {
name: 'JFormEngine',
props: {
schema: { type: Array, required: true },
model: { type: Object, required: true },
disabled: { type: Boolean, default: false }
},
render(h) {
// 动态渲染表单字段
const children = this.schema.map(field => {
// 根据字段配置选择组件
const component = this.getComponent(field)
// 构建组件属性
const props = {
value: this.model[field.field],
disabled: this.disabled || field.disabled,
...field.componentProps
}
// 构建组件事件
const on = {
input: value => {
this.$set(this.model, field.field, value)
this.$emit('change', field.field, value)
}
}
// 渲染表单项
return h('a-form-item', {
props: {
label: field.label,
required: field.rules && field.rules.some(r => r.required)
}
}, [h(component, { props, on })])
})
return h('a-form', children)
},
methods: {
getComponent(field) {
// 组件映射表
const componentMap = {
input: 'a-input',
select: 'a-select',
date: 'j-date',
user: 'j-select-user',
depart: 'j-select-depart',
editor: 'j-editor',
upload: 'j-upload'
}
return componentMap[field.component] || 'a-input'
}
}
}
3.2 后端架构:统一响应与异常处理
3.2.1 统一响应格式
JeecgBoot 定义了标准的 API 响应格式:
/**
* 统一 API 响应对象
*/
@Data
public class Result<T> {
/**
* 是否成功
*/
private boolean success;
/**
* 响应代码
*/
private Integer code;
/**
* 响应消息
*/
private String message;
/**
* 响应数据
*/
private T result;
/**
* 时间戳
*/
private Long timestamp;
/**
* 成功响应
*/
public static <T> Result<T> OK() {
Result<T> r = new Result<>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setMessage("操作成功");
r.setTimestamp(System.currentTimeMillis());
return r;
}
/**
* 成功响应(带数据)
*/
public static <T> Result<T> OK(T data) {
Result<T> r = new Result<>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setResult(data);
r.setMessage("操作成功");
r.setTimestamp(System.currentTimeMillis());
return r;
}
/**
* 失败响应
*/
public static <T> Result<T> error(String msg) {
return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
}
/**
* 失败响应(带错误码)
*/
public static <T> Result<T> error(int code, String msg) {
Result<T> r = new Result<>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
r.setTimestamp(System.currentTimeMillis());
return r;
}
}
3.2.2 全局异常处理
/**
* 全局异常处理器
*/
@RestControllerAdvice
@Slf4j
public class JeecgBootExceptionHandler {
/**
* 处理业务异常
*/
@ExceptionHandler(JeecgBootException.class)
public Result<?> handleJeecgBootException(JeecgBootException e) {
log.error(e.getMessage(), e);
return Result.error(e.getMessage());
}
/**
* 处理数据库异常
*/
@ExceptionHandler(DaoException.class)
public Result<?> handleDaoException(DaoException e) {
log.error("数据库操作异常", e);
return Result.error("数据库操作失败");
}
/**
* 处理权限异常
*/
@ExceptionHandler({UnauthorizedException.class, AuthorizationException.class})
public Result<?> handleAuthorizationException(AuthorizationException e) {
log.error("权限校验异常", e);
return Result.error(CommonConstant.SC_JEECG_NO_AUTHZ, "没有权限,请联系管理员授权");
}
/**
* 处理所有其他异常
*/
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception e) {
log.error("系统异常", e);
return Result.error("系统异常,请联系管理员");
}
}
3.3 前后端通信规范
3.3.1 API 请求封装
// 请求拦截器
service.interceptors.request.use(config => {
// 设置 token
const token = Vue.ls.get(ACCESS_TOKEN)
if (token) {
config.headers['X-Access-Token'] = token
}
// 设置时间戳,防止缓存
if (config.method === 'get') {
config.params = { ...config.params, _t: new Date().getTime() }
}
return config
}, error => {
return Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(response => {
// 处理文件下载
if (response.config.responseType === 'blob') {
return response
}
const res = response.data
// 处理成功响应
if (res.success) {
return res.result
}
// 处理 token 过期
if (res.code === 401) {
Modal.error({
title: '登录过期',
content: '登录已过期,请重新登录',
okText: '重新登录',
onOk: () => {
store.dispatch('Logout').then(() => {
location.reload()
})
}
})
return Promise.reject(new Error(res.message || 'Error'))
}
// 处理其他错误
const errMsg = res.message || '请求失败'
message.error(errMsg)
return Promise.reject(new Error(errMsg))
}, error => {
// 网络错误处理
if (!error.response) {
message.error('网络连接异常')
return Promise.reject(error)
}
// HTTP 状态码错误处理
const { status, data } = error.response
switch (status) {
case 400: message.error(data.message || '请求参数错误'); break
case 404: message.error('请求的资源不存在'); break
case 500: message.error('服务器内部错误'); break
default: message.error('请求失败')
}
return Promise.reject(error)
})
四、低代码模式下的开发者角色演变
4.1 从编码者到架构设计者
在低代码开发模式下,开发者的角色发生了重大转变:

4.2 低代码开发的边界与限制
虽然 JeecgBoot 等低代码平台大幅提升了开发效率,但仍存在明确的边界:
4.2.1 适合低代码开发的场景
- 常规业务管理系统:OA、CRM、ERP 等
- 数据收集与展示系统:报表系统、数据分析平台
- 工作流审批系统:各种审批流程管理
- 内部管理工具:员工管理、资产管理等
4.2.2 不适合低代码开发的场景
- 高性能计算系统:科学计算、实时交易等
- 复杂算法实现:机器学习、图像处理等
- 特定硬件交互:物联网设备控制、驱动程序
- 超高并发系统:大型电商、社交平台等
4.3 开发者需要关注的底层技术
即使在低代码环境下,开发者仍需掌握以下核心技术:
4.3.1 数据库设计与优化
-- 虽然低代码平台可以生成基础 CRUD,但复杂查询仍需手动优化
-- 示例:请假统计的优化查询
EXPLAIN
SELECT l.user_id, u.realname, d.depart_name,
COUNT(*) as total_count, SUM(l.leave_days) as total_days, MAX(l.create_time) as last_leave
FROM sys_leave l
LEFT JOIN sys_user u ON l.user_id = u.id
LEFT JOIN sys_depart d ON u.depart_id = d.id
WHERE l.status='2' AND l.create_time >= DATE_SUB(NOW(), INTERVAL 1 YEAR)
GROUP BY l.user_id, u.realname, d.depart_name
HAVING total_days > 10
ORDER BY total_days DESC LIMIT 100;
-- 创建优化索引
CREATE INDEX idx_leave_user_status ON sys_leave(user_id, status, create_time);
CREATE INDEX idx_leave_dept_time ON sys_leave(dept_id, create_time);
4.3.2 缓存策略设计
/**
* 请假信息缓存服务
*/
@Service
@Slf4j
public class LeaveCacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ISysLeaveService leaveService;
/**
* 获取用户请假统计(带缓存)
*/
public UserLeaveStats getUserLeaveStats(String userId) {
String cacheKey = String.format("leave:stats:user:%s", userId);
// 尝试从缓存获取
UserLeaveStats stats = (UserLeaveStats) redisTemplate.opsForValue().get(cacheKey);
if (stats != null) {
return stats;
}
// 缓存未命中,查询数据库
stats = leaveService.calculateUserLeaveStats(userId);
// 写入缓存,设置过期时间
if (stats != null) {
redisTemplate.opsForValue().set(cacheKey, stats, 1, TimeUnit.HOURS); // 缓存 1 小时
}
return stats;
}
/**
* 清除用户请假缓存
*/
public void clearUserLeaveCache(String userId) {
String cacheKey = String.format("leave:stats:user:%s", userId);
redisTemplate.delete(cacheKey);
// 同时清除相关的列表缓存
String listKey = String.format("leave:list:user:%s:*", userId);
Set<String> keys = redisTemplate.keys(listKey);
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
}
4.3.3 系统监控与性能优化
# application-monitor.yml
management:
endpoints:
web:
exposure:
include: health, info, metrics, prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
tags:
application: jeecg-leave-system
distribution:
percentiles-histogram:
http.server.requests: true
# 自定义监控指标
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "leave-system",
"region", System.getProperty("region", "unknown")
);
}
# 性能监控配置
logging:
level:
com.jeecg: DEBUG
file:
name: logs/jeecg-leave.log
logback:
rollingpolicy:
max-file-size: 10MB
max-history: 30
4.4 低代码开发的最佳实践
4.4.1 分层开发策略
- 基础层:使用低代码工具快速生成基础 CRUD
- 业务层:手动开发复杂业务逻辑和服务
- 集成层:处理外部系统对接和消息队列
- 表现层:使用低代码表单和报表设计器
4.4.2 代码管理规范
# JeecgBoot 项目代码管理规范
## 目录结构
src/main/java/com/jeecg/
├── modules/ # 业务模块
│ ├── leave/ # 请假模块
│ │ ├── entity/ # 实体类(低代码生成)
│ │ ├── mapper/ # 数据访问(低代码生成)
│ │ ├── service/ # 服务层
│ │ │ ├── impl/ # 服务实现
│ │ │ └── task/ # 定时任务
│ │ ├── controller/ # 控制器(低代码生成 + 手动增强)
│ │ └── api/ # API 接口定义
├── common/ # 通用模块
└── system/ # 系统模块(低代码生成)
## 命名规范
- 低代码生成的文件:保留原名
- 手动新增的文件:添加业务前缀,如 LeaveStatService.java
- 重写低代码生成的方法:添加@Override 注解
## 版本控制
- 低代码生成的代码:单独提交,备注'低代码生成'
- 手动修改的代码:详细说明修改原因
- 业务逻辑代码:遵循特性分支工作流
五、总结:JeecgBoot 低代码实践的价值与展望
5.1 一天开发的价值体现
通过本文的实战演示,我们可以看到使用 JeecgBoot 在一天内构建请假审批系统的实际效果:
- 开发效率提升:相比传统开发,效率提升显著
- 代码质量保证:生成的代码规范统一,减少人为错误
- 维护成本降低:可视化配置使得后期修改更加容易
- 团队协作优化:前后端分离,并行开发成为可能
5.2 JeecgBoot 的适用场景总结
JeecgBoot 特别适合以下类型的项目和团队:
| 项目类型 | 适合程度 | 关键考量 |
|---|---|---|
| 企业内部管理系统 | ★★★★★ | 快速响应业务变化需求 |
| 政府政务系统 | ★★★★☆ | 需要流程审批和权限控制 |
| 中小型企业 ERP | ★★★★☆ | 定制化需求多,预算有限 |
| 教育管理系统 | ★★★★☆ | 用户角色复杂,权限细分 |
| 互联网创新项目 MVP | ★★★☆☆ | 快速验证产品思路 |
5.3 未来发展趋势
随着低代码技术的不断发展,我们可以预见以下趋势:
- AI 增强:集成 AI 能力,实现智能代码生成和建议
- 多端统一:一套代码生成多端应用(Web、小程序、App)
- 云原生集成:深度集成云服务,实现一键部署和扩缩容
- 可视化深度:从界面可视化到业务逻辑可视化
- 生态开放:建立插件市场,汇集第三方组件和服务
5.4 给开发者的建议
对于正在或计划使用 JeecgBoot 等低代码平台的开发者,建议:
- 掌握核心原理:不要只停留在使用层面,要理解实现机制
- 保持编码能力:低代码不是无代码,复杂逻辑仍需编码实现
- 关注业务价值:从技术实现者转变为业务解决方案提供者
- 持续学习更新:低代码技术发展迅速,需要不断学习新特性
- 参与社区贡献:积极反馈问题,贡献代码,共同推动平台发展
结语
JeecgBoot 作为国内主流的 Java 低代码开发平台,通过本文的深度解析和实战演示,我们不仅看到了它如何大幅提升开发效率,更理解了其背后的技术原理和设计哲学。低代码不是要取代开发者,而是要解放开发者,让开发者从重复的 CRUD 编码中解脱出来,专注于更有价值的业务创新和技术攻关。
在数字化转型的浪潮中,掌握 JeecgBoot 这样的低代码工具,意味着你拥有了快速响应业务变化、高效交付企业应用的能力。这不仅是技术能力的提升,更是职业竞争力的重要组成部分。
希望本文能够帮助你深入理解 JeecgBoot 的低代码开发模式,并在实际项目中应用这些知识,真正实现快速搭建功能完备后台系统的目标。


