上半年帮一个创业团队做校园二手交易平台,从需求梳理到第一版上线用了不到两周。那段时间正好是我重度使用AI编程工具的阶段,所以整个项目几乎是"AI辅助开发"的完整样本——Cursor负责写代码,我负责定方向、审代码、踩坑、擦屁股。
先说结论:AI辅助开发校园管理系统这类业务系统,核心瓶颈从来不是"AI能不能写出来",而是"你能不能把需求说清楚,以及有没有能力判断AI给你的方案靠不靠谱"。这篇文章不聊AI有多神,就聊一个具体的事:在AI辅助下,从0到1搭一个以二手物品交易为主营方向的校园管理系统,整个过程有哪些关键节点、哪些坑、哪些必须自己亲自判断的东西。
1. 校园二手交易系统:需求比你想的细得多
很多人以为"校园二手交易"就是闲鱼换皮,但认真拆一遍需求就会发现,校园场景的特殊性导致它和通用二手平台在产品逻辑上有明显差异。
1.1 二手交易的主流程与校园场景的特殊约束
校园二手交易的核心流程可以概括为:发布商品、浏览搜索、查看详情、线下沟通成交、线下交易、确认评价。这不是我拍脑袋定的,而是结合校园用户的习惯推出来的。
第一,交易半径极小。买方和卖方大概率在同一栋宿舍楼或者同一个校区,绝大多数交易需要线下面交。这意味着系统完全不需要复杂的订单物流模块,甚至支付功能都可以砍掉——学生之间更习惯当面转账或者小额现金交易。
第二,信任机制特殊。校园熟人社群里,用户更愿意相信同校学生,所以身份认证(学号绑定、校园邮箱验证)会比信用评分更有效。系统设计时要考虑怎么把"校园身份"体现出来,比如用户页面上显示"xx大学在校生"的认证标识,而不是搞一套复杂的芝麻信用类体系。
第三,商品生命周期短。大学生的闲置物品(教材、台灯、小家电、篮球等)往往在学期初和毕业季集中出现,流动性强,所以发布流程必须极简,拍照、填个价格就能上架,任何多余的字段都会降低发布意愿。
基于这些判断,我列了一份第一版功能清单,主要包括:
- 用户模块:注册、登录、校园身份认证
- 商品模块:发布、编辑、上下架、图片上传
- 浏览模块:商品列表、分类筛选、关键词搜索、商品详情
- 互动模块:留言/私聊(校内用户可以直接电话或微信联系)
- 交易模块:约定购买、确认完成(核心是状态流转,不做在线支付)
- 管理后台:用户管理、商品审核、数据统计
1.2 让AI帮你把"模糊需求"变成功能清单
需求拆解这个环节,AI能帮上大忙。我一开始的想法很简单,就直接在Cursor的对话里输入:
"我打算做一个校园二手交易平台,面向大学生,主营教材、数码、生活用品交易,交易线下完成,不需要支付和物流。请帮我整理一份第一版的功能清单,标注优先级,并在最后列出哪些功能可以放到第二版。"
AI给的答案通常会很全面,还附带一些你没想到的模块,比如"举报与信用体系""消息推送""图片水印"等。这时候的关键不是照单全收,而是要学会取舍。我的经验是:AI列出的功能清单里,第一版只做"不可或缺"的,凡是能靠人工或者规则应付的先砍掉。比如举报功能,第一版直接放管理后台让管理员手动处理,不需要做一个完整的举报子系统。
还有一个很实用的技巧:让AI给功能清单标注"优先级",然后追问一句"如果开发周期只有两天,哪些功能可以去掉"。AI会按用户价值重新帮你排序,这个过程往往能帮你发现真正的核心功能。我当时的第一版核心就收敛成了三个:商品发布、商品浏览搜索、交易状态流转。管理后台的很多功能甚至都是后期补的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 单人开发的技术选型:别让AI帮你选出一个hold不住的架构
技术选型是AI辅助开发中最危险的一环。原因很简单:AI对任何技术栈都能给出看似合理的建议,但如果你自己心里没底,它会把你带进一个完全驾驭不了的架构里——微服务、Kafka、Redis集群、容器编排,全给你堆上来。
2.1 为什么最终选了Spring Boot + Vue3这套组合
我并没有直接用AI的推荐,而是自己先确定了大方向,再让AI在范围内细化。候选方案有三个:
| 方案 | 开发速度 | 部署难度 | AI生成代码的准确度 | 校园项目适配度 |
|---|---|---|---|---|
| Flask + Vue3 | 最快 | 低 | 高 | 单机小规模够用,但业务复杂后代码结构容易散 |
| Spring Boot + Vue3 | 中等 | 中 | 很高 | 生态全,后期加功能方便,适合管理系统 |
| Next.js 全栈 | 快 | 低 | 中 | 前后端一体,但面试/演示时技术分量不如Java |
最终选了Spring Boot + Vue3。原因有三点:第一,Spring Boot的AI训练语料极其丰富,几乎所有常见问题AI都能给出靠谱答案,因为这类代码在网上存量太大;第二,校园管理系统后续大概率要加管理后台、权限体系、报表这类企业级功能,Spring生态的组件成熟度远高于其他两个方案;第三,团队成员(学生)对Java相对熟悉,哪怕AI辅助,也需要人能看懂代码、能改代码。
这里我想强调一个观点:AI辅助开发不等于你可以不懂技术。你可以不熟悉每个API,但你必须有能力判断AI给出的方案是否合理。具体到选型上,如果你的项目只有一个演示Demo的规模,Flask更快;如果要做的是长期迭代的校园管理系统,Spring Boot更稳。AI可以帮你查资料、写代码,但选型这个决策,最好自己想清楚。
2.2 数据库表设计:AI给的结构哪些直接能用、哪些必须改
数据库设计我让AI先出第一版,然后自己逐个审。AI给的表结构大体合理,但有几个常见问题需要人工修正。
核心表有这么几张:
- users:用户表,关键字段包括id、学号、密码(加密存储)、昵称、头像、角色(学生/管理员)、认证状态、创建时间
- goods:商品表,关键字段包括id、标题、描述、价格、分类、成色、所在校区、卖家ID、状态、浏览量、创建时间
- goods_image:商品图片表,一个商品可能有多张图,独立建表更清晰
- message:留言/私聊表,记录买家与卖家的沟通内容
- transaction_order:交易单表,记录一次完整的"约定购买-确认完成"流程,核心是状态字段
AI建议的字段基本够用,但有几个坑要注意。
第一个坑是AI喜欢把所有状态字段设计成整数枚举(比如0代表在售,1代表已售),把可读性让给了省空间。我实践下来,小型项目里状态字段用字符串更直观。比如goods表里,状态直接写成ON_SALE、RESERVED、SOLD、OFF_SHELF,后台查数据时一目了然,不用每次翻字典表。
第二个坑是索引设计。AI生成的建表SQL经常只有主键索引,没有针对查询条件的普通索引。校园二手平台最高频的查询是按分类筛选和关键词搜索,所以goods表必须给category_id建索引,给created_at建索引(按时间排序),别等数据量大了再回头补。
第三个坑是AI偶尔会把图片直接存成BLOB字段塞进数据库。这个问题在大厂规范里很常见,但小项目千万别学。图片存服务器本地磁盘或者对象存储,数据库只存路径字符串,读取效率高得多,也方便后续做CDN加速。
3. 核心模块落地:商品发布、搜索和管理后台怎么一步步跑通
选好技术栈、定好表结构,接下来就是核心功能实现。这个环节是AI辅助开发最爽的部分——写代码的速度可以快到一个下午完成一个模块。
3.1 商品发布:图片上传的坑比你想的多
商品发布是整个系统的第一个核心闭环,包含前端表单、后端接口、图片上传三个部分。前端的表单页我用Vue3 + Element Plus,让AI生成一个包含标题、描述、价格、分类、成色、校区、图片上传的表单,大概十几分钟就出来了,基本能直接用。
真正花时间的是图片上传。我先说结论:校园项目图片存储选本地磁盘 + Nginx静态映射就够了,完全不需要上对象存储。具体实现上,后端接收MultipartFile后保存到服务器指定目录,文件名用UUID重命名,然后把访问路径存回数据库。
上传接口的核心逻辑大致长这样:
java复制@PostMapping("/api/goods/publish")
public Result publish(@RequestParam("file") MultipartFile[] files,
@RequestParam("title") String title,
@RequestParam("price") BigDecimal price,
@RequestParam(value = "description", required = false) String desc,
@RequestParam("categoryId") Long categoryId) {
// 1. 校验当前用户是否已认证
User current = userService.getCurrentUser();
if (current == null || !current.isVerified()) {
return Result.error("请先完成校园认证");
}
// 2. 保存商品主信息
Goods goods = new Goods();
goods.setTitle(title);
goods.setPrice(price);
goods.setDescription(desc);
goods.setCategoryId(categoryId);
goods.setSellerId(current.getId());
goods.setStatus(GoodsStatus.ON_SALE);
// 3. 保存图片
for (MultipartFile file : files) {
String url = fileStorageService.save(file);
goodsImageService.addImage(goods.getId(), url);
}
return Result.success();
}
这个接口本身不难,坑都在细节里:
第一个坑是Tomcat默认的POST大小限制只有2MB,多张商品图一传就报错。需要在application.yml里把限制调大:
yaml复制spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 50MB
第二个坑是Linux服务器的目录权限。开发环境在Windows上一切正常,部署到Linux之后图片上传报错,大概率是应用没有目标目录的写权限。这个排查了我二十分钟,最后一条chmod命令就解决了。
第三个坑是图片回显。上传完成后前端要能立即看到图片预览,最简单的方式是后端把图片路径返回给前端,前端把IMG标签的src指向这个路径。但路径不能写死成localhost,必须是动态拼接的完整URL,否则别人访问你的网站时图片会加载不出来。
3.2 搜索与筛选:别一上来就上Elasticsearch
校园二手平台的搜索逻辑没那么复杂,MySQL的模糊查询完全够用。AI可能会建议用全文索引或者Elasticsearch,但在数据量只有几千条的情况下,这纯属过度设计。
实际搜索接口的核心SQL大致是这样的:
xml复制<select id="searchGoods" resultType="com.example.vo.GoodsVO">
SELECT g.*, u.nickname AS sellerName
FROM goods g
LEFT JOIN users u ON g.seller_id = u.id
<where>
g.status = 'ON_SALE'
<if test="keyword != null and keyword != ''">
AND (g.title LIKE CONCAT('%', #{keyword}, '%')
OR g.description LIKE CONCAT('%', #{keyword}, '%'))
</if>
<if test="categoryId != null">
AND g.category_id = #{categoryId}
</if>
<if test="campus != null and campus != ''">
AND g.campus = #{campus}
</if>
</where>
ORDER BY g.created_at DESC
LIMIT #{offset}, #{pageSize}
</select>
几个细节值得注意。第一,模糊搜索写成AND条件拼接,别用动态SQL把条件拼到字符串里,防止SQL注入。第二,查询只取在售状态的商品,已下架的和已售出的不应该出现在普通用户搜索列表里。第三,搜索结果的排序规则用"按发布时间倒序"就好,不需要搞复杂的相关度算法。
如果后续数据量真的大了,可以在MySQL里启用全文索引,配合ngram解析器做中文分词,也能撑住几万条数据的检索,完全可以等真有需求再说。
3.3 交易状态机:让AI帮你梳理流程,但别让它自由发挥
二手交易模块是整个系统里最"业务"的部分,也是最容易写乱的部分。我建议把交易拆成三个状态:
- ON_SALE:在售,任何人可以联系购买
- RESERVED:已被买家锁定,等待线下交易
- SOLD:交易完成
- CANCELLED:交易取消,商品重新回到在售或直接下架
状态流转的逻辑,可以让AI帮你梳理成一张状态机表:
| 当前状态 | 触发动作 | 次状态 | 说明 |
|---|---|---|---|
| ON_SALE | 买家点击"我要买" | RESERVED | 生成交易单,锁定商品 |
| RESERVED | 买家确认收货/卖家确认完成 | SOLD | 交易完成,双方可评价 |
| RESERVED | 任意一方取消 | ON_SALE | 商品重新上架 |
| ON_SALE / RESERVED | 卖家主动下架 | CANCELLED | 管理员后台可见记录 |
但这里必须补一句:AI能帮你画状态机,但它不知道你的业务规则。比如,卖家能不能在买家锁定后把商品卖给另一个人?买家锁定后超时不交易怎么办?这些都要靠你自己定规则。我们第一版的规则很简单:买家点击"我要买"后,商品进入RESERVED状态,买家需要在24小时内完成线下交易并点击确认,超时自动释放回在售。这个规则AI是不会主动告诉你的,它是你根据自己的业务场景想清楚的。
后端实现上用简单的枚举 + Service层判断就能搞定,不需要引入Spring StateMachine那套重型框架。核心就是每次状态变更时校验当前状态是否允许跳转到目标状态,以及操作人是否有权限。
3.4 留言与私聊:别做复杂IM,做个会话列表就够了
很多初学开发者一提到"用户交流"就想着做即时通讯,WebSocket、消息推送全上。但在校园二手交易场景里,买家看到商品后最自然的动作是:在详情页看到卖家电话或者微信,然后线下聊。所以第一版的私聊功能其实只要做到"留言 + 联系方式展示"就够了。
我让AI实现的是一个简单的会话模型:以商品为维度,买家在商品详情页留言,卖家在"我的消息"页面查看。数据表就一张message表,记录商品ID、发送人ID、接收人ID、内容、时间。查询时按商品聚合展示,效果就是"这个商品有3条留言"。
这个模块AI写得很快,但有个细节我手动改了很久:消息列表的排序。AI默认按时间正序排列,这样最老的消息在最上面,新的消息沉在底部,和用户习惯完全相反。聊天消息必须时间倒序,最新回复要在最前面。这种产品体验层面的事,AI很难自己意识到。
4. 与AI协作的实操节奏:提示词怎么写,代码怎么审
这个项目跑下来,我对"AI辅助开发"的最大感受是:AI不是帮你写代码的机器人,更像一个能力很强但需要你不断给方向的说唱搭档。协作节奏很重要。
4.1 把任务拆成AI能执行的粒度
刚开始我用AI的方式是给一个大需求:"帮我写一个校园二手交易系统的全部代码",AI给出的结果是一堆文件,但拼起来根本跑不通。后来学乖了,把任务拆到"一个功能点"的粒度,每次只让AI做一件事。
比如商品模块,我拆成了下面这些子任务:
- 设计goods表的建表SQL
- 生成Goods实体的Java类,包含所有字段的getter/setter
- 写GoodsMapper接口和对应的XML文件,实现按分类查询
- 写GoodsService的实现,包含发布商品和修改商品状态的方法
- 写GoodsController,包含分页查询、发布、修改状态的RESTful接口
- 写Vue前端页面:商品列表页、商品发布表单页、商品详情页
提示词写法也很有讲究。最有效的模板是:任务描述 + 技术约束 + 示例期望。比如:
"在Spring Boot项目里,给GoodsController写一个分页查询接口。要求:返回Page对象,每页默认10条,可按分类和关键词筛选,商品状态为ON_SALE,按发布时间倒序。参考现有UserController的返回类型,统一用Result包装。"
这个提示词比"写个分页查询"好用的地方在于:它给了技术栈约束(Spring Boot)、业务约束(只查在售)和代码风格约束(Result包装)。AI生成的代码基本不用改就能用。
4.2 生成代码之后,必须自己做的三件事
AI生成代码再快,也不能直接信任。我的流程是:生成 -> 审查 -> 修改 -> 测试,每次AI给的代码都要过三关。
第一关是建表SQL必须自己看一遍。AI有时候会给字段加不必要的冗余列,有时候会漏掉外键约束和索引。尤其要注意decimal类型的价格字段有没有设置精度,用户表密码字段长度够不够(BCrypt加密后的密码有60个字符,很多AI会默认给50)。
第二关是接口的参数校验必须自己补。AI生成的Controller层基本没有参数校验,价格传负数、标题传空字符串这种事它不管。要自己加注解,比如@NotBlank、@Positive,或者手动判断,防止脏数据写进库。
第三关是手动跑一遍完整业务流程。AI生成代码是按"点"生成的,它不保证"线"能跑通。我从发布商品到搜索、到锁定、到确认完成,每一步都手动测一遍。这个过程不花很久,但能解决80%的隐藏Bug。
4.3 实测翻车:AI生成了一段"看起来很对"的越权漏洞代码
这个项目里让我印象最深的一个坑,是AI生成商品修改接口时,没有考虑越权问题。
当时AI生成的更新商品接口长这样:
java复制@PutMapping("/api/goods/{id}")
public Result updateGoods(@PathVariable Long id, @RequestBody GoodsUpdateDTO dto) {
Goods goods = goodsService.getById(id);
goods.setTitle(dto.getTitle());
goods.setPrice(dto.getPrice());
goodsService.updateById(goods);
return Result.success();
}
这段代码从语法到逻辑都没问题,但它忽略了一个关键的事:这个接口没有校验当前登录用户是不是该商品的所有者。也就是说,任何一个登录用户,只要知道商品ID,就能修改别人的商品信息,甚至把价格改成0.01元。
修复起来很简单,只需要在修改前加一步判断:
java复制User current = userService.getCurrentUser();
Goods goods = goodsService.getById(id);
if (!goods.getSellerId().equals(current.getId())) {
return Result.error("无权修改该商品");
}
这件事给我的启发是:AI能写出功能正确的代码,但它不会自动理解"当前用户"和"资源归属"这种权限模型。用AI辅助开发时,一定要主动检查所有涉及"修改""删除"的接口,是否做了归属权校验。这是安全底线,不能依赖AI自觉。
5. 部署上线与第一周真实反馈
功能开发完,接下来是部署。校园项目的部署方案和商业项目不同——预算有限、访问量有限、维护人力极少,所以一切从简。
5.1 低预算部署方案:一台云服务器够不够
我的部署方案是一台2核4G的云服务器就够了,系统用Ubuntu,环境用Docker Compose一次性编排起来。后端打jar包跑在Docker容器里,前端构建后由Nginx托管,MySQL单独一个容器,图片目录挂载到宿主机。
部署步骤核心就这几步:
- 后端项目执行mvn clean package -DskipTests打jar包
- 准备Dockerfile,基础镜像用eclipse-temurin:17-jre
- 前端项目执行npm run build生成dist目录
- Nginx配置:/ 指向前端静态文件,/api反向代理到后端容器的8080端口,/images指向图片目录
- docker-compose up -d 一键启动所有服务
实际跑起来,2C4G的配置承载一个几百人使用的校园二手平台完全没问题。数据库连接池默认配置就行,不需要调优。第一周实测峰值在线六十多人,接口响应基本在100ms以内,没有任何卡顿。
唯一需要提前做的是HTTPS证书。现在浏览器对非HTTPS网站的提示越来越不友好,而且小程序等场景强制要求HTTPS。用certbot申请Let's Encrypt证书,三分钟就能配好,别省这一步。
5.2 上线第一周冒出来的四个问题
部署上线不等于项目结束。第一周的真实用户反馈,暴露了很多开发阶段没想过的问题。
第一个是图片加载慢。原因是手机拍照原图直接上传,一张图动辄5-8MB,在校园网环境下加载很吃力。解决办法在后端加了一个压缩逻辑:上传时统一压缩到宽度不超过1280像素、质量80%。这个小改动让页面加载速度提升了将近一倍。
第二个是垃圾商品泛滥。有人批量注册账号发布广告,把二手平台当成免费广告位。第一版处理方式是后台管理员手动删,后来加了一个简单的关键字过滤:标题和描述里命中"兼职""刷单""代购"等词,直接进入待审核状态。这个方案不完美,但胜在实现快。
第三个是下架商品还能被收藏的问题。用户收藏了一个商品,结果卖家已经线下卖出去了,但收藏夹里还显示可购买。这个问题的根源是我在设计时给收藏表加了一个冗余的状态字段,商品状态变更时没有同步更新收藏状态。修复方法是查询收藏列表时强制用JOIN判断当前商品是否在售,而不是用收藏表里存的快照状态。
第四个是真实交易场景的信任问题。有个用户发布了一台游戏本,价格明显低于市场价,很快有人"锁定"了,但线下交易时发现对方根本不在学校,差点被骗。这个问题的根源在于我们的认证只是"填写学号",没有真正验证学号归属。后来加了校园邮箱验证:用户必须通过学校邮箱接收验证码才能完成认证。这个功能开发用了半天不到,但效果立竿见影,平台上的可信度明显提升了。
这里补充说一句,管理后台在这个阶段非常关键。我做的后台除了商品管理和用户管理,最有用的一个功能是"交易看板":显示当前有多少商品在售、多少笔交易完成、有多少用户注册。运营时一眼就能看出平台活跃度,不用写复杂的报表。AI辅助下做一个这样的后台,工作量很小,但价值很大。
最后再说一点我对AI辅助开发的切身体会。这个项目能两周上线,AI确实功不可没——它把大量样板代码、CRUD接口、前端表单页面的编写时间压缩到了原来的十分之一。但真正决定项目成败的,还是那些AI无法替你做的事情:清晰的业务逻辑设计、边界情况的人工审查、上线后的运营维护。你用AI越熟练,越会发现,它放大的是你的判断力和工程素养,而不是替代它们。
