
简介这是一套基于ASP.NET与C#开发的请假管理系统微信版采用VS2010SQL Server 2008环境适合需要快速搭建请假考勤平台的学习者或开发人员。系统覆盖请假条管理、违纪人员信息管理和人员信息管理三大模块支持请假条录入、修改、查询违纪信息维护以及人员信息批量导入、增删改查等操作可用于学校或企业日常考勤请假场景。资源包共265个文件压缩包约12.44MB以C#源代码、ASP.NET页面和Excel表格为主同时包含前端样式与交互脚本、图片素材以及SQL Server数据库文件和相关配置文件便于直接部署与二次开发。已有5801人学习下载。通过完整源码与页面结构可掌握请假条流转、违纪记录管理及人员信息维护的实现思路理解分层代码组织与数据库设计并能根据实际需求修改页面及数据库快速适配不同组织。1. 请假管理系统微信版把审批从聊天记录里捞出来“请假管理系统微信版”这个标题说白了就是做一套跑在微信生态里的请假审批工具员工在手机微信里提交请假申请主管在微信里点一下通过或驳回人事在后台能拉出月度考勤统计全程不需要额外安装 App。很多中小团队现在还在用“群里发一句、表格记一笔”的方式管请假月底对账时全靠人工翻聊天记录漏记错记是常态。这套系统要解决的就是这个具体痛点请假申请、审批流转、余额核算、统计导出全链路搬进微信。它适合两类人一类是公司或团队的行政/人事负责人被纸质假条和微信零散消息折磨过另一类是接外包或做内部工具的开发者需要一个需求明确、交付路径短、能快速上线的小型业务系统。接下来我按自己实际做这类项目的顺序来拆先定架构和数据模型再讲微信接入与前后端实现最后把踩过的坑和上线前验证清单列出来照着做基本能跑通。2. 整体设计微信载体怎么选审批流和数据模型怎么定2.1 先想清楚公众号、企业微信、小程序到底用哪个承载做微信版请假系统第一个要决策的不是写代码而是选“微信的哪个入口”。我一般会先按团队规模和现有工具来判断而不是一上来就定技术栈。如果团队本身已经在用企业微信那答案几乎不用想直接基于企业微信自建应用。企业微信自带通讯录、审批接口和消息推送员工身份不用额外绑定JS-SDK 可以直接拉起微工作台开发量能省掉一大截。如果团队没有企业微信平时主要用个人微信沟通那常见的做法是二选一微信公众号服务号里的 H5或者微信小程序。服务号 H5 的好处是开发门槛低、调试方便有一个网页就能跑坏处是入口藏得深员工得先关注公众号再从菜单点进去。小程序体验更接近原生但需要走审核、维护版本开发周期会略长。还有个容易被忽略的点无论选哪条路最终“审批通知”都得靠微信的消息模板或企业微信的应用消息推给审批人。个人微信生态对模板消息有严格的用户互动时限要求而企业微信没有这个限制。所以我的建议是团队能上企业微信就优先企业微信只有纯粹面向个人微信用户的场景比如外包给某个没有企业微信的小公司才退回服务号 H5 方案。下面的对比表能帮你快速做选型判断承载方式员工身份获取成本审批消息推送开发难度适用场景企业微信自建应用低通讯录直接同步应用消息无时限限制中已有企业微信的中小团队公众号服务号 H5中需用户授权 openid模板消息有 48 小时互动时限低无企业微信、员工用个人微信微信小程序中需登录授权订阅消息需用户主动订阅中高需要更好交互体验但愿意走审核2.2 核心数据模型请假单、审批记录、请假余额三张表打底无论前端用哪种微信载体后端数据模型几乎是通用的。核心就三张表请假单主表leave_order、审批记录表approval_record、请假余额表leave_balance。请假单主表存每次申请的业务数据审批记录表存每一步谁在什么时间做了什么操作请假余额表存每个员工各类假别的剩余天数。请假单主表往下拆关键字段其实就那么几个申请人、假别年假/事假/病假/调休、开始时间、结束时间、时长按半天或整天折算、请假事由、当前状态、审批人。这里要特别注意“时长计算”不要用简单的日期相减因为请假是按工作日算的周末和法定节假日得单独处理。我在设计表时会把 duration_days 做成 decimal(5,1)允许 0.5 天这种粒度和很多公司的考勤规则对齐。审批记录表建议做成 append-only也就是只插入不更新。每次审批动作都新插一条记录而不是在请假单上改一个状态字段了事。这样审批链条天然留痕往后出问题排查时能看到完整时间线不会出现“谁批的、什么时候批的”说不清的情况。请假余额表则要跟请假单主表联动提交申请时预占额度驳回时释放额度销假时也释放。2.3 状态机一个请假单从提交到归档要过哪些状态请假审批流看着简单但状态设计不谨慎后面统计报表会乱。我常用的状态枚举是草稿、待审批、审批中、已通过、已驳回、已撤销、已归档。其中“审批中”通常用于多级审批场景比如先组长批、再人事批如果只有一级审批草稿提交后直接进“待审批”即可。状态流转有两个容易出错的边界场景。第一个是“驳回后重新提交”通常做法是驳回后生成一个新版本的请假单而不是在原单上改完再让审批人重新看否则审批人无法区分自己看的是第几版。第二个是“销假”已经通过的请假单如果员工提前回来上班不能直接改结束时间应该走撤销或销假流程生成一条变更记录释放被预占的余额。整个状态机可以用一个简单表格统一维护当前状态触发动作下一状态说明草稿员工提交申请待审批提交后不可自行修改待审批审批人通过已通过通过时预占余额转实扣待审批审批人驳回已驳回驳回释放预占余额待审批员工撤销已撤销仅限审批人未操作前已通过管理员销假已归档提前回归释放剩余额度这套状态机设计能在后续写后端接口时省很多心。所有状态变更都走同一个入口用枚举值做白名单校验非法跳转直接拒绝避免出现“已驳回还能继续审批”这种匪夷所思的线上事故。3. 把审批流在微信里跑通接入、菜单、建表与后端实现3.1 微信接入AppID、Secret、回调 URL 的配置顺序先讲接入。以服务号 H5 方案为例第一步是去微信公众平台拿到 AppID 和 AppSecret。AppID 是公开的会出现在前端请求里AppSecret 必须保存在后端一旦泄露别人就能拿着它换取用户凭证后果严重。第二步是配置授权回调域名。在公众平台“设置与开发 - 接口权限 - 网页授权”里把 H5 页面所在的域名填进“网页授权域名”。这个域名必须是你备案过的域名且不能带协议头和路径。常见配置错误是填成了https://oa.example.com/h5/这种带路径的形式微信会直接提示配置失败正确写法是oa.example.com。第三步是配置菜单。服务号的底部菜单在“自定义菜单”里配置把“请假申请”菜单项的跳转链接指向 H5 页面的 URL链接要加上appid和redirect_uri参数让用户点击后自动进入 OAuth 授权流程。一个最小可用的授权跳转 URL 长这样https://open.weixin.qq.com/connect/oauth2/authorize?appidAPPIDredirect_uriREDIRECT_URIresponse_typecodescopesnsapi_basestateSTATE#wechat_redirect这里的REDIRECT_URI必须先经过urlencode编码再拼接。我见过有人直接把中文域名或带参 URL 拼上去结果微信跳转时报 400 错误排查半天才发现是编码问题。scope参数选snsapi_base静默授权就够了因为请假系统里只需要拿到用户 openid 去关联员工工号不需要获取昵称头像等资料用snsapi_userinfo反而会多弹一次授权确认页体验变差。3.2 数据库 DDL直接能用的三张表建表语句把表结构落成 SQL。下面这三张表我按实际项目里的字段精简过去掉了审计日志等外围内容保留核心业务字段。-- 请假单主表 CREATE TABLE leave_order ( id BIGINT AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL COMMENT 请假单号格式 LyyMMddHHmmss随机3位, employee_id BIGINT NOT NULL COMMENT 申请人ID关联员工表, leave_type TINYINT NOT NULL COMMENT 假别1年假 2事假 3病假 4调休, start_time DATETIME NOT NULL COMMENT 请假开始时间, end_time DATETIME NOT NULL COMMENT 请假结束时间, duration_days DECIMAL(5,1) NOT NULL COMMENT 请假时长支持0.5, reason VARCHAR(500) NULL COMMENT 请假事由, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0草稿 1待审批 2审批中 3已通过 4已驳回 5已撤销 6已归档, current_approver BIGINT NULL COMMENT 当前待审批人ID, create_time DATETIME NOT NULL, update_time DATETIME NOT NULL, KEY idx_employee_status (employee_id, status), KEY idx_order_no (order_no) ) COMMENT 请假单主表; -- 审批记录表 CREATE TABLE approval_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, order_id BIGINT NOT NULL COMMENT 关联请假单ID, approver_id BIGINT NOT NULL COMMENT 审批人ID, action TINYINT NOT NULL COMMENT 动作1通过 2驳回 3撤销 4销假, comment VARCHAR(500) NULL COMMENT 审批意见, create_time DATETIME NOT NULL, KEY idx_order_id (order_id) ) COMMENT 审批记录表只追加不修改; -- 请假余额表 CREATE TABLE leave_balance ( id BIGINT AUTO_INCREMENT PRIMARY KEY, employee_id BIGINT NOT NULL COMMENT 员工ID, leave_type TINYINT NOT NULL COMMENT 假别同上, total_days DECIMAL(5,1) NOT NULL COMMENT 年度总配额, used_days DECIMAL(5,1) NOT NULL DEFAULT 0 COMMENT 已用天数, remaining_days DECIMAL(5,1) NOT NULL COMMENT 剩余天数, year SMALLINT NOT NULL COMMENT 所属年度, UNIQUE KEY uk_emp_type_year (employee_id, leave_type, year) ) COMMENT 请假余额表;建表时有几个点值得展开。order_no不用数据库自增ID做单号而是L 时间戳 随机数的格式原因有二一是避免请假单号被直接遍历猜出总数二是后续对接其他系统时字符串单号更通用。duration_days用DECIMAL(5,1)而不是INT是因为很多公司允许半天请假用 INT 就得把半天拆成 0.5 的变体反而麻烦。审批记录表不设status字段每个动作天然通过action表达查询时可按时间排序回溯整个审批链路。3.3 后端核心接口提交申请、审批通过、消息推送的代码骨架后端我习惯用 Spring Boot 或类似框架实现核心接口只有三个提交申请、审批动作、消息推送。先看提交申请接口。这里最关键的是“预占余额”必须在事务里完成否则并发提交时两个请假单可能同时扣同一段余额。Transactional public LeaveOrder submitLeave(LeaveSubmitRequest req) { // 1. 校验请假时间是否合法 validateTimeRange(req.getStartTime(), req.getEndTime()); // 2. 查余额并预占用悲观锁锁住余额行防止并发超扣 LeaveBalance balance balanceMapper.selectForUpdate( req.getEmployeeId(), req.getLeaveType(), currentYear); if (balance.getRemainingDays() req.getDurationDays()) { throw new BusinessException(剩余假期不足); } // 3. 扣减余额预占 balance.setRemainingDays(balance.getRemainingDays() - req.getDurationDays()); balanceMapper.updateById(balance); // 4. 创建请假单状态置为待审批指定审批人 LeaveOrder order new LeaveOrder(); order.setOrderNo(generateOrderNo()); order.setEmployeeId(req.getEmployeeId()); order.setLeaveType(req.getLeaveType()); order.setStartTime(req.getStartTime()); order.setEndTime(req.getEndTime()); order.setDurationDays(req.getDurationDays()); order.setStatus(LeaveStatus.PENDING); order.setCurrentApprover(req.getApproverId()); orderMapper.insert(order); // 5. 异步推送微信通知给审批人 messageSender.sendApprovalNotification(order); return order; }selectForUpdate是这笔事务里的关键点MySQL 会对命中的余额行加行锁直到事务提交才释放。这样两个员工同时操作同一个余额账户时第二个请求必须等第一个事务结束才能读到最新值从根上避免超扣。messageSender建议用Async走异步线程池不能让推送失败影响主流程否则通知没发出去审批人那边永远收不到提醒。再来看审批通过接口。审批动作天然是写操作密集的要同时改请假单状态、插入审批记录、释放或实扣余额。通过和驳回的核心区别在于驳回要回补余额通过则要把预占变成实扣不用再动余额数值。Transactional public void approve(Long orderId, Long approverId, String comment) { LeaveOrder order orderMapper.selectByIdForUpdate(orderId); if (order.getCurrentApprover().equals(approverId) false) { throw new BusinessException(当前审批人不是您); } if (order.getStatus() ! LeaveStatus.PENDING) { throw new BusinessException(请假单状态已变更请刷新后重试); } // 插入审批记录 ApprovalRecord record new ApprovalRecord(); record.setOrderId(orderId); record.setApproverId(approverId); record.setAction(Action.PASS); record.setComment(comment); approvalRecordMapper.insert(record); // 更新请假单状态 order.setStatus(LeaveStatus.APPROVED); orderMapper.updateById(order); // 推送微信模板消息给申请人 messageSender.sendApprovalResult(order, 通过); }注意这里加了乐观校验当前审批人如果不是操作人直接拒绝。这个校验既是权限控制也是防止审批人 A 和 B 同时操作同一个单据时的竞态。comment参数在驳回时必填通过时可空前端需要据此决定是否渲染必填星号。推送微信模板消息是我在这类项目里花时间最多的地方。先看一段消息推送的代码骨架// 基于 axios 的微信模板消息推送 async function sendTemplateMessage(openid, templateId, order, url) { const token await getAccessToken(); // 内部带缓存与自动刷新 const payload { touser: openid, template_id: templateId, page: url, data: { thing1: { value: order.orderNo }, time2: { value: formatDateTime(order.startTime) 至 formatDateTime(order.endTime) }, thing3: { value: order.reason.length 20 ? order.reason.substring(0, 20) : order.reason } } }; const resp await axios.post( https://api.weixin.qq.com/cgi-bin/message/template/send?access_token${token}, payload ); if (resp.data.errcode ! 0) { // 失败时记录日志并补偿重试不要直接抛异常 logger.error(模板消息发送失败, resp.data); } }模板消息的字段名thing1、time2不是随便起的是你在微信公众平台申请模板时模板里预定义的关键词索引。申请模板时要做“类目匹配”瞎选类目会被驳回。thing类型字段限制 20 个字符超长会被截断或直接报错所以代码里做了substring(0, 20)兜底。getAccessToken必须做缓存微信对 access_token 的获取频率有限制每日 2000 次每次调用都重新获取会很快触发限流。3.4 前端 H5OAuth 授权、拉取身份、请假表单提交前端部分用 Vue 或 React 写 H5 都行核心流程是三步打开页面时先走 OAuth 静默授权拿 code拿 code 换 openid再拿 openid 换员工身份。页面加载时自动完成用户无感知。// 页面加载时自动执行身份引导 async function initPage() { const code new URLSearchParams(window.location.search).get(code); if (!code) { // 没有 code说明是第一次进入跳转微信授权 const redirectUri encodeURIComponent(window.location.href); const authUrl https://open.weixin.qq.com/connect/oauth2/authorize ?appid${APP_ID}redirect_uri${redirectUri} response_typecodescopesnsapi_basestateleave#wechat_redirect; window.location.href authUrl; return; } // 有 code调后端接口换 openid 和员工身份 const identity await fetch(/api/wechat/identity?code code).then(res res.json()); if (identity.employeeId) { renderLeaveForm(identity); } else { showBindPage(); // 提示员工先绑定工号 } }这里有个开发环境要注意的问题window.location.href在整个授权流程中会被微信跳转两次所以后端日志里看到的回调 URL 可能和前端配置的 redirect_uri 不完全一致。调试时直接用微信开发者工具看跳转链路的完整 URL比看后端日志更直观。授权完成后用户身份就是openid employeeId的绑定关系后续所有请假接口用employeeId做业务关联openid 只作为微信通信凭证两者不要混用。4. 避坑指南微信版请假系统的五个高频翻车点4.1 access_token 频繁过期导致消息推送大面积失败现象上线初期发现模板消息时不时推送失败报errcode 40001或42001有时候重启后端服务后短暂恢复过一阵又挂。原因多个后端实例各自调用getAccessToken每次调用都刷新 token导致后获取的 token 把之前有效的 token 挤下线。微信的 access_token 机制是“刷新后旧 token 立即失效”多实例同时刷新必然互相踩踏。解决把 access_token 的获取和刷新收敛到一个统一的服务里用数据库或 Redis 做全局缓存加分布式锁防并发刷新。我在项目里就是起了个定时任务每 100 分钟主动刷新一次并写入 Redis所有业务实例只从 Redis 读取彻底解决。4.2 回调域名配置正确却提示 redirect_uri 参数错误现象公众号后台回调域名明明填了oa.example.com用户点菜单却报“redirect_uri 参数错误”。原因最常见的是拼接 URL 时用了未编码的redirect_uri。另一种是前端代码里window.location.href取到的是完整含参路径把这个路径直接拼进来后微信端做比对时发现和公众号后台配置的域名不一致。解决统一先encodeURIComponent编码 redirect_uri再拼授权链接。另外建议在公众号后台把回调域名配置成根域名而不是某个具体页面路径这样授权后跳转的子页面更灵活。4.3 审批通知发给了离职或禁用状态的审批人现象A 员工提交请假单后审批人一直收不到微信通知后台看记录却是“已发送”。原因审批人已经离职工号被停用或取消关注了服务号导致微信端无法触达。代码里只判断了“推送接口调用成功”没判断用户是否可接收。解决在推送前查一下审批人的微信绑定状态和在职状态离职或未绑定微信的用户直接跳过微信推送降级为站内待办提醒。同时把“已发送但用户未读”的情况记录到日志行政人员可以在后台补发邮件或口头提醒。4.4 时区与跨天问题凌晨提交的假条算错了日期现象员工晚上 23:50 提交次日上午的请假系统显示“时长 0 天”审批人看着不对。原因请假时长按“工作日天数”算代码用了(endTime - startTime) / 86400000直接相减没排除周末也没按工作日历折算。而 23:50 提交次日 09:00 开始请假endTime和startTime相差不到 1 天被整数除法归零。解决写一个专门的工作日计算工具传入开始和结束时间依次排除周六日和国家法定节假日再按公司规则折算半天。排班制团队还需支持自定义休息日不能只依赖国家假日表。4.5 多级审批流程里“审批中”状态永远没人收尾现象设置了“组长审批 → 人事审批”两级流程组长通过后单据停在“审批中”不再流转人事那边看不到待办。原因后端只实现了“通过”动作没有在通过后判断下一级审批人是谁并把current_approver更新过去。这个逻辑在代码评审时容易被漏掉因为单级审批时根本走不到这分支。解决在审批通过接口里加一个nextApprover查询查流程配置表得到下一节点审批人。如果存在则更新current_approver并保持“审批中”如果不存在才把状态置为“已通过”。这个逻辑务必写在事务里避免“状态改了但审批人没更新”的中间态。5. 上线前验证与后续优化从能跑到好用5.1 上线前自测清单照着点一遍再放量微信生态的坑大多在上线后才会集中暴露因为生产环境的网络、用户行为和开发环境差别太大。我每次上线前都会拿一张清单过一遍大概这么几项第一用一个从未绑定过的微信号走完整流程确认能正确跳到绑定页第二用已绑定员工提交请假确认余额预占正确第三审批人通过后确认申请人微信能收到“已通过”模板消息第四驳回后确认余额回补到剩余天数第五查一下服务端日志确认 access_token 只在一个位置刷新。5.2 日志审计审批链路必须能追溯请假审批可能涉及工资核算日志不能只记info级别的一句话。我在项目里会为每个请假单维护一份完整的事件时间线包括提交、预占余额变更、审批动作、余额回补/扣减、消息推送结果全部落库。查问题时直接按order_no拉时间线一行不缺。5.3 往后的优化方向从“能请假”到“能核算”系统跑顺之后可以往两个方向加深。第一个方向是跟考勤打通请假审批通过后自动同步到考勤系统月底核算出勤时不用人工比对。第二个方向是报表可视化按部门、假别、时间维度假期消耗趋势给人事决策用。这些扩展在数据模型稳定的前提下都是增量开发不用推翻重来。最后说个我自己的教训第一次做这类系统时我花了大量精力在后端权限控制上结果上线后最先出问题的反而是微信回调配置和消息推送。给后来者的建议是先把微信侧的通路打通再回头优化业务代码。毕竟用户感知最直接的是“提交后审批人能不能马上看到”系统响应速度反而没那么敏感。希望帮到你。本文还有配套的精品资源点击获取