news 2026/11/3 15:17:28

mobile-mcp:基于MCP协议的移动端自动化新范式,统一Android与iOS操控

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
mobile-mcp:基于MCP协议的移动端自动化新范式,统一Android与iOS操控

1. 移动端自动化的痛点与 mobile-mcp 的破局思路

做过移动端自动化的人都有一个共同的感受:环境搭建比写用例还累。Appium 要装 Node、配 JDK、下 Android SDK、搞 WebDriverAgent 签名,一套流程走下来,半天时间没了,代码一行还没写。更别提跨平台了,Android 和 iOS 两套完全不同的工具链,维护成本直接翻倍。而 mobile-mcp 这个项目的出现,本质上是想用 MCP(Model Context Protocol)这套协议,把移动端设备的能力抽象成标准化的工具接口,让 AI Agent 或者自动化脚本通过统一的协议去操控手机,不再关心底层是 ADB 还是 WebDriverAgent。

先说清楚 mobile-mcp 是什么。它是一个基于 MCP 协议实现的移动端控制服务,核心思路是把 iOS 和 Android 设备的常见操作——点击、滑动、输入、截图、获取 UI 树、启动 App、安装应用等——封装成 MCP 工具,然后通过标准输入输出或者 SSE 的方式暴露给上层调用方。这意味着任何支持 MCP 协议的客户端,比如 Claude Desktop、各种 AI IDE、甚至你自己写的 Agent 框架,都能直接调用这些工具来控制真机或模拟器。

它解决的核心问题有三个。第一是协议统一,以前 Android 用 ADB、iOS 用 WebDriverAgent,接口风格完全不同,现在都收敛到 MCP 的工具调用格式。第二是AI 友好,MCP 本身就是为 LLM 设计的协议,工具描述、参数 schema 都是结构化的,AI 能直接理解每个工具干什么、需要什么参数。第三是降低门槛,你不需要精通 Appium 的 caps 配置,也不需要手动处理 WDA 的 session 管理,mobile-mcp 把这些脏活累活都包了。

适合谁来用?如果你在做移动端自动化测试、想用 AI Agent 操控手机做任务、或者需要批量管理多台设备做群控类操作,这个项目值得花时间研究。前端开发者想快速验证移动端页面的交互逻辑,也可以用它在真机上跑一些简单的自动化脚本。但如果你只是偶尔手动点几下手机,那确实没必要上这套工具。

2. 核心架构拆解:MCP 协议如何桥接移动设备

2.1 MCP 协议的本质:给 AI 用的 USB 接口

MCP 全称 Model Context Protocol,你可以把它理解成“AI 世界的 USB 接口标准”。以前每个 AI 应用要调用外部工具,都得自己写一套适配层,A 工具用 JSON-RPC,B 工具用 REST,C 工具用 gRPC,乱得不行。MCP 做的事情就是定义一套统一的通信规范,包括工具发现(tools/list)、工具调用(tools/call)、资源读取(resources/read)等标准方法,让 AI 客户端和工具服务端能即插即用。

mobile-mcp 作为 MCP 服务端,启动后会向客户端注册一系列工具。客户端连接上来第一件事就是发tools/list请求,拿到所有可用工具的名称、描述和参数 schema。比如mobile_click工具的描述可能是“在指定坐标点击屏幕”,参数是{x: number, y: number}。AI 看到这个描述就知道什么时候该调用它、怎么传参。这种设计的好处是工具是自描述的,新增一个工具不需要改客户端代码,AI 自动就能发现并使用。

传输层方面,mobile-mcp 通常支持两种模式:stdio 和 SSE。stdio 模式适合本地进程间通信,客户端把 mobile-mcp 当子进程启动,通过标准输入输出交换 JSON-RPC 消息。SSE 模式适合远程调用,mobile-mcp 跑在一台机器上,客户端通过网络连接过来。两种模式各有适用场景,本地开发调试用 stdio 更方便,多设备集中管理用 SSE 更合理。

2.2 双平台适配层:Android 走 ADB,iOS 走 WDA

