周末深夜,你收到紧急审批通知——却发现只能在 PC 端处理,只能摸黑起床开电脑……
这样的场景,你是否也经历过?
传统 .NET 系统与现代移动协同之间的鸿沟,正在悄悄吞噬着企业的效率。审批卡在桌面端、通知滞后、数据孤岛——这些问题让工作体验大打折扣。
推倒重来?成本太高,风险太大。
本文将带你走一条渐进式改造之路:保持 .NET 系统作为业务核心,将飞书审批作为移动门户,通过 API 实现无缝协同。从原理、设计到编码,完整呈现如何让传统系统焕发新生,实现移动化、实时化的现代化升级。
无论你是开发者、架构师还是技术管理者,都能收获一套可落地、可扩展的集成方案和直接复用的代码实践。
当传统业务遇上现代协同,为何必须"破壁"?
我们正在解决什么?
传统 .NET 系统的局限
许多企业拥有多年累积的 .NET 业务系统,这些系统在企业运营中扮演着核心角色。然而,随着移动办公和现代协同工具的普及,这些传统系统正面临着严峻的挑战:
| 痛点 | 具体表现 | 业务影响 |
|---|---|---|
| 审批流程封闭 | 审批只能在桌面端完成,无法随时随地处理 | 移动办公受阻,响应迟缓 |
| 通知方式滞后 | 依赖邮件或站内消息推送 | 审批人及时性差,流程延误 |
| 数据孤岛严重 | 审批数据与业务数据分离 | 无法形成完整的业务闭环 |
| 用户体验陈旧 | 界面风格陈旧,交互体验差 | 用户满意度低,使用意愿下降 |
飞书审批的赋能价值
飞书审批作为企业级的审批协作平台,为我们提供了一个理想的"流程协作中心":
我们的核心目标
通过本文的实践,我们将建立 ".NET 系统为业务核心,飞书审批为流程门户" 的现代化混合架构:
架构愿景:将飞书审批作为统一的移动审批门户,保持 .NET 系统作为业务逻辑和数据存储的核心,通过 API 实时同步,形成优势互补的协同体系。
你将收获什么?
- ✅ 一套端到端的集成方法论,覆盖从原理、设计到部署的全流程
- ✅ 清晰的 .NET 侧架构蓝图,包含关键的技术选型与设计决策
- ✅ 可直接复用的 C# 核心代码与实践中总结的"避坑指南"
- ✅ 一个完整的"请假审批"实战案例,助你从零到一完成验证
飞书审批开放平台如何与我们"对话"?
双向集成的关键流程
飞书审批与 .NET 系统的集成是一个双向数据流的过程:
流程输出(发起阶段)
当用户在 .NET 系统发起审批时,系统会:
- 保存业务数据,状态标记为"审批中"
- 调用飞书 API
CreateInstanceAsync创建审批实例 - 接收返回的
instance_code,持久化到关联表
流程输入(回调阶段)
当审批人在飞书 App 完成审批后:
- 飞书服务器主动回调 .NET 系统的 Webhook 接口
- .NET 系统解析事件,提取
instance_code和status - 根据关联表查询对应的业务记录
- 更新业务状态,完成闭环
必须理解的三个核心概念
审批定义(approval_code)
审批定义是审批流程的"蓝图",在飞书管理后台配置:
// 示例:请假审批的审批定义
var approvalCode = "7C468A54-8745-2245-9675-08B7C63E7A85";
定义包含:
- 表单结构(请假类型、开始时间、结束时间、请假事由等)
- 审批流程(直属主管审批 → 人事审批)
- 权限设置(谁可以发起、谁可以审批)
审批实例(instance_code)
审批实例是依据审批定义发起的一次具体审批任务:
// 创建审批实例时返回
public record CreateInstancesResult
{/// <summary>/// 审批实例 Code/// </summary>[JsonPropertyName("instance_code")]public string InstanceCode { get; set; } = string.Empty;
}
关键属性:
- 唯一标识一次审批流程
- 包含该次审批的所有表单数据
- 拥有独立的状态(审批中、通过、拒绝、撤回等)
身份映射(免登)
实现 .NET 系统用户与飞书用户的关联:
实现方式:
- 在 .NET 系统的用户表中添加
FeishuOpenId字段 - 用户首次登录时进行飞书免登录认证,获取并存储
open_id - 发起审批时,使用
open_id指定审批发起人
构建稳健、可扩展的 .NET 侧集成层
技术栈推荐
| 层次 | 技术选型 | 说明 |
|---|---|---|
| 应用框架 | .NET 6/8/10 | 长期支持版本,性能优异 |
| 飞书 SDK | Mud.Feishu | 高度封装的飞书 API 客户端 |
| Webhook 处理 | Mud.Feishu.Webhook | 飞书事件回调处理组件 |
| 认证授权 | ASP.NET Core Identity / JWT | 内部系统身份管理 |
| 异步解耦 | RabbitMQ / Hangfire | 回调消息队列处理,提升可靠性 |
| 数据存储 | SQL Server / PostgreSQL | 业务数据 + 审批关联表 |
分层架构图
关键设计:领域层抽象
在领域层引入 IApprovalService 接口,将飞书集成细节与核心业务逻辑解耦:
/// <summary>
/// 审批服务抽象接口 - 解耦飞书实现细节
/// </summary>
public interface IApprovalService
{/// <summary>/// 发起审批/// </summary>Task<string> CreateApprovalAsync(ApprovalRequest request);/// <summary>/// 处理审批结果回调/// </summary>Task HandleApprovalCallbackAsync(ApprovalCallbackEvent callbackEvent);
}/// <summary>
/// 飞书审批服务实现
/// </summary>
public class FeishuApprovalService : IApprovalService
{private readonly IFeishuTenantV4Approval _approvalApi;private readonly IApprovalRecordRepository _repository;public FeishuApprovalService(IFeishuTenantV4Approval approvalApi,IApprovalRecordRepository repository){_approvalApi = approvalApi;_repository = repository;}public async Task<string> CreateApprovalAsync(ApprovalRequest request){// 调用飞书 APIvar result = await _approvalApi.CreateInstanceAsync(...);return result.Data?.InstanceCode ?? string.Empty;}
}
优势:
- 业务逻辑不依赖具体飞书实现
- 便于单元测试(可 Mock 接口)
- 未来可轻松切换到其他审批平台
实战:手把手完成"请假审批"集成
第一步:飞书平台侧配置(审批流出口)
创建企业自建应用
登录飞书开放平台(https://open.feishu.cn),进入应用管理:
关键配置:
- 记录
App ID和App Secret - 配置应用权限:审批相关权限(
approval:approval:read,approval:instance:read,approval:instance:create)
配置审批定义
在飞书管理后台创建"请假审批"模板:
记录关键信息:
approval_code:审批定义的唯一标识- 表单控件的
id:用于程序填充表单数据
配置事件订阅
在飞书开放平台配置 Webhook:
| 配置项 | 值 |
|---|---|
| 请求网址 | https://your-domain.com/api/feishu/webhook |
| 验证 Token | your_verification_token(自定义) |
| 加密 Key | your_encrypt_key(自定义) |
| 订阅事件 | approval_instance(审批实例状态变更) |
第二步:.NET 侧基础搭建(集成基石)
封装飞书 API 客户端
基于 MudFeishu SDK 封装审批服务:
/// <summary>
/// 飞书审批服务封装
/// </summary>
public class FeishuApprovalClient
{private readonly IFeishuTenantV4Approval _approvalApi;private readonly ILogger<FeishuApprovalClient> _logger;public FeishuApprovalClient(IFeishuTenantV4Approval approvalApi,ILogger<FeishuApprovalClient> logger){_approvalApi = approvalApi;_logger = logger;}/// <summary>/// 创建审批实例/// </summary>public async Task<string> CreateInstanceAsync(CreateInstanceRequest request){var result = await _approvalApi.CreateInstanceAsync(request);if (result == null || result.Code != 0){_logger.LogError("创建审批实例失败: {Msg}", result?.Msg);throw new InvalidOperationException($"创建审批实例失败: {result?.Msg}");}_logger.LogInformation("创建审批实例成功: {InstanceCode}", result.Data?.InstanceCode);return result.Data?.InstanceCode ?? string.Empty;}/// <summary>/// 获取审批实例详情/// </summary>public async Task<GetApprovalInstanceResult?> GetInstanceAsync(string instanceCode){var result = await _approvalApi.GetInstanceByIdAsync(instanceCode);if (result == null || result.Code != 0){_logger.LogError("获取审批实例失败: {Msg}", result?.Msg);return null;}return result.Data;}
}
设计数据关联表
在业务数据库中添加审批关联表:
-- 审批关联表
CREATE TABLE ApprovalRecords (Id BIGINT PRIMARY KEY IDENTITY(1,1),BusinessType NVARCHAR(50) NOT NULL, -- 业务类型:LeaveRequest, PurchaseRequest...BusinessId BIGINT NOT NULL, -- 业务IDInstanceCode NVARCHAR(64) NOT NULL, -- 飞书审批实例CodeApprovalCode NVARCHAR(64) NOT NULL, -- 审批定义CodeStatus NVARCHAR(20) NOT NULL, -- 状态:PENDING, APPROVED, REJECTED...CallbackData NVARCHAR(MAX), -- 回调数据(JSON)CreatedTime DATETIME2 NOT NULL DEFAULT GETUTCDATE(),UpdatedTime DATETIME2 NOT NULL DEFAULT GETUTCDATE(),CONSTRAINT UK_ApprovalRecords_Business UNIQUE(BusinessType, BusinessId)
);CREATE INDEX IX_ApprovalRecords_InstanceCode ON ApprovalRecords(InstanceCode);
CREATE INDEX IX_ApprovalRecords_Status ON ApprovalRecords(Status);
对应的实体类:
/// <summary>
/// 审批关联记录
/// </summary>
public class ApprovalRecord
{public long Id { get; set; }public string BusinessType { get; set; } = string.Empty; // "LeaveRequest"public long BusinessId { get; set; } // 请假申请IDpublic string InstanceCode { get; set; } = string.Empty; // 飞书实例Codepublic string ApprovalCode { get; set; } = string.Empty; // 审批定义Codepublic string Status { get; set; } = string.Empty; // PENDING/APPROVED/REJECTEDpublic string? CallbackData { get; set; } // JSON格式public DateTime CreatedTime { get; set; }public DateTime UpdatedTime { get; set; }
}
第三步:核心业务流程编码(双向联通)
场景:用户提交请假单,发起审批
流程图:
代码实现:
/// <summary>
/// 请假服务
/// </summary>
public class LeaveService
{private readonly ILeaveRequestRepository _leaveRepo;private readonly IApprovalRecordRepository _approvalRepo;private readonly FeishuApprovalClient _feishuClient;private readonly ILogger<LeaveService> _logger;public LeaveService(ILeaveRequestRepository leaveRepo,IApprovalRecordRepository approvalRepo,FeishuApprovalClient feishuClient,ILogger<LeaveService> logger){_leaveRepo = leaveRepo;_approvalRepo = approvalRepo;_feishuClient = feishuClient;_logger = logger;}/// <summary>/// 提交请假申请并发起审批/// </summary>public async Task<long> SubmitLeaveRequestAsync(SubmitLeaveRequestDto dto){// 1. 保存请假业务数据var leaveRequest = new LeaveRequest{UserId = dto.UserId,LeaveType = dto.LeaveType,StartTime = dto.StartTime,EndTime = dto.EndTime,Days = dto.Days,Reason = dto.Reason,Status = LeaveStatus.Pending, // 审批中CreatedTime = DateTime.UtcNow};await _leaveRepo.AddAsync(leaveRequest);await _leaveRepo.SaveChangesAsync();// 2. 构造飞书审批表单数据var form = new List<object>{new { id = "leave_type", type = "select", value = dto.LeaveType },new { id = "start_time", type = "date", value = dto.StartTime.ToString("yyyy-MM-dd") },new { id = "end_time", type = "date", value = dto.EndTime.ToString("yyyy-MM-dd") },new { id = "days", type = "number", value = dto.Days.ToString() },new { id = "reason", type = "textarea", value = dto.Reason }};// 3. 调用飞书API创建审批实例var request = new CreateInstanceRequest{ApprovalCode = "7C468A54-8745-2245-9675-08B7C63E7A85", // 请假审批定义CodeUserId = dto.FeishuUserId, // 飞书用户IDForm = JsonSerializer.Serialize(form),Uuid = Guid.NewGuid().ToString() // 幂等ID};string instanceCode;try{instanceCode = await _feishuClient.CreateInstanceAsync(request);}catch (Exception ex){_logger.LogError(ex, "创建飞书审批实例失败");// 回滚业务数据leaveRequest.Status = LeaveStatus.Failed;await _leaveRepo.SaveChangesAsync();throw;}// 4. 保存审批关联记录var approvalRecord = new ApprovalRecord{BusinessType = "LeaveRequest",BusinessId = leaveRequest.Id,InstanceCode = instanceCode,ApprovalCode = request.ApprovalCode,Status = "PENDING",CreatedTime = DateTime.UtcNow,UpdatedTime = DateTime.UtcNow};await _approvalRepo.AddAsync(approvalRecord);await _approvalRepo.SaveChangesAsync();_logger.LogInformation("请假申请已提交并创建审批: LeaveId={LeaveId}, InstanceCode={InstanceCode}",leaveRequest.Id, instanceCode);return leaveRequest.Id;}
}
场景:审批完结,飞书回调通知结果
基于 Mud.Feishu.Webhook 实现安全的回调处理器
using Mud.Feishu.Abstractions;
using Mud.Feishu.Abstractions.DataModels.Approval;
using Mud.Feishu.Abstractions.EventHandlers;/// <summary>
/// 审批实例事件处理器
/// </summary>
public class ApprovalInstanceEventHandler : ApprovalInstanceEventHandler
{private readonly IApprovalRecordRepository _approvalRepo;private readonly ILeaveRequestRepository _leaveRepo;public ApprovalInstanceEventHandler(ILogger<ApprovalInstanceEventHandler> logger,IApprovalRecordRepository approvalRepo,ILeaveRequestRepository leaveRepo): base(logger){_approvalRepo = approvalRepo;_leaveRepo = leaveRepo;}/// <summary>/// 处理审批实例事件业务逻辑/// </summary>protected override async Task ProcessBusinessLogicAsync(EventData eventData,ObjectEventResult<ApprovalInstanceResult>? eventEntity,CancellationToken cancellationToken = default){if (eventEntity?.Object == null){_logger.LogWarning("审批实例事件数据无效");return;}var approvalEvent = eventEntity.Object;_logger.LogInformation("收到审批实例事件: InstanceCode={InstanceCode}, Status={Status}",approvalEvent.InstanceCode, approvalEvent.Status);// 幂等性处理:检查是否已处理过该事件var existingRecord = await _approvalRepo.GetByInstanceCodeAsync(approvalEvent.InstanceCode ?? string.Empty);if (existingRecord != null && existingRecord.Status == approvalEvent.Status){_logger.LogInformation("该事件已处理过,跳过: EventId={EventId}", eventData.EventId);return;}// 根据业务类型处理审批结果await ProcessApprovalResultAsync(eventData, approvalEvent, cancellationToken);}/// <summary>/// 处理审批结果/// </summary>private async Task ProcessApprovalResultAsync(EventData eventData,ApprovalInstanceResult approvalEvent,CancellationToken cancellationToken){if (string.IsNullOrEmpty(approvalEvent.InstanceCode)){_logger.LogWarning("审批实例Code为空,跳过处理");return;}// 查询审批关联记录var approvalRecord = await _approvalRepo.GetByInstanceCodeAsync(approvalEvent.InstanceCode);if (approvalRecord == null){_logger.LogWarning("未找到审批关联记录: InstanceCode={InstanceCode}",approvalEvent.InstanceCode);return;}// 更新审批记录状态approvalRecord.Status = approvalEvent.Status ?? string.Empty;approvalRecord.CallbackData = JsonSerializer.Serialize(approvalEvent);approvalRecord.UpdatedTime = DateTime.UtcNow;await _approvalRepo.SaveChangesAsync();// 根据业务类型处理switch (approvalRecord.BusinessType){case "LeaveRequest":await ProcessLeaveApprovalAsync(approvalRecord, approvalEvent.Status ?? string.Empty);break;// 可扩展其他业务类型default:_logger.LogWarning("未知的业务类型: {BusinessType}", approvalRecord.BusinessType);break;}}/// <summary>/// 处理请假审批结果/// </summary>private async Task ProcessLeaveApprovalAsync(ApprovalRecord approvalRecord, string status){var leaveRequest = await _leaveRepo.GetByIdAsync(approvalRecord.BusinessId);if (leaveRequest == null){_logger.LogWarning("未找到请假申请: BusinessId={BusinessId}", approvalRecord.BusinessId);return;}// 根据审批状态更新请假记录leaveRequest.Status = status switch{"APPROVED" => LeaveStatus.Approved,"REJECTED" => LeaveStatus.Rejected,"CANCELED" => LeaveStatus.Canceled,"DELETED" => LeaveStatus.Deleted,_ => LeaveStatus.Pending};leaveRequest.UpdatedTime = DateTime.UtcNow;await _leaveRepo.SaveChangesAsync();_logger.LogInformation("请假申请状态已更新: LeaveId={LeaveId}, Status={Status}",leaveRequest.Id, leaveRequest.Status);// TODO: 发送通知给申请人// TODO: 同步到考勤系统}
}
注册 Webhook 服务(Program.cs):
using Mud.Feishu.Webhook;
using Mud.Feishu;
using YourApp.Handlers;var builder = WebApplication.CreateBuilder(args);// 注册飞书 API 服务
builder.Services.AddFeishuServices().ConfigureFrom(builder.Configuration) // 从 "Feishu" 配置节读取.Build();// 注册飞书 Webhook 事件订阅服务
builder.Services.AddFeishuWebhookServiceBuilder().ConfigureFrom(builder.Configuration) // 从 "FeishuWebhook" 配置节读取.AddHandler<ApprovalInstanceEventHandler>() // 添加审批事件处理器.Build();// 注册业务服务
builder.Services.AddScoped<ILeaveRequestRepository, LeaveRequestRepository>();
builder.Services.AddScoped<IApprovalRecordRepository, ApprovalRecordRepository>();var app = builder.Build();app.UseFeishuWebhook(); // 添加 Webhook 中间件app.Run();
说明:
ApprovalInstanceEventHandler继承自ApprovalInstanceEventHandler基类- 基类已经实现了
HandleAsync方法,会自动反序列化ApprovalInstanceResult类型的事件数据 - 只需重写
ProcessBusinessLogicAsync方法实现具体的业务逻辑即可 - SDK 会根据
SupportedEventType属性自动路由对应的事件到这个处理器 AddFeishuServices()注册飞书 API 客户端服务,使用Feishu配置节AddFeishuWebhookServiceBuilder()注册 Webhook 事件订阅服务,使用FeishuWebhook配置节
配置文件(appsettings.json):
{// 飞书 Webhook 事件订阅配置"FeishuWebhook": {"VerificationToken": "your_verification_token","EncryptKey": "your_encrypt_key","RoutePrefix": "api/feishu/webhook","AutoRegisterEndpoint": true,"EnableRequestLogging": true,"EnableExceptionHandling": true,"EventHandlingTimeoutMs": 30000,"MaxConcurrentEvents": 10},// 飞书 API 客户端配置"Feishu": {"AppId": "your_app_id","AppSecret": "your_app_secret","BaseUrl": "https://open.feishu.cn","TimeOut": "30","RetryCount": 3,"EnableLogging": true}
}
第四步:功能完善与联调测试
状态同步展示
在请假列表页展示审批状态:
/// <summary>
/// 请假列表响应DTO
/// </summary>
public class LeaveRequestDto
{public long Id { get; set; }public DateTime StartTime { get; set; }public DateTime EndTime { get; set; }public int Days { get; set; }public string Status { get; set; } = string.Empty; // 业务状态:Approved, Rejectedpublic string ApprovalStatus { get; set; } = string.Empty; // 飞书审批状态:APPROVED, REJECTED, PENDINGpublic string? FeishuInstanceUrl { get; set; } // 飞书审批详情链接public DateTime CreatedTime { get; set; }
}/// <summary>
/// 查询请假列表
/// </summary>
public async Task<List<LeaveRequestDto>> GetLeaveListAsync(long userId)
{var leaves = await _leaveRepo.GetByUserIdAsync(userId);var instanceCodes = leaves.Select(l => l.InstanceCode).ToList();// 批量查询审批记录var approvals = await _approvalRepo.GetByInstanceCodesAsync(instanceCodes);var result = leaves.Select(leave =>{var approval = approvals.FirstOrDefault(a => a.InstanceCode == leave.InstanceCode);return new LeaveRequestDto{Id = leave.Id,StartTime = leave.StartTime,EndTime = leave.EndTime,Days = leave.Days,Status = leave.Status.ToString(),ApprovalStatus = approval?.Status ?? "UNKNOWN",FeishuInstanceUrl = !string.IsNullOrEmpty(approval?.InstanceCode)? $"https://www.feishu.cn/approval/approval/view/{approval.InstanceCode}": null,CreatedTime = leave.CreatedTime};}).ToList();return result;
}
添加"在飞书中查看"链接
在列表页添加操作按钮:
<!-- 前端页面示例 -->
<table class="leave-list"><thead><tr><th>开始时间</th><th>结束时间</th><th>天数</th><th>审批状态</th><th>操作</th></tr></thead><tbody>@foreach (var leave in Model.Leaves){<tr><td>@leave.StartTime.ToString("yyyy-MM-dd")</td><td>@leave.EndTime.ToString("yyyy-MM-dd")</td><td>@leave.Days</td><td><span class="status @leave.ApprovalStatus">@GetStatusText(leave.ApprovalStatus)</span></td><td>@if (!string.IsNullOrEmpty(leave.FeishuInstanceUrl)){<a href="@leave.FeishuInstanceUrl" target="_blank" class="btn">在飞书中查看</a>}</td></tr>}</tbody>
</table>
联调测试
| 测试场景 | 验证要点 | 测试工具 |
|---|---|---|
| 发起审批 | 飞书是否收到审批通知、表单数据是否正确 | 直接在系统发起 |
| 审批流程 | 各审批节点是否正确流转 | 飞书管理后台 |
| 回调接收 | Webhook是否正确接收事件、数据是否完整 | 飞书"模拟事件推送"工具 |
| 状态同步 | 业务状态是否正确更新、通知是否发送 | 数据库查询、日志查看 |
| 异常处理 | 网络异常、签名验证失败等边界情况 | 模拟异常场景 |
飞书模拟事件推送工具:
在飞书开放平台的"事件订阅"页面,可以使用"模拟事件推送"功能测试 Webhook 接口:
生产级注意事项
安全与可靠性
机密管理
切勿将敏感信息硬编码在代码中!
// ❌ 错误示例
var appSecret = "cli_xxxxxxxxxxxxxxx"; // 危险!// ✅ 正确示例
builder.Configuration.AddAzureKeyVault(new Uri($"https://{vaultName}.vault.azure.net/"),new DefaultAzureCredential());var appSecret = builder.Configuration["Feishu:AppSecret"];
推荐方案:
- Azure Key Vault / AWS Secrets Manager
- HashiCorp Vault
- Docker Secrets(容器化部署)
幂等性处理
飞书可能会重复推送同一个事件(网络重试等),必须保证业务逻辑的幂等性:
public async Task HandleAsync(EventData eventData, CancellationToken cancellationToken = default)
{// 使用 EventId 或 instance_code + status 组合作为幂等键var idempotencyKey = $"{approvalEvent.InstanceCode}_{approvalEvent.Status}";// 检查是否已处理过if (await _cache.ExistsAsync(idempotencyKey)){_logger.LogInformation("事件已处理过,跳过: Key={IdempotencyKey}", idempotencyKey);return;}// 标记为已处理(设置过期时间,如24小时)await _cache.SetAsync(idempotencyKey, "1", TimeSpan.FromHours(24));// 执行业务逻辑await ProcessEventAsync(approvalEvent, cancellationToken);
}
API 容错
使用 Polly 为飞书 API 调用添加重试和熔断机制:
// 注册 HttpClient 时添加 Polly 策略
builder.Services.AddHttpClient("Feishu").AddTransientHttpErrorPolicy(p =>p.WaitAndRetryAsync(3, retryAttempt =>TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))).AddPolicyHandler(Policy<HttpResponseMessage>.Handle<HttpRequestException>().CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));
边界情况与优雅降级
审批人失效处理
// 飞书审批定义中配置默认审批人
var request = new CreateInstanceRequest
{ApprovalCode = "xxxx",// 如果自选审批人为空,使用默认审批人NodeApproverUserIdLists = dto.ApproverUserId.HasValue? new[] { new NodeApprover { NodeId = "node1", ApproverUserIds = new[] { dto.ApproverUserId.Value } } }: null // 走默认审批人流程
};
网络超时与异步处理
回调处理应快速响应飞书(建议在 3 秒内),复杂逻辑移至后台作业:
public async Task HandleAsync(EventData eventData, CancellationToken cancellationToken = default)
{// 1. 快速保存事件到队列await _eventQueue.EnqueueAsync(eventData);// 2. 立即返回,由后台作业处理// Hangfire、RabbitMQ 等会异步消费队列await Task.CompletedTask;
}// 后台作业处理
[Queue("approval-callback")]
public async Task ProcessApprovalEventAsync(EventData eventData)
{// 复杂的业务逻辑处理await _approvalService.ProcessCallbackAsync(eventData);
}
监控与告警
建立关键节点的监控:
// 监控指标
public class ApprovalMetrics
{private readonly Counter _approvalCreatedCounter;private readonly Counter _callbackReceivedCounter;private readonly Histogram _processingTimeHistogram;public void RecordApprovalCreated(string approvalType){_approvalCreatedCounter.WithLabels(approvalType).Inc();}public void RecordCallbackReceived(string status){_callbackReceivedCounter.WithLabels(status).Inc();}public void RecordProcessingTime(TimeSpan duration){_processingTimeHistogram.Observe(duration.TotalSeconds);}
}// 告警规则(Prometheus 示例)
# 审批发起失败率超过 5% 触发告警
alert: ApprovalCreationFailureRate
expr: rate(approval_creation_failed_total[5m]) / rate(approval_creation_total[5m]) > 0.05
for: 5m
annotations:summary: "审批创建失败率过高"
扩展性与维护性
策略模式支持多平台
/// <summary>
/// 审批平台策略接口
/// </summary>
public interface IApprovalPlatformStrategy
{string PlatformName { get; }Task<string> CreateInstanceAsync(ApprovalRequest request);
}/// <summary>
/// 飞书审批策略
/// </summary>
public class FeishuApprovalStrategy : IApprovalPlatformStrategy
{public string PlatformName => "Feishu";// ... 实现
}/// <summary>
/// 钉钉审批策略
/// </summary>
public class DingTalkApprovalStrategy : IApprovalPlatformStrategy
{public string PlatformName => "DingTalk";// ... 实现
}/// <summary>
/// 审批策略工厂
/// </summary>
public class ApprovalStrategyFactory
{private readonly IEnumerable<IApprovalPlatformStrategy> _strategies;public IApprovalPlatformStrategy GetStrategy(string platformName){return _strategies.FirstOrDefault(s => s.PlatformName == platformName)?? throw new NotSupportedException($"不支持的审批平台: {platformName}");}
}
审计日志
详细记录审批流转换的关键日志:
public class ApprovalAuditService
{private readonly IApprovalAuditRepository _auditRepo;public async Task LogAsync(ApprovalAuditLog log){log.Timestamp = DateTime.UtcNow;await _auditRepo.AddAsync(log);await _auditRepo.SaveChangesAsync();// 结构化日志输出_logger.LogInformation("审批审计: {AuditType}, InstanceCode={InstanceCode}, BusinessId={BusinessId}",log.AuditType, log.InstanceCode, log.BusinessId);}
}// 使用示例
await _auditService.LogAsync(new ApprovalAuditLog
{AuditType = "ApprovalStarted",InstanceCode = instanceCode,BusinessId = leaveRequest.Id,OperatorId = userId,Details = new { leaveType, days, reason }
});
最后一点内容
核心价值
通过本文的实践,我们成功实现了:
| 价值点 | 实现方式 | 收益 |
|---|---|---|
| 移动化审批 | 飞书 App 作为审批门户 | 随时随地处理审批 |
| 即时通知 | 飞书强通知机制 | 审批人及时响应 |
| 数据闭环 | .NET 业务库 + 审批关联表 | 完整的业务流程追踪 |
| 解耦设计 | 领域层抽象 + 策略模式 | 便于扩展和维护 |
全文总结
本文提供了一个从理念、设计到编码落地的完整闭环:
扩展
场景扩展
将此模式快速复用于其他业务场景:
| 业务场景 | 审批流程 | 复杂度 |
|---|---|---|
| 报销审批 | 发起 → 直属主管 → 财务审核 | 中 |
| 采购申请 | 发起 → 部门主管 → 采购部 → 总经理 | 高 |
| 合同审批 | 法务审核 → 财务审核 → 总经理 | 高 |
| 加班申请 | 直属主管审批 | 低 |
深度集成
利用飞书更多能力,打造更丰富的协同体验:
功能扩展示例:
- 在飞书群聊中通过消息卡片直接查看审批详情
- 通过机器人智能回复,引导用户填写审批表单
- 将审批记录同步到飞书知识库,方便查阅
平台化
将审批集成能力抽象为中台服务,供企业内部所有系统统一调用:
/// <summary>
/// 统一审批服务中台
/// </summary>
public interface IApprovalCenterService
{/// <summary>/// 统一发起审批(支持多平台)/// </summary>Task<string> CreateApprovalAsync(UnifiedApprovalRequest request);/// <summary>/// 查询审批状态/// </summary>Task<ApprovalStatus> GetStatusAsync(string instanceId);/// <summary>/// 审批统计报表/// </summary>Task<ApprovalStatistics> GetStatisticsAsync(DateTime from, DateTime to);
}// 多个系统统一调用
await _approvalCenter.CreateApprovalAsync(new UnifiedApprovalRequest
{BusinessSystem = "HR",BusinessType = "LeaveRequest",BusinessId = leaveId,Platform = "Feishu" // 可切换到其他平台
});
结语
传统 .NET 系统无需推倒重来,通过合理的架构设计与飞书审批的深度集成,同样可以焕发新的活力。希望本文的实践能够为你的数字化转型之路提供有价值的参考。
让我们一起告别信息孤岛,拥抱现代化的协同办公体验!🚀
相关资源
项目地址
-
Gitee 仓库:https://gitee.com/mudtools/MudFeishu
-
GitHub 仓库:https://github.com/mudtools/MudFeishu
-
NuGet 包:
-
Mud.Feishu.Abstractions
-
Mud.Feishu
-
Mud.Feishu.WebSocket
-
Mud.Feishu.Webhook
-