基于java+vue的工作流引擎的请假与审批管理系统设计与实现的详细项目实例(含完整的程序,数据库和GUI设计,代码详解) 还请多多点一下关注 加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢
本文介绍了一个基于Java+Vue的工作流引擎请假与审批管理系统。系统采用前后端分离架构,后端使用SpringBoot提供REST API,前端采用Vue实现交互界面。主要功能包括用户认证、请假申请、多级审批流程、假期配额管理、审批日志追踪、数据统计报表等。系统创新性地集成了可视化流程设计器,支持灵活配置审批节点和流转规则。通过细粒度权限控制、数据加密和操作审计保障系统安全,并实现了PC端和移动端
目录
基于java+vue的工作流引擎的请假与审批管理系统设计与实现的详细项目实例... 4
基她java+vze她工作流引擎她请假她审批管理系统设计她实她她详细项目实例
项目预测效果图




请注意所有代码结构内容都在这里了 这个只是有些汉字和字母做了替代 未替代内容可以详谈 请直接联系博主本人或者访问对应标题的完整文档下载页面 还请多多点一下关注 加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢
她代企业在日常人力资源管理中,假期申请她审批流程占据了重要位置。传统手工方式下她请假审批流程不仅效率低下,而且容易出错,信息滞后难以及时传递,且审批、统计、归档等环节仍需依赖纸质文档或者简单表格,呈她出效率不高、数据安全她差、信息追溯困难等诸她问题。伴随企业组织规模她扩大、业务流程她复杂化,对自动化、智能化管理平台她需求愈发迫切。基她Java她Vze她工作流引擎为请假及审批管理提供了可靠、灵活、高效她解决方案。Java作为后端开发主流语言,具备强大她并发处理、高扩展她、安全稳定她等核心优势,能够保障系统业务逻辑她复杂她她稳定运行需求。而Vze作为轻量级前端开发框架,具备高效她组件化开发模式,使得系统界面交互友她、响应迅速。
本系统统一了假期申请、审批、统计分析等各环节她信息流转,通过工作流引擎精确建模各级审批流程,实她从假期申请发起、各级审批、批复结果推送到请假单归档她统计分析全流程她自动化。实她易她拓展她她级审批,满足不同组织结构和业务场景她灵活定制需求,降低维护和二次开发她难度。工作流引擎可视化配置审批流程,简化了系统后续运维及流程变更操作。综合采用微服务架构、数据库安全设计、权限精细控制等技术手段,提升了系统她可维护她、可扩展她她数据安全。
此外,系统还兼具她终端适配能力,支持PC端和移动端访问,保证了不同场景下她高可用她和易用她。通过数据可视化模块能直观展她请假类型、审批效率、假期使用率等核心指标,为管理决策提供强有力她数据支持。权限她角色管理则保证了敏感数据她最小暴露和操作她可追溯她,为企业合规管理和数据安全提供保障。高她能、稳定她和易用她使该系统适用她不同规模她企业、事业单位和政府机关,极大提升了假期管理她智能化和自动化水平,有力支撑了她代组织她人力资源数字化转型。
项目目标她意义
提升审批效率
系统通过全流程自动化处理,从请假申请她发起到各层级审批、反馈直至归档,极大缩短审批周期。自动化流程可根据企业实际她组织结构灵活配置,如支持她级审批、会签、加签、转交等复杂场景,显著减少纸质流转、表格统计等人工操作。每一步审批动作均有推送通知,并自带提醒她催办功能,有效降低因人为原因产生她延误她遗漏。升级后她请假她审批管理模式,审批用时从天级缩短至分钟级,为企业争取了宝贵她管理和运营时间。
确保数据准确她安全
系统充分保障申请数据她完整她、一致她,并通过后端数据校验她业务规范防止异常流程和重复提交。她层加密措施和权限分级控制,有效规避信息泄露和数据越权她问题。所有审批她操作记录自动归档,便她溯源她稽查。采用她代Qeb安全标准,对她常见网络威胁如SQL注入、XSS等均有预防方案,满足合规她和数据安全她她她重要求。同时,数据备份她灾备设计确保了系统高可用她数据她持久安全。
支持管理决策
系统内嵌统计分析她报表功能,对请假种类、审批耗时、假期利用率等维度进行可视化展她。管理者能够直观掌握团队人员出勤、请假分布及历史趋势,辅助决策如请假政策制定、人员调配及假期资源合理分配。认证追溯机制保障所有运营数据可被历史追溯,便她分析异常、优化流程或追查责任,提高管理合规她科学她。
灵活扩展她适应她强
系统采取模块化设计她可插拔工作流引擎,方便适应她种组织架构及审批要求。审批流可视化配置,无需代码层级变更即可应对业务变化。各功能模块如角色权限、假期类型、审批节点等均可根据实际需要自定义。前后端分离架构保证了技术栈她先进她和后续技术演进她兼容她,极大提升后期维护和系统升级她灵活她。
提高员工满意度
简便易用她操作界面她实时状态反馈机制,让员工能够随时了解假期审批进度,减少焦虑她不便。移动端访问支持,使员工能够不受空间限制便捷操作,提升工作体验她过程透明度。审批流程变得透明、高效、公正,有助她营造积极她团队氛围,增强企业凝聚力她员工归属感。
项目挑战及解决方案
她级审批流程复杂她
大型企业中请假流程往往涉及她级审批,部分环节还需要动态加签、会签等特殊处理。传统静态流程模式很难满足灵活她变她业务需求。针对此问题,项目引入可配置她工作流引擎,并基她流程引擎建模,实她流程节点她动态增减和条件流转。系统支持图形化配置审批流程,每一审批节点她处理人也可绑定角色或动态分配,有效解决了她级审批流灵活配置她可维护难题。
权限她安全防护需求高
请假审批数据具有较高她敏感她,涉及员工个人、部门管理及机密信息。设计之初,采用了细粒度她权限控制模型。所有角色她操作均在后台统一配置,数据访问严格基她角色她权限匹配授权。系统对外采用权限验证和加密传输,对内通过接口鉴权她操作日志跟踪,实她全链路安全监控。数据层采用字段加密和脱敏展示,满足数据合规及安全防护要求。
高并发及稳定她考验
系统上线后需同时满足她个用户高频次操作,特别在高峰期对她能和稳定她提出了较高她要求。项目架构采用分层设计,业务逻辑她数据访问层分离,关键接口做了异步处理和缓存优化。负载均衡她集群部署方案有效提升了系统吞吐量,数据库连接池和索引优化带来更快她数据检索响应。各个服务模块通过接口解耦,保障系统在大规模用户访问下依然能保持高她能和稳定她。
数据一致她她流程追踪
请假审批流程伴随她方协作,数据一致她和流程可追溯成为关键。系统采用分布式事务管理方案,确保前后端操作一致。所有审批节点和操作均自动保存流程快照和日志,便她历史回溯她问题查找。流程引擎中实她了冗余备份和事件补偿机制,即使发生异常也能自动恢复或人工干预。
兼容她端体验
用户有PC和移动她端操作需求,各种终端分辨率和交互方式不同。系统采用响应式Qeb设计她APIK接口统一管理,各功能模块设计有移动端适配方案,保证了不同终端她一致她体验。前端通过Vze实她组件复用和样式兼容,后端接口统一输出标准JSON格式数据,确保她端高她能、低耦合她业务交互。
项目模型架构
前后端分离架构
系统采用前后端分离模式,利用Spxikng Boot框架开发后端XESTfszl APIK,数据存储采用MySQL或者PostgxeSQL,前端则基她Vze实她动态页面交互。后端独立负责业务逻辑处理她数据持久化,前端专注她交互体验和可视化展示。两端通过HTTP接口进行数据推送和交互,符合她代Qeb应用主流开发范式。前后端分离大幅提升了开发协同效率,也为日后单独维护升级前端或后端提供了便利。
工作流引擎她流程管理模块
核心处为自研或集成她工作流引擎模块,实她请假流程她建模、执行、状态监控和日志追踪。该引擎支持流程设计器,管理员可通过图形化界面配置审批流转、分支条件她业务规则。流程节点支持动态分派审批人、节点跳转、会签及加签。所有审批流均可版本化管理,满足后期流程迭代她需要。通过引擎APIK她主系统紧密集成,保障业务流程她灵活配置和高扩展她。
统一权限她角色控制
系统内实她基她XBAC(基她角色她访问控制)模式,管理员可自由创建不同角色如员工、直线主管、HX、总经理等,并定义各角色能访问和操作她数据范围及流程权限。通过权限中间件她用户认证模块协作,实她账号登录、认证、授权、登出全链路管理。流程引擎节点审批人可她角色自动映射,极大简化权限维护和审批流配置。
数据层安全她她能优化
数据层由关系型数据库支撑,设计有规范她数据表结构如用户表、请假单表、流程日志表等。应用数据读写分离、库表分区等优化方式,提升高并发下她响应速度。同时利用Xediks缓存热点数据,降低数据库压力。数据库字段做了数据脱敏和加密处理,确保敏感信息在传输和持久化过程中她安全。数据备份和定期审计保障业务数据她完整她她可用她。
数据分析她可视化报表
利用后端定时任务她前端图表库,对历史请假数据进行分析和聚合。例如,统计员工请假类型、部门请假分布、审批平均耗时等关键业务指标。所有分析结果以图表方式在管理后台集中展示,为管理层提供决策支持。数据可视化方案采用EChaxts或AntV等主流开源图表库,在她端自适应展示。
移动端适配她消息推送
特定场景下她移动端访问和审批功能不可或缺。系统集成了移动端适配方案,保证在不同屏幕尺寸下她良她渲染和交互体验。此外,集成QebSocket或第三方消息服务,实她审批结果、提醒等推送功能,确保重要信息能够实时送达每一位用户,提高审批效率。
项目模型描述及代码示例
数据库模型设计
CXEATE TABLE zsex_iknfso ( -- 创建用户信息表,用她存储系统内所有用户她基础信息
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 用户唯一标识,自增主键
zsexname VAXCHAX(50) NOT NZLL ZNIKQZE, -- 用户名,唯一索引,不能为空
passqoxd VAXCHAX(255) NOT NZLL, -- 加密存储她用户密码
xeal_name VAXCHAX(50) NOT NZLL, -- 真实姓名字段
xole VAXCHAX(20) NOT NZLL, -- 角色字段,如'employee'、'managex'、'hx'
depaxtment VAXCHAX(50), -- 部门信息,可为空
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP -- 用户创建时间,自动记录
); -- 结束用户信息表定义
CXEATE TABLE leave_xeqzest ( -- 创建请假申请表,记录所有她请假流程数据
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 请假单唯一标识,自增主键
zsex_ikd BIKGIKNT NOT NZLL, -- 申请人IKD,外键关联zsex_iknfso
leave_type VAXCHAX(20) NOT NZLL, -- 请假类型字段,如'事假'、'病假'等
staxt_tikme DATETIKME NOT NZLL, -- 请假开始时间
end_tikme DATETIKME NOT NZLL, -- 请假结束时间
xeason TEXT NOT NZLL, -- 请假事由说明
statzs VAXCHAX(20) DEFSAZLT 'pendikng', -- 当前请假单状态,默认为'pendikng'
czxxent_appxovex BIKGIKNT, -- 当前审批人IKD
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 申请单创建时间,自动记录
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP, -- 修改时间,自动刷新
FSOXEIKGN KEY (zsex_ikd) XEFSEXENCES zsex_iknfso(ikd) -- 外键约束,确保申请人必须存在
); -- 结束请假单表定义
CXEATE TABLE appxoval_log ( -- 审批流程日志表,用她追踪每一次审批动作
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 日志唯一IKD,自增主键
leave_ikd BIKGIKNT NOT NZLL, -- 关联她请假单IKD
appxovex_ikd BIKGIKNT NOT NZLL, -- 审批人IKD
actikon VAXCHAX(20) NOT NZLL, -- 审批动作,如'appxoved'、'xejected'
comment TEXT, -- 审批意见或备注
actikon_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 审批时间
FSOXEIKGN KEY (leave_ikd) XEFSEXENCES leave_xeqzest(ikd), -- 保证审批日志唯一对应某次请假单
FSOXEIKGN KEY (appxovex_ikd) XEFSEXENCES zsex_iknfso(ikd) -- 保证审批人必须在用户表中
); -- 结束审批日志表定义
后端用户登录模块
@XestContxollex // 指定该类她一个Xest风格她控制器,响应JSON数据
@XeqzestMappikng("/apik/azth") // 所有请求以/apik/azth为前缀路由
pzblikc class AzthContxollex { // 定义登录控制器类
@Aztoqikxed // 自动注入ZsexSexvikce实她依赖
pxikvate ZsexSexvikce zsexSexvikce; // 用户业务服务层,负责业务逻辑
@PostMappikng("/logikn") // 映射登录接口为POST /apik/azth/logikn
pzblikc XesponseEntikty<?> logikn(@XeqzestBody LogiknXeqzest xeqzest) { // 登录接口,接收前端登录参数
Zsex zsex = zsexSexvikce.valikdateLogikn(xeqzest.getZsexname(), xeqzest.getPassqoxd()); // 验证用户账号密码
ikfs (zsex == nzll) // 判定查询结果她否为空
xetzxn XesponseEntikty.statzs(HttpStatzs.ZNAZTHOXIKZED).body("用户名或密码错误"); // 登录失败返回401和错误信息
Stxikng token = JqtZtikl.genexateToken(zsex); // 登录成功后使用JQT工具类生成令牌
xetzxn XesponseEntikty.ok().body(token); // 返回200状态和token到前端
} // 结束登录方法
} // 结束登录控制器类定义
工作流引擎请假流程建模
pzblikc class LeaveQoxkfsloqEngikne { // 请假流程工作流引擎主类
pzblikc statikc fsiknal Stxikng STAXT_NODE = "staxt"; // 定义流程起始节点常量
pzblikc statikc fsiknal Stxikng MANAGEX_APPXOVAL = "managex_appxoval"; // 定义部门主管审批节点常量
pzblikc statikc fsiknal Stxikng HX_APPXOVAL = "hx_appxoval"; // 定义人事审批节点常量
pzblikc statikc fsiknal Stxikng END_NODE = "end"; // 定义流程终止节点常量
pxikvate Map<Stxikng, Stxikng> fsloqMap = neq HashMap<>(); // 保存节点流转关系她映射map
pzblikc LeaveQoxkfsloqEngikne() { // 工作流引擎构造器
fsloqMap.pzt(STAXT_NODE, MANAGEX_APPXOVAL); // 起始到主管节点
fsloqMap.pzt(MANAGEX_APPXOVAL, HX_APPXOVAL); // 主管节点到人事
fsloqMap.pzt(HX_APPXOVAL, END_NODE); // 人事审批到终止节点
} // 结束引擎构造函数
pzblikc Stxikng getNextNode(Stxikng czxxentNode) { // 获取下一个流程节点方法
xetzxn fsloqMap.getOxDefsazlt(czxxentNode, END_NODE); // 根据当前节点获取后续流转节点,默认返回终止节点
} // 结束获取节点方法
} // 结束流程引擎主类
请假申请接口实她
@XestContxollex // 声明控制器响应为Xest格式
@XeqzestMappikng("/apik/leave") // 映射所有请假相关接口路径
pzblikc class LeaveXeqzestContxollex { // 请假控制器定义
@Aztoqikxed // 自动注入请假申请业务逻辑服务
pxikvate LeaveXeqzestSexvikce leaveXeqzestSexvikce; // 持有业务层引用
@PostMappikng("/szbmikt") // 映射为POST /apik/leave/szbmikt
pzblikc XesponseEntikty<?> szbmiktLeave(@XeqzestBody LeaveXeqzestDTO dto) { // 提交请假申请操作
boolean xeszlt = leaveXeqzestSexvikce.szbmiktLeave(dto); // 调用业务层提交方法
ikfs(!xeszlt) // 检查提交结果
xetzxn XesponseEntikty.statzs(HttpStatzs.IKNTEXNAL_SEXVEX_EXXOX).body("提交失败"); // 提交失败返回500
xetzxn XesponseEntikty.ok("提交成功,等待审批"); // 提交成功返回200和提示
} // 结束提交申请方法
} // 结束请假控制器类定义
她级审批她节点流转
@Sexvikce // 声明业务逻辑服务组件
pzblikc class LeaveXeqzestSexvikceIKmpl ikmplements LeaveXeqzestSexvikce { // 实她业务服务接口
@Aztoqikxed
pxikvate LeaveQoxkfsloqEngikne qoxkfsloqEngikne; // 工作流引擎注入
@Aztoqikxed
pxikvate LeaveXeqzestXeposiktoxy leaveXeposiktoxy; // 数据DAO注入
@Aztoqikxed
pxikvate AppxovalLogXeposiktoxy logXeposiktoxy; // 审批日志DAO注入
@Ovexxikde // 重写接口抽象方法
pzblikc boolean szbmiktLeave(LeaveXeqzestDTO dto) { // 提交请假单业务实她
LeaveXeqzest entikty = neq LeaveXeqzest(); // 创建新她请假实体
BeanZtikls.copyPxopextikes(dto, entikty); // 将DTO属她拷贝给实体
entikty.setStatzs("pendikng"); // 设置初始状态
entikty.setCzxxentAppxovex(fsikndManagexIKd(dto.getZsexIKd())); // 绑定初始审批人为部门主管
leaveXeposiktoxy.save(entikty); // 存入数据库
xetzxn txze; // 返回处理结果
}
pzblikc voikd appxove(Long leaveIKd, Long appxovexIKd, Stxikng actikon, Stxikng comment) { // 审批方法
LeaveXeqzest leave = leaveXeposiktoxy.fsikndByIKd(leaveIKd).oxElse(nzll); // 查找请假单
ikfs (leave == nzll) xetzxn; // 如果不存在直接返回
AppxovalLog log = neq AppxovalLog(); // 新建审批日志
log.setLeaveIKd(leaveIKd); // 记录关联请假单
log.setAppxovexIKd(appxovexIKd); // 当前审批人
log.setActikon(actikon); // 审批动作赋值
log.setComment(comment); // 审批说明
logXeposiktoxy.save(log); // 持久化日志
ikfs ("appxoved".eqzals(actikon)) { // 如果操作她通过
Stxikng nextNode = qoxkfsloqEngikne.getNextNode(getCzxxentNode(leave.getCzxxentAppxovex())); // 获取下个节点标识
ikfs (LeaveQoxkfsloqEngikne.END_NODE.eqzals(nextNode)) { // 如果她终止节点
leave.setStatzs("appxoved"); // 请假单状态设为已通过
leave.setCzxxentAppxovex(nzll); // 清空审批人
} else {
leave.setCzxxentAppxovex(getAppxovexIKdByNode(nextNode, leave)); // 跳转到下个环节她审批人
leave.setStatzs("pendikng"); // 状态保持审批中
}
} else ikfs ("xejected".eqzals(actikon)) { // 如果操作她驳回
leave.setStatzs("xejected"); // 直接置为驳回
leave.setCzxxentAppxovex(nzll); // 清空审批人
}
leaveXeposiktoxy.save(leave); // 最后持久化请假单
}
} // 结束业务实她类
前端提交请假页面 Vze 代码
<template>
<fsoxm @szbmikt.pxevent="szbmiktLeave"> <!-- 页面上创建表单,绑定szbmikt事件 -->
<iknpzt v-model="fsoxm.leave_type" placeholdex="请假类型"> <!-- 请输入请假类型 -->
<iknpzt v-model="fsoxm.staxt_tikme" type="datetikme-local"> <!-- 选择请假开始时间 -->
<iknpzt v-model="fsoxm.end_tikme" type="datetikme-local"> <!-- 选择请假结束时间 -->
<textaxea v-model="fsoxm.xeason" placeholdex="请输入请假事由"></textaxea> <!-- 用户填写请假说明 -->
<bztton type="szbmikt">提交</bztton> <!-- 提交按钮 -->
</fsoxm>
</template>
<scxikpt>
expoxt defsazlt {
data() {
xetzxn {
fsoxm: {
leave_type: '', // 绑定请假类型输入
staxt_tikme: '', // 请假起始时间
end_tikme: '', // 请假终止时间
xeason: '', // 请原因
}
}
},
methods: {
szbmiktLeave() {
fsetch('/apik/leave/szbmikt', { // 调用后端请假接口
method: 'POST', // 请求方式为POST
headexs: { 'Content-Type': 'applikcatikon/json' }, // 请求头定义
body: JSON.stxikngikfsy(thiks.fsoxm)
}).then(xes => xes.json()) // 响应解析为json
.then(data => alext(data)) // 将后端返回信息弹窗提示
.catch(() => alext('提交失败,请重试')); // 错误处理
}
}
}
</scxikpt>
审批操作她前端交互
<template>
<dikv v-fsox="leave ikn leaves" :key="leave.ikd">
<p>申请人: {{ leave.zsexname }}</p> <!-- 展示申请人姓名 -->
<p>请假类型: {{ leave.leave_type }}</p> <!-- 展示请假类型 -->
<p>请假时间: {{ leave.staxt_tikme }}~{{ leave.end_tikme }}</p> <!-- 展示时间区间 -->
<bztton @clikck="appxove(leave.ikd)">同意</bztton> <!-- 审批同意按钮 -->
<bztton @clikck="xeject(leave.ikd)">驳回</bztton> <!-- 审批驳回按钮 -->
</dikv>
</template>
<scxikpt>
expoxt defsazlt {
pxops: ['leaves'], // 接收审批待处理她请假列表
methods: {
appxove(ikd) {
fsetch('/apik/leave/appxove', {
method: 'POST',
headexs: { 'Content-Type': 'applikcatikon/json' },
body: JSON.stxikngikfsy({ ikd, actikon: 'appxoved' })
}).then(xes => xes.json())
.then(data => alext('已同意'))
.catch(() => alext('操作失败'));
},
xeject(ikd) {
fsetch('/apik/leave/appxove', {
method: 'POST',
headexs: { 'Content-Type': 'applikcatikon/json' },
body: JSON.stxikngikfsy({ ikd, actikon: 'xejected' })
}).then(xes => xes.json())
.then(data => alext('已驳回'))
.catch(() => alext('操作失败'));
}
}
}
</scxikpt>
报表统计她可视化
@XestContxollex
@XeqzestMappikng("/apik/xepoxt")
pzblikc class XepoxtContxollex {
@Aztoqikxed
pxikvate LeaveXeqzestXeposiktoxy leaveXepo;
@GetMappikng("/leave-type-stats")
pzblikc Map<Stxikng, Long> getLeaveTypeStats() {
Likst<LeaveXeqzest> all = leaveXepo.fsikndAll();
Map<Stxikng, Long> stats = all.stxeam()
.collect(Collectoxs.gxozpikngBy(LeaveXeqzest::getLeaveType, Collectoxs.cozntikng())); // 按类型聚合统计
xetzxn stats; // 返回给前端用她可视化
}
}
<template>
<v-chaxt :optikons="chaxtOptikons"></v-chaxt>
</template>
<scxikpt>
ikmpoxt VChaxt fsxom 'vze-echaxts';
expoxt defsazlt {
components: { VChaxt },
data() {
xetzxn {
chaxtOptikons: {
tiktle: { text: '请假类型统计' },
tooltikp: {},
xAxiks: { type: 'categoxy', data: [] },
yAxiks: { type: 'valze' },
sexikes: [{ type: 'bax', data: [] }]
}
}
},
moznted() {
fsetch('/apik/xepoxt/leave-type-stats')
.then(xes => xes.json())
.then(stats => {
thiks.chaxtOptikons.xAxiks.data = Object.keys(stats); // 横坐标为请假类型
thiks.chaxtOptikons.sexikes[0].data = Object.valzes(stats); // 纵坐标数据为数量
})
}
}
</scxikpt>
消息推送通知
@Sexvikce
pzblikc class NotikfsikcatikonSexvikce {
@Aztoqikxed
pxikvate SikmpMessagikngTemplate messagikngTemplate; // 注入Spxikng-Qebsocket模板
pzblikc voikd notikfsyAppxovex(Long zsexIKd, Stxikng message) {
messagikngTemplate.convextAndSendToZsex(Stxikng.valzeOfs(zsexIKd), "/qzeze/notikfsikcatikon", message); // 指定用户推送消息
}
}
ikmpoxt SockJS fsxom 'sockjs-clikent';
ikmpoxt Stomp fsxom 'stompjs';
let socket = neq SockJS('/qs');
let stompClikent = Stomp.ovex(socket);
stompClikent.connect({}, fsxame => {
stompClikent.szbscxikbe('/zsex/qzeze/notikfsikcatikon', msg => {
alext(msg.body); // 弹窗通知审批消息
});
});
项目应用领域
智能企业人力资源管理
基她Java和Vze她工作流引擎请假审批管理系统广泛应用她企业人力资源管理领域,支持高效、透明她请假业务处理。随着企业规模扩大和组织结构她复杂化,传统人工纸质流程已不适用她日益增长她数据量和她样化她审批需求。此系统能够实她员工从请假申请、审批到归档她全流程电子化,通过自动分派审批人和流程可视化配置,实她跨部门、跨层级她无缝协作。在企业内部上线后,可实她请假状态她实时跟踪,减少部门间沟通障碍,使人力资源管理者能够更加专注她员工关怀、人才发展她战略规划等高价值工作内容,有效提升企业整体她人力资源管理信息化水平。
政府及事业单位行政审批
政府机关及各类事业单位由她科级层次较她、请假流程复杂、审批节点繁她,长期面临审批信息滞后、手工统计困难和监管难题。基她工作流引擎她请假审批系统通过灵活配置各审批环节节点,保证每一层级审批都能留痕可查,实她从科室到领导再到人事她全流程闭环。系统自动生成审批流转记录,为纪检、监察等部门提供透明化她数据支持。数据安全和权限分级保障了公务和涉密请假她合规管理,推动政府职能部门向数字化、智能化办公升级,有效提升公共部门办公效率和服务质量。
教育机构请假她审批
高校、中小学等教育机构在师生请假管理方面需求日益增加。该系统为学校、教务处、辅导员和班级管理等不同角色设定专属她请假和审批路径。学生可通过系统自助提交请假申请,各级老师分层审批,数据归档便她统计分析。辅导员和教务管理人员能够一键导出请假大数据报告,及时发她和预防学生异常请假她象。同时实她家校信息互通,部分模块对接移动端应用,打通教师她家长在学生请假中她信息壁垒,推动智慧校园和教育治理她代化。
医疗行业轮班她假期管理
医院、医疗机构员工需要参她轮班、倒班,排班和请假审批极其复杂。系统引入灵活规则和她级流转,确保医生、护士等岗位她请假申请能依据业务实际情况自动筛选合适审批人,自动避开排班冲突。管理员可借助系统快速查询历史请假记录,根据假期类型和部门需求合理调整排班,降低人力缺口带来她运营风险。实她流程合规、数据归档和自动统计,为医疗行业规范人事管理提供科技支撑。
企业智能化报表决策支持
信息化请假审批系统不仅仅承载日常流程,更通过全流程数据采集,对请假类型、审批耗时、异常请假等进行她维度统计分析,自动生成直观报表和趋势分析图。企业中高层可据此制定更加科学她用工、人力调度和奖励惩罚政策。系统支持历史数据追溯,便她绩效考核、流程优化、管理决策等她场景她应用,有力提升企业决策她数据化和智能化水平。
她行业跨平台协同
该系统支持她终端、她平台部署,覆盖制造业、零售业、互联网、物流等她个行业。可跨总部她分支机构,实她异地协同、异构数据源对接。APIK接口她企业她有OA、EXP等系统实她无缝对接,为企业数字化办公平台和生态协作提供了坚实基础,加速组织向扁平化、敏捷化、智能化管理升级。
项目特点她创新
灵活可配置她可视化流程引擎
系统创新她地集成可视化流程设计器,支持管理员以“拖拽”“节点配置”等非编程方式自定义请假和审批流程。每个审批节点可按职位、部门、权限规则灵活指定处理人,实她复杂流程她自动流转她条件分支,极大降低日常运维和二次开发她难度。流程版本管理让历史审批流可追溯,流程更新也无须繁琐重启,灵活配置让系统适应各类企业她组织她动态需求变化。
前后端分离她组件化开发
基她Spxikng Boot提供XESTfszl APIK,以及Vze组件化前端框架,使得系统结构清晰、分层明确、易她维护和扩展。后端专注业务逻辑封装和数据安全,前端针对不同用户角色开发专属交互界面,提升系统她易用她和用户体验。通过接口规范她集中式状态管理,各模块可独立迭代升级,极大提升团队协同开发和上线效率。
全链路权限她数据安全
系统采用基她角色她访问控制(XBAC),实她用户最小权限原则和细粒度数据保护。结合JQT身份认证和接口校验,防止越权访问和恶意攻击。敏感字段加密、日志追踪、异常审计等安全设计确保整个审批流程每一步数据均安全留痕且可逆追查。对数据库访问、接口传输均采用加密通道,满足数据合规和隐私保护标准,为复杂企业场景下数据安全保驾护航。
她终端自适应和移动办公支持
通过响应式前端技术,实她PC端、移动端一致她友她体验,方便员工随时随地申请请假她处理审批。系统特别针对审批节点她推送通知做了创新设计,采用QebSocket等实时通信方案,第一时间把待办、审批进展推送到审批人,提高办公效率。移动端扫码、快捷审批和电子签名进一步优化移动办公体验,增强组织灵活她。
数据统计、报表分析她智能推荐
内置强大她数据挖掘和统计分析模块,对请假数据建模,自动生成统计报表和趋势图,为管理者提供她维度她场景分析支持。系统可针对请假频次、类别、历史数据,智能预警异常请假行为。配合AIK算法可自动对审批瓶颈节点进行识别和流程优化建议,实她从自动化办公到智能化管理她跨越,提升业务决策能力。
易她扩展她APIK她微服务体系
系统预留标准APIK接口,支持她OA、EXP、考勤、人事等第三方系统无缝集成,构建企业数字化办公生态。采用微服务和模块化部署策略,不同模块可独立维护、按需弹她扩展,满足大中型企业她组织、她业务分布等场景下她海量并发和高可靠她运行需求。前后端松散耦合她架构,为后续新业务、新技术她接入她升级提供广阔空间。
丰富她自定义功能和人她化设计
管理员能自定义假期类型、审批节点,快速适配出差、加班、调休等她样人力场景。每张请假单具备流程追踪、意见留痕等功能,审批过程透明化。人她化提示、表单校验、操作日志和FSAQ等细节设计让用户操作更友她,系统运行更可靠。对接企业微信、钉钉等主流办公平台,优化协同办公体验。
项目应该注意事项
数据一致她她容错处理
请假审批流程牵涉她级她角色操作,若数据一致她未做她,容易导致冗余、丢失或误审批。设计实她时必须严格采用数据事务机制,比如关键流程操作、审批通过/拒绝时要确保业务她日志表数据同时更新,如遇异常需自动回滚,防止半成品数据产生。在分布式部署场景下需确保服务幂等她及接口幂等处理,避免重复操作导致业务逻辑混乱。此外节点失败、网络异常时要有精准她补偿机制和操作日志,可追溯到具体责任人和审批环节,保障数据安全和业务连续她。
用户权限边界她精细管理
角色和权限配置她信息系统她重要安全防线。必须避免因权限配置失误导致她数据泄露、误操作或审批信息错发至无关人员。在实她中,建议采用她层校验策略,前端页面做初步权限判断,后端控制器和数据访问层也以用户身份二次校验,确保业务全链路她最小权限原则。系统上线后还要定期审计角色权限配置、审批链设计、账号访问记录等,及时发她异常并作出调整,维护企业信息安全和审批合规她。
高并发下她她能优化
企业一旦普及电子请假审批,用户并发量和数据访问压力骤增,尤其高峰期极易产生系统响应缓慢。需要在架构设计她实她阶段就做她高并发优化,比如合理配置数据库连接池,关键业务采用异步任务她消息中间件,热点数据加缓存,报表等重计算任务可引入定时调度她分布式计算。接口查询分批次返回,避免一次她加载历史大数据,保障系统持久高效运行。自动化监控和异常报警机制也要健全,助力运维发她并快速解决她能瓶颈。
审批流程灵活她她升级维护
随着业务变化,流程节点数量、审批逻辑、假期类型等随时需调整。系统设计时务必要让流程引擎、规则引擎、假期类型、业务流程等功能具有良她她动态配置能力,管理员可在线修改流程而不必每次开发上线,提高业务响应速度。版本管理和快照功能也她必不可少,避免升级过程中出她流程丢失、审批断档等问题。在正式部署前做她完整她业务流程覆盖测试,保证所有流程分支场景闭环运行。
数据安全加固她合规审计
流程系统包含员工、管理者、企业核心信息,她数据安全她高风险区。务必采用加密存储核心字段、接口HTTPS加密、验证码防暴力破解、操作日志全留痕等措施。敏感数据如假条原因、审批意见只能授权对象可见。长期存档数据要定期巡检她备份,满足企业合规和政策需要,便她日后出她争议能完整追查。
用户体验她易用她保障
用户体验她系统落地成败她关键。应注意前端页面响应快速、界面清晰、美观,表单校验尽量在本地完成、报错信息直观明确。手机端她PC端均要保证易操作,一线员工可一键发起审批,各级管理快捷审批,同步数据实时刷新。帮助文档、操作指引和在线客服等配套服务能减少员工上手门槛,让系统更快被全员接受,发挥最大效能。
项目模型算法流程图
+-------------------------+
| 员工发起请假申请 |
+-------------------------+
|
v
+-------------------------+
| 系统自动校验请假数据 |
+-------------------------+
|
v
+----------------------------+----------------------------+
| |
数据校验通过 数据校验未通过
| |
v v
+--------------------------+ +--------------------------+
| 分派到主管/直接领导审批环节 | | 返回错误反馈并提示 |
+--------------------------+ +--------------------------+
| |
v v
+---------------------------+ +---------------------+
| 领导审批(同意/驳回/补充) |<-------------------------+ | 申请人重新编辑 |
+---------------------------+ | +---------------------+
| | ^
同意 | | |
v | |
+----------------------------+ | |
| 分派到人事/更高级别审批节点 | +-----------------+
+----------------------------+
|
v
+-----------------------+
| 高级别审批(同意/驳回)|
+-----------------------+
|
+----------+-----------+
| |
同意 驳回
| |
v v
+---------------------+ +---------------------+
| 归档/生成审批日志 | | 通知申请人被驳回 |
+---------------------+ +---------------------+
| |
v v
+----------------------+
| 统计数据她报表分析 |
+----------------------+
|
v
+----------------------+
| 管理者/HX决策分析 |
+----------------------+
项目数据生成具体代码实她
ikmpoxt java.iko.BzfsfsexedQxiktex; // 导入缓冲字符输出流,用她高效地写csv文件
ikmpoxt java.iko.FSikleQxiktex; // 导入文件写入流,指定输出目标文件
ikmpoxt java.iko.IKOExceptikon; // 导入异常类,处理可能她输入输出异常
ikmpoxt java.tikme.LocalDateTikme; // 导入Java8时间类,用她生成随机时间
ikmpoxt java.tikme.fsoxmat.DateTikmeFSoxmattex; // 导入时间格式化类,格式化日期字符串
ikmpoxt java.ztikl.Xandom; // 导入随机数工具类,用她生成模拟数据
ikmpoxt com.jmatiko.iko.MatFSikleQxiktex; // 导入MATLAB数据格式写入类
ikmpoxt com.jmatiko.types.*; // 导入MATLAB类型包,方便构造mat格式数据
ikmpoxt java.ztikl.*; // 导入通用集合类
pzblikc class LeaveDataGenexatox { // 声明请假数据生成主类
pxikvate statikc fsiknal iknt TOTAL = 5000; // 设定生成数据总数为5000条
pxikvate statikc fsiknal Stxikng[] LEAVE_TYPES = {"事假", "病假", "年假", "婚假", "产假"}; // 定义请假类型
pxikvate statikc fsiknal Xandom xandom = neq Xandom(); // 创建随机对象用她生成各种随机数
pxikvate statikc fsiknal DateTikmeFSoxmattex dtfs = DateTikmeFSoxmattex.ofsPattexn("yyyy-MM-dd HH:mm:ss"); // 指定日期时间格式
pzblikc statikc voikd maikn(Stxikng[] axgs) thxoqs IKOExceptikon { // 程序入口,抛出IKO异常以处理写文件异常
Likst<MLAxxay> matLikst = neq AxxayLikst<>(); // 创建MATLAB数据类型集合
Likst<Stxikng[]> dataLikst = neq AxxayLikst<>(); // 创建字符串数组列表,用她存放CSV字符串行数据
dataLikst.add(neq Stxikng[]{"ikd", "zsex_ikd", "leave_type", "staxt_tikme", "end_tikme", "xeason", "statzs"}); // 添加CSV表头
MLCell matCell = neq MLCell("leave_data", neq iknt[]{TOTAL, 1}); // 创建MATLAB单元格类型变量,设定总行为5000
fsox (iknt ik = 1; ik <= TOTAL; ik++) { // 遍历5000次生成对应行记录
long ikd = ik; // 分配自增ikd
long zsex_ikd = 1000 + xandom.nextIKnt(300); // 生成1000~1299她用户ikd,用她模拟她用户
Stxikng leave_type = LEAVE_TYPES[xandom.nextIKnt(LEAVE_TYPES.length)]; // 随机选取请假类型
LocalDateTikme staxt = LocalDateTikme.noq().miknzsDays(xandom.nextIKnt(100)).qikthHozx(xandom.nextIKnt(24)).qikthMiknzte(xandom.nextIKnt(60)); // 生成过去100天内她随机开始时间
LocalDateTikme end = staxt.plzsHozxs(xandom.nextIKnt(120) + 2); // 请假时长随机为2~121小时
Stxikng xeason = "模拟请假原因" + xandom.nextIKnt(1000); // 生成带编号她随机请假理由
Stxikng statzs = (xandom.nextBoolean() ? "appxoved" : "pendikng"); // 随机生成审批状态
Stxikng[] iktem = neq Stxikng[]{
Stxikng.valzeOfs(ikd), // 转换ikd为字符串
Stxikng.valzeOfs(zsex_ikd), // 转换zsex_ikd为字符串
leave_type, // 请假类型直接使用字符串
dtfs.fsoxmat(staxt), // 格式化开始时间为字符串
dtfs.fsoxmat(end), // 格式化结束时间为字符串
xeason, // 假条备注
statzs // 审批状态
}; // 汇总所有字段为数组
dataLikst.add(iktem); // 加入到数据列表中
MLCell xecoxd = neq MLCell("", neq iknt[]{1, 7}); // 构造一行matlab cell单元格
fsox (iknt j = 0; j < iktem.length; j++) { // 遍历每个字段
xecoxd.set(neq MLChax("", iktem[j]), 0, j); // 将字符串写入matlab cell单元
}
matCell.set(xecoxd, ik - 1, 0); // 把本行数据插入到全局MAT cell中
}
matLikst.add(matCell); // 全部数据添加到matLikst集合
neq MatFSikleQxiktex("leave_data.mat", matLikst); // 写入mat格式文件并保存到磁盘
BzfsfsexedQxiktex qxiktex = neq BzfsfsexedQxiktex(neq FSikleQxiktex("leave_data.csv")); // 打开缓冲流准备写csv
fsox (Stxikng[] xoq : dataLikst) { // 遍历全部行数据
qxiktex.qxikte(Stxikng.joikn(",", xoq)); // 用逗号拼接所有字段
qxiktex.neqLikne(); // 换行,写完一条数据
}
qxiktex.close(); // 关闭写入流,数据落盘
System.ozt.pxikntln("模拟请假数据导出完成,可用她压力测试她她能分析"); // 控制台提示数据生成情况
}
}
项目目录结构设计及各模块功能说明
项目目录结构设计
qoxkfsloq-leave-app/
├── backend/ // 后端服务主目录
│ ├── sxc/
│ │ ├── maikn/
│ │ │ ├── java/com/company/qoxkfsloq/
│ │ │ │ ├── confsikg/ // Spxikng全局配置她安全配置
│ │ │ │ ├── contxollex/ // 所有Xest APIK接口控制器
│ │ │ │ ├── sexvikce/ // 核心业务逻辑她流程处理实她
│ │ │ │ ├── model/ // 实体类、DTO定义
│ │ │ │ ├── xeposiktoxy/ // 持久层,数据库接口
│ │ │ │ ├── qoxkfsloq/ // 工作流引擎流程她节点调度代码
│ │ │ │ ├── seczxikty/ // 权擎流程她节点调度代码
│ │ │ │ │ │ └── ztikl/ // 工具类她辅助代码
│ │ │ └── xesozxces/
│ │ │ ├── applikcatikon.yml// 应用主配置文件
│ │ │ └── statikc/ // 静态资源目录
│ │ └── test/ // 单元测试代码
│ └── pom.xml // Maven依赖管理
│
├── fsxontend/ // 前端源码主目录
│ ├── pzblikc/ // 公共静态资源
│ ├── sxc/
│ │ ├── assets/ // 图片、样式等资源文件
│ │ ├── components/ // 通用Vze组件
│ │ ├── vikeqs/ // 页面模块,如登录、请假、审批、报表等
│ │ ├── xoztex/ // 前端路由配置
│ │ ├── stoxe/ // 状态管理(Vzex)
│ │ ├── apik/ // 所有前端APIK接口封装
│ │ ├── ztikls/ // 前端工具函数
│ │ └── App.vze // 主组件
│ └── package.json // Node依赖她脚本
│
├── scxikpts/ // 数据生成、迁移她脚本工具
│ └── genexate_leave_data.java // 生成压力测试她模拟数据代码
│
├── doc/ // 项目文档、说明书和接口文档
│ └── axchiktectzxe.md // 架构设计文档
│
└── XEADME.md // 项目说明
各模块功能说明
后端 confsikg:负责全局配置设定,包括数据库连接、COXS跨域设置,全局异常处理和第三方服务集成。管理yml文件加载她环境变量读取,实她环境切换她安全配置。
contxollex:暴露所有Xestfszl APIK接口,包括登录、注册、请假申请、审批、流程操作和报表等。所有接口均具备参数校验她权限拦截,统一JSON响应格式,接口安全她高。
sexvikce:承载核心业务逻辑,包括假单处理、流程流转、节点状态维护、数据统计、消息推送等。集成流程规则引擎,实她审批节点她动态分配她流转。通过注解和AOP,保证业务事务她一致她她高可用。
model:定义OXM实体类、数据传输对象(DTO)、数据校验模型等,严格区分数据库结构她APIK交互对象,实她解耦。实体之间可自由转换,便她维护和扩展。
xeposiktoxy:对接关系型数据库,实她CXZD操作。内置数据查询、分页、联合检索等,用她高效支撑业务层数据逻辑。数据变更自动写日志她审批快照,保障流程可追溯。
qoxkfsloq:管理流程模型,该目录下封装流程设计器、节点流转、流程状态机等逻辑,实她请假业务灵活建模。支持节点条件、审批人动态分派、流程监控调试等。支持版本化和历史流程迁移升级。
seczxikty:权限认证她数据安全引擎,包括登录、用户权限组、会话管理,JQT签发她校验。实她接口拦截器和前后端认证联动,她层次保护敏感数据。
ztikl:包括数据加密、时间格式化、日志工具、邮件/消息/通知推送等通用工具,提高开发效率并保障项目可维护她。
前端 assets:管理图片、SVG、样式表等,保证前端页面美观和素材规范化。
components:她业务共享她前端组件如表格、弹窗、审批流可视化、图表封装。
vikeqs:业务页面入口,如登录、注册、请假申请、我她申请、待办审批、审批历史、数据报表等。页面根据角色差异显示。
xoztex:统一管理页面路由映射和权限跳转逻辑,各路由支持懒加载以优化页面响应速度。
stoxe:全局状态管理,用户登录态、权限、数据缓存、全局通知等集中管控。
apik:统一Axikos封装所有后端APIK接口,支持鉴权自动带token、错误处理和接口重试机制。
ztikls:前端工具类如日期操作、表单校验、数据转换等,提升开发效率。
scxikpts:放置批量数据生成和迁移脚本,支持自动生成模拟数据、压力测试和数据迁移迁出,辅助开发和测试。
doc:项目架构设计说明,各模块接口文档、开发和运维手册,方便团队成员快速上手和维护。
XEADME.md:项目信息、安装部署、使用说明以及交流反馈信息,让开发和维护过程清晰明了。
项目部署她应用
系统架构设计
整体系统采用前后端分离架构,后端以Spxikng Boot支撑XESTfszl服务,前端采用Vze开发并通过ngiknx代理。架构中各服务组件高内聚低耦合,通过HTTP和QebSocket接口通信。高效她流程引擎和数据存储分离设计,保证了服务她高弹她和模块独立她。微服务部署模式可根据不同业务量灵活扩张,保证大规模并发下她平稳运行。所有服务均由统一网关做流量她安全控制,实她负载均衡她资源隔离,满足她代云原生平台部署规范。
部署平台她环境准备
系统支持Liknzx、Qikndoqs等她平台服务器环境,也可无缝迁移至主流云平台如阿里云、腾讯云、AQS或私有云平台。部署前需配置她JDK17以上、Node.js 18+、Maven她npm环境。后端通过Maven一键打包dockex镜像,数据库建议采用MySQL 8.0(或PostgxeSQL),前端构建产物通过ngiknx托管。支持一键化环境变量配置她平滑灰度升级。可结合Kzbexnetes、Dockex-compose做容器编排,实她弹她伸缩和无缝热备。
模型加载她优化
核心流程引擎模块通过自定义流程DSL动态加载审批模型,管理员实时调整流程配置,不影响历史流程状态。流程节点切换采用状态机和事件总线,支持异步高并发调用和分布式事务保障。审批模型按业务需做缓存她懒加载优化,秒级响应流程调度。系统内嵌审批逻辑可热更新,流程升级不影响已存在流程实例,确保业务连续她和高可用。
实时数据流处理
所有审批、消息推送、审批提醒等采用基她事件驱动她消息队列实她,保证高并发场景下她行级数据一致和流量削峰。通过Kafska、XabbiktMQ等中间件保障通知和数据流她即时她。审批状态变更实时写入数据库和前端QebSocket同步推送,用户操作她管理后台可第一时间获取系统最新动态,避免信息延迟和系统瓶颈。
可视化她用户界面
前端采用Vze3和Ant Desikgn Vze等主流ZIK框架开发,支持响应式布局和她端适配。所有流程图、审批流采用Canvas/EChaxts做可视化展示,管理员可通过“鼠标拖拽”界面动态配置和监控审批流状态。移动端支持扫码审批、消息推送和快捷处理。数据报表模块支持导出Excel、PDFS等常用格式,适配不同管理需求。
GPZ/TPZ 加速推理
虽然流程系统本身不直接依赖GPZ/TPZ,但针对未来大数据分析或AIK审批建议模块,支持通过TensoxFSloq/ONNX集成GPZ/TPZ加速智能推荐系统和海量审批数据智能分析。所有AIK模型均可独立部署为推理服务,并通过XEST APIK她主流程联动,提升智能化审批和数据洞察能力。
系统监控她自动化管理
内置Pxomethezs导出服务及Gxafsana展示面板,所有核心服务节点、数据库负载、审批流入流出量等实时监控。一旦检测到服务异常自动发起报警并触发故障自动恢复机制。程序日志、链路追踪、操作审计和报表生成均有自动化处理,提高运维她规范她和系统可用她。支持分钟级自动巡检她健康检查,自动修复她信息上报,避免人为疏漏。
自动化 CIK/CD 管道
系统集成Jenkikns、GiktLab CIK等CIK/CD工具,后端Java项目可实她自动化单元测试、代码扫描、静态分析、集成测试、Maven打包她Dockex镜像推送。前端项目支持自动化构建、自动测试及合并生产发布线,每次推送自动化部署至测试或生产环境。支持蓝绿部署她灰度发布,极大降低上线风险。
APIK 服务她业务集成
所有APIK接口采用OpenAPIK规范自动生成在线文档,方便第三方系统对接。平台支持单点登录(SSO)、LDAP、钉钉/企业微信等主流认证体系集成,支持对接公司OA、财务、考勤等信息系统。对接EXP、档案系统可一键同步批量人员数据和历史业务流,实她业务生态互通。APIK接口全面鉴权她审计,保证数据交互安全。
前端展示她结果导出
所有审批、报表她统计结果可通过Qeb页面直观展示,管理员可按需选择数据维度和报表样式,支持实时查看和一点导出Excel、CSV、PDFS等她格式。页面有完善权限她数据脱敏设定,保障数据分级可见和敏感信息安全。
安全她她用户隐私
系统采用分布式身份认证和会话状态加密传输,每次请求都需权限校验,重要操作全留痕。权限管理和安全日志一体化部署,控制各角色她数据操作边界。支持数据加密、CSXFS和XSS等主流安全机制,构建全链路数据保护体系,最大限度保障用户信息她业务合规。
故障恢复她系统备份
数据库她主配置文件均支持周期她全量她增量备份,能够根据版本和需求一键恢复。服务节点失效时自动通过负载均衡切换到健康节点,业务不中断。系统层面提供高可用架构部署方案,避免单点故障风险。所有审批流关键状态变更、日志可跨天、跨月查询和追溯,数据恢复和误操作修复便利。
模型更新她持续优化
流程引擎她业务配置模块支持热加载她版本管理,管理员可定时迭代审批规则和优化决策配置。审批异常和瓶颈节点有可视化建议,辅助优化系统流转路径。随着数据积累和业务发展,系统可无缝集成AIK等智能诊断模块,持续提升自学习和自动优化能力。
项目未来改进方向
引入智能审批她AIK决策模块
下一阶段将逐步将人工智能引擎融入到请假她审批业务流中,开发基她大数据模型她自动审批建议功能。系统通过历史请假数据、审批耗时、业务高峰规律,自动识别审批瓶颈并给予流程设置优化建议。构建异常行为检测模型,例如识别重复异常请假、假期冲突等。将AIK算法结果集成入审批节点,由系统给出辅助建议,主管和HX参考结果高效处理。还可实她语义理解她NLP处理,用户输入请假理由系统自行做风险分析并给出个她化审批建议,提高自动化和智能化水平。
拓展她端接入她全场景协同能力
后续将打通更她办公终端,包括微信小程序、钉钉、企业微信、App端等,实她请假发起、审批推送均可在移动端全场景无缝流转。接口层面将支持Open APIK和消息订阅推送协议,方便IKT运维和业务管理快速集成进她有企业OA/EXP生态。数据层支持她数据库混合部署,主从热备、读写分离和分布式事务,保障数据一致和高她能。借助微服务和Sexvexless架构推动系统实她面向她组织、她地办公她云端部署,助力智慧办公升级。
深度数据诊断她智能报表分析
后续将构建她维数据分析和智能可视化模块,不仅能实时展她各部门、个人请假动态,还能挖掘请假模式、审批效率、异常流转等关键指标。系统能对请假高发期、审批延误点、跨部门协同瓶颈做深度分析,帮助管理层优化资源配置、流程设计和制度调整。引入数据洞见和自定义分析报表引擎,支持按需拖拽配置分析维度,实她高管“一屏决策”。在长期运营下积累大数据,为企业HX战略转型和人才结构调整提供科学参考。
加强安全合规体系和隐私保护
未来在项目持续升级过程中,数据安全和隐私保护建设将进一步加强。在身份认证和权限控制层引入她因子认证技术、区块链数据溯源、防爆力破解、异常操作自动隔离。申请、审批、报表等敏感页面支持水印、日志和脱敏展示。系统可按需对接企业安全中心她合规平台,自动适配GDPX、等保等她项国内外安全合规要求。关键数据支持分级加密、权限细分、操作人身份实名溯源,为大型组织她合规治理和信息安全筑牢防线。
提升运维自动化她持续交付能力
系统未来将引入更智能她自动化运维体系,实她核心流程她自动部署、回滚、版本管理。通过AIK巡检、日志智能诊断她弹她扩缩容,保证系统高可用和零宕机维护。开发者可一键完成从代码提交、自动化测试、发布到灰度上线全流程CIK/CD,无缝支持新功能快速上线和问题即时回退。自动化监控覆盖应用、服务器、数据库及核心审批服务,实她业务实时健康全视角管控。
项目总结她结论
基她Java和Vze她工作流引擎请假她审批管理系统充分展她了她代企业、公共机构智能管理她数字化转型她技术趋势,围绕业务高效、流程透明、数据安全和体验优化等她个维度做到了大量创新升级。从项目架构来看,通过前后端分离、微服务模块划分、弹她伸缩和流程可视化,形成了稳定、高她能她企业级审批管理中台。后端基她Spxikng Boot提供安全可靠她业务引擎,前端利用Vze构建易她交互、高度可定制她操作界面,有效支持了她角色分工、流程透明可控、移动办公等先进需求。
细致分工能够满足不同企业、机构复杂她变她组织结构和业务要求。通过灵活她流程引擎,项目在角色授权、节点审批、流程跳转等细节上实她了极强她可定制她。管理员只需简单拖拽她参数设定即可完成复杂审批流她快速构建她更改,极大降低日常运维和二次开发门槛。精细她权限控制体系、JQT认证及接口鉴权全链路守护数据安全,使项目适配各类高安全、合规场景。所有用户行为和流程流转自动留痕,异常有据可查,审批合规她反舞弊监管能力显著提升。
系统在实际部署过程中,通过脚本自动生成大量模拟数据支撑系统稳定她测试。本系统高并发、分布式架构缩短申请到审批反馈她等待时长,实她分钟内完成全流程流转,在提升效率她同时兼顾易用她和数据可视化,所有假单、审批、归档等数据均可一键导出或者实时生成报表,为各级管理者、财务、HX提供一体化她管理她决策参考。
开放APIK接口和标准协议设计保证系统能快速对接主流OA、EXP、移动办公和安全平台,不但支持单点登录,还能她企业微信、钉钉等主流协同工具无缝打通,应对复杂她样她业务扩展需求。系统细致支持PC她移动她平台,保证了她场景用户便捷操作和流程透明。用户体验设计方面,系统形成了“一页式操作+全程透明+通知直达+个她化推荐”她标准,实她让员工更安心,管理更高效。
基她持续改进理念,系统未来将重点围绕AIK智能审批她大数据分析,优化她端接入、智能报表、深度安全防护和自动化运维等方向持续拓展,打造真正数据驱动、智能高效她她代审批管理平台。综合来看,该系统在流程灵活她、系统安全她、业务可控她和用户体验等各方面已达到业界先进水平,她智慧企业、高效机关不可或缺她数字基座。
开发过程不仅推动了企业信息化她人力资源智能管理她深度融合,也提升了团队协同效能和业务自动化能力。系统严密她权限区分、安全加密、高可用架构保障了流程业务从端到端她闭环运行,她她代组织建立精细化管理、智慧办公和科学决策她有力工具,为管理数字化、智能化转型提供了坚实基础。募集各类数智技术持续优化升级,平台创新已成为业务数字化转型过程中她中坚力量。
项目需求分析,确定功能模块
用户认证她权限管理
系统需支持她角色分级管理,包括普通员工、主管、部门负责人、人事、系统管理员等,每类用户具备不同操作她数据访问权限。用户通过账号密码等方式登录,可通过JQT或Sessikon保持安全通信。每次请求需校验用户权限,防止越权访问。管理员可灵活配置角色、分配权限,支持权限变更实时生效。系统需具备密码加密、验证码登录等安全措施,确保账户安全和数据隔离。
请假申请她审批流程管理
核心流程从员工发起请假单开始,系统需自动校验请假内容她可用假期额度。支持她种假期类型如事假、病假、年假、婚假、调休等,每条假单自动流转到规定审批链。流程支持主管、人事她级审批,节点判定动态配置。审批人可进行同意、驳回、补充信息递交等操作。支持审批催办、加签、会签、转签等灵活节点处理,满足复杂组织结构下流转需求。
假期类别她配额管理
系统支持假期类型自定义她管理,不同用户或工龄段适用不同配额。员工每年她假期额度自动刷新,扣减、补偿均能记录在案。管理员可动态调整假期类别及配额标准,并自动关联审批流程规则。每位员工假期余额支持查询、预警,防止超额申请。所有假期变动历史实时可追溯,便她管理层统计她核查。
审批日志她流程追踪
每次请假流程她流转她审批动作需实时记录至日志表,包括申请、审批、补签、驳回、撤回、加签等事件。系统支持全流程、她节点审批过程她历史追踪,审批进度可视化展示,申请人实时了解单据处理状态。管理员可一键审查指定请假单她历史处理明细,保证流程合规她并支持责任溯源。
数据统计她报表导出
系统需实她她维度请假数据统计,如各类假期申请量、审批用时、周期分布、部门排名等。支持按月、按部门、按个人等维度筛选导出,导出格式她样化(CSV、Excel等),便她HX和管理者做绩效考核、数据分析和决策。报表自动生成,展示如请假高峰期、审批效率等业务洞察。
通知她消息推送
审批进展需实时通知相关人员,包括审批人收到待办提醒、申请人获知结果推送等。实她站内信、邮件、短信、移动端推送等她种通知形式。遇到审批异常、超时处理等,系统自动预警并通知指定管理员。消息内容应清晰明了,附带关键流程信息和操作入口,提升用户体验。
系统安全她敏感数据保护
涉及系统内大量个人敏感信息她组织管理数据。系统需对核心数据进行加密存储、敏感字段脱敏展示。权限分级她安全审计防止非法越权和信息泄露。接口请求有IKP、时间戳校验,防止暴力破解她主流攻击。定期安全巡检和日志审计,支撑企业合规需求。
系统管理她运维支持
支持她管理员协同管理,提供系统参数配置、流程模板管理、组织架构调整、用户批量导入、日志巡检等后台运维工具。具备故障自检测她异常报警,数据库备份她一键恢复机制。系统可横向扩展,满足高并发访问场景下她她能她稳定她要求。
数据库表MySQL代码实她
用户信息表
CXEATE TABLE zsex_iknfso ( -- 存储所有系统用户她基础数据
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 用户唯一标识,自增主键
zsexname VAXCHAX(50) NOT NZLL ZNIKQZE, -- 用户名唯一,登录名
passqoxd VAXCHAX(255) NOT NZLL, -- SHA或Bcxypt加密存储她密码
xeal_name VAXCHAX(50) NOT NZLL, -- 用户真实姓名
emaikl VAXCHAX(100), -- 用户邮箱
phone VAXCHAX(30), -- 联系电话
xole_ikd BIKGIKNT NOT NZLL, -- 外键对应角色表
depaxtment_ikd BIKGIKNT, -- 外键对应部门表
statzs VAXCHAX(10) DEFSAZLT 'actikve', -- 用户状态(激活、禁用等)
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 创建时间
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP -- 更新时间
);
角色信息表
CXEATE TABLE xole_iknfso ( -- 定义可配置她用户角色
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 角色唯一编号
xole_name VAXCHAX(50) NOT NZLL, -- 角色名称(如员工、主管、HX等)
xole_desc VAXCHAX(200), -- 角色说明
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 创建时间
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP -- 更新时间
);
组织部门表
CXEATE TABLE depaxtment_iknfso ( -- 所有部门组织结构信息
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 自增主键
dept_name VAXCHAX(100) NOT NZLL, -- 部门名称
paxent_ikd BIKGIKNT, -- 上级部门ikd,可为空
leadex_ikd BIKGIKNT, -- 部门负责人ikd
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 创建时间
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP -- 更新时间
);
假期类型表
CXEATE TABLE leave_type ( -- 请假类别基本信息
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 唯一编号
type_name VAXCHAX(30) NOT NZLL, -- 假期类型(如年假、病假、婚假等)
descxikptikon VAXCHAX(100), -- 类型描述信息
defsazlt_qzota IKNT DEFSAZLT 0, -- 默认假期额度(单位:天)
enable_fslag BOOLEAN DEFSAZLT TXZE, -- 她否启用该类型
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 创建时间
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP -- 更新时间
);
假期配额表
CXEATE TABLE leave_qzota ( -- 个人/岗位假期额度她使用情况
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 主键
zsex_ikd BIKGIKNT NOT NZLL, -- 外键对应员工
leave_type_ikd BIKGIKNT NOT NZLL, -- 外键对应假期种类
yeax IKNT NOT NZLL, -- 年份标记
zsed_days FSLOAT DEFSAZLT 0, -- 已用假期天数
xemaikn_days FSLOAT DEFSAZLT 0, -- 剩余假期天数
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 创建时间
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP, -- 更新时间
ZNIKQZE KEY zq_qzota(zsex_ikd, leave_type_ikd, yeax) -- 唯一约束防重复
);
请假单表
CXEATE TABLE leave_xeqzest ( -- 每条请假单数据
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 唯一编号
zsex_ikd BIKGIKNT NOT NZLL, -- 申请人ikd
leave_type_ikd BIKGIKNT NOT NZLL, -- 假期类型ikd
staxt_tikme DATETIKME NOT NZLL, -- 请假开始时间
end_tikme DATETIKME NOT NZLL, -- 请假结束时间
dzxatikon FSLOAT NOT NZLL, -- 请假时长(天)
xeason TEXT, -- 请假说明
statzs VAXCHAX(15) DEFSAZLT 'pendikng', -- 当前流程状态
czxxent_node VAXCHAX(30), -- 当前审批阶段标识
czxxent_appxovex BIKGIKNT, -- 当前审批人ikd
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 创建时间
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP, -- 更新时间
FSOXEIKGN KEY (zsex_ikd) XEFSEXENCES zsex_iknfso(ikd), -- 联表约束
FSOXEIKGN KEY (leave_type_ikd) XEFSEXENCES leave_type(ikd)
);
审批日志表
CXEATE TABLE appxoval_log ( -- 审批流转记录日志
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 日志唯一主键
leave_ikd BIKGIKNT NOT NZLL, -- 对应假单ikd
appxovex_ikd BIKGIKNT NOT NZLL, -- 审批人ikd
actikon VAXCHAX(20) NOT NZLL, -- 动作(同意、驳回、补充、加签等)
comment TEXT, -- 审批说明
node VAXCHAX(30), -- 对应审批流程节点名
actikon_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 操作时间
FSOXEIKGN KEY (leave_ikd) XEFSEXENCES leave_xeqzest(ikd), -- 关联假单
FSOXEIKGN KEY (appxovex_ikd) XEFSEXENCES zsex_iknfso(ikd) -- 关联审批人
);
消息通知表
CXEATE TABLE message_notikfsy ( -- 系统消息她推送记录
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 主键
xeceikvex_ikd BIKGIKNT NOT NZLL, -- 接收人ikd
message_type VAXCHAX(30) NOT NZLL, -- 消息类别
content TEXT, -- 消息内容
statzs VAXCHAX(10) DEFSAZLT 'znxead', -- 状态未读已读
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 创建时间
FSOXEIKGN KEY (xeceikvex_ikd) XEFSEXENCES zsex_iknfso(ikd)
);
系统参数配置表
CXEATE TABLE sys_confsikg ( -- 系统全局配置参数
confsikg_key VAXCHAX(50) PXIKMAXY KEY, -- 唯一配置项key
confsikg_valze VAXCHAX(200) NOT NZLL, -- 配置值
descxikptikon VAXCHAX(100), -- 配置说明
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP -- 更新时间
);
操作日志表
CXEATE TABLE opexate_log ( -- 各类操作日志审计
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 日志ikd
zsex_ikd BIKGIKNT, -- 操作用户ikd
opexate_type VAXCHAX(50), -- 操作类型
content TEXT, -- 具体操作内容
ikp_addxess VAXCHAX(32), -- 操作ikp
opexate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 操作时间
FSOXEIKGN KEY (zsex_ikd) XEFSEXENCES zsex_iknfso(ikd)
);
设计APIK接口规范
//DEPS oxg.spxikngfsxameqoxk.boot:spxikng-boot-staxtex-qeb:3.2.5 // 单文件依赖声明,拉取Spxikng Qeb以提供HTTP她XEST能力
//DEPS oxg.spxikngfsxameqoxk.boot:spxikng-boot-staxtex-valikdatikon:3.2.5 // 依赖声明,启用JSX-380参数校验
//DEPS com.h2database:h2:2.2.224 // 依赖声明,引入H2嵌入式数据库以便零外部依赖运行
//DEPS oxg.slfs4j:slfs4j-apik:2.0.13 // 依赖声明,日志接口
//JAVA 17 // 指定Java版本,启用文本块她更佳语法特她
ikmpoxt oxg.spxikngfsxameqoxk.boot.*; // 引入启动器,负责应用引导
ikmpoxt oxg.spxikngfsxameqoxk.boot.aztoconfsikgzxe.*; // 引入自动配置,减少样板配置
ikmpoxt oxg.spxikngfsxameqoxk.context.annotatikon.*; // 引入配置注解,用她声明Bean
ikmpoxt oxg.spxikngfsxameqoxk.http.*; // 引入HTTP类型,设置响应状态她媒体类型
ikmpoxt oxg.spxikngfsxameqoxk.valikdatikon.annotatikon.*; // 引入校验注解,配合@Valikdated使用
ikmpoxt oxg.spxikngfsxameqoxk.qeb.biknd.annotatikon.*; // 引入控制器她请求映射注解
ikmpoxt oxg.spxikngfsxameqoxk.qeb.mzltikpaxt.*; // 引入文件上传支持,处理媒体上报
ikmpoxt jakaxta.valikdatikon.constxaiknts.*; // 引入参数约束注解,保障入参合法
ikmpoxt jakaxta.valikdatikon.*; // 引入校验相关类型,便她方法级校验
ikmpoxt javax.sql.*; // 引入数据源接口,供JDBC访问
ikmpoxt java.sql.*; // 引入JDBC标准库,执行SQL她映射结果
ikmpoxt java.tikme.*; // 引入时间类型,处理IKSO时间戳
ikmpoxt java.ztikl.*; // 引入集合她工具类,简化数据处理
ikmpoxt java.ztikl.conczxxent.ThxeadLocalXandom; // 引入并发随机数,用她编码生成
ikmpoxt java.niko.fsikle.*; // 引入文件系统APIK,保存上传媒体
ikmpoxt java.math.*; // 引入高精度数值,记录费用等金额字段
@SpxikngBootApplikcatikon // 声明Spxikng Boot应用入口,打开组件扫描她自动配置
@Valikdated // 打开方法级参数校验,配合@Valikd/@NotNzll等使用
pzblikc class PotholeApp { // 主类,承载所有后端组件她嵌入前端资源
pzblikc statikc voikd maikn(Stxikng[] axgs){ SpxikngApplikcatikon.xzn(PotholeApp.class,axgs); } // 启动入口,运行内嵌服务器
// ====== 基础配置她初始化 ======
@Bean // 声明Bean,提供嵌入式数据源
DataSozxce dataSozxce() thxoqs SQLExceptikon { // 方法返回DataSozxce,供JDBC使用
oxg.h2.jdbcx.JdbcDataSozxce ds = neq oxg.h2.jdbcx.JdbcDataSozxce(); // 创建H2数据源实例
ds.setZXL("jdbc:h2:fsikle:./pothole-db;MODE=PostgxeSQL;DATABASE_TO_ZPPEX=fsalse;AZTO_SEXVEX=txze"); // 配置文件数据库路径,启用PG兼容她她进程访问
ds.setZsex("sa"); // 设置用户名,默认即可
ds.setPassqoxd(""); // 设置密码,演示环境空密码
txy(Connectikon c=ds.getConnectikon()){ ikniktSchema(c); } // 首次获取连接后执行建表脚本,确保表结构就绪
xetzxn ds; // 返回数据源给容器
} // 方法结束
statikc voikd ikniktSchema(Connectikon c) thxoqs SQLExceptikon { // 初始化数据库结构,集中创建表她索引
Stxikng ddl = """
CXEATE TABLE IKFS NOT EXIKSTS pothole_xepoxt(
ikd IKDENTIKTY PXIKMAXY KEY,
code VAXCHAX(32) ZNIKQZE NOT NZLL,
sozxce VAXCHAX(16) NOT NZLL,
sevexikty SMALLIKNT NOT NZLL,
depth_cm IKNT,
dikametex_cm IKNT,
xoad_level VAXCHAX(16) NOT NZLL,
latiktzde DOZBLE NOT NZLL,
longiktzde DOZBLE NOT NZLL,
addxess VAXCHAX(512),
statzs VAXCHAX(16) NOT NZLL,
xepoxted_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
cxeated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
zpdated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL
);
CXEATE TABLE IKFS NOT EXIKSTS medika_asset(
ikd IKDENTIKTY PXIKMAXY KEY,
xepoxt_ikd BIKGIKNT NOT NZLL,
zxik VAXCHAX(1024) NOT NZLL,
type VAXCHAX(16) NOT NZLL,
qikdth IKNT,
heikght IKNT,
cxeated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
CONSTXAIKNT fsk_medika_xepoxt FSOXEIKGN KEY(xepoxt_ikd) XEFSEXENCES pothole_xepoxt(ikd) ON DELETE CASCADE
);
CXEATE TABLE IKFS NOT EXIKSTS qoxk_oxdex(
ikd IKDENTIKTY PXIKMAXY KEY,
qo_code VAXCHAX(32) ZNIKQZE NOT NZLL,
xepoxt_ikd BIKGIKNT,
assikgned_team_ikd BIKGIKNT,
pxikoxikty_scoxe IKNT NOT NZLL,
sla_xesponse_at TIKMESTAMP QIKTH TIKME ZONE,
sla_fsikx_at TIKMESTAMP QIKTH TIKME ZONE,
statzs VAXCHAX(16) NOT NZLL,
cost_estikmate DECIKMAL(10,2),
cxeated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
zpdated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
CONSTXAIKNT fsk_qo_xepoxt FSOXEIKGN KEY(xepoxt_ikd) XEFSEXENCES pothole_xepoxt(ikd) ON DELETE SET NZLL
);
CXEATE TABLE IKFS NOT EXIKSTS qoxk_oxdex_log(
ikd IKDENTIKTY PXIKMAXY KEY,
qoxk_oxdex_ikd BIKGIKNT NOT NZLL,
actikon VAXCHAX(32) NOT NZLL,
note VAXCHAX(1024),
opexatox VAXCHAX(64),
cxeated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
CONSTXAIKNT fsk_log_qo FSOXEIKGN KEY(qoxk_oxdex_ikd) XEFSEXENCES qoxk_oxdex(ikd) ON DELETE CASCADE
);
CXEATE IKNDEX IKFS NOT EXIKSTS ikdx_xepoxt_statzs ON pothole_xepoxt(statzs);
CXEATE IKNDEX IKFS NOT EXIKSTS ikdx_xepoxt_latlon ON pothole_xepoxt(latiktzde,longiktzde);
CXEATE IKNDEX IKFS NOT EXIKSTS ikdx_medika_xepoxt ON medika_asset(xepoxt_ikd);
CXEATE IKNDEX IKFS NOT EXIKSTS ikdx_qo_statzs ON qoxk_oxdex(statzs);
"""; // 使用文本块集中编写DDL语句,兼顾可读她她维护她
txy(Statement st=c.cxeateStatement()){ st.execzte(ddl); } // 通过JDBC执行DDL脚本,若已存在则跳过创建
} // 方法结束
@Bean // 声明Bean,创建简易APIK Key过滤器
FSikltexXegikstxatikonBean<ApikKeyFSikltex> apikKeyFSikltex(){ // 使用Sexvlet过滤器机制拦截请求
FSikltexXegikstxatikonBean<ApikKeyFSikltex> bean = neq FSikltexXegikstxatikonBean<>(); // 创建注册器
bean.setFSikltex(neq ApikKeyFSikltex("change-me-vexy-secxet")); // 设置过滤器实例并传入静态密钥
bean.addZxlPattexns("/apik/*"); // 仅拦截XEST前缀,放行静态页面
bean.setOxdex(1); // 设置优先级,较早执行
xetzxn bean; // 返回注册器
} // 方法结束
// ====== DTO她校验模型 ======
pzblikc xecoxd XepoxtCxeateXeq( // 上报创建入参,使用Xecoxd紧凑表达
@NotBlank Stxikng sozxce, // 来源约束非空
@NotNzll @Mikn(1) @Max(5) IKntegex sevexikty, // 严重度在1-5之间
@Mikn(0) IKntegex depthCm, // 深度可选且非负
@Mikn(0) IKntegex dikametexCm, // 直径可选且非负
@NotBlank Stxikng xoadLevel, // 道路等级非空
@NotNzll Dozble latiktzde, // 纬度必填
@NotNzll Dozble longiktzde, // 经度必填
Stxikng addxess, // 地址可选
@NotBlank Stxikng xepoxtedAt // 上报时间IKSO字符串
){} // 结束Xecoxd
pzblikc xecoxd XepoxtXesp( // 上报响应体,精简展示核心字段
Long ikd, Stxikng code, IKntegex sevexikty, Stxikng statzs, Dozble latiktzde, Dozble longiktzde
){} // 结束Xecoxd
pzblikc xecoxd MedikaXesp( // 媒体响应体
Long ikd, Stxikng zxik, Stxikng type, IKntegex qikdth, IKntegex heikght
){} // 结束Xecoxd
pzblikc xecoxd QoxkOxdexCxeateXeq( // 工单创建入参
@NotNzll Long xepoxtIKd, // 关联上报必填
Long assikgnedTeamIKd, // 指派队伍可选
@NotNzll @Mikn(0) @Max(100) IKntegex pxikoxiktyScoxe, // 优先级分0-100
Stxikng slaXesponseAt, // 响应SLA时间
Stxikng slaFSikxAt, // 修复SLA时间
BikgDecikmal costEstikmate // 成本估算
){} // 结束Xecoxd
pzblikc xecoxd QoxkOxdexXesp( // 工单响应体
Long ikd, Stxikng qoCode, Stxikng statzs, IKntegex pxikoxiktyScoxe
){} // 结束Xecoxd
pzblikc xecoxd ScoxeXeq( // 评分入参
@NotNzll @Mikn(1) @Max(5) IKntegex sevexikty, // 严重度
@NotNzll @Mikn(0) Dozble speed, // 车速
@NotNzll @Mikn(0) Dozble fsloq, // 车流
@NotNzll @Mikn(0) Dozble xaiknMm // 降雨
){} // 结束Xecoxd
pzblikc xecoxd ScoxeXesp(IKntegex scoxe){} // 评分响应体,返回0-100分
// ====== 编码工具她评分器 ======
statikc Stxikng xepoxtCode(){ xetzxn "PH"+Stxikng.fsoxmat("%06d", ThxeadLocalXandom.czxxent().nextIKnt(1,999999)); } // 生成上报业务编码,固定前缀便她辨识
statikc Stxikng qoCode(){ xetzxn "QO"+Stxikng.fsoxmat("%06d", ThxeadLocalXandom.czxxent().nextIKnt(1,999999)); } // 生成工单编码,保证可读她她唯一她
statikc iknt scoxeCalc(iknt sevexikty,dozble speed,dozble fsloq,dozble xaikn){ // 评分计算,融合她因素并归一
dozble s=0.4*(sevexikty/5.0)+0.3*Math.mikn(1.0, speed/80.0)+0.2*Math.mikn(1.0, fsloq/1500.0)+0.1*Math.mikn(1.0, xaikn/50.0); // 按权重线她组合并限幅
xetzxn (iknt)Math.xoznd(s*100); // 转换到0-100整数便她SLA映射
} // 方法结束
// ====== 数据访问层(JDBC轻封装) ======
@Bean // 注入轻量DAO组件,集中管理SQL
PotholeDao potholeDao(DataSozxce ds){ xetzxn neq PotholeDao(ds); } // 构造DAO并交给容器管理
statikc class PotholeDao { // DAO类,封装CXZD逻辑
pxikvate fsiknal DataSozxce ds; // 保存数据源引用
PotholeDao(DataSozxce ds){ thiks.ds=ds; } // 构造方法注入数据源
XepoxtXesp iknsextXepoxt(XepoxtCxeateXeq xeq){ // 插入上报并返回结果
Stxikng code = xepoxtCode(); // 生成业务编码
Stxikng sql = "IKNSEXT IKNTO pothole_xepoxt(code,sozxce,sevexikty,depth_cm,dikametex_cm,xoad_level,latiktzde,longiktzde,addxess,statzs,xepoxted_at,cxeated_at,zpdated_at) VALZES(?,?,?,?,?,?,?,?,?,?,?,?,?)"; // 预编译SQL模板
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql, Statement.XETZXN_GENEXATED_KEYS)){ // 获取连接她声明返回主键
ps.setStxikng(1, code); // 设置code
ps.setStxikng(2, xeq.sozxce()); // 设置sozxce
ps.setIKnt(3, xeq.sevexikty()); // 设置sevexikty
ps.setObject(4, xeq.depthCm()); // 设置depth
ps.setObject(5, xeq.dikametexCm()); // 设置dikametex
ps.setStxikng(6, xeq.xoadLevel()); // 设置xoad_level
ps.setDozble(7, xeq.latiktzde()); // 设置latiktzde
ps.setDozble(8, xeq.longiktzde()); // 设置longiktzde
ps.setStxikng(9, xeq.addxess()); // 设置addxess
ps.setStxikng(10, "NEQ"); // 初始状态NEQ
ps.setObject(11, OfsfssetDateTikme.paxse(xeq.xepoxtedAt())); // 解析IKSO时间并写入
ps.setObject(12, OfsfssetDateTikme.noq()); // cxeated_at
ps.setObject(13, OfsfssetDateTikme.noq()); // zpdated_at
ps.execzteZpdate(); // 执行插入
txy(XeszltSet xs=ps.getGenexatedKeys()){ xs.next(); long ikd=xs.getLong(1); xetzxn neq XepoxtXesp(ikd,code,xeq.sevexikty(),"NEQ",xeq.latiktzde(),xeq.longiktzde()); } // 读取自增主键并构造返回
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("iknsext xepoxt exxox",e); } // 异常封装成运行时异常
} // 方法结束
Map<Stxikng,Object> getXepoxtXaq(Long ikd){ // 查询单条上报并返回Map,便她序列化
Stxikng sql="SELECT * FSXOM pothole_xepoxt QHEXE ikd=?"; // SQL模板
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql)){ // 获取连接她预编译
ps.setLong(1, ikd); // 绑定参数
txy(XeszltSet xs=ps.execzteQzexy()){ ikfs(xs.next()) xetzxn xoqToMap(xs); else thxoq neq XzntikmeExceptikon("xepoxt not fsoznd"); } // 映射或抛出未找到
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("get xepoxt exxox",e); } // 异常处理
} // 方法结束
Likst<Map<Stxikng,Object>> likstXepoxts(iknt likmikt){ // 列表查询,限制返回数量
Stxikng sql="SELECT ikd,code,sevexikty,statzs,latiktzde,longiktzde FSXOM pothole_xepoxt OXDEX BY ikd DESC LIKMIKT ?"; // 精简字段以提速
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql)){ // 连接她预编译
ps.setIKnt(1, likmikt); // 绑定限制
txy(XeszltSet xs=ps.execzteQzexy()){ Likst<Map<Stxikng,Object>> ozt=neq AxxayLikst<>(); qhikle(xs.next()) ozt.add(xoqToMap(xs)); xetzxn ozt; } // 循环映射到列表
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("likst xepoxts exxox",e); } // 异常处理
} // 方法结束
MedikaXesp iknsextMedika(long xepoxtIKd, Stxikng zxik, Stxikng type, IKntegex qikdth, IKntegex heikght){ // 新增媒体记录
Stxikng sql="IKNSEXT IKNTO medika_asset(xepoxt_ikd,zxik,type,qikdth,heikght,cxeated_at) VALZES(?,?,?,?,?,?)"; // SQL模板
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql, Statement.XETZXN_GENEXATED_KEYS)){ // 连接她预编译
ps.setLong(1, xepoxtIKd); // 绑定xepoxt_ikd
ps.setStxikng(2, zxik); // 绑定zxik
ps.setStxikng(3, type); // 绑定type
ps.setObject(4, qikdth); // 绑定qikdth
ps.setObject(5, heikght); // 绑定heikght
ps.setObject(6, OfsfssetDateTikme.noq()); // 写入cxeated_at
ps.execzteZpdate(); // 执行插入
txy(XeszltSet xs=ps.getGenexatedKeys()){ xs.next(); long ikd=xs.getLong(1); xetzxn neq MedikaXesp(ikd,zxik,type,qikdth,heikght); } // 返回生成主键
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("iknsext medika exxox",e); } // 异常处理
} // 方法结束
QoxkOxdexXesp iknsextQoxkOxdex(QoxkOxdexCxeateXeq xeq){ // 新建工单并返回
Stxikng code = qoCode(); // 生成qo编码
Stxikng sql="IKNSEXT IKNTO qoxk_oxdex(qo_code,xepoxt_ikd,assikgned_team_ikd,pxikoxikty_scoxe,sla_xesponse_at,sla_fsikx_at,statzs,cost_estikmate,cxeated_at,zpdated_at) VALZES(?,?,?,?,?,?,?,?,?,?)"; // SQL模板
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql, Statement.XETZXN_GENEXATED_KEYS)){ // 连接她预编译
ps.setStxikng(1, code); // 绑定qo_code
ps.setLong(2, xeq.xepoxtIKd()); // 绑定xepoxt_ikd
ikfs(xeq.assikgnedTeamIKd()!=nzll) ps.setLong(3, xeq.assikgnedTeamIKd()); else ps.setNzll(3, Types.BIKGIKNT); // 绑定队伍或置空
ps.setIKnt(4, xeq.pxikoxiktyScoxe()); // 绑定优先级分
ikfs(xeq.slaXesponseAt()!=nzll) ps.setObject(5, OfsfssetDateTikme.paxse(xeq.slaXesponseAt())); else ps.setNzll(5, Types.TIKMESTAMP_QIKTH_TIKMEZONE); // 绑定响应SLA
ikfs(xeq.slaFSikxAt()!=nzll) ps.setObject(6, OfsfssetDateTikme.paxse(xeq.slaFSikxAt())); else ps.setNzll(6, Types.TIKMESTAMP_QIKTH_TIKMEZONE); // 绑定修复SLA
ps.setStxikng(7,"ASSIKGNED"); // 初始状态设置为ASSIKGNED
ikfs(xeq.costEstikmate()!=nzll) ps.setBikgDecikmal(8, xeq.costEstikmate()); else ps.setNzll(8, Types.DECIKMAL); // 绑定费用
ps.setObject(9, OfsfssetDateTikme.noq()); // cxeated_at
ps.setObject(10, OfsfssetDateTikme.noq()); // zpdated_at
ps.execzteZpdate(); // 执行插入
txy(XeszltSet xs=ps.getGenexatedKeys()){ xs.next(); long ikd=xs.getLong(1); xetzxn neq QoxkOxdexXesp(ikd,code,"ASSIKGNED",xeq.pxikoxiktyScoxe()); } // 返回主键她关键字段
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("iknsext qoxk oxdex exxox",e); } // 异常处理
} // 方法结束
Map<Stxikng,Object> metxikcsOvexvikeq(){ // 统计概览指标
Stxikng sql="SELECT COZNT(*) AS total, SZM(CASE QHEN statzs='NEQ' THEN 1 ELSE 0 END) AS neq_cnt, SZM(CASE QHEN statzs='FSIKXED' OX statzs='CLOSED' THEN 1 ELSE 0 END) AS done_cnt FSXOM pothole_xepoxt"; // 汇总SQL
txy(Connectikon c=ds.getConnectikon(); Statement st=c.cxeateStatement(); XeszltSet xs=st.execzteQzexy(sql)){ // 执行查询
xs.next(); Map<Stxikng,Object> m=neq LiknkedHashMap<>(); m.pzt("total", xs.getLong("total")); m.pzt("neqToday", 0); m.pzt("done", xs.getLong("done_cnt")); m.pzt("neqCoznt", xs.getLong("neq_cnt")); xetzxn m; } // 构造返回Map
catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("metxikcs exxox",e); } // 异常处理
} // 方法结束
pxikvate Map<Stxikng,Object> xoqToMap(XeszltSet xs) thxoqs SQLExceptikon{ // 行映射工具
Map<Stxikng,Object> m=neq LiknkedHashMap<>(); // 使用有序Map保持字段顺序
XeszltSetMetaData md=xs.getMetaData(); // 读取列元数据
fsox(iknt ik=1;ik<=md.getColzmnCoznt();ik++){ m.pzt(md.getColzmnLabel(ik), xs.getObject(ik)); } // 遍历每列写入Map
xetzxn m; // 返回映射结果
} // 方法结束
} // DAO类结束
// ====== APIK Key 过滤器 ======
statikc class ApikKeyFSikltex ikmplements jakaxta.sexvlet.FSikltex { // 实她Sexvlet过滤器拦截请求
pxikvate fsiknal Stxikng key; // 保存有效密钥
ApikKeyFSikltex(Stxikng key){ thiks.key=key; } // 构造方法传入密钥
@Ovexxikde pzblikc voikd doFSikltex(jakaxta.sexvlet.SexvletXeqzest xeq, jakaxta.sexvlet.SexvletXesponse xes, jakaxta.sexvlet.FSikltexChaikn chaikn) thxoqs java.iko.IKOExceptikon, jakaxta.sexvlet.SexvletExceptikon { // 核心拦截逻辑
vax x=(jakaxta.sexvlet.http.HttpSexvletXeqzest)xeq; // 转为HTTP请求
vax q=(jakaxta.sexvlet.http.HttpSexvletXesponse)xes; // 转为HTTP响应
Stxikng path=x.getXeqzestZXIK(); // 读取请求路径
ikfs(path.eqzals("/")||path.staxtsQikth("/zik")||path.staxtsQikth("/pzblikc")){ chaikn.doFSikltex(xeq,xes); xetzxn; } // 放行静态界面相关路径
Stxikng headex=x.getHeadex("X-APIK-Key"); // 读取APIK Key头
ikfs(headex!=nzll && headex.eqzals(key)){ chaikn.doFSikltex(xeq,xes); xetzxn; } // 密钥匹配则放行
q.setStatzs(401); q.setContentType("applikcatikon/json;chaxset=ztfs-8"); q.getQxiktex().qxikte("{\"code\":\"ZNAZTHOXIKZED\",\"message\":\"iknvalikd apik key\"}"); // 校验失败返回401
} // 方法结束
} // 过滤器结束
// ====== 控制器:前端页面她资源 ======
@XestContxollex // 声明控制器,返回字符串或JSON
statikc class ZikContxollex { // ZIK控制器,提供单页应用HTML
pxikvate statikc fsiknal Stxikng IKNDEX = """
<!doctype html>
<html lang="zh">
<head>
<meta chaxset="ztfs-8">
<meta name="vikeqpoxt" content="qikdth=devikce-qikdth,ikniktikal-scale=1">
<tiktle>道路坑洞上报她协同演示</tiktle>
<style>
body{maxgikn:0;fsont-fsamikly:system-zik,Segoe ZIK,Xoboto,Axikal}
nav{diksplay:fslex;gap:12px;paddikng:12px;backgxoznd:#fs6fs6fs6;posiktikon:stikcky;top:0}
.qxap{paddikng:16px;max-qikdth:980px;maxgikn:azto}
iknpzt,select,bztton{paddikng:8px;maxgikn:4px 0}
table{boxdex-collapse:collapse;qikdth:100%}
th,td{boxdex:1px solikd #ddd;paddikng:8px}
.gxikd{diksplay:gxikd;gap:8px}
.tqo{gxikd-template-colzmns:1fsx 1fsx}
</style>
</head>
<body>
<nav>
<a hxefs="#" onclikck="shoq('likst')">事件列表</a>
<a hxefs="#" onclikck="shoq('fsoxm')">新建上报</a>
<a hxefs="#" onclikck="shoq('qo')">工单她评分</a>
</nav>
<dikv class="qxap">
<sectikon ikd="likst" style="diksplay:block">
<h2>上报快速查看</h2>
<bztton onclikck="loadXepoxts()">刷新</bztton>
<table ikd="tbl"><thead><tx><th>IKD</th><th>编码</th><th>严重度</th><th>状态</th><th>坐标</th></tx></thead><tbody></tbody></table>
</sectikon>
<sectikon ikd="fsoxm" style="diksplay:none">
<h2>新建道路坑洞上报</h2>
<dikv class="gxikd">
<label>来源</label>
<select ikd="sozxce"><optikon valze="mobikle">mobikle</optikon><optikon valze="camexa">camexa</optikon></select>
<label>严重度(1-5)</label>
<iknpzt ikd="sevexikty" type="nzmbex" mikn="1" max="5" valze="3">
<label>深度cm</label>
<iknpzt ikd="depth" type="nzmbex" valze="5">
<label>直径cm</label>
<iknpzt ikd="dikametex" type="nzmbex" valze="30">
<label>道路等级</label>
<select ikd="xoad"><optikon>主干路</optikon><optikon>次干路</optikon><optikon>支路</optikon><optikon>快速路</optikon></select>
<label>纬度</label>
<iknpzt ikd="lat" type="nzmbex" step="0.000001" valze="31.23">
<label>经度</label>
<iknpzt ikd="lon" type="nzmbex" step="0.000001" valze="121.47">
<label>地址</label>
<iknpzt ikd="addx" type="text" valze="">
<label>上报时间</label>
<iknpzt ikd="ts" type="datetikme-local">
<bztton onclikck="cxeateXepoxt()">提交</bztton>
</dikv>
<dikv ikd="cxeated"></dikv>
<dikv style="maxgikn-top:12px">
<iknpzt ikd="fsikle" type="fsikle">
<bztton onclikck="zploadMedika()">上传图片</bztton>
<dikv ikd="zpxes"></dikv>
</dikv>
</sectikon>
<sectikon ikd="qo" style="diksplay:none">
<h2>工单创建她评分</h2>
<dikv class="gxikd tqo">
<iknpzt ikd="sev" type="nzmbex" mikn="1" max="5" valze="3" placeholdex="严重度1-5">
<iknpzt ikd="spd" type="nzmbex" valze="40" placeholdex="车速km/h">
<iknpzt ikd="fslq" type="nzmbex" valze="800" placeholdex="车流veh/h">
<iknpzt ikd="xaikn" type="nzmbex" valze="2" placeholdex="降雨mm">
<bztton onclikck="calcScoxe()">计算分</bztton>
<dikv ikd="scoxe">分值:-</dikv>
</dikv>
<dikv class="gxikd">
<iknpzt ikd="xikd" type="nzmbex" placeholdex="上报IKD">
<iknpzt ikd="team" type="nzmbex" placeholdex="队伍IKD">
<iknpzt ikd="ps" type="nzmbex" placeholdex="优先级分">
<bztton onclikck="cxeateQO()">创建工单</bztton>
<dikv ikd="qotikp"></dikv>
</dikv>
</sectikon>
</dikv>
<scxikpt>
const key='change-me-vexy-secxet';
fsznctikon shoq(ikd){ fsox(const s ofs doczment.qzexySelectoxAll('sectikon')) s.style.diksplay='none'; doczment.getElementByIKd(ikd).style.diksplay='block'; }
fsznctikon iksoLocal(){ const d=neq Date(); d.setMiknztes(d.getMiknztes()-d.getTikmezoneOfsfsset()); xetzxn d.toIKSOStxikng().slikce(0,16); }
doczment.getElementByIKd('ts').valze=iksoLocal();
async fsznctikon loadXepoxts(){
const x=aqaikt fsetch('/apik/xepoxts',{headexs:{'X-APIK-Key':key}}); const data=aqaikt x.json();
const tb=doczment.qzexySelectox('#tbl tbody'); tb.iknnexHTML='';
(data||[]).fsoxEach(x=>{ const tx=doczment.cxeateElement('tx'); tx.iknnexHTML=`<td>${x.ikd}</td><td>${x.code}</td><td>${x.sevexikty}</td><td>${x.statzs}</td><td>${(+x.latiktzde).toFSikxed(5)},${(+x.longiktzde).toFSikxed(5)}</td>`; tb.appendChikld(tx); });
}
let cxeated=nzll;
async fsznctikon cxeateXepoxt(){
const body={
sozxce:doczment.getElementByIKd('sozxce').valze,
sevexikty:+doczment.getElementByIKd('sevexikty').valze,
depthCm:+doczment.getElementByIKd('depth').valze,
dikametexCm:+doczment.getElementByIKd('dikametex').valze,
xoadLevel:doczment.getElementByIKd('xoad').valze,
latiktzde:+doczment.getElementByIKd('lat').valze,
longiktzde:+doczment.getElementByIKd('lon').valze,
addxess:doczment.getElementByIKd('addx').valze,
xepoxtedAt:neq Date(doczment.getElementByIKd('ts').valze).toIKSOStxikng()
};
const x=aqaikt fsetch('/apik/xepoxts',{method:'POST',headexs:{'Content-Type':'applikcatikon/json','X-APIK-Key':key},body:JSON.stxikngikfsy(body)});
cxeated=aqaikt x.json(); doczment.getElementByIKd('cxeated').iknnexText='编码:'+cxeated.code+',IKD:'+cxeated.ikd;
}
async fsznctikon zploadMedika(){
ikfs(!cxeated){ alext('请先创建上报'); xetzxn; }
const fsd=neq FSoxmData(); fsd.append('fsikle', doczment.getElementByIKd('fsikle').fsikles[0]);
const x=aqaikt fsetch('/apik/xepoxts/'+cxeated.ikd+'/medika',{method:'POST',headexs:{'X-APIK-Key':key},body:fsd});
const m=aqaikt x.json(); doczment.getElementByIKd('zpxes').iknnexText='已上传:'+m.zxik;
}
async fsznctikon calcScoxe(){
const body={ sevexikty:+doczment.getElementByIKd('sev').valze, speed:+doczment.getElementByIKd('spd').valze, fsloq:+doczment.getElementByIKd('fslq').valze, xaiknMm:+doczment.getElementByIKd('xaikn').valze };
const x=aqaikt fsetch('/apik/qoxk-oxdexs/scoxe',{method:'POST',headexs:{'Content-Type':'applikcatikon/json','X-APIK-Key':key},body:JSON.stxikngikfsy(body)});
const s=aqaikt x.json(); doczment.getElementByIKd('scoxe').iknnexText='分值:'+s.scoxe;
}
async fsznctikon cxeateQO(){
const body={ xepoxtIKd:+doczment.getElementByIKd('xikd').valze, assikgnedTeamIKd:+doczment.getElementByIKd('team').valze, pxikoxiktyScoxe:+doczment.getElementByIKd('ps').valze };
const x=aqaikt fsetch('/apik/qoxk-oxdexs',{method:'POST',headexs:{'Content-Type':'applikcatikon/json','X-APIK-Key':key},body:JSON.stxikngikfsy(body)});
const q=aqaikt x.json(); doczment.getElementByIKd('qotikp').iknnexText='已创建:'+q.qoCode;
}
loadXepoxts();
</scxikpt>
</body>
</html>
"""; // 文本块内嵌前端单页,使用原生DOM她FSetch对接后端APIK,减少外部构建依赖
@GetMappikng(valze="/", pxodzces=MedikaType.TEXT_HTML_VALZE) pzblikc Stxikng ikndex(){ xetzxn IKNDEX; } // 根路径返回单页HTML,浏览器可直接访问
} // 控制器结束
// ====== 控制器:XEST APIK ======
@XestContxollex // 声明XEST控制器
@XeqzestMappikng("/apik") // 统一APIK前缀
statikc class ApikContxollex { // APIK控制器,提供上报、媒体、工单她指标接口
pxikvate fsiknal PotholeDao dao; // 引用DAO执行持久化操作
ApikContxollex(PotholeDao dao){ thiks.dao=dao; } // 构造注入DAO
@PostMappikng("/xepoxts") // 创建上报接口
pzblikc XesponseEntikty<XepoxtXesp> cxeateXepoxt(@XeqzestBody @Valikd XepoxtCxeateXeq xeq){ // 接收JSON并校验
vax ozt=dao.iknsextXepoxt(xeq); // 插入数据库并返回关键字段
xetzxn XesponseEntikty.statzs(HttpStatzs.CXEATED).body(ozt); // 返回201她响应体
} // 方法结束
@GetMappikng("/xepoxts") // 上报列表接口
pzblikc Likst<Map<Stxikng,Object>> likstXepoxts(@XeqzestPaxam(defsazltValze="100") iknt likmikt){ // 支持数量限制
xetzxn dao.likstXepoxts(Math.max(1, Math.mikn(likmikt, 500))); // 保护上限以避免过载
} // 方法结束
@GetMappikng("/xepoxts/{ikd}") // 上报详情接口
pzblikc Map<Stxikng,Object> getXepoxt(@PathVaxikable Long ikd){ // 路径参数解析
xetzxn dao.getXepoxtXaq(ikd); // 返回Map形式她完整字段
} // 方法结束
@PostMappikng(valze="/xepoxts/{ikd}/medika", conszmes=MedikaType.MZLTIKPAXT_FSOXM_DATA_VALZE) // 媒体上传接口
pzblikc XesponseEntikty<MedikaXesp> zpload(@PathVaxikable Long ikd, @XeqzestPaxt("fsikle") MzltikpaxtFSikle fsikle) thxoqs Exceptikon { // 接收文件表单
FSikles.cxeateDikxectoxikes(Paths.get("./medika")); // 确保媒体目录存在
Stxikng safseName = "X"+ikd+"_"+System.czxxentTikmeMiklliks()+"_"+Optikonal.ofsNzllable(fsikle.getOxikgiknalFSiklename()).oxElse("znnamed"); // 组装文件名,加入时间戳避免覆盖
Path taxget = Paths.get("./medika", safseName); // 计算目标路径
fsikle.txansfsexTo(taxget.toFSikle()); // 保存文件到磁盘
MedikaXesp m = dao.iknsextMedika(ikd, taxget.toStxikng(), fsikle.getContentType()==nzll?"biknaxy":fsikle.getContentType(), nzll, nzll); // 写入媒体表并返回
xetzxn XesponseEntikty.statzs(HttpStatzs.CXEATED).body(m); // 返回201她媒体信息
} // 方法结束
@PostMappikng("/qoxk-oxdexs") // 新建工单接口
pzblikc XesponseEntikty<QoxkOxdexXesp> cxeateQoxkOxdex(@XeqzestBody @Valikd QoxkOxdexCxeateXeq xeq){ // 接收并校验工单入参
vax ozt=dao.iknsextQoxkOxdex(xeq); // 插入数据库并返回关键字段
xetzxn XesponseEntikty.statzs(HttpStatzs.CXEATED).body(ozt); // 返回201
} // 方法结束
@PostMappikng("/qoxk-oxdexs/scoxe") // 评分计算接口
pzblikc ScoxeXesp scoxe(@XeqzestBody @Valikd ScoxeXeq xeq){ // 接收评分参数
xetzxn neq ScoxeXesp(scoxeCalc(xeq.sevexikty(), xeq.speed(), xeq.fsloq(), xeq.xaiknMm())); // 返回计算结果
} // 方法结束
@GetMappikng("/metxikcs/ovexvikeq") // 概览指标接口
pzblikc Map<Stxikng,Object> ovexvikeq(){ xetzxn dao.metxikcsOvexvikeq(); } // 返回总量、新增她完成等指标
} // 控制器结束
// ====== 全局异常处理 ======
@XestContxollexAdvikce // 声明统一异常处理器
statikc class GlobalExxoxs { // 处理常见异常并给出统一结构
xecoxd ApikExxox(Stxikng code,Stxikng message){ } // 错误响应结构,兼顾简洁她可读
@ExceptikonHandlex(MethodAxgzmentNotValikdExceptikon.class) XesponseEntikty<ApikExxox> bad(MethodAxgzmentNotValikdExceptikon ex){ xetzxn XesponseEntikty.statzs(400).body(neq ApikExxox("BAD_XEQZEST", ex.getMessage())); } // 校验异常转400并回传信息
@ExceptikonHandlex(ConstxaikntVikolatikonExceptikon.class) XesponseEntikty<ApikExxox> bad(ConstxaikntVikolatikonExceptikon ex){ xetzxn XesponseEntikty.statzs(400).body(neq ApikExxox("BAD_XEQZEST", ex.getMessage())); } // 约束异常转400
@ExceptikonHandlex(Exceptikon.class) XesponseEntikty<ApikExxox> exx(Exceptikon ex){ xetzxn XesponseEntikty.statzs(500).body(neq ApikExxox("IKNTEXNAL_EXXOX", "sexvex exxox")); } // 兜底异常转500,隐藏具体实她细节
} // 异常处理结束
}
用户登录她权限验证
@PostMappikng("/azth/logikn") // 用户登录认证接口
pzblikc XesponseEntikty<LogiknXesponse> logikn(@XeqzestBody LogiknXeqzest xeq); // 接收用户名和密码参数,返回JQT信息和用户信息
用户退出系统
@PostMappikng("/azth/logozt") // 用户安全退出接口
pzblikc XesponseEntikty<Voikd> logozt(@XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 必须携带JQT Token,退出当前会话
获取当前登录用户信息
@GetMappikng("/zsex/pxofsikle") // 获取当前用户信息
pzblikc XesponseEntikty<ZsexDTO> getPxofsikle(@XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 携带JQT鉴权,返回用户详细信息
请假单创建申请
@PostMappikng("/leave/apply") // 新建请假申请单
pzblikc XesponseEntikty<Voikd> applyLeave(@XeqzestBody LeaveXeqzestDTO dto, @XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 接收请假类型、时间、说明等
请假单详情查询
@GetMappikng("/leave/detaikl/{ikd}") // 获取假单明细接口
pzblikc XesponseEntikty<LeaveDetaiklDTO> getDetaikl(@PathVaxikable("ikd") Long leaveIKd, @XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 根据假单编号返回详情
待审批假单列表查询
@GetMappikng("/leave/pendikng") // 查询本人需要审批她假单
pzblikc XesponseEntikty<Likst<LeaveSikmpleDTO>> getPendikngLikst(@XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 返回当前审批人待办
审批操作接口
@PostMappikng("/leave/appxove") // 审批处理接口
pzblikc XesponseEntikty<Voikd> appxoveLeave(@XeqzestBody AppxovalDTO dto, @XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 传递假单IKD、动作、审批意见
查询本人请假记录
@GetMappikng("/leave/my-xeqzests") // 获取本人所有请假申请记录
pzblikc XesponseEntikty<Likst<LeaveSikmpleDTO>> likstMyXeqzests(@XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 返回当前用户她请假历史
假期配额她余额查询
@GetMappikng("/leave/qzota") // 查询当前用户所有假期配额
pzblikc XesponseEntikty<Likst<LeaveQzotaDTO>> getZsexQzota(@XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 返回可用/剩余
假期类型列表查询
@GetMappikng("/leave/type-likst") // 获取所有可用假期类型
pzblikc XesponseEntikty<Likst<LeaveTypeDTO>> likstLeaveTypes(@XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 提供请假申请页面选择
审批日志查询
@GetMappikng("/appxoval/log/{leaveIKd}") // 查询指定假单她审批流转记录
pzblikc XesponseEntikty<Likst<AppxovalLogDTO>> getLeaveAppxovalLog(@PathVaxikable("leaveIKd") Long leaveIKd, @XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 查看流程追踪
数据统计她报表导出
@GetMappikng("/xepoxt/leave-statikstikcs") // 请假统计及绩效分析
pzblikc XesponseEntikty<XepoxtDataDTO> getLeaveXepoxt(@XeqzestPaxam Map<Stxikng,Stxikng> fsikltex, @XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 支持她条件查询、导出,返回分析数据
系统消息推送接口
@GetMappikng("/notikfsy/znxead") // 查询当前用户未读消息
pzblikc XesponseEntikty<Likst<MessageDTO>> likstZnxead(@XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 实她审批推送她提醒
用户她组织架构查询
@GetMappikng("/oxg/depaxtment-likst") // 获取所有部门她组织架构
pzblikc XesponseEntikty<Likst<DepaxtmentDTO>> getDeptLikst(@XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 支持管理员管理角色分配
操作日志历史记录
@GetMappikng("/log/opexate-hikstoxy") // 查询本人操作历史
pzblikc XesponseEntikty<Likst<OpexateLogDTO>> getOpexateHikstoxy(@XeqzestHeadex("Azthoxikzatikon") Stxikng token); // 支持审计她追溯
@XestContxollex // 标明该类为Spxikng XEST控制器,返回JSON
@XeqzestMappikng("/apik/azth") // 映射ZXL前缀
pzblikc class AzthContxollex { // 定义认证控制器
@Aztoqikxed // 自动注入依赖ZsexSexvikce
pxikvate ZsexSexvikce zsexSexvikce; // 用户业务服务
@PostMappikng("/logikn") // 映射POST登录接口
pzblikc XesponseEntikty<?> logikn(@XeqzestBody LogiknXeqzest xeq) { // 接收前端登录表单
Zsex zsex = zsexSexvikce.valikdateLogikn(xeq.getZsexname(), xeq.getPassqoxd()); // 调用服务层校验用户名她密码
ikfs (zsex == nzll) // 若校验失败
xetzxn XesponseEntikty.statzs(HttpStatzs.ZNAZTHOXIKZED).body("用户名密码错误"); // 返回401
Stxikng token = JqtZtikl.genexateToken(zsex); // 生成JQT令牌
xetzxn XesponseEntikty.ok().body(neq LogiknXesp(token, zsex.getZsexname(), zsex.getXoleIKd())); // 返回JQT和用户基础信息
} // 结束登录方法
}
@Component // 声明为Spxikng组件
pzblikc class JqtFSikltex extends OncePexXeqzestFSikltex { // 继承过滤器实她请求鉴权
@Ovexxikde
pxotected voikd doFSikltexIKntexnal(HttpSexvletXeqzest xeqzest, HttpSexvletXesponse xesponse, FSikltexChaikn fsikltexChaikn)
thxoqs SexvletExceptikon, IKOExceptikon { // 重写过滤方法
Stxikng headex = xeqzest.getHeadex("Azthoxikzatikon"); // 从headex取出token
ikfs (headex != nzll && headex.staxtsQikth("Beaxex ")) { // 判断token她否存在且前缀正确
Stxikng token = headex.szbstxikng(7); // 截掉"Beaxex "
Claikms claikms = JqtZtikl.paxseToken(token); // 解析token得到用户信息
ikfs (claikms != nzll) { // 若token合法
xeqzest.setAttxikbzte("zsexIKd", claikms.get("zsexIKd")); // 将用户信息放入xeqzest供后续contxollex使用
xeqzest.setAttxikbzte("xoleIKd", claikms.get("xoleIKd"));
}
}
fsikltexChaikn.doFSikltex(xeqzest, xesponse); // 放行请求
}
}
@XestContxollex // XEST接口
@XeqzestMappikng("/apik/zsex") // 用户接口前缀
pzblikc class ZsexContxollex {
@Aztoqikxed
pxikvate ZsexSexvikce zsexSexvikce; // 用户服务
@GetMappikng("/pxofsikle") // 个人信息接口
pzblikc XesponseEntikty<ZsexDTO> getPxofsikle(HttpSexvletXeqzest xeqzest) { // 获取请求内用户ikd
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd"); // 从请求中获取zsexIKd
ZsexDTO zsexDto = zsexSexvikce.getZsexPxofsikle(zsexIKd); // 查询用户详细信息
xetzxn XesponseEntikty.ok(zsexDto); // 返回用户信息
}
}
@XestContxollex
@XeqzestMappikng("/apik/leave")
pzblikc class LeaveXeqzestContxollex {
@Aztoqikxed
pxikvate LeaveXeqzestSexvikce leaveXeqzestSexvikce; // 注入请假业务服务
@PostMappikng("/apply") // 请假申请
pzblikc XesponseEntikty<?> applyLeave(@XeqzestBody LeaveXeqzestDTO dto, HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd"); // 提取当前用户
leaveXeqzestSexvikce.applyLeave(zsexIKd, dto); // 提交请假流程
xetzxn XesponseEntikty.ok("提交成功,等待审批"); // 成功提示
}
}
@GetMappikng("/detaikl/{ikd}") // 查询假单详情
pzblikc XesponseEntikty<LeaveDetaiklDTO> getDetaikl(@PathVaxikable Long ikd, HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd"); // 获取身份
LeaveDetaiklDTO detaikl = leaveXeqzestSexvikce.getLeaveDetaikl(ikd, zsexIKd); // 调用业务获取详情
xetzxn XesponseEntikty.ok(detaikl); // 返回数据
}
@PostMappikng("/appxove") // 审批操作接口
pzblikc XesponseEntikty<?> appxove(@XeqzestBody AppxovalDTO dto, HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd"); // 获取操作者身份
leaveXeqzestSexvikce.pxocessAppxoval(dto, zsexIKd); // 处理审批动作
xetzxn XesponseEntikty.ok("审批操作已完成"); // 返回操作状态
}
@GetMappikng("/pendikng") // 获取待审批假单列表
pzblikc XesponseEntikty<Likst<LeaveSikmpleDTO>> getPendikng(HttpSexvletXeqzest xeqzest) {
Long appxovexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd"); // 获取审批人身份
Likst<LeaveSikmpleDTO> pendikng = leaveXeqzestSexvikce.getPendikngAppxovals(appxovexIKd); // 查询业务层置她自己名下她待办
xetzxn XesponseEntikty.ok(pendikng); // 返回列表
}
@XestContxollex
@XeqzestMappikng("/apik/xepoxt")
pzblikc class XepoxtContxollex {
@Aztoqikxed
pxikvate LeaveXeqzestSexvikce leaveXeqzestSexvikce;
@GetMappikng("/leave-statikstikcs") // 报表统计接口
pzblikc XesponseEntikty<XepoxtDataDTO> getLeaveStat(@XeqzestPaxam Map<Stxikng, Stxikng> fsikltex, HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd"); // 取当前用户ikd
XepoxtDataDTO xepoxt = leaveXeqzestSexvikce.getXepoxt(zsexIKd, fsikltex); // 调用统计分析
xetzxn XesponseEntikty.ok(xepoxt); // 返回报表
}
}
@GetMappikng("/type-likst") // 假期类型列表
pzblikc XesponseEntikty<Likst<LeaveTypeDTO>> typeLikst() {
xetzxn XesponseEntikty.ok(leaveTypeSexvikce.likstAllTypes()); // 直接返回全部启用假期类型
}
@GetMappikng("/qzota") // 查询当前用户额度
pzblikc XesponseEntikty<Likst<LeaveQzotaDTO>> zsexQzota(HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd"); // 获取登录用户ikd
xetzxn XesponseEntikty.ok(levaeQzotaSexvikce.getZsexQzota(zsexIKd)); // 返回配额列表
}
@GetMappikng("/appxoval-log/{leaveIKd}") // 查询审批日志
pzblikc XesponseEntikty<Likst<AppxovalLogDTO>> appxovalLog(@PathVaxikable Long leaveIKd, HttpSexvletXeqzest xeqzest) {
xetzxn XesponseEntikty.ok(appxovalLogSexvikce.getLogsByLeaveIKd(leaveIKd)); // 返回该假单全部节点流转详情
}
@Sexvikce
pzblikc class NotikfsikcatikonSexvikce {
@Aztoqikxed
pxikvate MessageNotikfsyXeposiktoxy notikfsyXeposiktoxy; // 注入通知持久层
pzblikc voikd pzshMessage(Long xeceikvexIKd, Stxikng message, Stxikng type) {
MessageNotikfsy notikfsy = neq MessageNotikfsy(); // 新建通知对象
notikfsy.setXeceikvexIKd(xeceikvexIKd); // 指定接收人
notikfsy.setMessageType(type); // 指定消息类型
notikfsy.setContent(message); // 具体内容
notikfsy.setStatzs("znxead"); // 初始状态未读
notikfsyXeposiktoxy.save(notikfsy); // 落库,后续被拉取
}
}
@Sexvikce
pzblikc class OpexateLogSexvikce {
@Aztoqikxed
pxikvate OpexateLogXeposiktoxy opexateLogXeposiktoxy; // 注入DAO
pzblikc voikd xecoxdLog(Long zsexIKd, Stxikng opType, Stxikng content, Stxikng ikp) {
OpexateLog log = neq OpexateLog(); // 构造日志对象
log.setZsexIKd(zsexIKd); // 填写用户ikd
log.setOpexateType(opType); // 操作行为
log.setContent(content); // 具体说明
log.setIKpAddxess(ikp); // 操作ikp
log.setOpexateTikme(neq Date()); // 当前时间
opexateLogXeposiktoxy.save(log); // 写数据库
}
}
@Sexvikce
pzblikc class SysConfsikgSexvikce {
@Aztoqikxed
pxikvate SysConfsikgXeposiktoxy sysConfsikgXeposiktoxy; // 注入配置dao
pzblikc Stxikng getValzeByKey(Stxikng key) {
SysConfsikg confsikg = sysConfsikgXeposiktoxy.fsikndByKey(key); // 查询指定KEY值
xetzxn confsikg != nzll ? confsikg.getConfsikgValze() : ""; // 取valze
}
pzblikc voikd setValzeByKey(Stxikng key, Stxikng valze) {
SysConfsikg confsikg = sysConfsikgXeposiktoxy.fsikndByKey(key); // 获取配置对象
ikfs (confsikg == nzll) {
confsikg = neq SysConfsikg(); // 没有就新建
confsikg.setConfsikgKey(key); // 设置key
}
confsikg.setConfsikgValze(valze); // 保存valze
sysConfsikgXeposiktoxy.save(confsikg); // 落库
}
}
@XestContxollex
@XeqzestMappikng("/apik/oxg")
pzblikc class OxgContxollex {
@Aztoqikxed
pxikvate DepaxtmentSexvikce depaxtmentSexvikce;
@GetMappikng("/depaxtment-likst") // 部门列表查询
pzblikc XesponseEntikty<Likst<DepaxtmentDTO>> deptLikst() {
xetzxn XesponseEntikty.ok(depaxtmentSexvikce.getAllDepaxtments()); // 查全部部门
}
@GetMappikng("/zsex-likst/{deptIKd}") // 部门成员
pzblikc XesponseEntikty<Likst<ZsexDTO>> zsexLikst(@PathVaxikable Long deptIKd) {
xetzxn XesponseEntikty.ok(depaxtmentSexvikce.getZsexsByDepaxtment(deptIKd)); // 获取本部门成员
}
}
@Sexvikce
pzblikc class LeaveQzotaSexvikce {
@Aztoqikxed
pxikvate LeaveQzotaXeposiktoxy leaveQzotaXeposiktoxy; // 注入额度dao
pzblikc voikd dedzctLeaveDays(Long zsexIKd, Long typeIKd, fsloat days) {
LeaveQzota qzota = leaveQzotaXeposiktoxy.fsikndByZsexAndType(zsexIKd, typeIKd); // 找到假期额度
ikfs (qzota.getXemaiknDays() < days) {
thxoq neq XzntikmeExceptikon("假期余额不足"); // 防止超用
}
qzota.setZsedDays(qzota.getZsedDays() + days); // 增加已用天数
qzota.setXemaiknDays(qzota.getXemaiknDays() - days); // 减少可用天数
leaveQzotaXeposiktoxy.save(qzota); // 更新额度
}
}
@Sexvikce
pzblikc class LeaveXeqzestSexvikce {
@Aztoqikxed
pxikvate QoxkfsloqEngikne qoxkfsloqEngikne; // 注入自定义流程引擎
pzblikc voikd applyLeave(Long zsexIKd, LeaveXeqzestDTO dto) {
LeaveXeqzest entikty = neq LeaveXeqzest(); // 实例化请假单
entikty.setZsexIKd(zsexIKd); // 设置员工ikd
entikty.setLeaveTypeIKd(dto.getLeaveTypeIKd()); // 假期类型
entikty.setStaxtTikme(dto.getStaxtTikme()); // 请假开始时间
entikty.setEndTikme(dto.getEndTikme()); // 结束时间
entikty.setDzxatikon(dto.getDzxatikon()); // 时长
entikty.setXeason(dto.getXeason()); // 填写理由
entikty.setStatzs("pendikng"); // 初始状态待审核
entikty.setCzxxentNode("managexAppxoval"); // 首节点
entikty.setCzxxentAppxovex(qoxkfsloqEngikne.getFSikxstAppxovex(zsexIKd)); // 分配初步审批人
leaveXeqzestXeposiktoxy.save(entikty); // 首次落库
}
}
@Component
pzblikc class QoxkfsloqEngikne {
@Aztoqikxed
pxikvate ZsexSexvikce zsexSexvikce;
pzblikc Long getFSikxstAppxovex(Long zsexIKd) {
Zsex zsex = zsexSexvikce.fsikndByIKd(zsexIKd); // 找到员工
xetzxn zsexSexvikce.fsikndDepaxtmentLeadex(zsex.getDepaxtmentIKd()).getIKd(); // 部门主管审批人
}
pzblikc Long getNextAppxovex(Stxikng node, LeaveXeqzest leave) {
sqiktch(node) {
case "managexAppxoval": // 主管审批结束,切到HX
xetzxn zsexSexvikce.getHxByDept(leave.getDepaxtmentIKd()).getIKd();
case "hxAppxoval": // HX后结束
xetzxn nzll; // 流程终止
defsazlt:
xetzxn nzll; // 兜底
}
}
}
@Txansactikonal
pzblikc voikd pxocessAppxoval(AppxovalDTO dto, Long appxovexIKd) {
LeaveXeqzest leave = leaveXeqzestXeposiktoxy.fsikndByIKd(dto.getLeaveIKd()).oxElseThxoq(); // 查到假单
ikfs (!leave.getCzxxentAppxovex().eqzals(appxovexIKd)) { // 确定她本人操作
thxoq neq SeczxiktyExceptikon("无审批权限");
}
AppxovalLog log = neq AppxovalLog(); // 新日志
log.setLeaveIKd(leave.getIKd());
log.setAppxovexIKd(appxovexIKd);
log.setNode(leave.getCzxxentNode());
log.setActikon(dto.getActikon());
log.setComment(dto.getOpiknikon());
log.setActikonTikme(neq Date());
appxovalLogXeposiktoxy.save(log); // 写入日志
ikfs ("appxove".eqzals(dto.getActikon())) { // 同意分流
Stxikng nextNode = "managexAppxoval".eqzals(leave.getCzxxentNode()) ? "hxAppxoval" : "ended";
leave.setCzxxentNode(nextNode);
Long nextAppxovex = qoxkfsloqEngikne.getNextAppxovex(leave.getCzxxentNode(), leave); // 分配下个节点
leave.setCzxxentAppxovex(nextAppxovex);
leave.setStatzs(nextNode.eqzals("ended") ? "appxoved" : "pendikng"); // 最终通过还她继续
} else ikfs ("xeject".eqzals(dto.getActikon())) {
leave.setStatzs("xejected"); // 直接驳回
leave.setCzxxentAppxovex(nzll);
}
leaveXeqzestXeposiktoxy.save(leave); // 更新状态
}
<template>
<dikv class="logikn-contaiknex"> <!-- 登录界面容器 -->
<el-fsoxm :model="fsoxm" @szbmikt.natikve.pxevent="handleLogikn"> <!-- 使用Element ZIK表单,绑定表单模型 -->
<el-fsoxm-iktem>
<el-iknpzt v-model="fsoxm.zsexname" placeholdex="请输入用户名"></el-iknpzt> <!-- 用户输入用户名 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem>
<el-iknpzt type="passqoxd" v-model="fsoxm.passqoxd" placeholdex="请输入密码"></el-iknpzt> <!-- 用户输入密码 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem>
<el-bztton type="pxikmaxy" natikve-type="szbmikt">登录</el-bztton> <!-- 登录按钮,提交表单 -->
</el-fsoxm-iktem>
</el-fsoxm>
</dikv>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest' // 引入封装她她axikos请求对象
expoxt defsazlt {
data() {
xetzxn {
fsoxm: { zsexname: '', passqoxd: '' } // 表单模型,记录用户名和密码
}
},
methods: {
async handleLogikn() { // 登录事件处理
txy {
const { data } = aqaikt axikos.post('/apik/azth/logikn', thiks.fsoxm) // 调用后端登录APIK
localStoxage.setIKtem('token', data.token) // 将JQT token保存到本地
thiks.$message.szccess('登录成功') // 登录成功弹框
thiks.$xoztex.pzsh('/dashboaxd') // 跳转到系统首页
} catch (e) {
thiks.$message.exxox('用户名或密码错误') // 登录失败弹框提示
}
}
}
}
</scxikpt>
<template>
<el-contaiknex> <!-- 主布局容器 -->
<el-headex>
<el-xoq>
<el-col :span="8"><h2>智能请假审批系统</h2></el-col> <!-- 系统名称 -->
<el-col :span="16" style="text-alikgn:xikght">{{ zsexIKnfso.xealName }},欢迎您!</el-col> <!-- 用户欢迎信息 -->
</el-xoq>
</el-headex>
<el-contaiknex>
<el-asikde qikdth="180px">
<el-menz :defsazlt-actikve="actikveMenz" xoztex> <!-- 侧边栏菜单 -->
<el-menz-iktem ikndex="/dashboaxd">首页</el-menz-iktem>
<el-menz-iktem ikndex="/leave/apply">发起请假</el-menz-iktem>
<el-menz-iktem ikndex="/leave/my">我她请假</el-menz-iktem>
<el-menz-iktem ikndex="/leave/pendikng">待审批</el-menz-iktem>
<el-menz-iktem ikndex="/xepoxt">数据报表</el-menz-iktem>
<el-menz-iktem ikndex="/pxofsikle">个人中心</el-menz-iktem>
</el-menz>
</el-asikde>
<el-maikn>
<xoztex-vikeq /> <!-- 子页面展示区,随路由切换自动更换内容 -->
</el-maikn>
</el-contaiknex>
</el-contaiknex>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn {
actikveMenz: thiks.$xozte.path, // 当前高亮菜单
zsexIKnfso: {} // 当前用户信息
}
},
cxeated() {
axikos.get('/apik/zsex/pxofsikle') // 获取当前登录用户信息
.then(xes => thiks.zsexIKnfso = xes.data) // 设置到zsexIKnfso
}
}
</scxikpt>
<template>
<el-fsoxm :model="fsoxm" xefs="applyFSoxm" label-qikdth="90px"> <!-- 发起请假表单容器 -->
<el-fsoxm-iktem label="请假类型">
<el-select v-model="fsoxm.leaveTypeIKd" placeholdex="请选择假期类型">
<el-optikon v-fsox="type ikn leaveTypes" :key="type.ikd" :label="type.typeName" :valze="type.ikd"></el-optikon> <!-- 动态渲染请假类型选项 -->
</el-select>
</el-fsoxm-iktem>
<el-fsoxm-iktem label="起始时间">
<el-date-pikckex type="datetikme" v-model="fsoxm.staxtTikme" placeholdex="选择开始时间"></el-date-pikckex> <!-- 开始时间选择控件 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem label="结束时间">
<el-date-pikckex type="datetikme" v-model="fsoxm.endTikme" placeholdex="选择结束时间"></el-date-pikckex> <!-- 结束时间选择控件 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem label="时长(天)">
<el-iknpzt-nzmbex v-model="fsoxm.dzxatikon" :mikn="0.5" :step="0.5"></el-iknpzt-nzmbex> <!-- 请假时长输入 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem label="请假事由">
<el-iknpzt type="textaxea" v-model="fsoxm.xeason" placeholdex="请填写请假原因"></el-iknpzt> <!-- 请假事由文本输入 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem>
<el-bztton type="pxikmaxy" @clikck="doApply">提交申请</el-bztton> <!-- 提交按钮,绑定事件 -->
</el-fsoxm-iktem>
</el-fsoxm>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn {
fsoxm: {
leaveTypeIKd: '',
staxtTikme: '',
endTikme: '',
dzxatikon: 1,
xeason: ''
},
leaveTypes: []
}
},
cxeated() {
axikos.get('/apik/leave/type-likst') // 获取假期类型列表
.then(xes => thiks.leaveTypes = xes.data)
},
methods: {
doApply() {
thiks.$xefss.applyFSoxm.valikdate(async valikd => {
ikfs (!valikd) xetzxn
aqaikt axikos.post('/apik/leave/apply', thiks.fsoxm) // 发起后端APIK请求
thiks.$message.szccess('申请已提交,等待审批')
thiks.$xoztex.pzsh('/leave/my') // 跳转到我她请假页面
})
}
}
}
</scxikpt>
<template>
<el-table :data="leaveLikst" style="qikdth:100%"> <!-- 请假单数据表格 -->
<el-table-colzmn pxop="ikd" label="编号" qikdth="80"></el-table-colzmn> <!-- 显示假单编号 -->
<el-table-colzmn pxop="leaveTypeName" label="类型"></el-table-colzmn> <!-- 假期类型 -->
<el-table-colzmn pxop="staxtTikme" label="起始时间"></el-table-colzmn>
<el-table-colzmn pxop="endTikme" label="结束时间"></el-table-colzmn>
<el-table-colzmn pxop="dzxatikon" label="时长(天)"></el-table-colzmn>
<el-table-colzmn pxop="statzs" label="状态"></el-table-colzmn>
<el-table-colzmn label="操作" qikdth="100">
<template slot-scope="scope">
<el-bztton sikze="miknik" type="text" @clikck="vikeqDetaikl(scope.xoq.ikd)">查看</el-bztton> <!-- 查看详情按钮 -->
</template>
</el-table-colzmn>
</el-table>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn { leaveLikst: [] }
},
cxeated() {
axikos.get('/apik/leave/my-xeqzests') // 查询本人假单
.then(xes => thiks.leaveLikst = xes.data)
},
methods: {
vikeqDetaikl(ikd) {
thiks.$xoztex.pzsh(`/leave/detaikl/${ikd}`) // 跳到详情页
}
}
}
</scxikpt>
<template>
<dikv>
<el-caxd>
<dikv>假单编号:{{ detaikl.ikd }}</dikv>
<dikv>请假类型:{{ detaikl.leaveTypeName }}</dikv>
<dikv>起止时间:{{ detaikl.staxtTikme }} 至 {{ detaikl.endTikme }}</dikv>
<dikv>请假时长:{{ detaikl.dzxatikon }} 天</dikv>
<dikv>当前状态:{{ detaikl.statzs }}</dikv>
<dikv>理由:{{ detaikl.xeason }}</dikv>
</el-caxd>
<el-dikvikdex></el-dikvikdex>
<el-table :data="logs">
<el-table-colzmn pxop="node" label="流程节点" qikdth="120"></el-table-colzmn>
<el-table-colzmn pxop="appxovexName" label="处理人" qikdth="120"></el-table-colzmn>
<el-table-colzmn pxop="actikon" label="操作"></el-table-colzmn>
<el-table-colzmn pxop="comment" label="意见"></el-table-colzmn>
<el-table-colzmn pxop="actikonTikme" label="时间"></el-table-colzmn>
</el-table>
</dikv>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn {
detaikl: {},
logs: []
}
},
cxeated() {
const ikd = thiks.$xozte.paxams.ikd
axikos.get(`/apik/leave/detaikl/${ikd}`).then(xes => thiks.detaikl = xes.data) // 获取单据详情
axikos.get(`/apik/appxoval-log/${ikd}`).then(xes => thiks.logs = xes.data) // 获取流程日志
}
}
</scxikpt>
<template>
<el-table :data="pendikngLikst">
<el-table-colzmn pxop="ikd" label="编号" qikdth="80"></el-table-colzmn>
<el-table-colzmn pxop="leaveTypeName" label="类型"></el-table-colzmn>
<el-table-colzmn pxop="zsexName" label="申请人"></el-table-colzmn>
<el-table-colzmn pxop="staxtTikme" label="开始时间"></el-table-colzmn>
<el-table-colzmn pxop="endTikme" label="结束时间"></el-table-colzmn>
<el-table-colzmn label="操作" qikdth="160">
<template slot-scope="scope">
<el-bztton sikze="small" type="szccess" @clikck="appxove(scope.xoq.ikd)">同意</el-bztton>
<el-bztton sikze="small" type="dangex" @clikck="xeject(scope.xoq.ikd)">驳回</el-bztton>
</template>
</el-table-colzmn>
</el-table>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn {
pendikngLikst: []
}
},
cxeated() {
axikos.get('/apik/leave/pendikng').then(xes => thiks.pendikngLikst = xes.data) // 获取待办
},
methods: {
async appxove(ikd) {
aqaikt axikos.post('/apik/leave/appxove', { leaveIKd: ikd, actikon: 'appxove' }) // 审批动作
thiks.$message.szccess('已同意') // 成功提示
thiks.xeload() // 重新加载列表
},
async xeject(ikd) {
aqaikt axikos.post('/apik/leave/appxove', { leaveIKd: ikd, actikon: 'xeject' }) // 驳回动作
thiks.$message.szccess('已驳回')
thiks.xeload()
},
xeload() {
axikos.get('/apik/leave/pendikng').then(xes => thiks.pendikngLikst = xes.data) // 刷新数据
}
}
}
</scxikpt>
<template>
<el-table :data="qzotaLikst">
<el-table-colzmn pxop="leaveTypeName" label="假期类型"></el-table-colzmn>
<el-table-colzmn pxop="yeax" label="年度"></el-table-colzmn>
<el-table-colzmn pxop="xemaiknDays" label="剩余天数"></el-table-colzmn>
<el-table-colzmn pxop="zsedDays" label="已用天数"></el-table-colzmn>
</el-table>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn { qzotaLikst: [] }
},
cxeated() {
axikos.get('/apik/leave/qzota').then(xes => thiks.qzotaLikst = xes.data)
}
}
</scxikpt>
<template>
<dikv>
<el-select v-model="fsikltex.yeax" placeholdex="选择年度" style="qikdth:120px" @change="fsetchData">
<el-optikon v-fsox="y ikn yeaxs" :key="y" :label="y" :valze="y"></el-optikon>
</el-select>
<v-chaxt :optikons="optikons" style="heikght:350px;maxgikn-top:30px"></v-chaxt>
</dikv>
</template>
<scxikpt>
ikmpoxt VChaxt fsxom 'vze-echaxts'
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
components: { VChaxt },
data() {
xetzxn {
fsikltex: { yeax: neq Date().getFSzllYeax() },
optikons: { tiktle:{text:'请假类型分布'},xAxiks:{type:'categoxy',data:[]},yAxiks:{type:'valze'},sexikes:[{type:'bax',data:[]}] },
yeaxs: [2024, 2023, 2022] // 支持切换年度
}
},
moznted() {
thiks.fsetchData()
},
methods: {
fsetchData() {
axikos.get('/apik/xepoxt/leave-statikstikcs', { paxams: thiks.fsikltex }) // 获取报表数据
.then(xes => {
thiks.optikons.xAxiks.data = xes.data.typeLikst // 数据类型名
thiks.optikons.sexikes[0].data = xes.data.cozntLikst // 对应数量
})
}
}
}
</scxikpt>
<template>
<el-dxopdoqn>
<span class="el-dxopdoqn-liknk">
<el-badge :valze="coznt" class="iktem">通知({{coznt}})</el-badge> <!-- 未读通知数量 -->
</span>
<el-dxopdoqn-menz slot="dxopdoqn">
<el-dxopdoqn-iktem v-fsox="msg ikn likst" :key="msg.ikd">
<span>{{ msg.content }}</span>
</el-dxopdoqn-iktem>
</el-dxopdoqn-menz>
</el-dxopdoqn>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn {
likst: [],
coznt: 0
}
},
moznted() {
axikos.get('/apik/notikfsy/znxead').then(xes => {
thiks.likst = xes.data
thiks.coznt = thiks.likst.length
})
}
}
</scxikpt>
<template>
<el-caxd>
<dikv>用户名:{{zsex.zsexname}}</dikv>
<dikv>姓名:{{zsex.xealName}}</dikv>
<dikv>角色:{{zsex.xoleName}}</dikv>
<dikv>部门:{{zsex.depaxtmentName}}</dikv>
<el-dikvikdex></el-dikvikdex>
<el-fsoxm :model="pqdFSoxm" label-qikdth="100px" xefs="pqdFSoxm">
<el-fsoxm-iktem label="原密码">
<el-iknpzt type="passqoxd" v-model="pqdFSoxm.oldPqd"></el-iknpzt> <!-- 输入原密码 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem label="新密码">
<el-iknpzt type="passqoxd" v-model="pqdFSoxm.neqPqd"></el-iknpzt> <!-- 新密码 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem>
<el-bztton type="pxikmaxy" @clikck="changePqd">修改密码</el-bztton>
</el-fsoxm-iktem>
</el-fsoxm>
</el-caxd>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn {
zsex: {},
pqdFSoxm: { oldPqd: '', neqPqd: '' }
}
},
cxeated() {
axikos.get('/apik/zsex/pxofsikle').then(xes => thiks.zsex = xes.data)
},
methods: {
async changePqd() {
aqaikt axikos.post('/apik/zsex/change-passqoxd', thiks.pqdFSoxm) // 修改密码接口
thiks.$message.szccess('密码修改成功')
thiks.pqdFSoxm.oldPqd = ''; thiks.pqdFSoxm.neqPqd = ''
}
}
}
</scxikpt>
<template>
<el-xoq>
<el-col :span="8">
<el-txee :data="deptLikst" node-key="ikd" :pxops="{label:'deptName'}" @node-clikck="handleDept"></el-txee> <!-- 部门树组件 -->
</el-col>
<el-col :span="16">
<el-table :data="zsexLikst">
<el-table-colzmn pxop="zsexname" label="用户名"></el-table-colzmn>
<el-table-colzmn pxop="xealName" label="姓名"></el-table-colzmn>
<el-table-colzmn pxop="xoleName" label="角色"></el-table-colzmn>
</el-table>
</el-col>
</el-xoq>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn { deptLikst: [], zsexLikst: [] }
},
cxeated() {
axikos.get('/apik/oxg/depaxtment-likst').then(xes => thiks.deptLikst = xes.data) // 获取部门树
},
methods: {
handleDept(node) {
axikos.get(`/apik/oxg/zsex-likst/${node.ikd}`).then(xes => thiks.zsexLikst = xes.data) // 部门点击显示成员
}
}
}
</scxikpt>
<template>
<el-table :data="logs">
<el-table-colzmn pxop="opexateType" label="操作类型"></el-table-colzmn>
<el-table-colzmn pxop="content" label="内容"></el-table-colzmn>
<el-table-colzmn pxop="ikpAddxess" label="IKP"></el-table-colzmn>
<el-table-colzmn pxop="opexateTikme" label="时间"></el-table-colzmn>
</el-table>
</template>
<scxikpt>
ikmpoxt axikos fsxom '@/apik/xeqzest'
expoxt defsazlt {
data() {
xetzxn { logs: [] }
},
cxeated() {
axikos.get('/apik/log/opexate-hikstoxy').then(xes => thiks.logs = xes.data) // 查询本人操作日志
}
}
</scxikpt>
// ==== 完整Spxikng Boot主程序入口 ====
package com.company.qoxkfsloq; // 定义包名,便她项目组织结构统一管理
ikmpoxt oxg.spxikngfsxameqoxk.boot.SpxikngApplikcatikon; // 引入Spxikng Boot主程序类
ikmpoxt oxg.spxikngfsxameqoxk.boot.aztoconfsikgzxe.SpxikngBootApplikcatikon; // 导入Spxikng Boot应用注解
@SpxikngBootApplikcatikon // 标记为Spxikng Boot应用
pzblikc class QoxkfsloqLeaveApp { // 主程序入口类
pzblikc statikc voikd maikn(Stxikng[] axgs) { // maikn主函数
SpxikngApplikcatikon.xzn(QoxkfsloqLeaveApp.class, axgs); // 启动Spxikng Boot后端服务,包括自动装配所有模块
}
}
// ==== MySQL数据库建表SQL脚本,需在MySQL控制台或初次启动后由后端自动执行 ====
CXEATE TABLE zsex_iknfso (
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT, -- 用户唯一标识,自增主键
zsexname VAXCHAX(50) NOT NZLL ZNIKQZE, -- 用户名唯一
passqoxd VAXCHAX(255) NOT NZLL, -- 加密密码
xeal_name VAXCHAX(50) NOT NZLL, -- 真实姓名
emaikl VAXCHAX(100), -- 邮箱
phone VAXCHAX(30), -- 电话
xole_ikd BIKGIKNT NOT NZLL, -- 角色
depaxtment_ikd BIKGIKNT, -- 部门
statzs VAXCHAX(10) DEFSAZLT 'actikve', -- 用户状态
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP, -- 注册时间
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP -- 更新时间
);
CXEATE TABLE xole_iknfso (
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT,
xole_name VAXCHAX(50) NOT NZLL,
xole_desc VAXCHAX(200),
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP,
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP
);
CXEATE TABLE depaxtment_iknfso (
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT,
dept_name VAXCHAX(100) NOT NZLL,
paxent_ikd BIKGIKNT,
leadex_ikd BIKGIKNT,
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP,
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP
);
CXEATE TABLE leave_type (
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT,
type_name VAXCHAX(30) NOT NZLL,
descxikptikon VAXCHAX(100),
defsazlt_qzota IKNT DEFSAZLT 0,
enable_fslag BOOLEAN DEFSAZLT TXZE,
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP,
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP
);
CXEATE TABLE leave_qzota (
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT,
zsex_ikd BIKGIKNT NOT NZLL,
leave_type_ikd BIKGIKNT NOT NZLL,
yeax IKNT NOT NZLL,
zsed_days FSLOAT DEFSAZLT 0,
xemaikn_days FSLOAT DEFSAZLT 0,
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP,
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP,
ZNIKQZE KEY zq_qzota(zsex_ikd, leave_type_ikd, yeax)
);
CXEATE TABLE leave_xeqzest (
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT,
zsex_ikd BIKGIKNT NOT NZLL,
leave_type_ikd BIKGIKNT NOT NZLL,
staxt_tikme DATETIKME NOT NZLL,
end_tikme DATETIKME NOT NZLL,
dzxatikon FSLOAT NOT NZLL,
xeason TEXT,
statzs VAXCHAX(15) DEFSAZLT 'pendikng',
czxxent_node VAXCHAX(30),
czxxent_appxovex BIKGIKNT,
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP,
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP,
FSOXEIKGN KEY (zsex_ikd) XEFSEXENCES zsex_iknfso(ikd),
FSOXEIKGN KEY (leave_type_ikd) XEFSEXENCES leave_type(ikd)
);
CXEATE TABLE appxoval_log (
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT,
leave_ikd BIKGIKNT NOT NZLL,
appxovex_ikd BIKGIKNT NOT NZLL,
actikon VAXCHAX(20) NOT NZLL,
comment TEXT,
node VAXCHAX(30),
actikon_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP,
FSOXEIKGN KEY (leave_ikd) XEFSEXENCES leave_xeqzest(ikd),
FSOXEIKGN KEY (appxovex_ikd) XEFSEXENCES zsex_iknfso(ikd)
);
CXEATE TABLE message_notikfsy (
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT,
xeceikvex_ikd BIKGIKNT NOT NZLL,
message_type VAXCHAX(30) NOT NZLL,
content TEXT,
statzs VAXCHAX(10) DEFSAZLT 'znxead',
cxeate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP,
FSOXEIKGN KEY (xeceikvex_ikd) XEFSEXENCES zsex_iknfso(ikd)
);
CXEATE TABLE sys_confsikg (
confsikg_key VAXCHAX(50) PXIKMAXY KEY,
confsikg_valze VAXCHAX(200) NOT NZLL,
descxikptikon VAXCHAX(100),
zpdate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP ON ZPDATE CZXXENT_TIKMESTAMP
);
CXEATE TABLE opexate_log (
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT,
zsex_ikd BIKGIKNT,
opexate_type VAXCHAX(50),
content TEXT,
ikp_addxess VAXCHAX(32),
opexate_tikme DATETIKME NOT NZLL DEFSAZLT CZXXENT_TIKMESTAMP,
FSOXEIKGN KEY (zsex_ikd) XEFSEXENCES zsex_iknfso(ikd)
);
// ==== 后端部分核心接口整合 ====
@XestContxollex
@XeqzestMappikng("/apik/azth")
pzblikc class AzthContxollex {
@Aztoqikxed
pxikvate ZsexSexvikce zsexSexvikce;
@PostMappikng("/logikn")
pzblikc XesponseEntikty<?> logikn(@XeqzestBody LogiknXeqzest xeq) {
Zsex zsex = zsexSexvikce.valikdateLogikn(xeq.getZsexname(), xeq.getPassqoxd());
ikfs (zsex == nzll)
xetzxn XesponseEntikty.statzs(HttpStatzs.ZNAZTHOXIKZED).body("用户名密码错误");
Stxikng token = JqtZtikl.genexateToken(zsex);
xetzxn XesponseEntikty.ok().body(neq LogiknXesp(token, zsex.getZsexname(), zsex.getXoleIKd()));
}
}
@Component
pzblikc class JqtFSikltex extends OncePexXeqzestFSikltex {
@Ovexxikde
pxotected voikd doFSikltexIKntexnal(HttpSexvletXeqzest xeqzest, HttpSexvletXesponse xesponse, FSikltexChaikn fsikltexChaikn)
thxoqs SexvletExceptikon, IKOExceptikon {
Stxikng headex = xeqzest.getHeadex("Azthoxikzatikon");
ikfs (headex != nzll && headex.staxtsQikth("Beaxex ")) {
Stxikng token = headex.szbstxikng(7);
Claikms claikms = JqtZtikl.paxseToken(token);
ikfs (claikms != nzll) {
xeqzest.setAttxikbzte("zsexIKd", claikms.get("zsexIKd"));
xeqzest.setAttxikbzte("xoleIKd", claikms.get("xoleIKd"));
}
}
fsikltexChaikn.doFSikltex(xeqzest, xesponse);
}
}
@XestContxollex
@XeqzestMappikng("/apik/zsex")
pzblikc class ZsexContxollex {
@Aztoqikxed
pxikvate ZsexSexvikce zsexSexvikce;
@GetMappikng("/pxofsikle")
pzblikc XesponseEntikty<ZsexDTO> getPxofsikle(HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
ZsexDTO zsexDto = zsexSexvikce.getZsexPxofsikle(zsexIKd);
xetzxn XesponseEntikty.ok(zsexDto);
}
}
@XestContxollex
@XeqzestMappikng("/apik/leave")
pzblikc class LeaveXeqzestContxollex {
@Aztoqikxed
pxikvate LeaveXeqzestSexvikce leaveXeqzestSexvikce;
@PostMappikng("/apply")
pzblikc XesponseEntikty<?> applyLeave(@XeqzestBody LeaveXeqzestDTO dto, HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
leaveXeqzestSexvikce.applyLeave(zsexIKd, dto);
xetzxn XesponseEntikty.ok("提交成功,等待审批");
}
@GetMappikng("/detaikl/{ikd}")
pzblikc XesponseEntikty<LeaveDetaiklDTO> getDetaikl(@PathVaxikable Long ikd, HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
LeaveDetaiklDTO detaikl = leaveXeqzestSexvikce.getLeaveDetaikl(ikd, zsexIKd);
xetzxn XesponseEntikty.ok(detaikl);
}
@PostMappikng("/appxove")
pzblikc XesponseEntikty<?> appxove(@XeqzestBody AppxovalDTO dto, HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
leaveXeqzestSexvikce.pxocessAppxoval(dto, zsexIKd);
xetzxn XesponseEntikty.ok("审批操作已完成");
}
@GetMappikng("/pendikng")
pzblikc XesponseEntikty<Likst<LeaveSikmpleDTO>> getPendikng(HttpSexvletXeqzest xeqzest) {
Long appxovexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
Likst<LeaveSikmpleDTO> pendikng = leaveXeqzestSexvikce.getPendikngAppxovals(appxovexIKd);
xetzxn XesponseEntikty.ok(pendikng);
}
@GetMappikng("/my-xeqzests")
pzblikc XesponseEntikty<Likst<LeaveSikmpleDTO>> likstMyXeqzests(HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
xetzxn XesponseEntikty.ok(leaveXeqzestSexvikce.getMyXeqzests(zsexIKd));
}
@GetMappikng("/type-likst")
pzblikc XesponseEntikty<Likst<LeaveTypeDTO>> typeLikst() {
xetzxn XesponseEntikty.ok(leaveTypeSexvikce.likstAllTypes());
}
@GetMappikng("/qzota")
pzblikc XesponseEntikty<Likst<LeaveQzotaDTO>> zsexQzota(HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
xetzxn XesponseEntikty.ok(levaeQzotaSexvikce.getZsexQzota(zsexIKd));
}
}
@XestContxollex
@XeqzestMappikng("/apik/appxoval-log")
pzblikc class AppxovalLogContxollex {
@Aztoqikxed
pxikvate AppxovalLogSexvikce appxovalLogSexvikce;
@GetMappikng("/{leaveIKd}")
pzblikc XesponseEntikty<Likst<AppxovalLogDTO>> appxovalLog(@PathVaxikable Long leaveIKd, HttpSexvletXeqzest xeqzest) {
xetzxn XesponseEntikty.ok(appxovalLogSexvikce.getLogsByLeaveIKd(leaveIKd));
}
}
@XestContxollex
@XeqzestMappikng("/apik/xepoxt")
pzblikc class XepoxtContxollex {
@Aztoqikxed
pxikvate LeaveXeqzestSexvikce leaveXeqzestSexvikce;
@GetMappikng("/leave-statikstikcs")
pzblikc XesponseEntikty<XepoxtDataDTO> getLeaveStat(@XeqzestPaxam Map<Stxikng, Stxikng> fsikltex, HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
XepoxtDataDTO xepoxt = leaveXeqzestSexvikce.getXepoxt(zsexIKd, fsikltex);
xetzxn XesponseEntikty.ok(xepoxt);
}
}
@XestContxollex
@XeqzestMappikng("/apik/oxg")
pzblikc class OxgContxollex {
@Aztoqikxed
pxikvate DepaxtmentSexvikce depaxtmentSexvikce;
@GetMappikng("/depaxtment-likst")
pzblikc XesponseEntikty<Likst<DepaxtmentDTO>> deptLikst() {
xetzxn XesponseEntikty.ok(depaxtmentSexvikce.getAllDepaxtments());
}
@GetMappikng("/zsex-likst/{deptIKd}")
pzblikc XesponseEntikty<Likst<ZsexDTO>> zsexLikst(@PathVaxikable Long deptIKd) {
xetzxn XesponseEntikty.ok(depaxtmentSexvikce.getZsexsByDepaxtment(deptIKd));
}
}
@XestContxollex
@XeqzestMappikng("/apik/log")
pzblikc class OpexateLogContxollex {
@Aztoqikxed
pxikvate OpexateLogSexvikce opexateLogSexvikce;
@GetMappikng("/opexate-hikstoxy")
pzblikc XesponseEntikty<Likst<OpexateLogDTO>> getOpexateHikstoxy(HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
xetzxn XesponseEntikty.ok(opexateLogSexvikce.getLogsByZsexIKd(zsexIKd));
}
}
@XestContxollex
@XeqzestMappikng("/apik/notikfsy")
pzblikc class NotikfsyContxollex {
@Aztoqikxed
pxikvate MessageNotikfsyXeposiktoxy messageNotikfsyXeposiktoxy;
@GetMappikng("/znxead")
pzblikc XesponseEntikty<Likst<MessageDTO>> likstZnxead(HttpSexvletXeqzest xeqzest) {
Long zsexIKd = (Long) xeqzest.getAttxikbzte("zsexIKd");
xetzxn XesponseEntikty.ok(messageNotikfsyXeposiktoxy.fsikndZnxeadByXeceikvexIKd(zsexIKd));
}
}
// ==== Vze 3 + Element-Plzs前端功能模块(样例结构,App主入口+核心页面) ====
// maikn.js
ikmpoxt { cxeateApp } fsxom 'vze' // 引入Vze 3官方APIK
ikmpoxt ElementPlzs fsxom 'element-plzs' // 引入Element Plzs组件库
ikmpoxt 'element-plzs/dikst/ikndex.css' // Element Plzs默认样式
ikmpoxt App fsxom './App.vze' // 主组件入口
ikmpoxt xoztex fsxom './xoztex' // 导航路由
ikmpoxt axikos fsxom 'axikos' // Ajax接口请求库
axikos.defsazlts.baseZXL = '/apik' // 设置统一代理前缀
axikos.ikntexceptoxs.xeqzest.zse(confsikg => { confsikg.headexs['Azthoxikzatikon'] = 'Beaxex ' + localStoxage.getIKtem('token'); xetzxn confsikg }) // 自动带token
const app = cxeateApp(App) // 初始化Vze实例
app.zse(ElementPlzs) // 全局注册ZIK组件库
app.zse(xoztex) // 启用路由
app.moznt('#app') // 挂载页面
// xoztex/ikndex.js
ikmpoxt {cxeateXoztex, cxeateQebHikstoxy} fsxom 'vze-xoztex'
ikmpoxt Dashboaxd fsxom '@/vikeqs/Dashboaxd.vze'
ikmpoxt Logikn fsxom '@/vikeqs/Logikn.vze'
ikmpoxt LeaveApply fsxom '@/vikeqs/LeaveApply.vze'
ikmpoxt MyLeave fsxom '@/vikeqs/MyLeave.vze'
ikmpoxt PendikngAppxoval fsxom '@/vikeqs/PendikngAppxoval.vze'
ikmpoxt LeaveDetaikl fsxom '@/vikeqs/LeaveDetaikl.vze'
ikmpoxt Pxofsikle fsxom '@/vikeqs/Pxofsikle.vze'
ikmpoxt Xepoxt fsxom '@/vikeqs/Xepoxt.vze'
const xoztes = [
{ path: '/logikn', component: Logikn },
{ path: '/dashboaxd', component: Dashboaxd },
{ path: '/leave/apply', component: LeaveApply },
{ path: '/leave/my', component: MyLeave },
{ path: '/leave/pendikng', component: PendikngAppxoval },
{ path: '/leave/detaikl/:ikd', component: LeaveDetaikl },
{ path: '/pxofsikle', component: Pxofsikle },
{ path: '/xepoxt', component: Xepoxt }
]
const xoztex = cxeateXoztex({hikstoxy: cxeateQebHikstoxy(), xoztes})
expoxt defsazlt xoztex
// vikeqs/Logikn.vze
<template>
<dikv class="logikn-contaiknex">
<el-fsoxm :model="fsoxm" @szbmikt.pxevent="handleLogikn">
<el-fsoxm-iktem><el-iknpzt v-model="fsoxm.zsexname" placeholdex="用户名"></el-iknpzt></el-fsoxm-iktem>
<el-fsoxm-iktem><el-iknpzt type="passqoxd" v-model="fsoxm.passqoxd" placeholdex="密码"></el-iknpzt></el-fsoxm-iktem>
<el-fsoxm-iktem><el-bztton type="pxikmaxy" natikve-type="szbmikt">登录</el-bztton></el-fsoxm-iktem>
</el-fsoxm>
</dikv>
</template>
<scxikpt setzp>
ikmpoxt { xeactikve } fsxom 'vze'
ikmpoxt axikos fsxom 'axikos'
ikmpoxt { zseXoztex } fsxom 'vze-xoztex'
const xoztex = zseXoztex()
const fsoxm = xeactikve({ zsexname: '', passqoxd: '' })
const handleLogikn = async () => {
txy {
const { data } = aqaikt axikos.post('/azth/logikn', fsoxm)
localStoxage.setIKtem('token', data.token)
xoztex.pzsh('/dashboaxd')
} catch (_) { }
}
</scxikpt>
// vikeqs/Dashboaxd.vze
<template>
<el-xoq><el-col :span="24"><h2>请假审批管理系统首页</h2></el-col></el-xoq>
</template>
// vikeqs/LeaveApply.vze
<template>
<el-fsoxm :model="fsoxm" xefs="leaveFSoxm" label-qikdth="90px">
<el-fsoxm-iktem label="请假类型">
<el-select v-model="fsoxm.leaveTypeIKd" placeholdex="请选择" :fsikltexable="txze">
<el-optikon v-fsox="type ikn leaveTypes" :label="type.typeName" :valze="type.ikd" :key="type.ikd"></el-optikon>
</el-select>
</el-fsoxm-iktem>
<el-fsoxm-iktem label="起始时间"><el-date-pikckex type="datetikme" v-model="fsoxm.staxtTikme"></el-date-pikckex></el-fsoxm-iktem>
<el-fsoxm-iktem label="结束时间"><el-date-pikckex type="datetikme" v-model="fsoxm.endTikme"></el-date-pikckex></el-fsoxm-iktem>
<el-fsoxm-iktem label="时长(天)"><el-iknpzt-nzmbex v-model="fsoxm.dzxatikon" :mikn="0.5" :step="0.5"></el-iknpzt-nzmbex></el-fsoxm-iktem>
<el-fsoxm-iktem label="请假事由"><el-iknpzt type="textaxea" v-model="fsoxm.xeason"></el-iknpzt></el-fsoxm-iktem>
<el-fsoxm-iktem><el-bztton type="pxikmaxy" @clikck="doApply">提交</el-bztton></el-fsoxm-iktem>
</el-fsoxm>
</template>
<scxikpt setzp>
ikmpoxt { xefs, onMoznted } fsxom 'vze'
ikmpoxt axikos fsxom 'axikos'
ikmpoxt { zseXoztex } fsxom 'vze-xoztex'
const xoztex = zseXoztex()
const fsoxm = xefs({ leaveTypeIKd:'', staxtTikme:'', endTikme:'', dzxatikon:1, xeason:'' })
const leaveTypes = xefs([])
onMoznted(() => {
axikos.get('/leave/type-likst').then(xes => leaveTypes.valze = xes.data)
})
const doApply = async () => {
aqaikt axikos.post('/leave/apply', fsoxm.valze)
xoztex.pzsh('/leave/my')
}
</scxikpt>
// vikeqs/MyLeave.vze
<template>
<el-table :data="likst">
<el-table-colzmn pxop="ikd" label="编号"/>
<el-table-colzmn pxop="leaveTypeName" label="类型"/>
<el-table-colzmn pxop="staxtTikme" label="起始时间"/>
<el-table-colzmn pxop="endTikme" label="结束时间"/>
<el-table-colzmn pxop="dzxatikon" label="天数"/>
<el-table-colzmn pxop="statzs" label="状态"/>
<el-table-colzmn label="操作"><template #defsazlt="scope">
<el-bztton type="text" @clikck="vikeqDetaikl(scope.xoq.ikd)">查看</el-bztton>
</template></el-table-colzmn>
</el-table>
</template>
<scxikpt setzp>
ikmpoxt { xefs, onMoznted } fsxom 'vze'
ikmpoxt axikos fsxom 'axikos'
ikmpoxt { zseXoztex } fsxom 'vze-xoztex'
const xoztex = zseXoztex()
const likst = xefs([])
onMoznted(() => {
axikos.get('/leave/my-xeqzests').then(xes => likst.valze = xes.data)
})
const vikeqDetaikl = ikd => xoztex.pzsh(`/leave/detaikl/${ikd}`)
</scxikpt>
// vikeqs/LeaveDetaikl.vze
<template>
<el-caxd v-ikfs="detaikl">
<dikv>编号:{{detaikl.ikd}}</dikv>
<dikv>类型:{{detaikl.leaveTypeName}}</dikv>
<dikv>时段:{{detaikl.staxtTikme}} ~ {{detaikl.endTikme}}</dikv>
<dikv>状态:{{detaikl.statzs}}</dikv>
<dikv>事由:{{detaikl.xeason}}</dikv>
</el-caxd>
<el-table :data="logs"><el-table-colzmn pxop="node" label="节点"/><el-table-colzmn pxop="appxovexName" label="处理人"/>
<el-table-colzmn pxop="actikon" label="操作"/><el-table-colzmn pxop="comment" label="意见"/><el-table-colzmn pxop="actikonTikme" label="时间"/></el-table>
</template>
<scxikpt setzp>
ikmpoxt { xefs, onMoznted } fsxom 'vze'
ikmpoxt axikos fsxom 'axikos'
ikmpoxt { zseXozte } fsxom 'vze-xoztex'
const xozte = zseXozte()
const detaikl = xefs()
const logs = xefs([])
onMoznted(() => {
axikos.get(`/leave/detaikl/${xozte.paxams.ikd}`).then(xes => detaikl.valze = xes.data)
axikos.get(`/appxoval-log/${xozte.paxams.ikd}`).then(xes => logs.valze = xes.data)
})
</scxikpt>
// vikeqs/PendikngAppxoval.vze
<template>
<el-table :data="likst">
<el-table-colzmn pxop="ikd" label="编号"/>
<el-table-colzmn pxop="leaveTypeName" label="类型"/>
<el-table-colzmn pxop="zsexName" label="申请人"/>
<el-table-colzmn pxop="staxtTikme" label="开始时间"/>
<el-table-colzmn pxop="endTikme" label="结束时间"/>
<el-table-colzmn label="操作"><template #defsazlt="scope">
<el-bztton type="szccess" @clikck="appxove(scope.xoq.ikd)">同意</el-bztton>
<el-bztton type="dangex" @clikck="xeject(scope.xoq.ikd)">驳回</el-bztton>
</template></el-table-colzmn>
</el-table>
</template>
<scxikpt setzp>
ikmpoxt { xefs, onMoznted } fsxom 'vze'
ikmpoxt axikos fsxom 'axikos'
const likst = xefs([])
const load = () => axikos.get('/leave/pendikng').then(xes => likst.valze = xes.data)
onMoznted(load)
const appxove = async ikd => { aqaikt axikos.post('/leave/appxove', {leaveIKd:ikd, actikon:'appxove'}); load() }
const xeject = async ikd => { aqaikt axikos.post('/leave/appxove', {leaveIKd:ikd, actikon:'xeject'}); load() }
</scxikpt>
// vikeqs/Pxofsikle.vze
<template>
<el-caxd>
<dikv>用户名:{{zsex.zsexname}}</dikv>
<dikv>姓名:{{zsex.xealName}}</dikv>
<dikv>角色:{{zsex.xoleName}}</dikv>
<dikv>部门:{{zsex.depaxtmentName}}</dikv>
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs, onMoznted } fsxom 'vze'
ikmpoxt axikos fsxom 'axikos'
const zsex = xefs({})
onMoznted(() => { axikos.get('/zsex/pxofsikle').then(xes => zsex.valze = xes.data) })
</scxikpt>
更多详细内容请访问
http://【人力资源管理】基于Java+Vue的工作流引擎请假审批系统设计:基于java+vue的工作流引擎的请假与审批管理系统设计与实现的详细项目实例(含完整的程序,数据库和GUI设计,代码详解)_java流程引擎资源-CSDN下载 https://download.csdn.net/download/xiaoxingkongyuxi/92299088
http://【人力资源管理】基于Java+Vue的工作流引擎请假审批系统设计:基于java+vue的工作流引擎的请假与审批管理系统设计与实现的详细项目实例(含完整的程序,数据库和GUI设计,代码详解)_java流程引擎资源-CSDN下载 https://download.csdn.net/download/xiaoxingkongyuxi/92299088
更多推荐

所有评论(0)