mobile-mcp 内部对 Android 和 iOS 做了差异化处理,但对外暴露的工具接口是一致的。Android 侧主要依赖 ADB(Android Debug Bridge),通过adb shell input tap、adb shell input swipe、adb shell uiautomator dump等命令实现点击、滑动、UI 树获取。截图用adb exec-out screencap -p,安装应用用adb install,启动 App 用adb shell am start。这些都是 ADB 的标准能力,稳定性和兼容性经过多年验证。

iOS 侧就复杂一些,核心依赖 WebDriverAgent(WDA)。WDA 是 Facebook 开源的一个 WebDriver 服务,跑在 iOS 设备上,通过 HTTP 接口接收指令并调用 XCUITest 框架执行。mobile-mcp 需要先确保 WDA 已经安装并运行在目标设备上,然后通过 HTTP 请求与 WDA 通信。点击操作对应 WDA 的/session/:id/wda/touch/perform接口,截图对应/screenshot,UI 树对应/source。WDA 的痛点在于签名和部署,免费开发者账号每 7 天要重新签名,这是绕不过去的坑。

注意:iOS 真机使用 WDA 需要开启开发者模式,并且设备要信任对应的开发者证书。模拟器相对简单,不需要签名,但模拟器无法覆盖所有真机场景,比如某些传感器、摄像头、生物识别相关的功能。

2.3 工具集设计:从原子操作到组合能力

mobile-mcp 的工具集设计遵循“原子操作优先”的原则,每个工具只做一件事,复杂流程由上层组合。目前核心工具大概分几类:

工具类别代表工具功能说明
设备管理list_devices列出当前连接的设备
应用管理install_app、launch_app、terminate_app安装、启动、终止应用
交互操作click、swipe、input_text、press_key点击、滑动、输入、按键
信息获取screenshot、get_ui_tree、get_screen_size截图、UI 树、屏幕尺寸
系统操作press_home、press_back、open_urlHome 键、返回键、打开链接

这种设计的优势在于可组合性。比如你要实现“打开微信,搜索一个联系人,发一条消息”,可以拆解成:launch_app(wechat)→click(搜索框坐标)→input_text(联系人名)→click(搜索结果)→input_text(消息内容)→click(发送按钮)。每一步都是独立的工具调用,AI 可以根据 UI 树的反馈动态调整下一步操作,而不是写死一个固定流程。

3. 环境搭建实操:从零到跑通第一条指令

3.1 Android 侧环境准备

Android 的环境相对简单,核心就是 ADB 和 USB 驱动。先确认你的电脑上装了 ADB,命令行执行adb version能看到版本号就行。如果没有,去 Android 开发者官网下载 platform-tools 包,解压后把路径加到系统环境变量里。Windows 用户注意 USB 驱动,大部分国产手机需要单独装驱动,或者去厂商开发者官网下载。Mac 和 Linux 通常免驱,插上就能识别。

手机端要开启开发者选项和 USB 调试。开发者选项的开启方式一般是连续点击“关于手机”里的版本号 7 次,不同厂商略有差异。开启后在开发者选项里找到“USB 调试”并打开。插上电脑后手机会弹出一个“允许 USB 调试吗”的对话框,勾选“始终允许”并确认。这时候在电脑上执行adb devices,应该能看到设备列表里有一台设备,状态是device。如果显示unauthorized,说明手机上还没确认授权;如果显示offline,试试重新插拔或者adb kill-server && adb start-server。

实操心得:有些国产手机即使开了 USB 调试,ADB 连接也不稳定,经常断连。我试过在开发者选项里额外打开“USB 调试(安全设置)”和“禁止权限监控”,断连问题会好很多。另外,用原装数据线,别用那种只能充电的劣质线,这是很多人踩过的坑。

3.2 iOS 侧环境准备

iOS 侧的门槛明显高一个档次。首先你需要一台 Mac,Windows 和 Linux 没法跑 WDA。然后装 Xcode,从 App Store 下载就行,安装完成后打开一次,同意许可协议,让它自动安装必要的组件。接着装 Appium 或者直接装 WDA,如果你用 Appium,npm install -g appium然后appium driver install xcuitest就行。如果直接用 WDA,去 GitHub 克隆 WebDriverAgent 仓库,用 Xcode 打开项目,配置签名后部署到设备上。

