news 2026/11/3 22:56:50

购物商城App期末大作业:安卓高分项目从0到1实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
购物商城App期末大作业:安卓高分项目从0到1实战指南

简介:在安卓应用开发中,SQLite作为轻量级本地数据库,是管理结构化数据的常用方案;RecyclerView则通过ViewHolder复用高效渲染列表界面,二者结合能快速搭建数据驱动型应用。理解其原理,有助于在课程设计中实现功能完整、逻辑清晰的移动应用。购物商城App正是典型的综合性练习场景,覆盖数据表设计、列表展示、页面跳转与状态交互。本文围绕期末大作业这一实际需求,从技术选型、功能清单、报告大纲到演示避坑,给出了一套可复现的工程实践路径,帮助开发者用扎实的本地存储与原生组件完成可运行、可讲解、不翻车的高分项目。

1. 购物商城App期末大作业:一套拿到98分的个人项目打法

期末大作业里,购物商城App几乎是安卓开发课程中出现频率最高的选题。功能层级清楚、演示效果好,报告也好写,但正因为选的人多,交上去的作品十个里有七个长得一样。下面这套拿到98分的个人项目做法,核心不是功能堆得多,而是把“能运行的源码 + 结构完整的报告 + 不翻车的演示”三个环节全部做齐。适合正在做安卓app开发期末大作业的学生,也适合想快速跑通一个完整商城demo、为后续源码二次开发打底子的读者。

2. 动手前先定方案:技术栈、功能清单与报告大纲

2.1 技术选型:Java还是Kotlin,SQLite还是远程后端

期末大作业的评分逻辑和公司项目不一样,老师最看重的是“你在一个学期里真的把安卓组件用起来了”。技术栈没必要追新,本地SQLite加RecyclerView加Activity跳转,是绝大多数安卓开发课都会覆盖的内容,把这些做扎实,比引一个自己都讲不清楚的第三方框架要稳得多。

如果用Java,代码可读性好,网上参考多,不容易被环境坑。用Kotlin的话代码更简洁,但如果你对lambda和协程不熟,报告里写不出来,答辩时也容易穿帮。我一般的原则是:课程用什么语言教学,就用什么语言交作业,别自己换赛道。数据库同理,本地数据用SQLite就够了。不要为了体现“高并发”去搭远程服务器,期末答辩的现场在教室,网络一断就是事故现场,你精心准备的云端接口会变成当场翻车的黑匣子。

这套方案还有一个隐性好处:源码包体积小、可复现性强。老师拿到你的zip,解压导入Android Studio就能跑,不需要额外装MySQL、配Tomcat,光这一点就能减少大量无效扣分点。你想想,一个期末项目如果连“一键跑起来”都做不到,报告写得再漂亮也很难拿高分。

2.2 功能清单怎么定:做多少功能才像个商城

一个购物商城App,最少要覆盖四个模块:首页商品展示、商品详情、购物车、结算生成订单。在此基础上加一个用户登录(用户名密码本地校验),已经算完整。功能在这基础上每加一项,工作量都会翻倍,因为报告里每个功能都要配截图、测试用例和代码说明。

这里给一个参考拆分,我做过几次这类指导后总结出来的比较稳的组合:普通用户端做5个界面,分别是登录注册页、首页商品列表、商品详情页、购物车页、订单确认页。数据表4张,用户表、商品表、购物车表、订单表。用户故事写6条,覆盖登录、浏览、加购、修改数量、提交订单、查看订单列表。

做功能前先问自己:每个功能的截图和异常用例写不写得出来?写不出来就不做。比如“找回密码”这个功能,看起来简单,但要接邮箱验证码、要设计密保问题,任何一个分支在报告里解释不清楚,答辩时就可能被追问到卡壳。98分项目不是靠功能列表得分,而是靠每一块都能在报告里闭环,这个账要算清楚。

2.3 报告大纲先行:代码没写也能把架子搭起来

我习惯先建报告大纲,再写代码,顺序反了容易返工。大纲就五章:引言与需求分析、总体设计、详细设计、测试、总结与展望。每个界面的功能描述、每张表的字段,先在Word里列出来,代码照着实现。这样代码写完,报告已经完成一半,不会出现最后发现少一张表、缺一个异常分支的情况。

