乐于分享
好东西不私藏

用 AI 开发App从0到上架全过程 | 第 13 篇:文本笔记接口开发与前后端联调实战

用 AI 开发App从0到上架全过程 | 第 13 篇:文本笔记接口开发与前后端联调实战

文本笔记接口开发与前后端联调实战

上一篇我们做好了文本笔记的编辑页面,但保存只是 console.log 模拟,数据关掉页面就没了。

这一篇来解决数据持久化的问题:怎样用 AI 开发后台接口、怎样把前端的 mock 调用替换为真实接口、以及联调过程中踩了哪些坑。


一、先理清需要哪些接口

在动手之前,我先对照前端页面的功能,列出需要后台提供哪些接口:

功能
请求方式
说明
新建笔记
POST
提交标题、正文、分类、标签、字数
修改笔记
PUT
更新已有笔记内容
查询详情
GET
根据 id 获取笔记内容
分页列表
GET
笔记列表页分页查询
批量删除
DELETE
传入 id 数组,逻辑删除
数量统计
GET
今日新增数 + 总数
分类字典
GET
获取分类列表(杂记/想法/工作/学习/生活)

理清了,开始给 AI 发提示词。


二、第一个提示词:设计数据表

后台开发的第一步是设计数据库表。我给 AI 发了这样的提示词:

提示词内容:

请参照前端文本笔记编辑页面(edit.vue)的内容,设计对应的数据库表。表名用 digital_note,生成建表 SQL 文件并加上字段注释,生成后直接导入到数据库。

真实场景:不需要手动列出每个字段,AI 会自动读取前端页面的内容--标题输入框、正文文本域、分类选择、标签输入、字数统计,根据这些功能自行规划数据库需要哪些字段,包括字段类型、长度、默认值,还会自动加上创建人、更新人、创建时间、更新时间、逻辑删除标记这些通用字段。

AI 做了什么:

  • 读取前端编辑页面的代码,分析出需要存储的数据:标题、正文、分类、标签、字数

  • 自动补充了通用字段:主键 id(雪花算法)、创建人、更新人、创建时间、更新时间、逻辑删除标记

  • 生成了建表 SQL 文件,包含完整的字段定义和注释

  • 自动执行 SQL 将表导入到数据库,可以直接使用了

什么是逻辑删除?:逻辑删除不是真的把数据删掉,而是把 del_flag 字段从 '0' 改成 '-1'。查询时只查 del_flag='0' 的数据,被标记为 '-1' 的数据不会出现在结果里,但数据库里还保留着。好处是误删可以恢复,坏处是数据会越来越多。对于笔记类 App,逻辑删除比物理删除更安全。

什么是雪花算法?:雪花算法(Snowflake)是 Twitter 开源的一种 ID 生成算法,生成的 ID 是一个 64 位整数,保证分布式环境下全局唯一、趋势递增。简单说就是每条数据都有一个不会重复的数字 ID,不需要数据库自增。


三、第二个提示词:生成后端各层代码

表建好了,接下来生成 Java 后端代码。这个项目基于 PIG 后台框架(Spring Boot + MyBatis-Plus),有成熟的代码规范和目录结构。我给 AI 发了很简单的提示词:

提示词内容:

请参照 PIG 后台框架的相关规范,生成文本笔记页面需要的接口,以及后台管理页面可能需要的接口。用户端的控制器需要和管理端的控制器区分开,以便做权限控制。

真实场景:不需要手动列出每个接口的请求方式和路径,AI 会自动参照 PIG 框架的目录结构和代码规范,读取前端页面的功能需求,自行规划需要哪些接口。PIG 框架本身有完善的代码生成体系,AI 会遵循框架的分层规范(Entity -> Mapper -> Service -> Controller),自动生成标准代码。

顺便说说为什么把三个项目放在同一个目录下?:最开始我们说过把后台服务、后台管理页面、前端 uniapp 这三个项目放在同一个目录下,再导入到 Trae 编辑器里。这样做最大的好处是:AI 可以同时读取三个项目的代码。当我在前端页面里调用了某个还不存在的接口时,直接告诉 AI「参照前端页面的功能生成对应的接口」,AI 就会自动读取前端代码,分析出需要哪些字段、哪些接口,然后在后台服务里生成对应的 Controller、Service、Entity。不需要自己切换编辑器窗口、复制粘贴代码、重新描述需求。AI 在处理前端页面时如果发现缺少需要的接口,甚至会主动提醒你并自动生成。三个项目在一个工作区里,AI 全都能看到,全程无缝衔接。