签名是 iOS 自动化最大的坑。免费开发者账号每 7 天要重新签名一次,而且最多只能同时签名 3 个 App。付费开发者账号(每年 99 美元)可以一年签一次,适合长期使用。签名配置在 Xcode 的 Signing & Capabilities 里,选择你的 Team,修改 Bundle Identifier 为一个唯一的字符串,然后点 Build 部署到设备。部署成功后,手机上会多一个 WebDriverAgentRunner 的 App,点开它,如果屏幕上显示一个 IP 地址和端口号,说明 WDA 跑起来了。

注意:iOS 16 以后,真机使用 WDA 需要在“设置 → 隐私与安全性 → 开发者模式”里开启开发者模式,并且重启设备。这个选项默认是隐藏的,需要先用 Xcode 部署一次任意 App 才会出现。

3.3 mobile-mcp 安装与配置

mobile-mcp 本身通常是一个 Node.js 项目或者 Python 项目,安装方式取决于具体实现。假设是 Node.js 版本,npm install -g mobile-mcp或者从源码git clone后npm install && npm run build。安装完成后,你需要配置 MCP 客户端来连接它。以 Claude Desktop 为例,编辑配置文件claude_desktop_config.json,在mcpServers里加一段:

{ "mcpServers": { "mobile-mcp": { "command": "npx", "args": ["-y", "mobile-mcp"], "env": { "ANDROID_HOME": "/path/to/android/sdk", "DEVICE_ID": "your-device-id" } } } }

配置完成后重启客户端,如果一切正常,你会在工具列表里看到 mobile-mcp 注册的那些工具。这时候可以试着调用list_devices,看看能不能正确返回设备列表。如果返回空列表,检查 ADB 是否能识别设备;如果报错连接失败,检查环境变量路径是否正确。

4. 核心工具实战:点击、滑动、输入与 UI 树解析

4.1 坐标点击与元素定位的取舍

mobile-mcp 的点击工具通常接受坐标参数,而不是元素选择器。这是有意为之的设计选择。坐标点击的好处是跨平台一致,Android 和 iOS 都支持,不依赖特定框架的元素定位能力。坏处是脆弱,屏幕分辨率一变、布局一改,坐标就失效了。所以实际使用中,坐标通常不是硬编码的,而是通过 UI 树动态计算出来的。

UI 树是 mobile-mcp 最核心的能力之一。Android 侧通过uiautomator dump获取当前界面的 XML 结构,iOS 侧通过 WDA 的/source接口获取。UI 树里包含了每个元素的类型、文本、坐标、尺寸、可点击性等属性。你可以写一个简单的查找逻辑:遍历 UI 树,找到文本等于“登录”的按钮,取它的中心坐标,然后调用click。这样即使布局变了,只要按钮文本没变,脚本依然能跑通。

# 伪代码示例:通过 UI 树查找元素并点击 def find_and_click(ui_tree, text): for node in ui_tree.iter(): if node.text == text and node.clickable: x = node.bounds.center_x y = node.bounds.center_y mobile_mcp.click(x, y) return True return False

实操心得:Android 的 UI 树有时候会漏掉一些自定义 View 的文本,因为开发者没有设置 contentDescription。这种情况下只能靠坐标或者图像识别兜底。iOS 的 UI 树相对完整,但 WebView 内部的元素需要额外开启webview相关的配置才能拿到。

4.2 滑动操作的参数计算

滑动工具一般接受起点坐标、终点坐标和持续时间三个参数。持续时间很关键,太短会被系统识别成 fling(快速滑动),太长又会影响执行效率。经验值:普通滚动列表用 300 到 500 毫秒,翻页用 200 到 300 毫秒,下拉刷新用 500 到 800 毫秒。滑动距离一般取屏幕高度的 1/3 到 1/2,太短了没效果,太长了容易滑过头。

Android 的滑动命令是adb shell input swipe x1 y1 x2 y2 duration,iOS 对应 WDA 的/wda/touch/perform接口,传一个 actions 数组描述按下、移动、抬起的完整过程。mobile-mcp 把这两个接口统一成了swipe工具,参数格式一致,底层自动适配。实际使用中,我习惯先调get_screen_size拿到屏幕宽高,然后按比例计算滑动坐标,这样换一台分辨率不同的设备也不用改代码。

4.3 文本输入的坑与绕行方案