大纲细化到这种程度才算能用:登录模块要写用户名密码校验、空值提示;商品模块要写列表加载、详情跳转;购物车要写数量加减、总价计算;订单要写提交按钮的防重复点击。把这些条目作为报告里的功能需求逐条列出来,后面写测试用例时直接对应到功能条目上,一套编号对应下来,老师会觉得你的工程化意识很到位。

报告里最容易被扣分的点,其实是“需求描述和代码实现不一致”。比如需求分析里写了“支持修改密码”,但App里根本没有入口,这种低级矛盾一眼就能看出来。大纲先行就是为了避免这个问题:需求阶段写什么,代码阶段就做什么,测试阶段就验什么,三条线始终咬合。

3. 从零搭起购物商城App:页面、数据与交互的最小实现

3.1 工程结构与依赖:用Android Studio建出原生项目

在Android Studio里新建一个Empty Activity项目,包名建议写成com.example.shopmall,不要用默认的com.example.myapplication,因为报告里要写包结构,包名太随意显得不认真。工程结构我一般这样分包:activity放页面,adapter放列表适配器,db放SQLite帮助类,model放实体类。

依赖只保留官方库:RecyclerView、AppCompat。花哨的图片加载库、网络库都不要加,期末项目用不到,加了还要在报告里解释为什么引入,纯属给自己挖坑。下面是db/DBHelper.java里最核心的建表逻辑,直接用SQLiteOpenHelper写:

public class DBHelper extends SQLiteOpenHelper { // 数据库名固定 shopmall.db,版本号从 1 开始 // 版本号每次改表结构必须 +1,否则升级安装时不会执行 onUpgrade public DBHelper(Context context) { super(context, "shopmall.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { // 商品表:id 主键自增,price 用 REAL 存小数金额 db.execSQL("CREATE TABLE goods (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT NOT NULL," + "price REAL NOT NULL," + "icon TEXT)"); // 用户表:用户名唯一,密码明文存储,考试项目够用 db.execSQL("CREATE TABLE users (" + "username TEXT PRIMARY KEY," + "password TEXT NOT NULL)"); // 购物车表:goods_id 关联商品 id,count 记录购买数量 db.execSQL("CREATE TABLE cart (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "goods_id INTEGER NOT NULL," + "count INTEGER NOT NULL)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldV, int newV) { // 演示项目常见做法:删表重建,生产环境不能这么干 db.execSQL("DROP TABLE IF EXISTS goods"); db.execSQL("DROP TABLE IF EXISTS users"); db.execSQL("DROP TABLE IF EXISTS cart"); onCreate(db); } }

这段代码有几个参数值得注意:数据库名、版本号、表结构。版本号只要不冲突,答辩时被问到你为什么设成1,可以答“这是第一个可用版本,后续再加字段时通过onUpgrade做迁移”,这个回答比“照着网上抄的”体面得多。密码明文存储这一点,报告里要主动写明局限性,这是加分的“诚实点”,老师会觉得你有安全意识。

3.2 数据层:让首页列表从SQLite读出商品

建好表后,不要在onCreate里写死插入数据,而是检测表为空时再插入测试商品。这样第一次启动App首页就有东西看,演示时不会出现“空白首页”的尴尬场面。这个初始化逻辑放在一个叫DataInitializer的工具类里,代码很简单:

// 首次启动时给商品表填充 8 条测试数据 public static void initGoodsIfEmpty(Context context) { DBHelper helper = new DBHelper(context); SQLiteDatabase db = helper.getWritableDatabase(); Cursor cursor = db.rawQuery("SELECT COUNT(*) FROM goods", null); cursor.moveToFirst(); int count = cursor.getInt(0); cursor.close(); if (count == 0) { db.execSQL("INSERT INTO goods (name, price, icon) VALUES ('咖啡', 28, 'coffee')"); db.execSQL("INSERT INTO goods (name, price, icon) VALUES ('手机壳', 39, 'case')"); db.execSQL("INSERT INTO goods (name, price, icon) VALUES ('机械键盘', 399, 'keyboard')"); // 其余 5 条商品数据按同样格式补全 } db.close(); }

查询商品列表的方法写在GoodsDao里,用rawQuery还是query其实都可以,固定表结构时我更推荐rawQuery,SQL一眼能看懂,适合写进报告:

public List<Goods> getAllGoods() { List<Goods> list = new ArrayList<>(); SQLiteDatabase db = getReadableDatabase(); Cursor cursor = db.rawQuery( "SELECT id, name, price, icon FROM goods ORDER BY id", null); while (cursor.moveToNext()) { list.add(new Goods( cursor.getInt(0), cursor.getString(1), cursor.getDouble(2), cursor.getString(3))); } cursor.close(); // 游标不关闭,重复查询会内存泄漏 db.close(); // 查询完关闭连接,避免长时间占用 return list; }

逻辑说明:ORDER BY id保证每次加载顺序稳定,moveToNext遍历游标,getInt、getString按列索引取值,索引对应SQL里SELECT字段的顺序,这里顺序写错会取到错列,调试时不好发现。游标和数据库连接一定要关闭,否则反复进出页面会卡顿甚至崩溃。

3.3 界面层:RecyclerView列表跳转详情的标准写法

列表页用RecyclerView,这是安卓开发课必考的点,也是答辩老师最爱问的地方。要讲清楚三个东西:LinearLayoutManager是线性布局、ViewHolder是复用的容器、Intent通过putExtra传参跳详情。Adapter里最核心的是onBindViewHolder:

@Override public void onBindViewHolder(ViewHolder holder, int position) { Goods goods = goodsList.get(position); holder.nameView.setText(goods.getName()); holder.priceView.setText("¥" + goods.getPrice()); // 图片用项目自带 mipmap 资源,避免网络加载图片在教室里抽风 holder.iconView.setImageResource(getIconRes(goods.getIcon())); holder.itemView.setOnClickListener(v -> { if (listener != null) listener.onClick(goods); }); } private int getIconRes(String iconName) { // key 是数据库里存的字符串,value 是 R.drawable 下的资源名 // 写死映射关系,比用反射拿资源ID更直观 if ("coffee".equals(iconName)) return R.drawable.ic_coffee; if ("case".equals(iconName)) return R.drawable.ic_case; return R.drawable.ic_default; }

这段代码里的getIconRes是很容易被忽略但很加分的细节。直接用getIdentifier反射拿资源虽然代码少,但答辩时老师问“资源不存在怎么办”会很难圆;写死映射关系虽然笨,但每条映射都在代码里,逻辑透明。列表点击事件用接口回调,不在Adapter里直接写跳转,这样Adapter只负责展示,跳转逻辑留在Activity里,职责分离,报告里也好写。

商品详情页的跳转代码放在MainActivity里:

Intent intent = new Intent(this, GoodsDetailActivity.class); intent.putExtra("goods_id", goods.getId()); startActivity(intent);

goods_id作为唯一标识传递,详情页再根据id从数据库查完整信息。这样设计的好处是列表页只传轻量数据,详情页数据自己查,流程清晰,也避免了把整个对象序列化传过去的尴尬。

3.4 让项目跑起来:模拟器与真机的最小验证

写完界面后,先把App装到安卓模拟器上跑一遍。模拟器优先用Android Studio自带的AVD,CPU选x86_64,API级别选你本机SDK里稳定那个,不要盲目追最新,最新镜像有时会踩到预览版的坑。真机调试要注意手机开启开发者选项和USB调试,部分国产机型还要在“ USB配置”里选择文件传输模式,否则adb devices里看不到设备。

如果只想快速验证APK能不能装,用命令行是最直接的:

adb install app-debug.apk

看到Success表示安装成功。如果提示INSTALL_FAILED_UPDATE_INCOMPATIBLE,说明手机上装过不同签名的旧版本,先卸载旧包再装,命令是:

adb uninstall com.example.shopmall

卸载再安装这个操作,在演示前检查设备时很常用。另外,每次改了代码不要直接拔线重装,先在Android Studio里跑一遍Run按钮,确认没有编译错误再装APK。期末现场最怕的就是改了一行代码,结果编译报错,越急越找不到问题,最后只能对着投影仪道歉。这个场景我见过太多次,属于典型的“功能都做完了但演示翻车”。

4. 把报告写成98分的样子:骨架、图表与代码说明的规范

4.1 报告骨架:五章结构与每章页数建议

报告不需要写长,17页左右最合适。太短像没干活,太长像凑字数,老师翻起来也烦。下面这个页数分配比较均衡:

章节核心内容建议页数
引言与需求分析选题背景、功能需求列表、用户故事3-4页
总体设计系统架构图、功能模块划分、ER图4-5页
详细设计数据库表结构、核心流程、关键代码说明5-6页
测试测试环境、功能测试用例、结果截图3-4页
总结与展望完成情况、遗留问题、改进方向1-2页

需求分析这一章最容易写成“我认为用户需要”的空话。正确写法是把上一章列的6条用户故事原样搬进去,每条描述一个具体场景。比如“用户点击商品列表中的任意一项,进入商品详情页,详情页显示完整信息”,这种可验证的描述才是老师想看到的需求。

报告里每张表都要有字段说明。比如用户表就写四列:字段名、类型、约束、含义,username设为主键是因为登录时要用它做唯一匹配。表格里的字段名必须和代码里的完全一致,大小写都不能错,我见过不少报告和代码对不齐的情况,答辩现场被老师翻出来,场面会很尴尬。

4.2 截图与图表的规范:让老师一眼看到工作量

截图是有规范讲究的。每个页面截图都要带文字标注,用红色框圈出当前功能点,在图片下方写一行说明,比如“图3-2 首页商品列表,点击任意商品可跳转详情”。不要一张页面截图从第一章用到第四章,老师一眼就能看出来你的图是临时凑的。

最能体现工作量的是测试用例表。最少写10条用例,覆盖正常流程和异常输入,格式参照下面这个结构:

用例编号模块操作步骤预期结果实际结果
TC-01登录输入正确用户名和密码,点击登录跳转到首页与预期一致
TC-02登录输入错误的密码提示“密码错误”与预期一致
TC-03登录用户名或密码为空提示“不能为空”与预期一致
TC-04购物车点击商品详情页的“加入购物车”购物车数量+1与预期一致
TC-05购物车点击数量减少按钮,数量为1时再次点击数量不变,提示不能减与预期一致
TC-06订单购物车为空时点击提交订单提示“购物车为空”与预期一致

不要把截图全部堆在测试章节里,正确做法是每个界面截图在详细设计里出现一次,然后在测试章节里以“操作前、操作后”的对比形式再出现一次,两次截图的用途不同——前面展示设计,后面展示验证。这个细节能让报告的整体逻辑感强很多。

4.3 代码说明怎么写:关键方法配上逻辑图

代码说明不要贴大段源码,老师最反感“源码复制进报告凑页数”。参考写法是:先说方法职责,再说入参与返回值,最后解释关键一行语句的作用。比如MainActivity的onCreate,只写“初始化数据库帮助类,创建RecyclerView适配器,设置列表点击监听”,然后配一张方法调用流程图,而不是把整段代码粘进去。

流程图不一定用Visio,Word里自带的文本框加箭头就能画清楚。画流程图的重点是只画主路径,不要画所有异常分支。比如登录流程就画“输入用户名密码→校验非空→查询数据库→比对密码→跳转首页”,登录失败的分支用一句文字说明即可。把每个核心方法都画成这种小流程图,报告会显得工程味很足。

报告的加分项放在总结与展望里:主动承认缺陷并提出改进方向。写三个就够:密码明文存储需要换成哈希加密、SQLite只适合单机需要换成远程数据库、没有接入支付是因为课程范围限制。这三个“遗憾”反而能体现你的思考深度,比通篇夸自己做得完美可信得多。这也是个人项目和代做源码最大的区别,老师一眼就能分辨。

5. 期末演示与提交避坑:模拟器、真机和打包的5个翻车点

5.1 演示前必查的四个细节

现象:模拟器上点购物车按钮,页面闪退,Logcat报RecyclerView相关错误。

原因:模拟器API级别和项目compileSdk不匹配,或者布局里用了较新的属性但模拟器镜像版本过低。

解决:演示前用老师电脑或教室机器的模拟器跑一遍完整流程,不要只在自己电脑上测。如果教室电脑的模拟器版本太老,提前把项目minSdkVersion调低重新编译,不要等到上台再改。

现象:演示过程中设备自动锁屏,老师等着看购物车页面,你在那输密码解屏。

原因:模拟器和真机的默认休眠时间是30秒,演示时没有人会记得碰屏幕。

解决:在设置里把休眠时间调到10分钟或永不,如果用的是模拟器,直接用命令行更保险:

adb shell settings put system screen_off_timeout 600000

这条命令把超时时间改成10分钟,演示前执行一次,比在设置界面里到处翻菜单快得多。结束后要记得改回来,不然你对着一台永不休眠的手机过夜,第二天它还在那亮着。

现象:模拟器滑动列表掉帧严重,点半天没反应。

原因:教室电脑配置低,模拟器吃内存,后台还挂着微信和PPT。

解决:演示前关闭所有不必要的软件,模拟器窗口调小,用流畅度优先模式。更靠谱的做法是带一台自己的真机,用USB线连上演示,性能和稳定性都远好于模拟器。

现象:系统字体被放大,界面布局错位,按钮文字显示不全。

原因:演示设备开启了“大字模式”,sp单位的文字会随系统缩放。

解决:准备设备检查一遍字体设置,把显示大小调回默认。代码层面尽量用带权重或match_parent的布局,减少固定dp宽度导致的内容溢出,这也是安卓开发课里常考的“适配”考点,答辩时顺带提一句你对sp和dp的理解,印象分会上去。

5.2 真机调试的驱动与签名问题

现象:手机连上电脑,adb devices列表是空的。

原因:可能是没开开发者模式、没开USB调试,也可能是数据线只能充电不能传数据,这种情况占了一半以上。

解决:先换一根原装数据线试,然后在手机设置里找到“开发者选项”,打开“USB调试”,部分手机还要改成“文件传输模式”。执行下面命令确认识别:

adb devices

看到device状态就是识别成功,unauthorized状态需要在手机弹窗上点“允许USB调试”。

现象:安装APK时报INSTALL_FAILED_SIGNATURE_MISMATCH。

原因:手机上装过另一个签名证书打出来的旧版本,Android不允许同包名不同签名的覆盖安装。

解决:先用命令行卸载旧包再装:

adb uninstall com.example.shopmall adb install app-debug.apk

这个现象在期末周特别常见,因为你可能把自己修改的版本发给过同学测试,同学手机里那个包和你现在要装的包签名不一致,一覆盖就报错。提前把卸载命令写在备忘录里,现场不至于慌。

现象:代码在Android Studio里跑得好好的,打包成APK后安装却闪退。

原因:debug和release构建配置有差异,最常见的是release下混淆没配好,或者你在某个Activity里用了只有debug模式才有的资源。

解决:这个阶段干脆不要打release包,期末提交APK时直接用app-debug.apk,文件名改成购物商城.apk即可。老师要的是能运行,不是上架市场,debug包完全可以交。

5.3 提交物清单:源码、报告、APK与演示视频

期末提交物一般是四个:源码工程zip、报告Word和PDF各一份、APK安装包、演示视频3到5分钟。源码压缩包提交前一定要删掉build目录和.gradle目录,这两个目录动辄几十MB,里面全是编译缓存,删完之后源码包通常只有几MB,老师解压也更快。

不要直接在网上下源码改包名就交。现在不少老师会把学生作品做代码对比,甚至做反编译检查,包名、资源ID、注释风格都是线索。源码这一关靠的是“每一步都能说清楚为什么这么写”,而不是“这段代码看起来高级”。就算网上的源码功能更全,报告那关你写不过去,答辩一问你连自己项目里的注释都解释不了,分数反而更低。

演示视频拍的时候注意三点:用模拟器录屏时把鼠标光标隐藏,进到App首页时停顿两秒让老师看清界面,操作购物车时慢慢点击不要连点。视频文件名按“学号_姓名_项目名_演示视频”格式命名,有的老师会批量存档,规范命名能减少你最后被找到时费劲解释“这个文件是不是你的”的概率。

6. 进阶验证:从单机商城到可上架App雏形的三件事

6.1 用Monkey命令做稳定性验证

App写完别急着交,先跑一轮Monkey压力测试,模拟随机的触摸、滑动、按键操作,能暴露大多数闪退问题。命令很简单:

adb shell monkey --pct-touch 70 -v 500 com.example.shopmall

--pct-touch 70表示把70%的事件设为触摸操作,500是事件总数,-v输出日志。跑完在Logcat里搜索ANR和FATAL EXCEPTION,有就说明稳定性不过关。跑这个命令之前先把购物车加满商品、登录好账号,让测试数据更接近真实使用场景,确实能测出不少只走“正确路径”时发现不了的毛病。

6.2 上架前要做的三件事

如果你后面想把购物商城发展到能上架安卓应用市场的程度,需要做三件事:一是把项目的debug签名换成正式签名,在Android Studio的Build菜单里选择Generate Signed App Bundle or APK,新建一个keystore文件,这一步上架前绕不开;二是精简权限,检查AndroidManifest里只留真正用到的权限,每多一个权限应用市场审核都会多一分质疑;三是把本地SQLite的数据层抽成接口,换成Retrofit访问远程API,UML图里数据层和网络层各自独立。

联网这一块,如果未来你考虑同时覆盖iOS端,常见的路线是换成uniapp这类跨端框架重跑一遍业务,源码大部分可以复用,但这不是期末阶段该碰的东西。期末阶段把单机版做稳、把报告写完、把演示练熟,这三件事比任何炫技都值得投入。我做这个项目最后悔的是没从第一天就用好版本控制,改坏一个文件只能凭记忆重写,同一个坑你踩过一次就会长记性。先建仓库、再动代码,哪怕只有你一个人开发,这条习惯也值得从一开始就养成。希望帮到你。

本文还有配套的精品资源,点击获取

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

Flutter鸿蒙开发实战:首页布局拆解与组件组合指南

把Flutter鸿蒙开发的系列写到第五篇&#xff0c;前几篇把环境搭建、工程骨架和状态管理聊完&#xff0c;总算可以动手写一个真实的页面了。这篇文章我准备聚焦在首页基础布局上&#xff1a;从拿到设计稿怎么拆结构开始&#xff0c;逐个讲透Flutter布局里最常用的Flex、Column、…

作者头像 李华
网站建设 2026/10/24 3:58:34

WPF DispatcherTimer异步任务取消策略:CancellationToken与MVVM封装实践

聊到 WPF 里的 DispatcherTimer&#xff0c;十个人里九个第一反应是“定时刷新数据的玩意儿”。这没错&#xff0c;但真正到了生产环境&#xff0c;尤其是把 DispatcherTimer 和 async/await 这种异步任务捏在一起用的时候&#xff0c;问题就远不是“设置个 Interval、挂个 Tic…

作者头像 李华
网站建设 2026/10/24 4:02:07

SpringBoot+Vue学生课外活动管理系统全栈实战:从需求拆解到部署避坑

最近帮人做了一套基于SpringBoot的学生课外活动管理系统&#xff0c;前后端完整跑通&#xff0c;折腾了小半个月。这个项目技术栈不算复杂&#xff0c;但麻雀虽小五脏俱全——用户体系、权限控制、活动发布、报名审核、消息通知、数据统计全都要顾到。如果你正在准备JavaWeb方向…

作者头像 李华
网站建设 2026/10/24 10:16:11

基于Spring Boot和MyBatis Plus的代码作业查重系统实战解析

简介&#xff1a;这是一套基于Spring Boot和MyBatis Plus的代码作业查重系统完整源码&#xff0c;面向需要实现作业提交、自动查重与评分管理场景的各类开发者&#xff0c;适合作为毕业设计或企业级教学平台的基础。系统覆盖学生、教师、管理员三类角色&#xff0c;支持作业发布…

作者头像 李华
网站建设 2026/11/3 10:12:38

Spring Boot社交后端实战:从好友申请到WebSocket实时聊天

在夜间节目场景里&#xff0c;“交个朋友”是一个非常典型的陌生人社交话题。本文不讨论节目内容本身&#xff0c;而是从开发者的视角&#xff0c;把“黑夜排档交个朋友”这个选题落地成一个可以运行的社交后端项目&#xff1a;用户注册、兴趣标签匹配、好友申请、实时聊天、离…

作者头像 李华