实战指南:基于飞书/Slack构建智能测试通知机器人

实战指南:基于飞书/Slack构建智能测试通知机器人
1. 项目概述为什么你需要一个测试通知机器人在持续集成和敏捷开发的日常里测试结果的反馈速度直接决定了团队的修复效率。想象一下你刚提交了一段代码触发了一轮自动化测试。传统的做法是你需要时不时地去CI/CD平台比如Jenkins、GitLab CI上刷新页面看看测试有没有跑完是绿了还是红了。这个过程不仅低效而且容易遗漏关键信息尤其是在并行开发多个功能或修复多个Bug时测试失败的通知如果被淹没在众多消息里可能会导致线上问题。这就是自建测试通知机器人的核心价值所在将“人找信息”变为“信息找人”。通过将测试执行结果实时推送到团队日常高频使用的协作工具如Slack或飞书我们能在第一时间将成功/失败的状态、关键错误日志、甚至失败用例的截图或录屏精准地推送到相关开发或测试人员的聊天窗口或群组中。这不仅仅是发一条“测试失败”的消息那么简单它集成了失败分析、责任归属、快速定位等一系列自动化流程让团队响应速度从“小时级”提升到“分钟级”。从技术角度看这背后是一个典型的“事件驱动”架构。你的CI/CD流水线在测试任务完成时会触发一个Webhook事件。我们的机器人服务一个常驻的后端应用监听这个Webhook解析其中的测试报告数据通常是JUnit XML、Allure JSON或自定义格式然后通过Slack或飞书官方提供的机器人API将结构化的、富含信息量的消息卡片发送到指定频道。更进一步我们还可以集成代码仓库信息自动相关代码提交者或者将失败用例与项目管理工具如Jira联动自动创建Bug工单。2. 核心需求解析与方案选型2.1 核心需求拆解一个高效的测试通知机器人远不止是“发送消息”。我们需要拆解其核心需求实时性测试任务一结束通知必须在数秒内抵达。这要求我们的服务具有低延迟的处理能力并且与CI/CD工具、消息平台之间的网络链路稳定。信息结构化与可读性推送的消息不能是一大段难以阅读的日志。它应该是一个“信息卡片”包含项目/任务名称一眼就知道是哪个项目的测试。整体状态成功绿色、失败红色、不稳定黄色。关键数据总用例数、通过数、失败数、跳过数、执行时长。失败摘要列出失败用例的名称和可能的错误分类。快速链接一键跳转到详细的CI构建页面、测试报告页面或失败日志。失败分析与归因这是进阶需求。机器人应能尝试分析失败原因例如关联代码变更通过构建信息关联到最近的Git提交并提交者。错误模式识别如果是网络超时、数据库连接失败等常见错误可以在消息中给出初步判断。附件支持对于UI自动化测试能附带失败时的截图或视频。可配置性与灵活性不同团队、不同项目对通知的格式、接收人、触发条件如仅失败时通知、或每次构建都通知可能有不同要求。机器人需要支持灵活的配置。可靠性机器人服务本身必须稳定可靠不能成为单点故障。需要具备错误重试、消息队列缓冲等机制。2.2 技术方案选型Slack vs. 飞书Slack和飞书是目前国内外团队最主流的两个协作平台它们的机器人生态都非常成熟。Slack方案优势生态成熟API文档极其详尽社区支持强大。其Block Kit消息构建框架功能非常灵活可以创建极其复杂和交互式的消息界面。与海外CI/CD工具如CircleCI, Travis CI的集成通常更原生。劣势在国内访问可能存在速度问题且对于纯国内团队使用门槛和成本较高。关键技术点使用Incoming Webhooks或Slack App的chat.postMessageAPI。推荐使用Block Kit Builder在线设计消息样式。飞书方案优势国内访问速度快集成微信、钉钉等国内生态更顺畅。对于使用飞书作为日常办公工具的团队信息流转路径最短。其“多维表格”功能为结构化数据展示提供了新思路例如可将每次测试结果作为一条记录自动写入表格形成历史趋势图。劣势国际知名度相对较低与一些海外工具的集成可能需要自研。关键技术点使用“自定义机器人”获取Webhook URL或创建“企业自建应用”获取更高权限。消息卡片使用飞书特定的interactive消息格式类似Slack的Block Kit。选型建议如果你的团队主要使用飞书或者项目用户、服务器都在国内优先选择飞书。它的机器人功能完全能满足需求且更符合国内的使用习惯。如果你的团队是分布式、或者主要使用海外工具链GitHub, Jira, Jenkins等Slack是更通用的选择。一个有趣的趋势从网络热词“飞书多维表格”可以看出飞书正在强化其作为“轻量级数据平台”的能力。我们可以设想一个场景测试机器人不仅发送通知还将每次构建的关键指标通过率、耗时自动写入飞书多维表格团队在一个表格里就能看到测试健康度的历史趋势。这比单纯的即时消息通知又进了一步。基础架构选型 无论选择哪个平台机器人的后端服务架构是类似的。一个轻量级、高可用的方案是语言PythonFastAPI/Flask或 Node.jsExpress/Koa。它们生态丰富处理HTTP请求和JSON数据非常方便。核心流程CI/CD Webhook - 你的后端服务解析、处理、增强 - Slack/飞书 API。增强组件为了可靠性可以在CI Webhook和后端服务之间加入一个消息队列如Redis Streams/RabbitMQ用于削峰填谷和失败重试。对于简单的项目初期可以不用。3. 实战构建从零搭建飞书测试通知机器人我将以飞书为例展示一个完整的、包含失败分析功能的机器人搭建过程。Slack的实现逻辑几乎完全一致只是API调用和消息格式不同。3.1 第一步在飞书上创建并配置机器人创建群组在飞书上创建一个专门接收测试通知的群聊例如“QA警报中心”。添加自定义机器人在群设置中找到“群机器人” - “添加机器人” - “自定义机器人”。设置机器人名称如“CI守护者”和描述。关键一步在“安全设置”中至少添加一个“自定义关键词”例如“测试”、“构建”、“FAILED”。这可以防止机器人被恶意调用。你也可以设置IP白名单进一步增强安全。创建完成后飞书会提供一个Webhook URL格式类似https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxx。妥善保存这个URL它是机器人的唯一入口。3.2 第二步设计消息卡片模板飞书的消息卡片通过JSON定义。一个优秀的测试通知卡片应该清晰明了。我们可以使用飞书提供的 消息卡片工具 进行可视化设计。一个基础的消息卡片JSON结构如下{ msg_type: interactive, card: { config: { wide_screen_mode: true }, header: { title: { tag: plain_text, content: [FAILED] 项目API自动化测试 #123 }, template: red // 成功为green失败为red不稳定为yellow }, elements: [ { tag: div, text: { tag: lark_md, content: **构建信息**\n**分支**: feature/login\n**触发者**: 张三\n**耗时**: 5分23秒\n**时间**: 2023-10-27 14:30:22 } }, { tag: div, fields: [ { is_short: true, text: { tag: lark_md, content: **用例总数**: 156 } }, { is_short: true, text: { tag: lark_md, content: **通过**: 150 } }, { is_short: true, text: { tag: lark_md, content: **失败**: 6 } }, { is_short: true, text: { tag: lark_md, content: **通过率**: 96.2% } } ] }, { tag: hr }, { tag: div, text: { tag: lark_md, content: **失败用例摘要 (最近3个)**\n1. test_user_login_with_wrong_password - AssertionError: Expected status 200, got 401\n2. test_create_product_without_auth - TimeoutError: Request timeout after 10s\n3. test_get_order_list - ConnectionError: Database connection failed } }, { tag: action, actions: [ { tag: button, text: { tag: plain_text, content: 查看详细报告 }, type: primary, url: https://ci.your-company.com/job/API-Test/123/allure }, { tag: button, text: { tag: plain_text, content: 查看构建日志 }, url: https://ci.your-company.com/job/API-Test/123/console }, { tag: button, text: { tag: plain_text, content: 创建Bug工单 }, type: default, url: https://jira.your-company.com/secure/CreateIssue.jspa?pid10000summaryAutomated Test Failed: Build #123 } ] } ] } }注意飞书消息卡片对JSON格式和字段有严格限制建议先在卡片工具中调试成功再将JSON模板化到代码中。3.3 第三步编写后端服务Python FastAPI示例我们将创建一个简单的HTTP服务接收来自Jenkins/GitLab CI等的Webhook处理数据并调用飞书API。项目结构test-notification-bot/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 应用入口 │ ├── feishu.py # 飞书API封装 │ ├── parser.py # 测试报告解析器 │ └── config.py # 配置文件 ├── requirements.txt └── Dockerfile1. 安装依赖 (requirements.txt):fastapi0.104.1 uvicorn[standard]0.24.0 httpx0.25.1 pydantic2.5.0 python-multipart0.0.62. 配置文件 (app/config.py):import os from pydantic_settings import BaseSettings class Settings(BaseSettings): # 飞书机器人Webhook URL从环境变量读取 FEISHU_WEBHOOK_URL: str os.getenv(FEISHU_WEBHOOK_URL, ) # 是否仅通知失败 (True/False) NOTIFY_ONLY_FAILURE: bool os.getenv(NOTIFY_ONLY_FAILURE, False).lower() true # 服务端口 PORT: int int(os.getenv(PORT, 8000)) settings Settings()提示敏感信息如Webhook URL务必通过环境变量传入不要硬编码在代码中。3. 飞书消息发送模块 (app/feishu.py):import httpx import logging from typing import Dict, Any from .config import settings logger logging.getLogger(__name__) class FeishuNotifier: def __init__(self): self.webhook_url settings.FEISHU_WEBHOOK_URL if not self.webhook_url: raise ValueError(FEISHU_WEBHOOK_URL environment variable is not set) self.client httpx.AsyncClient(timeout10.0) async def send_message(self, card_body: Dict[str, Any]) - bool: 发送飞书交互卡片消息 try: headers {Content-Type: application/json} response await self.client.post( self.webhook_url, jsoncard_body, headersheaders ) resp_data response.json() if resp_data.get(code) 0: logger.info(飞书消息发送成功) return True else: logger.error(f飞书消息发送失败: {resp_data}) return False except httpx.RequestError as e: logger.error(f请求飞书API失败: {e}) return False finally: await self.client.aclose() staticmethod def create_test_card(project: str, build_num: int, status: str, total: int, passed: int, failed: int, duration: str, branch: str, committer: str, failures: list, report_url: str, console_url: str) - Dict[str, Any]: 根据测试结果动态生成飞书卡片JSON # 根据状态决定颜色和表情 status_config { SUCCESS: {template: green, emoji: , text: 通过}, FAILURE: {template: red, emoji: , text: 失败}, UNSTABLE: {template: yellow, emoji: , text: 不稳定} } config status_config.get(status, status_config[FAILURE]) # 构建失败摘要文本 failure_summary if failures and len(failures) 0: failure_items [] for i, fail in enumerate(failures[:3]): # 只显示最近3个失败 failure_items.append(f{i1}. {fail.get(name, Unknown)} - {fail.get(error, No error message)}) failure_summary **失败用例摘要 (最近3个)**\n \n.join(failure_items) # 返回完整的卡片JSON return { msg_type: interactive, card: { config: {wide_screen_mode: True}, header: { title: { tag: plain_text, content: f{config[emoji]} [{config[text]}] {project} #{build_num} }, template: config[template] }, elements: [ { tag: div, text: { tag: lark_md, content: f**构建信息**\n**分支**: {branch}\n**触发者**: {committer}\n**耗时**: {duration}\n**时间**: {datetime.now().strftime(%Y-%m-%d %H:%M:%S)} } }, { tag: div, fields: [ {is_short: True, text: {tag: lark_md, content: f**用例总数**: {total}}}, {is_short: True, text: {tag: lark_md, content: f**通过**: {passed}}}, {is_short: True, text: {tag: lark_md, content: f**失败**: {failed}}}, {is_short: True, text: {tag: lark_md, content: f**通过率**: {(passed/total*100 if total0 else 0):.1f}%}}, ] }, {tag: hr}, { tag: div, text: { tag: lark_md, content: failure_summary if failure_summary else **所有测试用例均已通过** } }, { tag: action, actions: [ { tag: button, text: {tag: plain_text, content: 查看详细报告}, type: primary, url: report_url }, { tag: button, text: {tag: plain_text, content: 查看构建日志}, url: console_url } ] } ] } }4. 测试报告解析器 (app/parser.py): 不同的测试框架生成不同格式的报告。这里以最常见的JUnit XML格式为例。import xml.etree.ElementTree as ET from typing import List, Dict import logging logger logging.getLogger(__name__) def parse_junit_xml(xml_content: str) - Dict: 解析JUnit XML格式的测试报告 try: root ET.fromstring(xml_content) total int(root.get(tests, 0)) failures int(root.get(failures, 0)) errors int(root.get(errors, 0)) skipped int(root.get(skipped, 0)) passed total - failures - errors - skipped # 收集失败的用例详情 failure_cases [] for testcase in root.findall(.//testcase): failure testcase.find(failure) error testcase.find(error) if failure is not None or error is not None: case_name testcase.get(name, ) class_name testcase.get(classname, ) full_name f{class_name}.{case_name} if class_name else case_name error_msg failure.get(message) if failure is not None else error.get(message) error_text failure.text if failure is not None else error.text failure_cases.append({ name: full_name, error: error_msg or Unknown error, detail: error_text[:200] if error_text else # 截取前200字符 }) return { total: total, passed: passed, failed: failures errors, skipped: skipped, failures: failure_cases, status: SUCCESS if (failures errors) 0 else FAILURE } except ET.ParseError as e: logger.error(f解析JUnit XML失败: {e}) raise5. 主应用入口 (app/main.py):from fastapi import FastAPI, HTTPException, Request from fastapi.responses import JSONResponse import logging from .feishu import FeishuNotifier from .parser import parse_junit_xml from .config import settings from datetime import datetime app FastAPI(titleTest Notification Bot) notifier FeishuNotifier() logger logging.getLogger(__name__) app.post(/webhook/jenkins) async def handle_jenkins_webhook(request: Request): 处理来自Jenkins的通用Webhook try: payload await request.json() logger.debug(f收到Jenkins Webhook: {payload}) # 提取Jenkins构建信息 (根据你的Jenkins通知格式调整) # 这里假设使用了Generic Webhook Trigger插件或类似方式传递了测试报告数据 build_status payload.get(build, {}).get(status, UNKNOWN) project_name payload.get(name, Unknown Project) build_number payload.get(build, {}).get(number, 0) build_url payload.get(build, {}).get(full_url, #) git_branch payload.get(git, {}).get(branch, main) committer payload.get(git, {}).get(committer, Unknown) # 检查是否包含测试报告例如作为base64编码的字符串或URL test_report_data payload.get(test_report, {}) if test_report_data.get(format) junit and test_report_data.get(content): # 如果是base64编码的内容需要解码 import base64 xml_content base64.b64decode(test_report_data[content]).decode(utf-8) test_result parse_junit_xml(xml_content) else: # 如果没有提供报告内容尝试从构建状态推断 test_result { total: 0, passed: 0, failed: 1 if build_status FAILURE else 0, skipped: 0, failures: [], status: SUCCESS if build_status SUCCESS else FAILURE } # 根据配置决定是否发送通知 if settings.NOTIFY_ONLY_FAILURE and test_result[status] SUCCESS: logger.info(构建成功且配置为仅失败时通知跳过。) return JSONResponse(content{status: skipped}) # 创建飞书消息卡片 card notifier.create_test_card( projectproject_name, build_numbuild_number, statustest_result[status], totaltest_result[total], passedtest_result[passed], failedtest_result[failed], durationpayload.get(build, {}).get(duration, 0s), branchgit_branch, committercommitter, failurestest_result[failures], report_urlf{build_url}allure if allure in build_url else f{build_url}testReport, console_urlf{build_url}console ) # 发送消息 success await notifier.send_message(card) if success: return JSONResponse(content{status: ok, message: Notification sent}) else: raise HTTPException(status_code500, detailFailed to send Feishu message) except Exception as e: logger.exception(f处理Webhook时发生错误: {e}) raise HTTPException(status_code500, detailstr(e)) app.get(/health) async def health_check(): 健康检查端点 return {status: healthy, timestamp: datetime.now().isoformat()} if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, portsettings.PORT)3.4 第四步配置CI/CD工具以Jenkins为例在Jenkins中我们需要在构建后步骤中调用我们刚刚部署的机器人服务。安装插件确保安装了Generic Webhook Trigger插件和用于收集测试报告的插件如JUnit。配置构建后操作在Jenkins任务配置页找到“构建后操作”。添加“Publish JUnit test result report”指定测试报告XML文件的路径如**/target/surefire-reports/*.xml。添加“Generic Webhook Trigger”的构建后步骤或使用curl命令。使用curl发送Webhook推荐更灵活 在“执行shell”或“构建后步骤”中添加类似命令# 假设你的机器人服务部署在 http://your-bot-server:8000 BOT_WEBHOOK_URLhttp://your-bot-server:8000/webhook/jenkins # 收集测试报告内容并Base64编码 if [ -f target/surefire-reports/TEST-*.xml ]; then TEST_REPORT_CONTENT$(cat target/surefire-reports/TEST-*.xml | base64 | tr -d \n) else TEST_REPORT_CONTENT fi # 构建JSON payload JSON_PAYLOAD$(cat EOF { name: ${JOB_NAME}, build: { number: ${BUILD_NUMBER}, status: ${currentBuild.currentResult}, full_url: ${BUILD_URL}, duration: ${currentBuild.durationString} }, git: { branch: ${GIT_BRANCH##*/}, committer: ${CHANGELOG_AUTHOR} }, test_report: { format: junit, content: ${TEST_REPORT_CONTENT} } } EOF ) # 发送请求 curl -X POST ${BOT_WEBHOOK_URL} \ -H Content-Type: application/json \ -d ${JSON_PAYLOAD}注意CHANGELOG_AUTHOR等变量可能需要安装额外插件如Email Extension Plugin或通过脚本获取。4. 进阶功能与深度优化基础功能实现后我们可以让机器人变得更智能、更强大。4.1 失败智能分析与归因简单的通知只是第一步。我们可以让机器人尝试分析失败原因提供更 actionable 的信息。1. 错误日志模式匹配 在parser.py中我们可以增强对失败原因的分析。def analyze_failure(error_text: str) - str: 根据错误日志文本分析可能的失败原因 error_lower error_text.lower() if timeout in error_lower or timed out in error_lower: return ⏱️ 疑似超时失败 (检查网络或服务响应) elif connection refused in error_lower or failed to connect in error_lower: return 疑似连接失败 (检查依赖服务是否启动) elif assertionerror in error_lower: return ❌ 断言失败 (业务逻辑或数据不符合预期) elif no such element in error_lower: return 疑似UI元素定位失败 (页面结构可能已变更) elif out of memory in error_lower: return 疑似内存溢出 (检查资源限制) else: return ⚠️ 需人工介入分析在生成消息卡片时可以将这个分析结果添加到失败用例的展示中帮助开发者第一时间缩小排查范围。2. 关联代码变更与责任人 这需要集成Git信息。在CI流水线中我们可以获取本次构建对应的Git提交哈希、作者信息。在Webhook payload中传递git_commit_hash和git_author。在后端服务中可以调用GitLab/GitHub API根据提交哈希获取具体的代码变更文件列表。在飞书消息中可以使用at email\zhangsancompany.com\/at的格式来具体责任人。但注意这需要机器人在飞书上是“自建应用”而非简单的“自定义机器人”并且需要获取用户的邮箱信息权限。4.2 与飞书多维表格集成构建测试质量仪表盘这是从“即时通知”到“数据洞察”的跨越。思路是每次测试执行后不仅发消息还将关键指标写入飞书多维表格的一条记录中。在飞书中创建多维表格新建一个表格包含字段项目名、构建号、执行时间、总用例数、通过数、失败数、通过率、耗时、构建状态、报告链接。获取多维表格API权限在飞书开发者后台为你创建的自建应用开启“多维表格”权限并获取app_token和table_id。在后端服务中添加写表逻辑import httpx async def write_to_feishu_bitable(app_token, table_id, record_data): url fhttps://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records headers {Authorization: Bearer YOUR_ACCESS_TOKEN} # 需要先获取tenant_access_token data {records: [{fields: record_data}]} async with httpx.AsyncClient() as client: resp await client.post(url, jsondata, headersheaders) return resp.json()在收到Webhook并解析测试结果后调用此函数。这样一个随时间变化的测试质量仪表盘就自动生成了。你可以在多维表格中创建各种视图如“最近一周通过率趋势”、“失败最多的模块排行”等。4.3 实现消息的交互与反馈飞书的交互卡片支持按钮动作。我们可以让通知消息不仅仅是查看还能进行简单操作。例如在失败通知中增加一个“标记为误报”的按钮。当测试工程师点击这个按钮时飞书会向你的服务发送一个回调请求需要在机器人配置中设置“请求网址”。你的服务收到回调后可以记录这次“误报”反馈。自动在对应的测试用例上打上标签如果测试报告系统支持API。甚至可以向反馈者发送一条确认消息。这需要处理飞书的“交互事件”实现一个额外的回调端点逻辑会复杂一些但能极大提升流程的自动化程度。5. 部署、监控与避坑指南5.1 部署方案本地/测试环境直接使用uvicorn app.main:app --reload运行方便调试。生产环境方案一简单使用gunicorn或uvicorn配合systemd或supervisor部署在云服务器上。方案二推荐使用Docker容器化部署便于迁移和扩展。FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [uvicorn, app.main:app, --host, 0.0.0.0, --port, 8000]方案三高可用部署在Kubernetes上并配置Horizontal Pod Autoscaler (HPA)根据负载自动伸缩。5.2 关键配置与监控超时与重试网络调用可能失败。在HTTP客户端如httpx中务必设置合理的超时时间如10秒并为飞书API调用实现简单的重试机制如最多3次指数退避。速率限制飞书和Slack的API都有调用频率限制。如果你的CI非常频繁可能需要实现一个简单的内存队列来缓冲消息避免触发限流。日志与告警为你的机器人服务配置完善的日志记录收到的Webhook、发送的消息、任何错误。并设置监控告警例如如果服务连续1分钟没有收到心跳或处理消息失败超过10次则通过其他渠道告警。安全性Webhook验证Jenkins等CI工具可以在Webhook请求头中添加签名如X-Jenkins-Signature你的后端服务应验证此签名确保请求来源可信。IP白名单在飞书机器人设置和安全组规则中只允许你的CI服务器和机器人服务器的IP调用。密钥管理Webhook URL、飞书app_token等敏感信息必须通过环境变量或密钥管理服务如Vault传入绝不能写在代码里。5.3 常见问题与排查技巧问题1收不到飞书消息。检查点1飞书机器人的“安全设置”中是否设置了“自定义关键词”你发送的消息卡片标题或内容中必须包含至少一个关键词。检查点2飞书群聊是否已添加该机器人机器人被移除后需要重新添加。检查点3查看机器人服务的日志确认是否收到了Webhook请求以及调用飞书API的返回结果。飞书API的错误码通常很明确。检查点4网络连通性。确保你的机器人服务器可以访问open.feishu.cn。问题2消息卡片显示不正常布局错乱。检查点1使用飞书的 消息卡片调试工具 在线预览你的JSON工具会直接指出格式错误。检查点2飞书卡片JSON对字段顺序、嵌套结构有严格要求务必严格按照官方文档的示例构建。检查点3检查是否有字段内容过长。飞书卡片对单个文本字段的长度有限制。问题3CI构建成功/失败时机器人没有按预期触发。检查点1确认CI的Webhook配置是否正确特别是URL和触发条件是“仅失败时”还是“总是”。检查点2在CI的构建脚本中curl命令是否被执行检查CI的构建日志看是否有网络错误如DNS解析失败、连接超时。检查点3你的机器人服务健康吗调用/health端点检查。问题4如何区分不同项目的通知并发送到不同的群方案不要在代码里写死Webhook URL。可以在Webhook的payload中传递一个channel或project_id字段。你的后端服务根据这个字段去查询数据库或配置文件找到对应的飞书机器人Webhook URL。这样一个机器人服务就可以为全公司所有项目服务。搭建这样一个机器人从最简单的消息推送开始到集成失败分析、数据面板再到实现交互反馈是一个迭代的过程。我的建议是从核心需求出发先跑通最小闭环让CI构建失败后能在飞书群里看到一条包含基本信息的通知。这个最小版本可能只需要几十行代码但带来的效率提升是立竿见影的。之后再根据团队的实际痛点逐步添加上述的进阶功能。