文本输入看起来简单,实际上坑最多。Android 的adb shell input text不支持中文和特殊字符,输入中文需要借助 ADBKeyboard 这样的第三方输入法,或者用adb shell am broadcast发送广播。iOS 的 WDA 输入中文也有问题,需要设置typeText的编码方式,或者用粘贴板的方式绕过。

mobile-mcp 通常会对这些差异做封装,但底层限制依然存在。我的建议是:如果目标 App 支持粘贴板输入,优先用粘贴板。流程是先把文本写到系统粘贴板,然后模拟长按输入框、点击粘贴。Android 用adb shell am broadcast -a clipper.set -e text "内容",iOS 用pbcopy配合 WDA 的粘贴操作。这种方式兼容性最好,中文、emoji、特殊符号都能搞定。

注意:部分 App 会禁用粘贴功能,或者对粘贴板内容做校验。这种情况下只能老老实实用输入法方案,或者让 AI 生成拼音再逐个选字,但效率很低,不推荐。

5. 多设备管理与批量操作实战

5.1 设备发现与连接池设计

当你有十几台甚至几十台设备需要同时操作时,单设备的管理方式就不够用了。mobile-mcp 支持通过list_devices获取所有已连接设备,然后每个工具调用都可以指定device_id参数来路由到特定设备。这为批量操作提供了基础。

实际落地时,我建议做一个简单的连接池。启动时扫描所有设备,为每台设备建立一个会话上下文,记录设备状态(在线、离线、忙碌、空闲)。任务调度器从队列里取任务,分配给空闲设备,执行完成后释放。这样能最大化设备利用率,避免某台设备卡死导致整个队列阻塞。

# 伪代码:简单的设备连接池 class DevicePool: def __init__(self): self.devices = {} for d in mobile_mcp.list_devices(): self.devices[d.id] = {"status": "idle", "session": None} def acquire(self): for did, info in self.devices.items(): if info["status"] == "idle": info["status"] = "busy" return did return None # 没有空闲设备 def release(self, device_id): self.devices[device_id]["status"] = "idle"

5.2 并行任务编排与异常隔离

多设备并行执行时,最大的风险是异常扩散。一台设备上的脚本崩溃了,不能影响其他设备。所以每个设备的执行上下文要完全隔离,异常要捕获并记录,不能往上抛导致整个调度器挂掉。我通常用 Python 的concurrent.futures.ThreadPoolExecutor或者asyncio来做并发,每个任务包一层 try-except,失败的任务重新入队或者标记为失败。

另一个问题是设备漂移。长时间运行后,某些设备可能会因为内存不足、系统更新、USB 断连等原因掉线。调度器需要定期健康检查,发现掉线的设备及时从池子里移除,并尝试重连。重连失败就告警,让人工介入。这套机制听起来复杂,但用几十行代码就能实现,比事后手动排查划算得多。

5.3 批量安装与配置下发

批量操作里最常见的场景是给所有设备安装同一个 App,或者下发同一份配置文件。Android 用adb -s <device_id> install app.apk,iOS 用xcrun simctl install或者 WDA 的安装接口。mobile-mcp 的install_app工具封装了这些差异,你只需要传 APK 或 IPA 的路径和目标设备 ID。

配置下发稍微麻烦一点。Android 可以通过adb push把文件推到/sdcard/目录,然后 App 自己去读。iOS 因为沙盒机制,不能直接推文件到 App 的沙盒里,只能通过 App 自身的导入功能或者 iCloud 同步。如果 App 支持 URL Scheme 导入,可以用open_url工具触发。如果不支持,那就只能走 UI 自动化,模拟用户手动导入的流程。

实操心得:批量安装时,Android 的adb install默认是阻塞的,一台装完才装下一台。加-r参数可以覆盖安装,加-d可以降级安装。如果要并行安装,用adb -s <id> install配合多线程,但注意 USB 带宽有限,同时装太多台会互相拖慢,一般 4 到 6 台并行比较合适。

6. 常见问题排查与避坑指南

6.1 连接类问题速查

现象可能原因排查步骤
adb devices列表为空USB 驱动未装、线缆问题、调试未开换线、换口、重装驱动、确认开发者选项
设备显示unauthorized手机未确认授权弹窗拔插重试,勾选“始终允许”
设备显示offlineADB 服务异常adb kill-server && adb start-server
WDA 启动失败签名过期、开发者模式未开重新签名、开启开发者模式并重启
MCP 客户端连不上配置路径错误、端口占用检查配置文件、确认进程启动、看日志

