“这审批流到底什么时候能上线?业务那边已经催了三次了。”这是我两周前在项目群里看到的消息。一个运行了快十年的.NET Framework老系统,要加一套完整的审批流程,业务方点名要求“手机上能批、能看进度、最好跟飞书消息打通”。说实话,第一反应是头疼——老系统改工作流引擎,牵扯到数据库表、状态机、权限体系,没两三个月下不来。但后来我们换了个思路:不自己造审批流,直接把飞书审批流接进来,系统只负责发起审批和接收结果。整个集成从动手到上线,两个星期。这篇文章就把这次集成的完整思路、关键代码和踩过的坑整理出来,给同样被老系统审批需求困扰的朋友一个参考。
1. 为什么传统.NET系统要接飞书审批流
1.1 自建审批流的老路有多疼
我见过太多团队在自建审批流上栽跟头。业务刚开始提需求时,听起来很简单:提交一个申请,主管审批,完了归档。但一旦开始设计数据库,问题就来了——审批节点怎么存?审批人怎么配?会签还是或签?驳回之后是回到上一级还是退回发起人?加签、转交、催办、超时自动处理怎么办?这些还只是引擎层面的。等系统上线,移动端适配又成了新问题,总不能要求审批人天天开着电脑点审批。
当时我们盘了一下,这套自建方案如果做完整,光后端服务加前端页面,保守估计三个人干两个月。而且老系统的技术栈是.NET Framework 4.7.2 + Web Forms,团队里已经没人愿意在这个基础上继续堆业务代码。业务部门的需求其实很朴素——能有个地方审批,审批人能收到通知,能在手机上处理。他们要的从来不是审批流引擎,而是审批这件事能被高效完成。
1.2 飞书审批流能提供什么
飞书审批这个模块,底层是一个完整的工作流引擎,节点类型、审批人规则、消息通知、移动端、PC端、操作记录、导出报表,这些都是现成的。作为开发者,我们不需要去理解引擎内部的实现,只需要通过API做三件事:发起审批实例、查询审批结果、接收审批状态回调。
这种集成方式的好处是显而易见的。第一,开发量大幅减少,核心代码量也就几百行。第二,审批体验是业界标准,员工用飞书就能处理审批,不需要重新学习一套后台系统。第三,后续维护成本低,飞书侧的更新迭代由官方负责,我们只需要保证接口稳定。第四,消息通知天然打通,审批人收到卡片消息,点击直接跳转审批详情,这个体验自建系统很难做好。
更重要的是,飞书审批流是“外部系统可嵌入”的。通过开放平台的API,可以把审批实例嵌入到任何系统的业务流程中——老系统里点提交,飞书里走审批,审批结果再通过回调推回老系统。整个过程对老系统来说是异步解耦的,不会因为审批引擎的负载影响主业务。
1.3 什么样的场景适合这种方案
并不是所有审批需求都适合接飞书。我个人的判断标准很简单:如果审批流程主要用于内部管理、审批人都是企业内部员工、审批过程需要良好的移动端体验,那接飞书就是性价比最高的方案。比如费用报销审批、采购申请、请假加班、合同会签、用章申请这类场景,流程本身不复杂,但对通知能力和移动端体验要求高,非常适合。
反过来,如果审批是核心业务的一部分,需要跟业务数据深度绑定,比如订单审批通过后要自动触发下游系统、需要灵活的审批路由策略、审批过程中要动态展示业务数据,那可能还是要做自建引擎。但在实际项目中,80%的审批需求都没到这个复杂度,用飞书完全够用。我们这次做的就是一个设备领用审批,需要关联设备编号、使用人、存放地点这些字段,流程只有两级审批,飞书审批流完全能覆盖。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 集成前的准备:应用创建与权限配置
2.1 创建企业自建应用
去飞书开放平台(open.feishu.cn),用管理员账号登录,进入“开发者后台”,点击“创建应用”,选择“企业自建应用”。这里需要填应用名称和描述,名称建议用业务相关的名字,比如“设备管理系统-审批助手”,这样在审批列表里用户能一眼认出来这个审批来自哪个系统。
创建完成后,进入应用详情页,有几个关键信息需要先记下来:App ID、App Secret。这两个参数相当于应用在飞书开放平台上的账号和密码,后续调用所有API都需要用到。注意App Secret一定不能泄露到前端代码或者公共代码仓库里,我们团队甚至会在代码评审时专门检查这个。
还有一个细节,如果企业用的是飞书专业版或旗舰版,开放平台的接口权限会更全,审批接口属于“审批”应用能力。如果你发现某个接口返回“权限不足”,多半不是代码问题,而是应用没开通对应权限,或者企业版本不支持。
2.2 权限点申请与发布
在应用详情页左侧菜单里找到“权限管理”,搜索审批相关的权限点。我们需要重点关注这几个权限:
- approval:approval(查询审批定义)
- approval:approval_instance(创建和查询审批实例)
- contact:user.base:readonly(读取用户基本信息,用于匹配审批人)
点击“开通”后,权限状态会变成“申请中”。这里有个容易踩的坑:自建应用的权限申请需要企业管理员审核。如果是在开发环境调试,建议直接用管理员账号创建应用并自行审核通过;如果是在客户现场,需要提前跟客户的管理员打好招呼,避免权限卡住导致项目延期。
权限通过后,还需要“创建版本并发布”。这一步是很多新手容易忽略的——权限开了,但应用还没发布,调用API时依然会提示“应用未发布”。发布流程是:在“版本管理与发布”页面创建版本,填写版本号、更新说明,然后提交发布。企业自建应用发布后,企业内所有用户就都能看到这个应用了,包括审批流里会用到的审批人。
2.3 事件订阅与回调地址配置
审批结果不能全靠轮询,飞书支持通过事件订阅主动推送审批状态变化。在应用的“事件与回调”页面,添加“审批实例状态变更”事件(approval_instance_status_change)。配置这个事件有两个关键参数:请求地址(Request URL)和Encrypt Key。
请求地址必须是一个公网可访问的HTTPS地址,飞书服务器会向这个地址发送事件通知。Encrypt Key用于事件内容的加密解密,飞书推送的事件内容默认是加密的,需要在服务端解密后才能读取明文。
这里提醒一下,开发调试阶段如果没有公网地址,可以用内网穿透工具把本地服务暴露到公网,但要注意安全。正式环境建议部署在公司的API网关后面,确保飞书的请求能被正确路由到.NET服务。请求地址的校验机制是飞书先发送一个验证请求,服务端需要按指定规则返回加密后的challenge,校验通过后订阅才算生效。这个校验逻辑后面会详细说。
3. .NET侧的核心对接细节
3.1 获取tenant_access_token
飞书开放平台API的鉴权方式是Bearer Token,需要先调用接口获取tenant_access_token。这个Token的有效期是2小时,过期后需要重新获取。为了保证性能,我们会在服务端做缓存,而不是每次调用都去获取。
Token获取接口很简单,用App ID和App Secret换。但有几个细节需要注意:一是Token接口本身有频率限制,频繁调用会返回错误码99991663;二是多实例部署时,如果每个实例各自缓存一份Token,可能会出现并发获取导致限流的问题。我们的做法是引入一个简单的分布式锁,保证同一时间只有一个实例去刷新Token。
csharp复制public class FeishuTokenService
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IMemoryCache _cache;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
public async Task<string> GetTenantAccessTokenAsync()
{
if (_cache.TryGetValue("feishu_tenant_token", out string token))
return token;
await _semaphore.WaitAsync();
try
{
if (_cache.TryGetValue("feishu_tenant_token", out token))
return token;
var client = _httpClientFactory.CreateClient("feishu");
var request = new
{
app_id = _options.AppId,
app_secret = _options.AppSecret
};
var response = await client.PostAsJsonAsync(
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
request);
var result = await response.Content.ReadFromJsonAsync<TokenResponse>();
_cache.Set("feishu_tenant_token", result.TenantAccessToken,
TimeSpan.FromSeconds(result.Expire - 300));
return result.TenantAccessToken;
}
finally
{
_semaphore.Release();
}
}
}
缓存时间设置为过期时间减5分钟,是留出余量避免边界失效。这里用的是.NET 6的IMemoryCache和SemaphoreSlim组合,如果你还在.NET Framework上,可以用MemoryCache类和lock语句,原理一致。
3.2 发起审批实例
发起审批实例是集成的核心步骤。调用接口为POST /open-apis/approval/v4/instances,需要在请求头中带上tenant_access_token。
发起审批前,需要先获取审批定义(approval_code)。审批定义可以在飞书管理后台创建,也可以在代码里通过创建审批定义接口动态创建。如果审批流程比较固定,建议直接在管理后台配置好,代码里写死approval_code即可;如果需要根据业务动态创建不同流程,那就用代码创建。
发起审批时最核心的是form数据,也就是审批表单里的字段。这个字段结构必须跟审批定义里配置的表单保持一致,否则会报错。我碰到过最多的问题就是表单字段类型不匹配——比如审批定义里配置的是“数字”输入框,提交时却传了字符串,飞书会直接返回错误码。
csharp复制public async Task<CreateInstanceResult> CreateApprovalInstanceAsync(
string approvalCode, string userId, string deptId,
Dictionary<string, object> formValues, string bizId)
{
var token = await _tokenService.GetTenantAccessTokenAsync();
var client = _httpClientFactory.CreateClient("feishu");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var form = formValues.Select(kv => new
{
name = kv.Key,
value = kv.Value != null ? kv.Value.ToString() : ""
}).ToList();
var request = new
{
approval_code = approvalCode,
user_id = userId,
dept_id = deptId,
form = form,
open_id = bizId // 业务唯一标识,用于关联老系统数据
};
var response = await client.PostAsJsonAsync(
"/open-apis/approval/v4/instances", request);
var result = await response.Content.ReadFromJsonAsync<ApiResult<InstanceData>>();
if (result.Code != 0)
throw new FeishuApiException(result.Code, result.Msg);
return result.Data;
}
表单字段的name必须是审批定义里字段的控件ID,而不是显示的标签名。在管理后台配置审批时,每个字段都可以设置一个“自定义ID”,建议把自定义ID设置为有意义的英文标识,比如device_code、applicant_remark,这样代码里引用起来比较清晰。
3.3 接收审批结果回调
审批实例状态变化后,飞书会向配置的回调地址推送事件。接收回调的接口需要处理好两件事:一是URL验证(验签),二是事件处理。
URL验证的逻辑是:飞书发送GET请求到回调地址,带上参数challenge、token、type。我们需要用encrypt_key对challenge进行AES解密,然后原样返回。这里有个坑,很多人会忘记把解密后的challenge再加密回去,导致验证永远不通过。实际上验签接口的返回格式是固定的,需要用一个特定的JSON结构。
事件推送的body是加密的JSON字符串,需要先解密。解密算法是AES-256-CBC,密钥是Encrypt Key的SHA256摘要,IV是密钥的前16字节。这个过程在代码里实现起来有些绕,但好在飞书官方提供了C# SDK,可以直接用SDK里的解密方法。
csharp复制[HttpPost("callback")]
public async Task<IActionResult> ReceiveCallback()
{
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
// 判断是否是URL验证请求
var challengeRequest = JsonSerializer.Deserialize<ChallengeRequest>(body);
if (!string.IsNullOrEmpty(challengeRequest.Challenge))
{
var encrypted = FeishuCryptography.Encrypt(
challengeRequest.Challenge, _options.EncryptKey);
return Content(JsonSerializer.Serialize(new { challenge = encrypted }),
"application/json");
}
// 解密事件内容
var eventBody = FeishuCryptography.Decrypt(
challengeRequest.Encrypt, _options.EncryptKey);
var approvalEvent = JsonSerializer.Deserialize<ApprovalEvent>(eventBody);
// 根据事件类型处理
if (approvalEvent.EventType == "approval_instance_status_change")
{
var data = approvalEvent.InstanceStatusChange;
await _approvalService.HandleStatusChangeAsync(data);
}
return Ok();
}
处理完事件后,需要返回HTTP 200,否则飞书会认为推送失败并重试。飞书的重试策略是:第一次失败后间隔2分钟重试,之后每次翻倍,最多重试3次。如果重试也失败,事件会进入“失败事件”列表,去开放平台后台可以看到失败记录并手动重推。这个机制在实际项目中帮大忙了,至少不会因为服务重启丢事件。
3.4 用户关联与免登集成
审批实例里指定审批人时,需要传飞书用户ID。但老系统的用户体系里存的是员工工号,怎么把工号转成飞书user_id?我们通过“获取用户ID信息”接口,用手机号或邮箱从通讯录里查。这个接口一次可以批量查多个用户,效率很高。
还有一种情况:老系统内部的“审批人”是角色,比如“部门经理”。这时需要在代码里先通过老系统的组织架构找到具体人员,再获取对应的飞书user_id。我建议在集成前先做一次用户ID映射表,把老系统的工号和飞书user_id对应关系落到一张表里,之后发起审批时直接查表,避免频繁调用通讯录接口。
还有一个关于免登的问题。如果老系统也想用飞书扫码登录,可以走飞书的免登授权流程。飞书免登的原理是:前端跳转到飞书授权页,用户确认后获得授权码,后端用授权码换用户身份。这样可以实现“打开老系统网页,用飞书扫码即可登录”。我们在这次集成中把免登也做了,这样业务人员不用记两套账号密码,登录老系统用飞书扫一下就行。但这个功能需要额外配置重定向URL和权限,工作量不大,但对体验提升明显。
4. 实操过程:从零跑通一条审批
4.1 开发环境准备
我建议先把环境分三层:开发环境、测试环境、生产环境。在开发环境里,回调地址可以用内网穿透工具,比如natapp或frp,把本地IIS Express暴露到一个临时域名,方便本地调试。但要注意,飞书回调要求HTTPS,内网穿透工具免费版一般只支持HTTP,需要开通HTTPS映射或者用带HTTPS支持的版本。
代码层面,需要在NuGet里引入几个包:Microsoft.Extensions.Http(用于IHttpClientFactory)、Newtonsoft.Json或者System.Text.Json(用于序列化)。如果是.NET Framework老项目,建议用HttpClient类而不是WebClient,因为HttpClient支持更好的超时和并发控制。SDK方面,飞书官方提供了FeishuSDK的C#版本,但版本更新比较快,如果不想引包、担心SDK不稳定,完全可以自己封装,核心接口不多,我们这次就是全部手写的,代码量也就三百多行。
配置项建议集中放在appsettings.json或web.config里,包括AppId、AppSecret、EncryptKey、审批定义编码、回调路由地址。注意生产环境的配置要覆盖到,不要出现测试环境的回调地址被推到生产的情况。我习惯在启动时打印配置的来源,这样排查问题能迅速定位环境错了没有。
4.2 发起审批的完整链路
以一个“设备领用申请”为例,完整跑通一次发起审批。老系统页面里,用户填写设备编号、使用人、存放地点、申请说明,点击提交。后端收到请求后,做四件事:
第一步,从老系统数据库查出当前登录用户的工号,再通过映射表找到飞书user_id,同时查出他的直属上级作为审批人。第二步,构造审批表单数据,字段名是审批定义里的自定义ID,比如device_code对应“设备编号”、device_user对应“使用人”。第三步,调用创建审批实例接口,把业务单据号biz_id传给飞书,方便后续关联。第四步,把飞书返回的审批实例ID(instance_code)存到老系统的业务表里,用于后续查询状态。
这里有一个重要的设计决策:老系统自己的业务表一定要加一个字段存instance_code。后续查看审批进度、取消审批、关联业务数据,都靠这个ID。如果业务单据对应多张审批单(比如一单多批),还要考虑一对多的存储结构。
csharp复制// 业务侧保存审批关联信息
var instanceResult = await _feishuApprovalService.CreateApprovalInstanceAsync(
approvalCode: "approval_device_use",
userId: applicantFeishuId,
deptId: deptId,
formValues: new Dictionary<string, object>
{
["device_code"] = request.DeviceCode,
["device_use_user"] = request.UseUser,
["device_location"] = request.Location,
["apply_reason"] = request.Reason
},
bizId: request.BizId
);
await _deviceApplyRepository.SaveApprovalInstanceAsync(
bizId: request.BizId,
instanceCode: instanceResult.InstanceCode,
status: "PENDING"
);
审批发起后,业务人员就能在飞书工作台里看到审批卡片。卡片上的字段就是我们在form里传的那些值。审批人可以在手机上直接同意或拒绝,也可以在飞书里查看审批流程图、发表评论。整个过程不需要打开老系统页面,这就把审批动作从PC端解放出来了。
4.3 回调接收与业务处理
审批结果通过回调推回老系统后,需要更新老系统的业务状态。我们定义了一个处理服务,订阅审批实例状态变更事件。状态变更有四种:PENDING(审批中)、APPROVED(已通过)、REJECTED(已拒绝)、CANCELED(已撤回)。
处理逻辑里最需要关注的是幂等性。飞书的事件推送不保证只推一次,网络异常时可能重复推送。所以处理回调时,一定要先检查这条instance_code是不是已经处理过了。我们的做法是在数据库里加一张事件处理记录表,唯一键是instance_code + event_id,处理成功后才写入。如果发现重复事件,直接返回成功,不再重复处理。
csharp复制public async Task HandleStatusChangeAsync(InstanceStatusChangeEvent data)
{
var bizId = await _approvalInstanceRepository.GetBizIdByInstanceCodeAsync(
data.InstanceCode);
if (bizId == null)
{
_logger.LogWarning("未找到关联的业务单据: {InstanceCode}", data.InstanceCode);
return;
}
// 幂等检查
if (await _eventProcessRepository.IsProcessedAsync(data.EventId))
return;
switch (data.Status)
{
case "APPROVED":
await _deviceApplyRepository.UpdateStatusAsync(bizId, "APPROVED");
break;
case "REJECTED":
await _deviceApplyRepository.UpdateStatusAsync(bizId, "REJECTED");
break;
case "CANCELED":
await _deviceApplyRepository.UpdateStatusAsync(bizId, "CANCELED");
break;
}
await _eventProcessRepository.MarkProcessedAsync(data.EventId);
}
另外,审批通过后如果需要通知老系统其他模块做后续动作,比如更新库存、生成出库单,可以引用事件总线或消息队列。我们这里直接用了一个简单的后台任务轮询待处理列表,避免引入额外的消息中间件。如果老系统里有现成的MQ也完全可以,回调处理代码只负责更新业务状态,具体的后续动作异步执行,防止回调接口超时。
4.4 在Web Forms老项目中如何落地
如果你手里的老系统是Web Forms,没有ASP.NET Core那样的中间件管道,接收回调一样可以做。在Web Forms里添加一个一般处理程序(.ashx)或Page方法,重写ProcessRequest方法,手动解析请求体,返回JSON结果即可。请求进来后,先读InputStream,然后按同样的解密逻辑处理。返回响应时,设置ContentType为application/json,用Response.Write输出结果。
老项目里的HttpClient用法也略有不同,需要一个静态单例而不是每次new,避免端口耗尽。还要注意.NET Framework默认的TLS版本比较老,飞书接口要求TLS 1.2以上,记得在Application_Start里加上ServicePointManager.SecurityProtocol设置,否则可能报“请求被中止:未能创建SSL/TLS安全通道”。这个问题几乎每个接外部API的.NET Framework项目都会遇到,基本是必踩的坑。
5. 常见问题与排查技巧实录
5.1 错误码速查与应对
飞书API返回的错误码比较规范,但中文文档里有些错误码的解释不够直接。我把这次集成的实践中遇到的高频错误码整理成一张表,方便大家快速定位。
| 错误码 | 含义 | 常见原因 | 解决思路 |
|---|---|---|---|
| 99991663 | 请求过于频繁 | Token获取接口被高频调用 | 启用缓存,加分布式锁 |
| 99991672 | Token无效或过期 | Token串错误或缓存异常 | 重新获取Token,检查缓存策略 |
| 99991673 | 应用未发布 | 应用没有创建版本并发布 | 到开发者后台创建版本并发布 |
| 20001 | 权限不足 | 应用未开通对应的权限点 | 在权限管理里申请对应权限 |
| 173002 | 审批定义不存在 | approval_code错误 | 检查审批定义编码,是否已停用 |
| 173003 | 表单字段不合法 | 字段名或类型与定义不匹配 | 对照审批定义检查表单配置 |
| 173005 | 审批人不存在 | user_id无效 | 检查用户ID映射关系 |
排查错误码时,我习惯先把返回的原始JSON完整打出来,因为很多细节在错误信息里,比如“invalid field name: xxx”,直接告诉你哪个字段有问题。千万不要只看错误码就臆断原因。
5.2 回调验签与解密问题
回调验签失败是最常见的问题。典型的症状是:在飞书后台配置回调地址时,点击“保存”一直提示验签失败。排查思路分几步:先检查回调地址是否公网可达,直接浏览器访问这个地址,应该能看到一个错误页面而不是404;再检查Encrypt Key是否配置一致,后台配置和代码里用的必须完全一致;最后检查返回格式,返回值必须是一个JSON对象,包含challenge字段,且值是被加密后的。
解密失败的问题,多半出现在Encrypt Key的处理上。飞书的AES加密逻辑里,密钥是Encrypt Key的SHA256摘要的十六进制字符串,再取这个字符串的前16个字符作为IV。这个规则文档里写得不显眼,但代码实现差了分毫都解不出来。建议在写解密代码前,先用飞书开放平台提供的调试工具生成一条测试事件,验证解密逻辑通了再联调。
还有一个容易忽视的问题:回调地址如果配置了“签名校验”,飞书会在请求头里带X-Lark-Signature,需要在代码里做二次校验。如果应用开启了签名校验但代码里没处理,飞书也会认为校验失败。我们最后在回调处理里统一做了签名校验和解密,两步都通过才继续业务处理,稳妥一些。
5.3 发起审批后流程卡住的排查
有一次我们测试时发现,审批发起成功,数据库里也有instance_code,但审批人始终收不到待办通知。查了一圈API状态,审批实例状态是PENDING,审批人也有,但就是没推送消息。后来发现是审批人在飞书里的“消息通知设置”里把审批通知关了。这个坑跟代码无关,纯粹是用户偏好设置。如果业务方反馈某人收不到审批,先别急着查代码,让他检查一下飞书设置里的消息通知是否开启。
还有一种情况是审批实例卡在“审批中”但一直在某个节点不动。这多半是审批节点配置了“审批人自动通过”或“超时自动同意”之类的策略,但条件没有满足。在飞书管理后台打开这个审批的配置页面,可以直接看到每个节点的详细规则。我们这次项目里就遇到一个节点配了“若审批人连续3次未审批则自动提醒”,看起来像卡住了,其实是规则生效了但大家不知道。这种问题沟通清楚就完了,不算真正的技术故障。
5.4 免登集成中的典型问题
飞书免登遇到的问题主要集中在redirect_uri不一致和scope权限不足。redirect_uri必须在开发者后台配置,而且回调地址必须严格一致,包括端口和路径,差一个斜杠都可能导致授权失败。scope权限里需要勾选“获取用户基本信息”,否则换取不到用户的手机号或邮箱,也就无法和老系统的用户做匹配。
免登还有一个体验细节:用户第一次扫码后,飞书会跳转到一个确认授权页,需要用户点击“同意”。这个页面是可以配置的,在应用的安全设置里可以关闭“每次都需要用户确认”。对于企业内部应用,建议关闭这个确认页,否则每次登录都要多点一步,业务人员会觉得麻烦。
5.5 性能与安全方面的几点建议
对接飞书API时,老系统要注意几个性能风险点。一是所有HTTP请求必须设置超时,建议连接超时15秒,请求超时30秒。二是Token缓存必须做,否则每次发起审批都去获取Token,轻则限流,重则把飞书API打出性能问题。三是回调处理逻辑不要做太重的事,比如审批通过后要同步生成PDF、发邮件,这些应该放到后台任务里异步执行,否则飞书重试策略会不断重推。
安全方面,除了App Secret不泄露,还要注意回调接口本身的鉴权。虽然飞书推送的事件里带了签名校验,但最好在回调接口前面再加一层IP白名单,只允许飞书服务器的IP段访问。飞书官方公布的IP段在文档里可以找到,建议定期更新。我们的做法是在IIS前面加了一层防火墙规则,只放行飞书IP段访问回调路径,这样即使有人拿伪造的请求来打回调接口,也进不来。
结尾
这次“.NET传统信息系统无缝集成飞书审批流”的项目做下来,我最大的体会是:老系统不是不能焕新,关键是要找对路径。与其在系统内部硬造一个审批流引擎,不如把成熟的审批能力从外部“接”进来。飞书审批流相当于帮我们补齐了工作流引擎、移动端、消息通知、用户体系这些最重、最不擅长做的东西,而老系统只需要专注自己的业务逻辑,两边各干各擅长的事。这种集成思路,对于大量还在跑着.NET Framework、维护团队规模有限的企业内部系统来说,可能是最务实的升级方式。
最后再分享一个小技巧:对接飞书开放平台时,不要急着写代码,先把开放平台的API调试工具用起来。飞书官方的调试工具可以直接在线调用接口,输入参数就能看到返回结果,很多参数格式问题在调试工具里就能暴露出来,比在代码里反复试错快很多。等调试工具里跑通了,再落代码,整个集成过程会顺畅得多。这套流程用顺手了,后续你想继续对接飞书的文档、日历、多维表格,都是同一个套路,一通则百通。