AI 做了什么:

  • 参照 PIG 框架规范,自动生成了 Entity、Mapper、Service、Controller 各层代码

  • 生成了两个 Controller:用户端控制器(路径前缀 /note/text)给 App 调用,管理端控制器(路径前缀 /admin/note/text)给后台管理页面调用

  • 两个控制器共用同一套 Service、Entity、Mapper,避免代码重复

  • 用户端控制器:按当前登录用户过滤数据(用户只能看到自己的笔记),支持分页查询、详情、新增、修改、删除、统计

  • 管理端控制器:加了 @PreAuthorize 权限注解,可以查看所有用户的数据,需要对应权限才能操作

  • Entity 类用了 MyBatis-Plus 注解(@TableName、@TableId、@TableLogic),逻辑删除自动处理

  • Service 额外加了 countToday 方法,统计当前用户今日新增数量

为什么要区分用户端和管理端?:用户端接口给 App 用,用户只能操作自己的数据;管理端接口给后台管理页面用,管理员可以查看所有用户的数据。两套接口共用同一套 Service 逻辑,但 Controller 层的权限控制和数据过滤不一样。这样既保证了数据安全,又避免了代码重复。

什么是 MyBatis-Plus?:MyBatis-Plus 是 MyBatis 的增强版,最大的好处是不用写基础 SQL。继承 BaseMapper 就自动有了增删改查方法,继承 IService 就自动有了分页查询方法。PIG 框架内置了 MyBatis-Plus,AI 生成的代码会自动遵循这套规范。

AI 生成的后端文件一览

AI 参照 PIG 框架的分层规范,自动生成了以下文件:

分层
文件名
说明
实体层
NoteText.java
映射 digital_note 表,带 MyBatis-Plus 注解
数据访问层
NoteTextMapper.java
继承 BaseMapper,自带增删改查
业务层
NoteTextService.java
业务接口,含 countToday 统计方法
业务实现
NoteTextServiceImpl.java
继承 ServiceImpl,实现业务逻辑
用户端控制器
NoteTextController.java
路径 /note/text,给 App 调用
管理端控制器
AdminNoteTextController.java
路径 /admin/note/text,带权限注解

实体类的核心注解(省略字段细节):

两个控制器的核心结构(省略方法体):

提示:以上代码只展示了类的核心结构和路径声明,方法内部的实现逻辑没有贴出来。实际开发中 AI 会参照 PIG 框架现有的 Controller(如 pig-upms 里的代码)生成完整的方法体,包括参数校验、分页封装、返回值统一格式等,不需要自己写。


四、第三个提示词:同步数据到聚合索引表

这个项目之前设计了一张聚合索引表,所有类型的笔记(文本/语音/图片/手绘)都有一份索引记录在这张表里,首页和列表页统一从这张表查询。所以文本笔记的新增、修改、删除都需要同步到聚合表。我给 AI 发了提示词:

提示词内容:

文本笔记的增删改需要同步到聚合索引表(NoteBaseService):

  • 新增笔记后:调用 noteBaseService.syncSave,传入类型"text"、笔记 id、标题、正文摘要(截取前 200 字)、分类、标签

  • 修改笔记后:调用 noteBaseService.syncUpdate,参数同上

  • 删除笔记后:调用 noteBaseService.syncDelete,传入笔记 id

  • 正文摘要用 StrUtil.maxLength(content, 200) 截取,避免存太长

  • 同步逻辑放在 Controller 层,在主表操作成功后执行

AI 做了什么:

  • 在 save 方法里,noteTextService.save 成功后调用 noteBaseService.syncSave

  • 在 update 方法里,updateById 成功后先查出完整数据再调用 syncUpdate

  • 在 remove 方法里,removeBatchByIds 成功后遍历 id 数组逐个调用 syncDelete

  • 正文摘要用 StrUtil.maxLength 截取前 200 字

为什么要同步到聚合表?:假设有 4 种笔记类型,分别存在 4 张表里。首页要展示所有笔记的列表,如果不用聚合表,就要查 4 张表再合并,很慢。有了聚合表,每条笔记在新增时同步一份索引到聚合表,首页只查聚合表一张就够了。这是典型的「空间换时间」优化。


五、后台接口测试

后台代码写完了,先不急着对接前端,先用接口测试工具验证一下接口是否正常。我用的是 Apifox(类似 Postman 的接口测试工具)。