6.2 执行类问题与绕行方案

点击无效是最常见的问题。原因可能是坐标算错了、元素被遮挡、或者 App 做了防自动化检测。排查方法:先截图看当前界面,确认目标元素确实在预期位置;然后检查 UI 树里该元素的clickable属性,如果是 false,可能需要点击它的父节点;如果都正常但还是点不中,试试加一点随机偏移,有些 App 会检测完全一致的点击坐标。

输入框无法输入,除了前面说的中文问题,还有一种情况是输入框没有获取焦点。解决方法是先点击输入框,等 200 到 300 毫秒,再执行输入。有些 App 的输入框点击后会弹出一个自定义键盘,这时候input_text可能不生效,需要改用模拟按键的方式逐个字符输入。

截图返回黑屏,Android 上通常是因为 App 设置了FLAG_SECURE,禁止截屏。这是安全机制,没法绕过。iOS 上如果 WDA 截图黑屏,检查是不是设备锁屏了,或者 WDA 的 session 断了。重新连接 session 通常能解决。

6.3 稳定性优化的几个关键点

第一,加等待。不要假设点击后界面立刻响应,每次操作后加一个 500 毫秒到 1 秒的等待,或者用轮询的方式检查目标元素是否出现。轮询比固定等待更高效,但实现起来复杂一点。

第二,重试机制。网络波动、设备卡顿都可能导致单次操作失败,关键步骤加 2 到 3 次重试,每次重试前先截图确认当前状态,避免在错误的状态下继续操作。

第三,日志留痕。每一步操作都记录时间戳、设备 ID、操作类型、参数、结果。出问题的时候,日志是唯一的线索。我习惯把截图也存下来,按时间戳命名,排查的时候一目了然。

第四,定期重启。长时间运行的设备,ADB 连接和 WDA session 都可能变得不稳定。我的做法是每跑完 50 到 100 个任务,主动断开重连一次,清理一下状态。这个操作看起来多余,但能避免很多莫名其妙的偶发失败。

7. 与 AI Agent 结合的进阶玩法

mobile-mcp 最大的想象空间在于和 AI Agent 的结合。传统的移动端自动化是“写死流程”,每一步都预先定义好。而 AI Agent 可以根据当前屏幕内容动态决策,遇到没见过的界面也能尝试操作。比如你说“帮我在外卖 App 上点一份黄焖鸡米饭”,Agent 会自己打开 App、搜索、选店、加购物车、下单,全程不需要你写一行定位代码。

实现路径是:Agent 拿到用户指令后,先调get_ui_tree和screenshot了解当前界面,然后推理下一步该调什么工具。比如看到搜索框,就调click点击搜索框,再调input_text输入关键词。每一步执行完再获取一次 UI 树,确认操作结果,然后继续推理。这个循环直到任务完成或者 Agent 判断无法继续为止。

这种模式对 UI 树的依赖很重,UI 树的质量直接决定 Agent 的成功率。所以实际部署时,建议对目标 App 做一定的适配,比如给关键元素加上contentDescription或者accessibilityLabel,让 UI 树更完整。另外,Agent 的推理速度受限于 LLM 的响应时间,复杂任务可能需要几十秒甚至几分钟,适合对时效性要求不高的场景。

实操心得:AI Agent 操控手机目前还处于早期阶段,成功率远不如传统脚本。我的建议是混合使用:固定流程用脚本,异常处理用 Agent。比如正常下单流程用脚本跑,遇到弹窗、验证码、网络异常这些脚本处理不了的情况,再唤起 Agent 来决策。这样兼顾了效率和灵活性。

8. 性能调优与规模化部署建议

当设备数量从个位数涨到几十台,性能问题就会暴露出来。首先是ADB 连接数限制,一台电脑同时连接的 ADB 设备太多,USB 控制器会扛不住,表现为设备频繁掉线、命令响应变慢。解决方案是分机器部署,每台电脑管 8 到 12 台设备,通过中心调度器统一分发任务。中心调度器可以用 Redis 做任务队列,每台工作机跑一个 Worker 进程,从队列里取任务执行。

