1. 从一句“能不能做个剧本快读”开始项目缘起与整体设计去年年底一个做短剧和网文的朋友找到我说他们团队每天要处理几十上百个剧本文件格式五花八门有 PDF、有 Word、有扫描件甚至还有拍照发过来的。编辑们一个个打开看光筛选就耗掉大半天。他问我“能不能搞个小程序上传剧本自动识别文字再给个快速阅读的界面”这个需求听起来简单但拆开来看涉及文件上传、文字识别、数据存储、阅读器交互、用户体系好几个模块。我当时正好在深度使用 WorkBuddy 这个 AI 编程助手就想拿它当主力工具从零把这个小程序跑通。最终成品叫“剧本快读”已经通过审核上架日活虽然不算高但编辑团队用得很顺手。这篇文章不是官方文档的复述而是我把整个开发过程——从需求拆解、技术选型、WorkBuddy 的实际使用方式到云开发配置、OCR 接入、审核踩坑——完整记录下来。如果你也在用 WorkBuddy 做微信小程序或者想找一个“AI 辅助 云开发”的实战案例这篇应该能帮你省下不少试错时间。核心关键词先摆出来WorkBuddy、微信小程序、云开发、OCR、云函数。这五个词贯穿了整个项目后面每个环节我都会结合它们来讲。先说清楚这个项目适合谁看。如果你是完全没碰过小程序的新手建议先补一下微信开发者工具的基本操作和云开发的开通流程否则有些步骤会卡住。如果你已经有一定小程序开发经验但想看看 WorkBuddy 在实际项目中怎么用、OCR 怎么接、云函数怎么写那可以直接跳到对应章节。如果你只是好奇“AI 编程助手到底能不能做出上架产品”那这篇记录应该能给你一个真实的答案。整体设计上我遵循了一个原则能交给云开发的绝不自己搭服务器。原因很简单个人开发者维护服务器成本太高而微信云开发自带数据库、存储、云函数还有免费的额度对于这种中小规模工具类小程序来说完全够用。WorkBuddy 在这个过程中扮演的角色更像是一个“随时在线的结对程序员”——我描述需求它给代码我贴报错它给排查思路我拿不准 API 用法它给示例。但它不是万能的有些地方它给的方案需要我根据实际情况调整后面会具体说。2. 技术选型与 WorkBuddy 的实战用法2.1 为什么选微信云开发而不是自建后端这个决定其实没花太多时间。我列了一个简单的对比表维度自建后端微信云开发服务器成本至少一台云服务器月付几十到几百免费额度够用超出按量付费数据库需要自己安装、配置、备份自带 NoSQL 数据库控制台可视化管理文件存储需要对象存储服务单独计费自带云存储和小程序无缝集成用户鉴权需要自己实现登录态维护云函数直接获取 openid无需额外鉴权运维需要监控、日志、扩容基本不用管微信侧维护上线速度慢配置环节多快开通即用对于“剧本快读”这种工具类小程序用户量不会爆发式增长云开发的免费额度数据库 2GB、存储 5GB、云函数 40 万次调用/月完全撑得住。而且云函数里可以直接调用微信的 OCR 接口省去了自己对接第三方 OCR 服务的麻烦。注意云开发的免费额度不是永久不变的微信偶尔会调整政策。上线前建议去官方文档确认一下当前额度避免超限后产生意外费用。2.2 WorkBuddy 在项目中的四种使用模式我用 WorkBuddy 的方式大致可以分成四类每一类都有不同的提问技巧和注意事项。第一种生成基础代码骨架。比如我要写一个云函数来处理文件上传我会直接说“帮我写一个微信云函数接收小程序端上传的文件调用 OCR 接口识别文字把结果存到云数据库返回识别文本。” WorkBuddy 会给出一个完整的云函数模板包括wx-server-sdk的引入、数据库操作、错误处理。这个骨架大概能省我 30 分钟的基础编码时间。第二种排查报错。这是 WorkBuddy 最实用的场景。小程序开发过程中报错信息往往很模糊比如“errCode: -1”或者“cloud function execution error”。我把完整报错贴给 WorkBuddy它通常能给出几个可能的原因和对应的排查方向。有一次云函数一直超时它提示我检查“云函数入口文件是否正确导出 main 函数”以及“数据库连接是否在函数外部初始化”结果真的是入口文件写错了。第三种API 用法查询。微信小程序的 API 更新频繁有些用法文档写得不够直观。比如wx.chooseMessageFile和wx.chooseMedia的区别、云存储uploadFile的返回值结构、OCR 接口的imgUrl参数格式这些我都是直接问 WorkBuddy它给的示例比翻文档快。第四种代码审查和优化建议。写完一个模块后我会把代码贴给 WorkBuddy问“这段代码有没有性能问题或者安全隐患”。它有时候能指出一些我忽略的细节比如云函数里没有做参数校验、数据库查询没有加索引、文件上传没有限制大小。不过 WorkBuddy 也有明显的局限。它给的代码有时候会“幻觉”出一些不存在的 API或者把旧版本的写法混进来。所以我的习惯是WorkBuddy 给的代码关键部分一定要去官方文档核对一遍。尤其是涉及支付、用户隐私、审核相关的接口不能完全依赖它。2.3 项目目录结构与核心模块划分整个小程序的项目结构如下剧本快读/ ├── cloudfunctions/ │ ├── ocrRecognize/ # OCR 识别云函数 │ ├── getFileList/ # 获取用户文件列表 │ └── deleteFile/ # 删除文件 ├── miniprogram/ │ ├── pages/ │ │ ├── index/ # 首页文件列表 │ │ ├── upload/ # 上传页面 │ │ ├── reader/ # 阅读器页面 │ │ └── profile/ # 个人中心 │ ├── components/ │ │ └── file-card/ # 文件卡片组件 │ ├── utils/ │ │ ├── format.js # 文本格式化工具 │ │ └── request.js # 云函数调用封装 │ └── app.js └── project.config.json核心模块就三个上传模块负责接收文件并上传到云存储OCR 模块负责调用云函数识别文字阅读器模块负责展示识别结果并支持快速阅读交互。其他都是辅助。这个结构不算复杂但对于个人项目来说足够清晰。WorkBuddy 在生成目录结构建议时我特意让它“按照微信小程序官方推荐的分层方式”来给避免后期重构。3. 核心细节解析OCR 接入、云函数编写与阅读器交互3.1 OCR 方案选型为什么最终用了微信自带的 OCROCR 是这个项目的核心能力。我一开始考虑过几种方案方案优点缺点是否选用微信 OCR 接口和小程序无缝集成云函数直接调用免费额度够用识别精度一般对复杂排版支持有限选用第三方 OCR API精度高支持表格、手写体需要额外付费要自己处理鉴权和网络请求备用本地 OCR 库不依赖网络小程序环境不支持包体积太大排除自己训练模型可定制成本极高不现实排除最终选微信 OCR 的原因很简单省事。云函数里一行cloud.openapi.ocr.printedText就能调用不需要额外申请 key不需要处理跨域不需要担心网络延迟。虽然精度不是顶尖但对于印刷体剧本来说够用了。如果遇到识别效果差的文件我会在阅读器里提供一个“手动修正”入口让用户自己改几个字。提示微信 OCR 接口目前支持印刷体、身份证、银行卡、行驶证等几种类型。剧本属于印刷体用printedText就行。调用时传入图片的云存储 fileID 或者网络 URL 都可以。3.2 云函数 ocrRecognize 的完整实现与参数计算这个云函数是整个项目最核心的部分。我先把完整代码贴出来然后逐段解释。// cloudfunctions/ocrRecognize/index.js const cloud require(wx-server-sdk) cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db cloud.database() exports.main async (event, context) { const { fileID, fileName, fileType } event const wxContext cloud.getWXContext() const openid wxContext.OPENID // 参数校验 if (!fileID || !fileName) { return { code: 400, msg: 缺少必要参数 } } try { // 调用微信 OCR 接口 const ocrResult await cloud.openapi.ocr.printedText({ imgUrl: fileID }) if (!ocrResult || !ocrResult.items || ocrResult.items.length 0) { return { code: 500, msg: 识别失败未获取到文字 } } // 拼接识别结果 const textContent ocrResult.items.map(item item.text).join(\n) // 存入云数据库 const addResult await db.collection(scripts).add({ data: { openid: openid, fileName: fileName, fileType: fileType || image, fileID: fileID, content: textContent, wordCount: textContent.length, createTime: db.serverDate(), updateTime: db.serverDate() } }) return { code: 200, msg: 识别成功, data: { id: addResult._id, content: textContent, wordCount: textContent.length } } } catch (err) { console.error(OCR 识别失败, err) return { code: 500, msg: 识别失败 err.message } } }几个关键点解释一下。参数校验不能省。我一开始没写校验结果有用户传了空文件云函数直接报错日志里一堆异常。加上if (!fileID || !fileName)之后至少能返回一个友好的错误提示。cloud.openapi.ocr.printedText的imgUrl参数。官方文档说支持云存储 fileID 和网络 URL但实测下来fileID 的识别速度更快因为不需要云函数再去下载图片。所以上传流程是小程序端先把图片传到云存储拿到 fileID再把 fileID 传给云函数。识别结果的拼接方式。OCR 返回的items是一个数组每个元素包含text和pos位置信息。我一开始直接join()结果所有文字挤在一起阅读体验很差。后来改成join(\n)按行拼接效果好很多。如果你想要更精细的排版可以根据pos里的坐标信息做换行判断但那样代码会复杂不少我权衡后没做。数据库字段设计。openid用来区分用户fileName和fileType方便展示content存识别文本wordCount存字数阅读器里显示用createTime和updateTime用db.serverDate()保证时间准确。字数统计的一个小坑。textContent.length统计的是字符数包括换行符和空格。如果你想要更准确的“字数”可以用正则去掉空白字符再统计const wordCount textContent.replace(/\s/g, ).length我在阅读器里显示的是去掉空白后的字数这样更符合用户直觉。3.3 上传模块文件选择、大小限制与云存储路径规划上传模块看起来简单但实际踩了不少坑。小程序端选择文件主要有两个 APIwx.chooseMessageFile和wx.chooseMedia。前者可以从聊天记录里选文件支持 PDF、Word、图片等后者只能选图片和视频。因为剧本可能是 PDF 或 Word所以我用了wx.chooseMessageFile。// miniprogram/pages/upload/upload.js Page({ data: { fileList: [], uploading: false }, chooseFile() { wx.chooseMessageFile({ count: 5, type: file, extension: [pdf, doc, docx, jpg, jpeg, png], success: (res) { const files res.tempFiles // 大小限制单文件不超过 10MB const validFiles files.filter(f f.size 10 * 1024 * 1024) if (validFiles.length files.length) { wx.showToast({ title: 部分文件超过10MB已过滤, icon: none }) } this.setData({ fileList: validFiles }) } }) }, async uploadFile() { const { fileList } this.data if (fileList.length 0) return this.setData({ uploading: true }) wx.showLoading({ title: 上传中... }) try { for (const file of fileList) { const cloudPath scripts/${Date.now()}-${file.name} const uploadRes await wx.cloud.uploadFile({ cloudPath: cloudPath, filePath: file.path }) // 调用 OCR 云函数 const ocrRes await wx.cloud.callFunction({ name: ocrRecognize, data: { fileID: uploadRes.fileID, fileName: file.name, fileType: file.name.split(.).pop() } }) if (ocrRes.result.code ! 200) { throw new Error(ocrRes.result.msg) } } wx.hideLoading() wx.showToast({ title: 识别完成, icon: success }) setTimeout(() { wx.navigateBack() }, 1500) } catch (err) { wx.hideLoading() wx.showToast({ title: 上传失败 err.message, icon: none }) } finally { this.setData({ uploading: false }) } } })文件大小限制。微信云存储单文件最大支持 50MB但 OCR 接口对图片大小有限制而且大文件上传慢、识别慢。我设了 10MB 的上限超过就过滤掉。这个值可以根据实际情况调整但建议不要超过 20MB。云存储路径规划。我用scripts/${Date.now()}-${file.name}作为路径时间戳保证唯一性文件名保留方便识别。但这里有个坑如果文件名包含特殊字符比如中文、空格、括号云存储路径可能会出问题。后来我加了一个简单的文件名清洗const safeName file.name.replace(/[^a-zA-Z0-9.\u4e00-\u9fa5]/g, _)这样只保留字母、数字、点和中文其他字符替换成下划线。批量上传的处理。我用for...of循环逐个上传而不是Promise.all并发。原因是并发上传容易触发云存储的频率限制而且 OCR 接口也有调用频率限制。逐个上传虽然慢一点但稳定。如果你要上传很多文件可以在循环里加一个await new Promise(r setTimeout(r, 500))来降低频率。3.4 阅读器页面快速阅读交互的核心逻辑阅读器是用户停留时间最长的页面交互设计直接影响体验。我的核心思路是把长文本切成短句让用户一眼扫过减少滚动。具体做法是把识别出来的文本按标点符号切分成句子每个句子作为一个“卡片”用户点击或滑动切换到下一句。这样比传统滚动阅读快很多尤其适合剧本这种对话密集的文本。// miniprogram/pages/reader/reader.js Page({ data: { sentences: [], currentIndex: 0, fontSize: 32, lineHeight: 1.8 }, onLoad(options) { const id options.id this.loadScript(id) }, async loadScript(id) { const db wx.cloud.database() const res await db.collection(scripts).doc(id).get() const content res.data.content // 按标点切分句子 const sentences content .split(/[。\n]/) .map(s s.trim()) .filter(s s.length 0) this.setData({ sentences }) }, nextSentence() { const { currentIndex, sentences } this.data if (currentIndex sentences.length - 1) { this.setData({ currentIndex: currentIndex 1 }) } else { wx.showToast({ title: 已读完, icon: none }) } }, prevSentence() { const { currentIndex } this.data if (currentIndex 0) { this.setData({ currentIndex: currentIndex - 1 }) } }, changeFontSize(e) { const delta e.currentTarget.dataset.delta const newSize Math.max(24, Math.min(48, this.data.fontSize delta)) this.setData({ fontSize: newSize }) } })切分句子的正则。我用[。\n]作为分隔符覆盖了中文常见句末标点和换行。但剧本里经常有省略号、破折号这些没有作为分隔符。后来我加上了…和——效果更好。你可以根据实际文本特点调整正则。字体大小调节。我提供了 24 到 48 的调节范围步长 4。这个范围是实测下来比较舒服的太小看不清太大一屏显示不了几个字。默认 32适合大多数手机屏幕。阅读进度记忆。我一开始没做这个功能结果用户反馈“每次打开都从头开始”。后来在onUnload里把currentIndex存到本地缓存下次打开时读取onUnload() { wx.setStorageSync(reader_progress_ this.data.scriptId, this.data.currentIndex) }这个改动很小但用户体验提升明显。4. 实操过程与核心环节实现从零到上架的完整流程4.1 环境准备与 WorkBuddy 的初始配置在开始写代码之前需要先把环境搭好。这一步看起来简单但新手很容易卡住。第一步注册小程序账号。去微信公众平台注册选择“小程序”类型。个人主体可以注册但有些类目需要企业主体。剧本快读属于工具类个人主体可以过。注册完成后在“开发管理”里拿到 AppID。第二步下载微信开发者工具。去官网下载稳定版安装后扫码登录。新建项目时填入 AppID选择“不使用云服务”还是“使用云服务”——这里一定要选“使用云服务”否则后面开通云开发会麻烦。第三步开通云开发。在开发者工具左上角点击“云开发”按钮按提示开通。开通后会得到一个环境 ID这个 ID 后面在云函数和小程序端都会用到。第四步配置 WorkBuddy。WorkBuddy 有多个版本我用的桌面版。安装完成后需要配置项目路径让它能读取到小程序项目文件。具体操作是在 WorkBuddy 里打开项目文件夹然后就可以在对话框里直接引用文件了。注意WorkBuddy 的缓存目录默认在系统盘如果项目多了缓存会占不少空间。可以在设置里把缓存目录改到其他盘。这个操作在 WorkBuddy 的设置面板里找到“缓存目录”选项点击更改即可。4.2 云开发环境初始化与数据库集合创建云开发开通后需要在控制台创建数据库集合。我创建了两个集合scripts存储剧本识别结果字段包括 openid、fileName、fileType、fileID、content、wordCount、createTime、updateTime。users存储用户信息字段包括 openid、nickName、avatarUrl、createTime。创建集合时权限设置很关键。scripts集合我设置为“仅创建者可读写”这样每个用户只能看到自己的剧本。users集合设置为“所有用户可读仅创建者可写”方便后续做用户展示。数据库索引也要加。我在scripts集合的openid字段上建了索引因为查询“我的剧本列表”时会频繁用到。不加索引的话数据量大了查询会变慢。// 查询我的剧本列表 const db wx.cloud.database() const res await db.collection(scripts) .where({ openid: openid }) .orderBy(createTime, desc) .skip(page * 20) .limit(20) .get()分页用skip和limit每页 20 条。这个数字是权衡后的结果太少要频繁加载太多一次查询数据量大。20 条刚好一屏能滑完。4.3 云函数部署与调试的完整记录云函数写完后需要部署到云端。在开发者工具里右键云函数文件夹选择“上传并部署云端安装依赖”。这一步会把你本地的代码和package.json里的依赖一起打包上传。依赖安装的坑。wx-server-sdk是必须的版本要和你云开发环境匹配。我一开始用了最新版结果部署后报错“模块找不到”。后来改成wx-server-sdk: ~2.6.3问题解决。建议在package.json里锁定版本不要用^或*。云函数调试。开发者工具的云函数面板里可以右键云函数选择“本地调试”。本地调试可以打断点比看日志方便。但本地调试需要配置环境变量把云开发环境 ID 填进去。这个在调试面板的设置里操作。日志查看。云函数执行日志在云开发控制台的“云函数”-“日志”里查看。日志会显示每次调用的入参、出参、执行时间、错误信息。我排查 OCR 识别失败时就是靠日志发现是图片格式不支持OCR 只支持 jpg、png、bmp 等几种格式webp 不支持。4.4 小程序端页面开发与组件复用小程序端有四个页面首页、上传页、阅读器页、个人中心。首页展示文件列表上传页负责选文件和上传阅读器页展示识别结果个人中心展示用户信息和设置。文件卡片组件。首页的文件列表项我抽成了一个组件file-card接收fileName、wordCount、createTime三个属性点击时触发tap事件。这样首页的代码简洁很多。// components/file-card/file-card.js Component({ properties: { fileName: String, wordCount: Number, createTime: String }, methods: { onTap() { this.triggerEvent(tap) } } })页面间通信。上传完成后需要刷新首页列表。我用的是wx.navigateBack返回首页然后在首页的onShow里重新加载数据。这样虽然会多一次查询但逻辑简单不容易出错。顶部导航栏高度适配。小程序顶部导航栏在不同机型上高度不一样尤其是全面屏手机。我用wx.getSystemInfoSync()获取状态栏高度然后动态设置导航栏的 paddingconst systemInfo wx.getSystemInfoSync() const statusBarHeight systemInfo.statusBarHeight this.setData({ statusBarHeight })然后在 WXML 里用stylepadding-top: {{statusBarHeight}}px。这样在 iPhone 和安卓上都能正常显示。4.5 审核上架前的自查清单与提交过程小程序开发完成后不能直接发布需要先提交审核。审核前我做了一轮自查清单如下检查项状态备注用户隐私协议已配置在“设置”-“用户隐私保护指引”里填写服务器域名无需配置云开发不需要配置域名类目选择工具-效率个人主体可选功能页面完整性已检查所有按钮都有响应无空白页测试账号已提供审核员需要登录才能看完整功能内容安全已接入用security.msgSecCheck检查用户输入版本号1.0.0首次提交隐私协议是重点。因为小程序收集了用户 openid 和上传的文件必须在隐私协议里说明收集目的、使用方式、存储期限。这个在微信公众平台的“设置”-“基本设置”-“用户隐私保护指引”里填写。不填的话审核会被驳回。内容安全接口。用户上传的剧本内容需要做安全检测我用的是cloud.openapi.security.msgSecCheck在 OCR 识别完成后调用一次检查识别文本是否包含违规内容。如果有就不存入数据库直接返回错误。const securityRes await cloud.openapi.security.msgSecCheck({ content: textContent }) if (securityRes.result.suggest risky) { return { code: 403, msg: 内容包含违规信息无法保存 } }审核时间。首次提交审核大概用了 1 天被驳回一次原因是“隐私协议未配置”。配置后重新提交半天就过了。所以隐私协议一定要提前弄好不要等审核驳回再补。5. 常见问题与排查技巧实录5.1 OCR 识别失败的五种典型情况和解决方法OCR 识别失败是我遇到最多的问题没有之一。整理了一下主要有五种情况问题现象可能原因解决方法返回空结果图片模糊、文字太小提示用户重新拍摄或建议上传更清晰的图片报错“imgUrl invalid”fileID 格式不对或文件已删除检查 fileID 是否完整确认云存储文件存在识别结果乱码图片包含手写体或特殊字体微信 OCR 对印刷体支持好手写体效果差建议用户手动修正云函数超时图片太大OCR 处理时间长限制上传文件大小或在云函数里设置更长的超时时间调用频率超限短时间内大量调用加队列或延迟控制调用频率最坑的一个问题有一次用户上传了一张 PNG 图片OCR 一直返回空。我排查了半天最后发现是图片的透明通道导致 OCR 无法识别。解决方法是在上传前把图片转成 JPG 格式去掉透明通道。这个转换在小程序端可以用 canvas 做但比较麻烦。后来我直接在云函数里加了一个判断如果 fileType 是 png先下载图片用sharp库转成 JPG 再识别。不过sharp库比较大会增加云函数包体积权衡后我选择了在前端提示用户“建议上传 JPG 格式图片”。5.2 云函数调用超时与内存不足的排查思路云函数默认超时时间是 3 秒内存 256MB。对于 OCR 识别来说3 秒有时候不够尤其是图片大的时候。我遇到过几次超时排查过程如下第一步看日志。云开发控制台的日志会显示每次调用的执行时间。如果接近 3 秒那就是超时了。第二步调整超时时间。在云函数的config.json里可以设置timeout最大 60 秒。我改成了 20 秒足够 OCR 处理了。{ permissions: { openapi: [ocr.printedText] }, timeout: 20 }第三步检查内存。如果日志显示“内存不足”需要在云函数配置里调大内存。我调到了 512MB因为 OCR 返回的结果可能比较大加上数据库操作256MB 有点紧张。第四步优化代码。数据库连接不要在函数内部反复创建放在函数外部初始化。这样云函数复用实例时不需要每次都重新连接。// 放在 exports.main 外面 const db cloud.database() exports.main async (event, context) { // 直接用 db不需要重新初始化 }5.3 小程序端真机调试与开发者工具表现不一致的处理这个问题很常见开发者工具里跑得好好的真机上就出问题。我遇到过的几个问题一接口访问正常真机失败。开发者工具里云函数调用正常真机上一直报“网络错误”。排查后发现是手机网络问题切换到 WiFi 后正常。但这不是代码问题是环境问题。后来我在代码里加了重试机制失败后自动重试一次。问题二文件选择器在真机上打不开。wx.chooseMessageFile在开发者工具里可以选本地文件但真机上只能从聊天记录里选。这是 API 的限制不是 bug。我在上传页加了一行提示“请先从聊天记录中发送文件再从这里选择”。问题三字体大小在真机上显示不一致。开发者工具用的是系统字体真机上可能被用户设置了字体大小。我在app.json里加了fontSize: medium限制字体缩放范围。问题四息屏和切出的区分。小程序在息屏和切出时都会触发onHide但行为不同。息屏时不应该保存阅读进度切出时应该保存。我用了wx.onAppShow和wx.onAppHide配合时间戳来判断但实现比较复杂。后来简化了不管息屏还是切出都保存进度反正下次打开时读取最新进度就行。5.4 审核被驳回的常见原因与应对策略我一共被驳回过两次第一次是隐私协议未配置第二次是“功能不完整”。第二次的原因让我很意外审核员说“上传文件后没有看到识别结果”。我排查后发现审核员上传的文件格式是 webp而 OCR 不支持 webp导致识别失败页面显示空白。审核员以为功能没做完。应对策略在 OCR 云函数里加一个格式白名单不支持的格式直接返回友好提示而不是空白页。const supportedFormats [jpg, jpeg, png, bmp] const ext fileName.split(.).pop().toLowerCase() if (!supportedFormats.includes(ext)) { return { code: 400, msg: 暂不支持该格式请上传 JPG 或 PNG 图片 } }另外审核时最好提供一个测试账号并在审核备注里写清楚操作步骤。比如“请先用测试账号登录然后在聊天记录里发送一张 JPG 图片再在小程序里选择该图片上传即可看到识别结果。” 这样审核员不会因为不知道怎么操作而误判。5.5 性能优化从识别到展示的耗时压缩用户从点击上传到看到识别结果中间经历了选择文件 - 上传云存储 - 调用云函数 - OCR 识别 - 写入数据库 - 返回结果 - 页面刷新。整个链路走下来快的时候 3 秒慢的时候 10 秒以上。我做了几项优化优化一压缩图片。上传前用wx.compressImage压缩图片质量设为 80%尺寸限制在 2000px 以内。这样上传快OCR 也快。const compressRes await wx.compressImage({ src: file.path, quality: 80, compressedWidth: 2000 })优化二并行处理。多个文件上传时上传和 OCR 可以并行。但要注意云函数调用频率限制我用了Promise.all但加了并发数控制最多同时处理 3 个。优化三结果缓存。识别结果存入数据库后下次打开阅读器直接从数据库读不需要重新识别。这个已经做了。优化四骨架屏。识别过程中显示骨架屏而不是空白页让用户知道正在处理。这个用小程序自带的skeleton组件就能做。实测下来优化后平均耗时从 8 秒降到了 4 秒左右用户感知明显提升。6. 一些个人体会和后续可扩展的方向这个项目从开始到上架前后大概用了三周时间其中一半时间花在调试和审核上。WorkBuddy 在其中帮了不少忙尤其是写云函数和排查报错的时候省了很多翻文档的时间。但它不是“一键生成”的神器关键逻辑还是得自己把关尤其是涉及微信 API 的地方一定要去官方文档核对。后续我打算加几个功能一是支持 PDF 直接解析不经过 OCR这样速度快很多二是加一个“角色提取”功能自动识别剧本里的角色名和对话方便演员快速定位三是做多端适配目前只做了微信小程序后面可能考虑支付宝小程序或 H5。如果你也在做类似的项目我的建议是先把核心链路跑通不要一开始就追求完美。上传、识别、展示这三个环节能跑通就已经完成了 80%。剩下的都是优化和打磨。遇到问题多查日志云开发的日志系统很完善大部分问题都能从日志里找到线索。