测试流程:

  1. 先调用登录接口获取 token

  2. 把 token 放到后续请求的 Header 里(Authorization: Bearer xxx)

  3. 调用 POST /note/text 新建一条笔记,验证返回的 id

  4. 调用 GET /note/text/{id} 查询刚建的笔记,验证内容正确

  5. 调用 PUT /note/text 修改笔记,再查一次验证修改生效

  6. 调用 GET /note/text/page 分页查询,验证列表能查到

  7. 调用 DELETE /note/text 删除,再查验证查不到了

  8. 调用 GET /note/text/count 验证统计数据

测试通过后,后台接口就算完成了。接下来进入前后端联调阶段。


六、第四个提示词:前端对接保存接口

后台接口好了,现在要把前端的 mock 调用替换成真实接口。第一个要替换的是保存功能。我给 AI 发了提示词:

提示词内容:

把编辑页的 doSave 函数从 mock 改为调用真实接口:

  • 新建笔记:调用 POST /note/text,提交标题、正文、分类、标签、字数

  • 修改笔记:调用 PUT /note/text,提交 id + 标题、正文、分类、标签、字数

  • 新建成功后,从返回数据里拿到笔记 id,保存到 noteId 变量(后续自动保存就变成修改了)

  • 保存中状态显示「保存中...」,成功显示「已保存」,失败显示「保存失败」

  • 请求头带 token(Authorization: Bearer xxx)

  • 在 api/note.ts 里新增 saveText 和 updateText 两个函数

AI 做了什么:

  • 在 api/note.ts 里新增了 saveText(POST)和 updateText(PUT)两个函数

  • doSave 函数改为:如果有 noteId 就调 updateText,没有就调 saveText

  • 新建成功后从返回数据取 id 赋值给 noteId.value

  • 保存状态在 try/catch/finally 里更新

在 api/note.ts 中新增的接口函数:

编辑页面调用接口的关键片段:

七、第五个提示词:前端对接查询接口

保存搞定了,接下来是查询。打开已有笔记时需要从后台加载数据。我给 AI 发了提示词:

提示词内容:

把 loadNote 函数从 mock 改为调用真实接口:

  • 调用 GET /note/text/{id} 获取笔记详情

  • 把返回的 title、content、category、tags 填充到页面变量

  • 加载中显示 loading 状态,加载完关闭

  • 加载失败显示「加载失败」提示

  • 在 api/note.ts 里新增 getTextById 函数

  • 加载成功后记录一条浏览历史(调用 recordView 接口)

AI 做了什么:

  • 在 api/note.ts 里新增了 getTextById 函数

  • loadNote 改为调用 getTextById,把返回数据填充到 title、content、category、tagText

  • 加载成功后调用 recordView 记录浏览历史

  • 用 isLoading 变量控制加载状态,加载中不触发自动保存

在 api/note.ts 中新增的查询函数:

编辑页面加载数据的关键片段:

注意:这里的 isLoading.value = true 就是后面踩坑篇「加载数据触发自动保存」的预防措施。如果不加这行,给 title、content 赋值时会触发 watch 监听,导致刚打开页面就触发了一次保存。


八、第六个提示词:分类字典对接

分类标签之前用的是 mock 数据,现在要改为从字典接口加载。但这里有个问题:PIG 框架原本只提供了查询单个字典的接口,如果页面需要多个字典(比如分类字典 + 标签字典),就要发多次请求,网络不好的时候体验很差。所以我让 AI 在字典控制器里加了一个批量查询方法:

在字典控制器里新增一个 getDictByTypes 方法,支持批量查询多个字典:传入一个字典 key 的数组(比如 note_category,note_tag),一次返回所有字典数据。前端调用 GET /dict/types?types=note_category,note_tag 就能同时获取多个字典。

AI 做了什么:

  • 在字典控制器新增了批量查询接口,接收逗号分隔的字典 key,一次返回多个字典数据

  • 前端在 onLoad 里调用 getDictByTypes('note_category') 获取分类字典

  • 如果以后需要多个字典,只需传 getDictByTypes('note_category,note_tag') 一次搞定

  • 把返回的数据映射为 { label, value } 格式赋值给 categories

  • mock 数据删除,完全由接口驱动

前端调用字典接口的片段:

什么是字典接口?:字典就是「选项列表」。分类(杂记/想法/工作/学习/生活)就是一种字典,存在数据库的字典表里。后台提供字典接口,前端调用后拿到选项列表。好处是:要加新分类不用改代码,在后台管理页面加一条字典记录就行。