其次是截图和 UI 树的传输开销。截图是 PNG 格式,一张 1080P 的截图大概 1 到 2 MB,UI 树的 XML 也有几十到几百 KB。如果每步操作都传一次,网络带宽和存储都会吃紧。优化方法是:截图降分辨率或者转 JPEG 压缩,UI 树只传关键属性,不需要的节点裁剪掉。如果 Agent 和 mobile-mcp 在同一台机器上,用 stdio 模式比 SSE 模式快很多,省去了网络传输的开销。

最后是任务调度策略。简单的轮询调度在设备性能不一致的时候效率很低,快的设备早早跑完闲着,慢的设备还在慢慢磨。更好的做法是根据设备的历史执行时间做加权调度,快的设备多分配任务,慢的设备少分配。另外,任务要分优先级,紧急任务插队执行,普通任务排队等待。这套调度逻辑用 Python 的heapq或者celery都能实现,关键是把设备状态和任务队列管理好。

规模化部署还有一个容易被忽视的点:设备散热。几十台手机同时跑自动化,发热量很大,尤其是充电的时候。温度过高会导致 CPU 降频、电池鼓包、甚至设备关机。我的做法是设备不充电的时候跑任务,电量低于 20% 就暂停任务去充电,充到 80% 再继续。另外搞个小风扇对着吹,成本不高但效果明显。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/3 15:17:17

SSM+Java商超零售系统开发与毕业设计实践

1. 项目概述&#xff1a;SSMJava商超零售送货到家系统 这个基于SSM框架和Java语言的毕业设计项目&#xff0c;是一个完整的商超零售送货到家购物系统解决方案。作为2026届计算机相关专业学生的毕设选题&#xff0c;它包含了可运行的源码和配套论文&#xff0c;实现了从商品展示…

作者头像 李华
网站建设 2026/11/3 15:17:01

S32K14x CAN任意实例休眠唤醒:RX引脚外部中断方案

简介&#xff1a;面向S32K14x系列芯片嵌入式开发者的CAN休眠唤醒技术文档&#xff0c;聚焦普通模式下CAN模块不支持休眠唤醒、且节点未使用CAN0或驱动不支持Pretended Networking模式时的替代实现思路。文档以CAN RX Pin外部中断为核心&#xff0c;梳理进入休眠与唤醒两条流程&…

作者头像 李华
网站建设 2026/11/3 15:15:42

航空业座位优化:K-means聚类与实时响应系统实践

1. 项目背景与行业现状 西南航空作为全球最大的低成本航空公司之一&#xff0c;其座位分配策略一直是业内关注的焦点。传统航空业通常采用预先指定座位模式&#xff0c;乘客需支付额外费用选择心仪座位。而西南航空自1971年成立以来&#xff0c;始终坚持"开放座位"政…

作者头像 李华
网站建设 2026/11/3 15:15:39

程序员高效排查Bug与科学项目排期实战指南

1. 程序员Bug排查与项目排期的核心挑战程序员日常工作中最头疼的两件事莫过于Bug排查和项目排期。前者考验技术功底&#xff0c;后者考验项目管理能力。在实际开发中&#xff0c;这两者往往相互影响——Bug越多&#xff0c;排期越乱&#xff1b;排期越紧&#xff0c;Bug越多。我…

作者头像 李华
网站建设 2026/11/3 15:15:35

从零开始学AI工程:系统构建大模型应用与推理优化能力

1. 项目概述&#xff1a;为什么要从零开始学AI工程 “ai-engineering-from-scratch”这个标题&#xff0c;我第一眼看到就很有感触。市面上教AI的课程、帖子、付费社群多到让人眼花缭乱&#xff0c;但真正能让人从零开始、系统性地把AI工程这条链路走通的内容&#xff0c;反而稀…

作者头像 李华
网站建设 2026/11/3 15:15:33

基于Node.js+Vue+SpringBoot的检测实验室SaaS平台开发实践

1. 项目概述与背景分析"基于移动互联网的检测实验室广告云服务平台"是一个融合了Node.js、Vue和SpringBoot三大技术栈的现代化SaaS解决方案。该项目旨在为各类检测实验室&#xff08;如食品检测、环境监测、医疗器械检测等&#xff09;提供一站式的广告投放、客户管理…

作者头像 李华