在企业日常运营中,代办事项的处理效率直接影响着团队协作效能与事务推进的透明度。传统依赖纸质单据、电子邮件或即时通讯工具的代办管理方式,普遍存在流程节点不清晰、处理状态难以追踪、历史记录易丢失以及责任归属模糊等问题。这些问题不仅导致任务积压与逾期,也为团队协同与绩效评估带来了障碍。
为解决上述痛点,我们设计并实现了一套基于SSM(Spring + Spring MVC + MyBatis)技术栈的企业级代办流程协同平台。该平台将线下非标准化的代办事项转化为线上可配置、可追踪、可审计的数字化流程,旨在为企业内部行政、财务报销、客户服务等场景提供一套标准化的任务流转与协同解决方案。
技术架构与选型
系统采用经典的三层架构模式,确保各层职责分离,便于维护与扩展。
- 表现层:基于Spring MVC框架构建,负责接收前端HTTP请求并进行路由分发。通过
@Controller和@RequestMapping等注解,实现了请求参数绑定、数据验证及视图解析。前端页面采用HTML、CSS和JavaScript构建,结合Ajax技术实现局部刷新,提升用户体验。 - 业务逻辑层:由Spring框架的IoC容器统一管理各类Service组件。利用Spring的声明式事务管理(
@Transactional),确保业务流程中涉及多个数据库操作时的一致性,例如代办任务的创建、分配、状态更新等操作在一个事务内完成。 - 数据持久层:选用MyBatis作为ORM框架,通过XML映射文件或注解方式定义SQL语句,实现了Java对象与数据库表记录的灵活映射。MyBatis的动态SQL能力支持根据业务条件灵活拼接查询语句,满足复杂查询需求。
- 数据库:使用MySQL作为关系型数据库存储核心业务数据,通过合理的表结构设计与索引优化,保障数据操作的高效性。
项目采用Maven进行依赖管理,统一了第三方库的版本,规范了项目结构。
核心数据模型设计
数据库设计是系统稳定性的基石。本系统共设计10张核心数据表,以下重点分析其中3个关键表的结构与设计思路。
1. 用户表 (user_table)
用户表是系统权限体系的基础,记录了所有系统用户的核心信息。
CREATE TABLE user_table (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE COMMENT '登录用户名',
password VARCHAR(255) NOT NULL COMMENT '加密后的密码',
real_name VARCHAR(50) NOT NULL COMMENT '用户真实姓名',
role ENUM('admin', 'manager', 'staff') NOT NULL COMMENT '用户角色',
department VARCHAR(100) COMMENT '所属部门',
email VARCHAR(100),
phone VARCHAR(20),
status ENUM('active', 'inactive') DEFAULT 'active' COMMENT '账号状态',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
last_login_time DATETIME
) COMMENT='系统用户表';
设计亮点分析:
- 角色枚举约束:
role字段使用MySQL的ENUM类型,严格限定用户角色为admin(管理员)、manager(部门主管)、staff(普通员工)三种,避免了无效数据的插入,同时在应用层便于进行基于角色的权限控制(RBAC)。 - 状态管理:
status字段实现了用户的软删除或停用功能。将用户状态置为inactive而非直接删除记录,可以保留该用户的历史操作数据,满足审计要求。 - 时间戳跟踪:
created_time和last_login_time分别记录了用户的创建时间和最后登录时间,为系统使用情况分析和用户行为追踪提供了数据支持。
2. 流程实例表 (process_instance)
流程实例表是系统的核心,记录了每一个代办事项的完整生命周期。
CREATE TABLE process_instance (
instance_id VARCHAR(32) PRIMARY KEY COMMENT '流程实例ID,全局唯一',
process_definition_key VARCHAR(50) NOT NULL COMMENT '关联的流程定义KEY',
title VARCHAR(200) NOT NULL COMMENT '代办事项标题',
initiator_id INT NOT NULL COMMENT '流程发起人ID',
current_assignee_id INT COMMENT '当前处理人ID',
status ENUM('draft', 'active', 'suspended', 'completed', 'cancelled') NOT NULL DEFAULT 'draft' COMMENT '实例状态',
priority ENUM('low', 'medium', 'high', 'urgent') DEFAULT 'medium' COMMENT '优先级',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (initiator_id) REFERENCES user_table(id),
FOREIGN KEY (current_assignee_id) REFERENCES user_table(id)
) COMMENT='流程实例表';
设计亮点分析:
- 状态机设计:
status字段明确定义了流程实例可能处于的几种状态,构成了一个清晰的状态机。例如,从draft(草稿)到active(活跃),再到completed(完成)或cancelled(取消),状态流转逻辑清晰,便于业务逻辑的实现和查询。 - 优先级机制:
priority字段允许用户对代办事项进行分级,系统可以根据优先级进行任务排序或推送提醒,确保重要任务得到优先处理。 - 时间戳自动更新:
updated_time字段通过ON UPDATE CURRENT_TIMESTAMP实现自动更新,任何对实例的修改都会记录更新时间,无需在代码中手动维护,简化了开发。
3. 任务操作记录表 (task_operation_log)
此表用于审计追踪,记录流程实例每一步的操作细节。
CREATE TABLE task_operation_log (
log_id BIGINT PRIMARY KEY AUTO_INCREMENT,
instance_id VARCHAR(32) NOT NULL COMMENT '关联的流程实例ID',
operator_id INT NOT NULL COMMENT '操作人ID',
from_assignee_id INT COMMENT '操作前处理人',
to_assignee_id INT COMMENT '操作后处理人',
operation_type VARCHAR(50) NOT NULL COMMENT '操作类型,如:create, claim, complete, transfer',
operation_comment TEXT COMMENT '操作意见或备注',
operation_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (instance_id) REFERENCES process_instance(instance_id),
FOREIGN KEY (operator_id) REFERENCES user_table(id)
) COMMENT='任务操作记录表';
设计亮点分析:
- 全链路追踪:通过记录
from_assignee_id和to_assignee_id,可以完整还原任务在用户间的流转路径。结合operation_type和operation_comment,可以清晰地了解每一步“谁、在何时、做了什么、为什么这么做”。 - 非侵入式审计:该表的设计使得审计功能的实现对核心业务代码侵入性极低。通过在Service层的方法调用后插入日志记录,即可实现全流程的追踪,符合设计模式中的“装饰器”或“切面”思想。
核心功能模块实现
1. 用户登录与权限验证
系统采用基于Session的传统身份认证机制。用户登录成功后,其基本信息(如用户ID、角色)会被存入Session中,用于后续的接口权限校验。
登录控制器代码示例:
@Controller
@RequestMapping("/auth")
public class LoginController {
@Autowired
private UserService userService;
@PostMapping("/login")
@ResponseBody
public ResponseEntity<Map<String, Object>> login(@RequestParam String username,
@RequestParam String password,
HttpSession session) {
Map<String, Object> result = new HashMap<>();
try {
// 调用Service进行用户名密码验证
User user = userService.authenticate(username, password);
if (user != null && "active".equals(user.getStatus())) {
// 登录成功,将用户信息存入Session
session.setAttribute("currentUser", user);
result.put("success", true);
result.put("message", "登录成功");
result.put("role", user.getRole()); // 返回角色用于前端路由
} else {
result.put("success", false);
result.put("message", "用户名或密码错误,或账号已被禁用");
}
} catch (Exception e) {
result.put("success", false);
result.put("message", "系统错误,登录失败");
}
return ResponseEntity.ok(result);
}
}
权限拦截器代码示例:
为了保证系统安全,对于需要登录才能访问的接口,我们配置了拦截器进行统一校验。
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
User currentUser = (User) session.getAttribute("currentUser");
// 检查用户是否登录
if (currentUser == null) {
// 如果是Ajax请求,返回JSON错误信息
if (isAjaxRequest(request)) {
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("{\"success\":false, \"message\":\"用户未登录或会话已过期\"}");
out.flush();
} else {
// 普通请求重定向到登录页
response.sendRedirect(request.getContextPath() + "/login.html");
}
return false;
}
return true;
}
private boolean isAjaxRequest(HttpServletRequest request) {
return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
}
图:用户登录界面,根据不同角色(用户、跑腿小哥、管理员)展示相应的入口。
2. 代办流程的创建与启动
流程的创建是系统的起点。核心逻辑在于初始化一个流程实例,并根据预定义的流程规则,设置第一处理人。
流程创建Service代码示例:
@Service
@Transactional
public class ProcessInstanceServiceImpl implements ProcessInstanceService {
@Autowired
private ProcessInstanceMapper processInstanceMapper;
@Autowired
private TaskOperationLogMapper logMapper;
@Autowired
private ProcessDefinitionService definitionService;
@Override
public String createProcessInstance(CreateProcessRequest request, User initiator) {
// 1. 生成全局唯一的实例ID
String instanceId = "PI_" + System.currentTimeMillis() + "_" + ThreadLocalRandom.current().nextInt(1000, 9999);
// 2. 根据流程定义KEY,获取流程的起始节点信息(例如,第一处理人规则)
ProcessDefinition definition = definitionService.getDefinitionByKey(request.getProcessDefinitionKey());
Integer firstAssigneeId = definition.getFirstAssigneeId(); // 可能是具体用户ID,或根据规则计算
// 3. 构建流程实例对象并保存
ProcessInstance instance = new ProcessInstance();
instance.setInstanceId(instanceId);
instance.setProcessDefinitionKey(request.getProcessDefinitionKey());
instance.setTitle(request.getTitle());
instance.setInitiatorId(initiator.getId());
instance.setCurrentAssigneeId(firstAssigneeId);
instance.setStatus("active"); // 创建后直接激活
instance.setPriority(request.getPriority());
processInstanceMapper.insert(instance);
// 4. 记录创建日志
TaskOperationLog log = new TaskOperationLog();
log.setInstanceId(instanceId);
log.setOperatorId(initiator.getId());
log.setOperationType("create");
log.setOperationComment("创建了代办事项:" + request.getTitle());
logMapper.insert(log);
return instanceId;
}
}
图:管理员在创建流程前,可先定义不同的服务类型(即流程模板),每个类型可预设处理人、步骤等规则。
3. 任务处理与流程推动
这是系统的核心交互功能。当前处理人可以对任务执行“处理完成”、“转交他人”等操作,系统需要更新实例状态、当前处理人,并记录操作日志。
任务完成Service代码示例:
@Override
public void completeTask(String instanceId, String comment, User operator) {
// 1. 查询当前流程实例
ProcessInstance instance = processInstanceMapper.selectById(instanceId);
if (instance == null || !"active".equals(instance.getStatus())) {
throw new BusinessException("流程实例不存在或已结束");
}
// 2. 权限校验:当前操作人必须是任务的处理人
if (!operator.getId().equals(instance.getCurrentAssigneeId())) {
throw new BusinessException("无权限处理此任务");
}
// 3. 根据流程定义,获取下一步逻辑
ProcessDefinition definition = definitionService.getDefinitionByKey(instance.getProcessDefinitionKey());
Integer nextAssigneeId = definitionService.calculateNextAssignee(instance, "complete");
if (nextAssigneeId == null) {
// 没有下一步,流程结束
instance.setStatus("completed");
instance.setCurrentAssigneeId(null);
} else {
// 流转到下一步
instance.setCurrentAssigneeId(nextAssigneeId);
// 状态保持 'active'
}
// 4. 更新流程实例
processInstanceMapper.update(instance);
// 5. 记录完成操作日志
TaskOperationLog log = new TaskOperationLog();
log.setInstanceId(instanceId);
log.setOperatorId(operator.getId());
log.setFromAssigneeId(operator.getId());
log.setToAssigneeId(nextAssigneeId);
log.setOperationType("complete");
log.setOperationComment(comment);
logMapper.insert(log);
// 6. 可在此处集成消息通知,通知下一处理人
// notificationService.notifyNewTask(nextAssigneeId, instance);
}
图:用户视角的订单(即代办事项)管理界面,可查看自己发起或处理的各项任务及其状态。
4. 我的待办与任务查询
系统为不同角色的用户提供了个性化的任务视图。例如,普通员工主要看到分配给自己的待办任务,而管理者可以看到整个部门的任务进展。
我的待办查询Mapper XML配置示例:
在MyBatis的Mapper XML文件中,使用动态SQL构建复杂的查询条件。
<!-- ProcessInstanceMapper.xml -->
<mapper namespace="com.xxx.mapper.ProcessInstanceMapper">
<select id="selectMyTodoList" parameterType="map" resultMap="ProcessInstanceResultMap">
SELECT
pi.*,
u1.real_name as initiator_name,
u2.real_name as current_assignee_name
FROM process_instance pi
LEFT JOIN user_table u1 ON pi.initiator_id = u1.id
LEFT JOIN user_table u2 ON pi.current_assignee_id = u2.id
WHERE pi.status = 'active'
<if test="currentUserId != null">
AND pi.current_assignee_id = #{currentUserId}
</if>
<if test="department != null">
AND u2.department = #{department} <!-- 部门主管查看本部门所有活跃任务 -->
</if>
<if test="priority != null">
AND pi.priority = #{priority}
</if>
<if test="keyword != null and keyword != ''">
AND (pi.title LIKE CONCAT('%', #{keyword}, '%'))
</if>
ORDER BY
FIELD(pi.priority, 'urgent', 'high', 'medium', 'low'),
pi.updated_time DESC
</select>
</mapper>
对应的Service层调用:
@Service
public class ProcessQueryServiceImpl implements ProcessQueryService {
@Autowired
private ProcessInstanceMapper processInstanceMapper;
@Override
public PageInfo<ProcessInstanceVO> getMyTodoList(User currentUser, Integer pageNum, Integer pageSize, String keyword) {
Map<String, Object> params = new HashMap<>();
params.put("currentUserId", currentUser.getId());
// 如果是部门主管,可以查看部门下所有任务,而非仅自己的
if ("manager".equals(currentUser.getRole())) {
params.remove("currentUserId"); // 移除个人ID条件
params.put("department", currentUser.getDepartment());
}
params.put("keyword", keyword);
// 使用PageHelper进行分页
PageHelper.startPage(pageNum, pageSize);
List<ProcessInstance> list = processInstanceMapper.selectMyTodoList(params);
PageInfo<ProcessInstance> pageInfo = new PageInfo<>(list);
// 将Entity转换为前端所需的VO(View Object)
return convertToVOList(pageInfo);
}
}
图:管理员拥有全局视角,可以查看、筛选和管理系统中的所有流程实例。
实体模型与业务对象
系统的核心业务对象(Entity)与数据库表一一对应,并通过MyBatis进行映射。以ProcessInstance实体为例:
@Data // Lombok注解,自动生成getter, setter等方法
public class ProcessInstance {
private String instanceId;
private String processDefinitionKey;
private String title;
private Integer initiatorId;
private Integer currentAssigneeId;
private String status;
private String priority;
private Date createdTime;
private Date updatedTime;
// 非数据库字段,用于关联查询结果的展示
private String initiatorName;
private String currentAssigneeName;
}
未来优化方向
- 集成工作流引擎:当前系统的流程逻辑相对简单(线性审批)。未来可集成如Activiti、Flowable等成熟的工作流引擎,以支持更复杂的流程模式,如并行网关、条件分支、子流程等,使流程配置更加灵活和强大。
- 强化消息推送机制:目前的任务通知可能依赖页面待办数字提醒。可以集成WebSocket实现实时推送,或对接企业微信、钉钉、邮件等渠道,确保处理人能够及时获知新任务,减少任务延迟。
- 数据分析与报表功能:基于积累的任务操作数据,开发数据分析看板。例如,统计各部门任务处理平均时长、任务积压情况、个人绩效等,为管理决策提供数据支持。可使用ECharts等前端图表库进行可视化展示。
- 移动端适配或APP开发:开发响应式前端或独立的移动端APP,让用户能够随时随地通过手机处理待办事项,进一步提升系统的便捷性和可用性。
- 微服务架构改造:随着业务复杂度的提升,可以考虑将单体应用拆分为微服务