基于SpringBoot+Vue的工位管理系统设计与实现
公司上个月刚把办公区重新规划完当时行政部门拿着Excel表挨个部门统计工位占用情况折腾了两周才勉强把数据理清楚。没过几天又有新同事入职、有人换工位那张表又得重新改一遍。我就想着与其每次手工核对不如直接做一套工位管理系统把预约、分配、统计这些事全部线上化。于是就有了这套基于SpringBootVue的工位管理系统后端用MyBatis操作MySQL前端用Vue做交互界面从立项到跑通整体流程花了两周左右。这篇文章就把整个项目的核心设计思路、数据库建模、前后端关键实现和我在开发过程中踩过的那些坑完整梳理一遍给正好要做类似管理系统的朋友一个参考。1. 核心需求与功能边界拆解1.1 工位管理的真实痛点工位管理这个场景听起来简单真做起来牵扯的事情不少。很多公司工位一直靠行政口口相传或者纸质登记表维护但工位上坐的是谁、哪些位置空置、哪些位置被长期占用但实际没人来这些信息基本是一团乱麻。做这套系统之前我先梳理了核心痛点工位信息分散在Excel、聊天记录、口头约定里查不到准确数据新员工入职分配工位行政要反复问各个部门负责人临时出差、居家办公留下的空位没人释放资源利用率上不去领导想看工位使用率行政拿不出直观的统计报表这套系统的核心目标就是把这些混乱状态线上化、可视化、流程化。我参考了市面上几个商用云办公产品的功能设计再把范围收敛到适合中小团队内部部署的粒度。最终确定的功能边界是工位管理、预约申请、审批流转、签到释放、统计看板这样五个核心模块。1.2 角色权限与业务流程设计系统涉及两类角色普通员工和管理员。员工可以查看工位地图、发起预约、签到、退座。管理员除了员工的所有权限还负责工位类型维护、审批配置、数据统计和部门维度管理。业务主流程我设计成了这样员工登录系统查看可预约的工位列表或可视化地图选择工位、时间段提交预约申请管理员审批也可以配置成免审批模式灵活处理审批通过后员工在规定时间内到工位签到工位状态从“已预约”变为“使用中”下班退座工位恢复“空闲”状态这个流程表面看没什么特别但真正落地时每个环节都有细节要考虑。比如预约之后没有签到怎么办工位被占用但系统显示空闲怎么处理这些我在后面第4和第7部分详细展开说。2. 技术选型深度解析2.1 后端为什么选SpringBoot工位管理系统本质上就是典型的CRUD加业务流程流转应用SpringBoot在这个领域几乎是标准答案。它的自动配置机制能省掉大量XML配置内嵌Tomcat让应用可以直接通过jar包启动配合统一的依赖管理和Starter体系项目里加功能基本不需要关心底层的依赖版本冲突问题。版本我选的是SpringBoot 2.7系列。有人可能会问SpringBoot 3.x都出了那么久了为什么不直接用最新的这里说下我的考虑2.7是2.x最后一个维护版本生态非常成熟遇到的坑基本都有现成解决方案团队对javax.*包名更熟悉不需要处理迁移到jakarta.*带来的改动项目对性能没有极致要求2.7在中小规模并发下完全够用当然如果是新起项目且团队对3.x已经熟悉直接上3.2以上版本也没问题。这个选择更多是考虑稳定性和团队熟练度不是说越新就一定越好。2.2 前端Vue版本与构建选型前端选了Vue 3配合Vite构建。Vue 3的组合式APIComposition API在组件逻辑复用上比Vue 2的选项式API舒服太多。工位地图、预约表单、审批列表这些功能之间有不少共享的状态和操作逻辑用setup函数和composables可以很自然地把重复代码抽出来。Vite作为构建工具开发环境冷启动基本在1秒以内热更新也是毫秒级响应。对比之前用webpack开发时动不动等十几秒编译这个体验差距不是一点半点。生产构建默认按需打包配合静态资源路径配置部署起来也省心。2.3 MyBatis和MyBatis-Plus到底怎么选标题里写的是MyBatis但实际开发中我用的是MyBatis-Plus这套增强框架。准确说MyBatis-Plus是在MyBatis基础上做的增强扩展它没有改变MyBatis的核心执行流程只是把大量通用CRUD操作封装成了现成方法。我这么说吧如果你的项目里90%的数据库操作是单表增删改查用MyBatis-Plus能节省至少三分之一的工作量。它内置的分页插件、条件构造器、逻辑删除支持都正好是后台管理系统最常用到的能力。像工位列表按状态、区域、部门筛选这种查询用LambdaQueryWrapper写起来非常简洁LambdaQueryWrapperWorkstation wrapper Wrappers.lambdaQuery(); wrapper.eq(StringUtils.isNotBlank(status), Workstation::getStatus, status) .like(StringUtils.isNotBlank(area), Workstation::getArea, area) .orderByAsc(Workstation::getFloor, Workstation::getSortNo);这种代码可读性高也避免了拼接字符串SQL容易出错的问题。不过需要提醒的是非常复杂的多表关联查询、动态报表类查询该手写XML的还是要手写MyBatis-Plus不擅长的地方不要硬套。2.4 MySQL版本与表结构设计考量数据库用的MySQL 8.0主要看中三块能力默认字符集utf8mb4对emoji和生僻字的支持、窗口函数做统计报表非常方便、性能对比5.7有明显提升。存储引擎统一用InnoDB因为系统涉及预约、审批这类写操作比较频繁的事务场景。字符集在创建库的时候就明确指定utf8mb4和utf8mb4_unicode_ci避免上线之后出现中文乱码或者排序不一致的问题。3. 数据库设计与核心表结构3.1 关系模型设计思路工位管理系统的数据模型核心围绕“工位资源”和“人的行为”两条线展开。工位本身是静态资源它属于某个楼层、区域有类型和状态属性。人的行为是动态记录包括预约申请、审批操作、签到退座和操作日志。我把这两条线拆成了十张核心业务表数据线表名作用资源线office_building楼宇信息资源线office_area区域信息资源线office_workstation工位信息资源线workstation_type工位类型行为线sys_user用户信息行为线workstation_reservation预约记录行为线workstation_checkin签到记录行为线approval_record审批记录行为线workstation_log操作日志维度线sys_dept部门信息3.2 工位表和预约表设计细节工位表是全局最核心的一张表我把关键字段定义在这里CREATE TABLE office_workstation ( id bigint NOT NULL AUTO_INCREMENT, ws_code varchar(32) NOT NULL COMMENT 工位编号, ws_name varchar(64) DEFAULT NULL COMMENT 工位名称, floor int NOT NULL COMMENT 所在楼层, area_id bigint DEFAULT NULL COMMENT 区域ID, type_id bigint DEFAULT NULL COMMENT 工位类型ID, status tinyint NOT NULL DEFAULT 0 COMMENT 状态: 0-空闲 1-预约中 2-使用中 3-维护中 4-禁用, dept_id bigint DEFAULT NULL COMMENT 固定占用部门ID, sort_no int DEFAULT 0 COMMENT 排序号, remark varchar(255) DEFAULT NULL COMMENT 备注, deleted tinyint NOT NULL DEFAULT 0 COMMENT 逻辑删除标识, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_ws_code (ws_code), KEY idx_area_status (area_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT工位信息表;这里的status字段是整个业务流程流转的关键。工位的状态变化是这样的链路空闲 - 预约中有人提交预约申请预约中 - 使用中到工位签到使用中 - 空闲主动退座或超时释放空闲 - 维护中管理员标记维修状态机设计得好业务逻辑就会非常清晰。我特意加了一个deleted字段做逻辑删除而不是物理删除因为工位的历史状态数据对后续统计工位利用率非常重要。比如统计上个月某区域实际使用了多少工位天就得依赖历史预约和签到记录物理删了就补不回来了。预约表同样值得关注CREATE TABLE workstation_reservation ( id bigint NOT NULL AUTO_INCREMENT, reservation_no varchar(32) NOT NULL COMMENT 预约单号, user_id bigint NOT NULL COMMENT 预约人ID, workstation_id bigint NOT NULL COMMENT 工位ID, reservation_date date NOT NULL COMMENT 预约日期, time_type tinyint NOT NULL DEFAULT 2 COMMENT 时段: 1-上午 2-全天, status tinyint NOT NULL DEFAULT 0 COMMENT 状态: 0-待审批 1-已通过 2-已拒绝 3-已取消 4-已签到 5-已完成, sign_in_time datetime DEFAULT NULL COMMENT 签到时间, sign_out_time datetime DEFAULT NULL COMMENT 退座时间, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_reservation_no (reservation_no), KEY idx_user_date (user_id, reservation_date), KEY idx_ws_date (workstation_id, reservation_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT工位预约记录表;唯一键uk_reservation_no保证预约单号不重复业务上拿它做幂等控制防止前端重复点击提交产生相同预约。3.3 索引设计的一些心得索引这事看似基础但工位预约场景有个非常典型的并发问题同一时间段内多个用户抢同一个工位。索引设计就要为这种冲突检测做保障。我先在workstation_id和reservation_date上建了联合索引idx_ws_date查询“某工位某天是否已被预约”时走这个索引效率非常高。再配合代码层的事务和行锁机制就能有效防止超卖问题。具体实现方案在第4部分专门讲。还有一点日志流水表增长会非常快我把建了create_time普通索引后续如果数据量上来了可以直接按时间分区或者做归档处理。现在系统规模不大这一层设计保留扩展能力就行。4. 后端核心实现与难点突破4.1 工程分包与统一响应结构后端工程我用了经典的分层架构。controller包放接口层service包放业务逻辑mapper包放MyBatis接口entity包放数据库实体dto包放前端交互的数据对象。这种分包方式的好处是职责边界清晰。同事接手代码时根据包名基本就能定位到改动范围。我在实际项目里看到过不少人把所有的类都堆在同一个包下面几百个文件挤在一起那维护起来真是灾难。统一响应结构是这类管理系统必须做的一个基础封装。前端拿到接口返回的数据需要能判断成功还是失败、拿到提示信息和业务数据。我定义了一个通用返回体Data public class ResultT { private Integer code; private String message; private T data; private Long timestamp; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); result.setTimestamp(System.currentTimeMillis()); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); result.setTimestamp(System.currentTimeMillis()); return result; } }code统一用200表示成功500表示业务失败401表示未认证403表示无权限。前端拿到非200的code就直接弹message提示不用针对每个接口单独写错误处理逻辑。4.2 登录认证与接口安全管理系统虽然大多是内网部署但认证这关不能省。我用了JWTJSON Web Token做无状态认证服务端不保存会话状态每次请求带上token鉴权通过后直接放行。登录流程是这样的用户提交账号密码后端校验用户信息如果通过生成JWT token把用户ID和角色等信息封装在token里前端把token存到localStorage前端请求拦截器统一在header里带上Authorization字段后面每次请求到达后端时通过拦截器解析token还原用户信息public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (StringUtils.isBlank(token) || !token.startsWith(Bearer )) { return unauthorized(response); } try { Claims claims JwtUtil.parseToken(token.substring(7)); request.setAttribute(userId, claims.get(userId)); request.setAttribute(userRole, claims.get(role)); return true; } catch (Exception e) { return unauthorized(response); } } }JWT的密钥我在配置文件里单独维护生成token时把过期时间设为24小时过期后前端会收到401状态码并自动跳转到登录页。密码存储用的是BCrypt加密这个必须强调一下千万别用MD5存密码拖库之后就是裸奔。BCrypt自带随机盐每次加密结果都不一样安全性高得多。4.3 工位预约的并发防超卖方案这是整个后端开发中最值得深挖的环节。工位预约场景本质上是一个有限的共享资源分配问题——同一个工位在同一个时间段只能分配给一个人但在高并发下两个请求可能会同时读到“空闲”状态然后同时更新成功导致超卖。解决方案的第一步是给工位表的状态更新加乐观锁机制。我在office_workstation表增加了一个version字段更新工位状态时带上版本号条件Update(UPDATE office_workstation SET status #{newStatus}, version version 1 WHERE id #{id} AND status #{oldStatus} AND version #{version}) int updateStatusWithVersion(Param(id) Long id, Param(oldStatus) Integer oldStatus, Param(newStatus) Integer newStatus, Param(version) Integer version);如果更新影响行数为0说明有其他请求抢先修改了数据业务层立即抛出“该工位刚刚被预约请刷新查看”的提示。这个方案实现简单不需要引入额外的分布式锁组件对于中小规模管理系统的并发量来说完全够。第二步是预约记录的唯一约束。前面建表时我在workstation_id和reservation_date上建了联合索引idx_ws_date这里我直接在这个索引上做了个策略尽量保证同一工位同一天不能有两条“待审批”状态的预约。不过这个没法直接在数据库层用唯一索引实现因为一条工位在一天内可能存在“已完成”的历史记录和“待审批”的新预约。所以数据库这一层约束思路调整为在事务里先通过悲观锁把工位行锁住再查预约记录最后做插入。具体事务代码的逻辑我整理一下Transactional(rollbackFor Exception.class) public boolean reserveWorkstation(ReservationRequest request) { // 1. 行锁锁定工位 Workstation ws workstationMapper.selectByIdForUpdate(request.getWorkstationId()); if (ws null || ws.getStatus() 3 || ws.getStatus() 4) { throw new BusinessException(工位不可预约); } // 2. 检查该工位该天是否有冲突预约 Integer conflictCount reservationMapper.checkConflict( request.getWorkstationId(), request.getReservationDate(), request.getTimeType()); if (conflictCount 0) { throw new BusinessException(该工位在这个时间段已被预约); } // 3. 检查用户是否已有同天预约 Integer userCount reservationMapper.countByUserIdAndDate( request.getUserId(), request.getReservationDate()); if (userCount 0) { throw new BusinessException(您当日已有预约记录); } // 4. 创建预约记录 Reservation reservation new Reservation(); reservation.setReservationNo(generateReservationNo()); // ... 填充其他字段 reservationMapper.insert(reservation); // 5. 更新工位状态为预约中 if (workstationMapper.updateStatus(request.getWorkstationId(), 0, 1) 0) { throw new BusinessException(工位状态更新失败请重试); } return true; }selectByIdForUpdate就是加了FOR UPDATE语句的查询事务没提交前其他事务对这个工位的行操作会阻塞等待。因为这个流程整体在同一个事务里加上前面第2步就检查了冲突记录双重机制基本能保证并发场景不超卖。4.4 超时未签到的自动释放机制业务上有个很常见的场景用户预约了工位但当天根本来不了或者来了去坐别的空位。如果系统不处理这些工位会一直停留在“预约中”状态其他想用的人也没法预约。我实现方式是定时任务扫描。使用Spring框架自带的Scheduled注解设定每5分钟跑一次Scheduled(fixedRate 300000) public void autoReleaseExpiredReservations() { ListReservation expiredList reservationMapper.selectExpiredCheckin( LocalDate.now(), LocalDateTime.now().minusMinutes(30)); for (Reservation reservation : expiredList) { reservationMapper.updateStatus(reservation.getId(), 1, 3); // 已通过 - 已取消 workstationMapper.updateStatus(reservation.getWorkstationId(), 1, 0); // 预约中 - 空闲 logMapper.insert(LogUtil.buildLog(预约超时未签到系统自动释放工位, reservation.getWorkstationId())); } }预约成功的30分钟内必须签到超时自动取消预约工位重新释放到空闲状态。定时任务本身要加幂等控制我通过查询条件严格限定只处理“状态为已通过”且“签到时间为空”且“预约日期是今天”且“当前时间已超过签到截止时间”的记录这样即使多个实例部署也不会重复处理。4.5 统计报表的SQL实现统计工位使用率这个需求用MySQL 8.0的窗口函数非常方便。比如统计每周各楼层的工位使用情况SELECT DATE_FORMAT(r.reservation_date, %Y-%m-%d) AS stat_date, ws.floor, COUNT(DISTINCT r.workstation_id) AS used_count, COUNT(DISTINCT CASE WHEN r.status IN (4, 5) THEN r.workstation_id END) AS signed_count FROM workstation_reservation r LEFT JOIN office_workstation ws ON r.workstation_id ws.id WHERE r.reservation_date BETWEEN DATE_SUB(NOW(), INTERVAL 7 DAY) AND NOW() GROUP BY DATE_FORMAT(r.reservation_date, %Y-%m-%d), ws.floor ORDER BY stat_date DESC;前端拿到这个统计结果后用折线图展示每天各楼层的使用趋势管理层一眼就能看出资源利用情况。5. 前端Vue实现要点梳理5.1 组件库选型与项目结构前端项目基于Vue 3组件库选了Element Plus。这套组件库的表格、表单、弹窗、日期选择器都非常完善后台管理系统基本需要的交互组件它都有而且支持按需引入打包体积控制得不错。项目结构按照views、components、api、router、stores这样几个目录拆分。views下面按业务模块分目录每个页面对应一个目录里面再放index.vue作为主页面需要拆分的局部组件放在同目录的components子文件夹里。api目录下按模块拆分接口请求文件stores目录用Pinia管理全局状态。5.2 工位分布地图的可视化方案工位分布图是这个系统前端最核心的交互模块。我需要把楼层的工位布局可视化呈现出来员工可以直观地看到哪些位置是空的、哪些被占用、哪些正在维护中。一开始想过用Canvas画图但方位计算太繁琐。后来改用CSS Grid布局加绝对定位的方案每个楼层的工位坐标在数据库里用x和y字段保存前端渲染时根据这两个坐标计算left和top值把工位卡片定位到对应位置div classfloor-map :stylemapStyle div v-forws in workstationList :keyws.id classws-card :classstatusClassMap[ws.status] :style{ left: ws.xPos %, top: ws.yPos % } clickselectWorkstation(ws) span{{ ws.wsName }}/span span classws-status{{ statusTextMap[ws.status] }}/span /div /div工位状态通过颜色做区分绿色空闲、橙色预约中、蓝色使用中、灰色维护中。鼠标悬停时显示工位编号和状态点击后弹出确认预约面板。这个可视化方案实现成本低效果直观没有引入额外的地图库或可视化库对后期维护也友好。5.3 接口请求封装与Mock联调前端请求我用axios做了统一封装。baseURL通过环境变量控制开发环境指向本地的后端服务生产环境指向实际服务器地址。请求拦截器统一带上JWT token响应拦截器处理全局错误码service.interceptors.response.use( (response) { if (response.data.code 401) { localStorage.removeItem(token) router.push(/login) } return response.data }, (error) { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } )开发期间我是用Mock数据先跑通了前端所有的页面交互等后端接口完整了再切换成真实请求。5.4 状态管理与权限控制全局状态我用Pinia来管理。主要存三部分数据当前登录用户信息、系统菜单权限、工位地图当前选中的楼层层级。权限控制这里不是特别复杂前端登录后根据角色动态渲染菜单和按钮。管理员的菜单多一个“系统管理”分组普通员工就只看得到预约和我的工位相关的页面。接口层面后端已经做了权限拦截前端的功能隐藏更多是交互友好性不让普通员工点一个按钮然后被弹窗提示“无权限”这种体验很差。6. 项目部署与基础运维6.1 环境依赖清单整个项目部署需要的基础环境软件版本要求说明JDK1.8及以上后端运行环境MySQL5.7或8.0数据存储Node.js16.14及以上前端打包构建Nginx1.18及以上前端静态资源托管和反向代理后端jar包通过java -jar启动前端打包后放到Nginx的html目录。前后端分离部署最大的一个配置点是跨域问题。我是在Nginx配置了反向代理把/api路径的请求转发到后端的8080端口server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } }这样配置的好处是前端请求相对路径不用在代码里写绝对地址也不会出现开发和生产环境接口地址不一致的问题。6.2 数据库初始化脚本管理数据库脚本我用Flyway来做版本管理。每次表结构变更都新增一个V开头的SQL脚本启动时自动执行未运行的脚本。这个习惯在多人协作和后期升级时特别重要。举个例子第一版上线后我发现工位表里缺少一个区域编码字段直接修改原来的建表脚本肯定不行因为生产库里已经执行过了。正确做法是新增一个V2脚本ALTER TABLE office_workstation ADD COLUMN area_code varchar(32) DEFAULT NULL COMMENT 区域编码 AFTER area_id;Flyway通过schema_version表记录已应用的脚本版本重启后自动执行V2脚本表结构无缝升级。这种管理方式对系统演进太重要了省去了大量手动维护生产库的麻烦。6.3 配置文件的多环境管理SpringBoot项目我按dev、prod两套环境做了多套配置。核心差异在数据库连接地址和日志级别。开发环境日志级别是DEBUG方便排查问题生产环境是INFO减少日志量。配置切换用启动参数java -jar workstation-system.jar --spring.profiles.activeprod简单明了。数据库密码这类敏感信息我放在配置里提取了环境变量引用避免把密钥提交到代码仓库。7. 实操中踩过的坑与问题排查心得7.1 前端token过期后页面白屏这个算是我在开发过程中遇到的最常见的问题。一开始登录逻辑是token存localStorage页面刷新时检查是否为空为空就跳登录页有值就放行。但token过期后接口返回401前端没有处理页面数据渲染失败直接白屏。解决方案是在响应拦截器中统一处理401状态码清除本地token并跳转登录页面同时加了一个sessionStorage的标记提示“登录已过期请重新登录”。这一套处理下来用户重新登录后能清楚知道为什么被踢出来不再是莫名其妙的白屏。7.2 并发预约测试时出现的重复记录测试阶段我用并发工具模拟20个用户同时抢同一个工位结果生成了多条预约记录。定位过程研究了挺长时间发现是事务隔离级别和行锁时序的问题。乐观锁虽然保护了工位状态更新但预约记录插入发生在工位状态更新之前在并发下两个请求都可能先插入预约记录成功。最终的修复方案就是我在第4.3部分讲的把工位行的悲观锁查询调整到整个事务的第一步让后续操作统统排队执行。而且锁的顺序要保证所有请求都按同一个方向获取避免死锁风险。实测下来20个并发只会有1个成功其余都被拦截问题解决。7.3 跨天预约时日期边界模糊工位预约有个天然的时间维度跨天问题。如果是夜晚加班预约用户选择24点到第二天凌晨6点系统要不要允许跨天我第一版直接对预约日期做了字符串比较结果跨天时段被错误拦截。后来重新梳理了业务定义系统只按自然日管理工位预约暂不支持跨天预约。同时增加了一个兜底规则超过晚间10点的预约强制要求选择第二天的上午时段。这样既避免了复杂的时间段重叠判断逻辑也符合大多数公司的实际使用场景。7.4 定时任务在集群部署下的重复执行自动释放工位的定时任务如果部署了多个服务实例每个实例都会跑一遍相同的扫描逻辑可能产生重复的状态更新操作。在我的设计里释放逻辑做了状态条件过滤重复执行影响不大但操作日志会重复记录。针对日志重复的问题我增加了日志插入前查重同一个工位ID和同一个预约单号如果已存在释放日志就跳过。后续如果服务做真正的集群扩展可以把定时任务切到独立的调度组件或者用分布式锁来约束。7.5 MySQL连接池参数调优系统刚上线时出现过一个诡异的问题运行一段时间后接口偶尔变慢重启后恢复。排查到最后发现是数据库连接池配置不当。HikariCP连接池把maximum-pool-size设成了默认的10并发稍微上来就把连接占满了后面的请求排队等待连接释放导致响应时间飙升。观察了实际的并发特征后我把参数调整成了spring: datasource: hikari: maximum-pool-size: 30 minimum-idle: 5 connection-timeout: 30000 max-lifetime: 1800000调整之后接口响应稳定了这套参数在500人规模的公司内部使用实测下来完全够用。写在最后的一些经验总结整个项目做下来最大的体会是一个管理系统的好坏不在技术多花哨而在于业务流程是否真正被梳理通顺了。工位管理表面上是资源分配实际上牵扯的是行政流程、部门协作和员工使用习惯。技术选型上SpringBoot、Vue、MyBatis、MySQL这套组合经过这么多年的沉淀已经有非常丰富的社区经验和解决方案遇到问题基本都能找到答案用它做企业内部管理系统是目前非常可靠的选择。最后分享一个小技巧开发阶段给数据库建一个独立的测试账号权限只开放给本地测试库生产库账号单独设置强密码并绑定内网访问白名单。很多管理系统上线后的安全问题源头都是开发阶段图省事共用了账号。另外记得定期备份MySQL数据工位预约记录这种数据一旦丢失恢复的成本会非常麻烦。这套工位管理系统后续我还在迭代计划增加地图选位拖拽编辑、与企业微信免登对接、移动端H5适配这几个方向如果大家也在做类似的东西欢迎交流。