为什么要批量查询?:框架原本只支持一次查一个字典,如果页面需要 3 个字典就要发 3 次请求。在弱网环境下,3 次请求意味着 3 倍的等待时间,还可能有的成功有的失败。批量查询一次返回所有字典,请求次数从 N 次变成 1 次,体验好很多。


九、踩坑时刻:联调过程中的问题

9.1 问题:新建笔记后自动保存变重复新建

现象:新建笔记第一次保存成功了,但继续编辑触发自动保存时,又创建了一条新笔记,导致重复。

原因:新建保存成功后,没有把返回的 id 存下来,noteId 还是 null,下一次自动保存又走了新建逻辑。

怎么告诉 AI 修复

新建笔记保存成功后,从返回数据里取 id 赋值给 noteId.value。这样下一次自动保存时 noteId 有值,就会走 updateText 修改逻辑,不会重复新建。

9.2 问题:加载笔记时触发了自动保存

现象:打开已有笔记,数据刚加载到输入框里,还没等用户操作呢,自动保存就触发了,保存状态显示「保存中...」。

原因:loadNote 给 title、content 赋值时,触发了 watch 监听,watch 调用了 scheduleSave。

怎么告诉 AI 修复

加一个 isLoading 变量,加载笔记时设为 true,加载完设为 false。scheduleSave 里检查 isLoading,如果是 true 就直接 return,不触发保存。这样数据填充到输入框时不会误触发自动保存。

9.3 问题:跨域请求被拦截

现象:前端请求后台接口,浏览器控制台报跨域错误(CORS)。

原因:前端开发服务器端口和后台端口不一样,浏览器默认拦截跨域请求。

怎么告诉 AI 修复

前端开发时配置代理,把 /api 开头的请求代理到后台地址。在 vite.config 里配置 server.proxy,把 /api 代理到 http://localhost:9999。

什么是跨域?:浏览器有个安全策略叫「同源策略」,只允许网页请求和自己在同一域名、同一端口的接口。前端在 localhost:5173,后台在 localhost:9999,端口不一样就是跨域,浏览器会拦截。解决办法是配置代理,让前端请求先发给前端服务器,前端服务器再转发给后台,绕过浏览器的同源策略。

vite 代理配置片段:

9.4 问题:保存接口返回 401

现象:保存笔记时后台返回 401 未授权。

原因:请求头没带 token,后台不知道是谁在操作。

怎么告诉 AI 修复

请求头需要带 Authorization: Bearer + token。token 从本地存储获取(uni.getStorageSync('access_token'))。请在 http 请求拦截器里统一添加,不用每个请求手动加。

AI 在请求拦截器里统一加了 Authorization 头,所有请求自动带上 token。关键片段:


十、前后端联调的完整流程

最后总结一下前后端联调的完整流程:

步骤
操作
验证方式
1. 后台接口测试
用 Apifox 逐个测试接口
返回数据正确
2. 前端替换 mock
把 mock 调用改为真实接口
无报错
3. 新建笔记
输入内容,等 1 秒自动保存
状态显示「已保存」
4. 查看笔记
退出重进,打开刚建的笔记
内容正确显示
5. 编辑笔记
修改内容,等自动保存
状态显示「已保存」
6. 删除笔记
在列表页删除
列表中不再显示
7. 统计验证
查看今日数量和总数
数据正确

十一、总结

后台接口开发和前后端联调的几个关键经验:

  1. 后台先测试再联调:先用接口测试工具验证后台接口正常,再对接前端,能快速定位问题在前端还是后台

  2. MyBatis-Plus 省了大量 SQL:继承 BaseMapper 就有 CRUD,LambdaQuery 拼条件,大部分接口零 SQL

  3. 新建成功后一定要存 id:不存 id 就分不清是新建还是修改,会导致重复创建

  4. 加载数据时要屏蔽自动保存:用 isLoading 标记,加载中不触发 watch 的保存逻辑

  5. token 在拦截器统一添加:不用每个请求手动加,拦截器自动处理

  6. 跨域用代理解决:开发时配置 vite 代理,上线后同域名就没有跨域问题

  7. 同步聚合表保证数据一致:主表增删改时同步到聚合表,首页和列表页统一查聚合表

下一篇预告:下一篇将展示语音笔记的开发过程,包括录音功能、音频文件上传、语音转文字等功能。