ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Django购物商城毕业设计全解析:从源码到部署避坑指南

Django购物商城毕业设计全解析:从源码到部署避坑指南

简介:这份基于Django框架的购物商城系统毕业设计资料,面向计算机专业毕业生与Python Web开发初学者。资源以学术论文为核心,完整覆盖商城系统从需求分析、架构设计、功能实现到测试优化流程,涉及用户注册登录、商品分类展示、购物车结算、订单状态管理、支付接口集成及管理员后台等典型模块。通过阅读论文,可深入理解Django的ORM模型、模板引擎和URL路由机制,掌握电商系统分层设计与数据库建模方法,为毕业设计选题、系统开发和文档撰写提供直接参考。论文内容从背景目标到总结展望均有系统阐述,方便读者对照自身项目查漏补缺。压缩包仅4.79MB,内容精炼,便于快速获取核心方案。目前已有90人学习,适合需要借鉴Django电商项目整体架构、完善自身毕设思路的开发者。

1. 这个django购物商城系统毕业设计包,凭什么值得你下载

如果你正在为毕业设计找一套能看懂、能跑通、能写进论文的django购物商城系统,这个包含论文和完整源码的压缩包就是那味药。它本质上是一个单体Django项目,实现了用户注册登录、商品展示、购物车、订单管理、后台管理这些商城标配功能,论文部分则把需求分析、数据库设计、模块实现和测试报告串成了完整的毕设链条。我拆过不少毕设源码,这套的特点是全:论文不是贴上去的说明文字,而是照着代码就能对应上的真实设计。适合两类人:一是想快速搭一个可演示商城的毕业生,二是想从源码里学Django MTV架构和ORM写法的初学者。接下来我按拆包顺序,把论文重点、部署步骤、核心逻辑和常见坑一次讲完。

2. 先从论文下手:看懂购物商城的架构设计和模块划分

2.1 论文里藏着什么:从需求分析到系统设计的完整链路

拿到压缩包,我习惯先解压看目录结构。一般会出现两个大头:一个是论文的Word或PDF文件,另一个是源码文件夹。毕设论文大多按学校模板走,但真正对写代码有指导价值的不是摘要和绪论,而是第三章需求分析和第四章系统设计。这两章会把系统拆成前台和后台两大块:前台是面向用户的,包含注册登录、浏览商品、搜索、加入购物车、下单、支付、查看订单;后台是面向管理员的,包含用户管理、分类管理、商品管理、订单管理和公告管理。论文里对应的会画功能结构图,那图基本等于Django项目里各个app的划分。比如图上画一个“商品展示”,对应源码里就可能是一个goods模块(叫product或shop也可以),图中画一条用户下单的线,对应order模块。顺着功能图去src目录里找同名目录,比对一下,架构就清楚了。

我一般会先翻论文的结构图,再对照源码目录,用表格记下功能模块和app的对应关系。比如:

论文功能模块源码app目录对应数据表
用户管理usersauth_user + user_profile
商品分类goodsgoods_category
商品信息goodsgoods_info
购物车cartcart_item
订单管理orderorder + order_goods

这个表格看起来简单,但它能让你把论文里的用例图、顺序图翻译成Django里的MTV结构。熟手拿到项目会直接看models.py,但新手先做这张映射表,后面看代码就不会迷路。

论文里的时序图或者活动图也值得看。购物车加购的时序图会画用户、商品页面、购物车服务、数据库之间的消息流动,这对应Django里的URL到View到Model操作流程。你在源码里找一个加购的View,把里面每一步对应时序图上的消息,比如“添加购物车记录”“更新数量”“返回总金额”,这就是一个很好的理解入口。论文最后的测试章节通常会写功能测试用例表,比如“输入正确用户名密码,登录成功”。我拿到源码后,会把这些用例逐个在本地跑一遍,跑不通的记录下来,很可能就是源码里隐藏的bug。这个习惯能让你在答辩时说出“我复现并修复了什么问题”,比干巴巴说“通过测试”有力得多。

2.2 用ER图和用例图反推数据库表结构:30分钟理清业务

论文第三章末尾一般有一个ER图,里面画了实体框和连线。以这套购物商城为例,核心实体差不多是:用户、商品分类、商品、购物车条目、订单。其中用户和商品是多对多(通过购物车或订单),用户和订单是一对多,订单和商品是多对多(通过订单详情表)。如果你发现论文里的ER图有“公告”或“收货地址”实体,说明这个毕设还加了额外模块,别觉得奇怪,很多学校要求必须有增删改查,所以会加公告、轮播图这类容易实现的功能。

我在拆这个项目时,先用ER图列出了所有外键关系。这张表能直接对应到models.py里的ForeignKey字段。多数毕设的Django版本会用MySQL,配置写在settings.py里,所以表的命名会在数据库迁移脚本里有迹可循。你可以先用grep确认一下:

grep -E "class .*\(models.Model\)" app/*/models.py

这条命令会把所有models文件中定义的模型类名打出来。每出现一个类,基本就是ER图里一个实体。然后对比论文中的ER图,能快速定位哪些表是核心表,哪些是辅助表。

提一个关键点:毕设论文里的ER图经常会画得比代码里多几张表,比如“角色表”“权限表”,但实际代码里可能直接用Django自带的auth_user和group表,没单独建。这不是代码偷懒,而是Django已经内置了权限模型,毕设系统设计中用“RBAC设计”来描述也算合理。你在写答辩稿时,要说清楚这一点,别自己把论文和代码的矛盾当bug。

确认完表结构,再去看order表里的状态字段。通常是一个IntegerField或者CharField,定义几个常量表示待付款、待发货、待收货等。我见过有些源码用IntegerField存0/1/2/3,但注释里没写含义,这种最容易在测试订单流程时绕晕。建议你在复现时,给状态字段加一个可读性更好的choices,后面我们做状态机会用上。

再往深一点,我要看外键字段是否都在代码里正确使用。有些毕设为了省事,会在models里用一个整数字段存关联id,而不是用ForeignKey,这样看起来数据库简单,但做联表查询时就要自己join,非常难受。你可以在python shell里验证一下关系是否声明完整:

python manage.py shell -c "from django.apps import apps; [print(m.__name__, [f.name for f in m._meta.fields if f.is_relation]) for m in apps.get_models()]"

这行命令会打印每个模型的所有关联字段。如果订单模型里只有'user'和'goods'两个关系,没有'address',那说明订单地址可能被设计成订单上的冗余字段了。理解这一点,比死记ER图要重要。

另外一个很常见的坑:很多毕设项目里和用户关联用的不是Django的auth_user,而是自己建的user_profile,用OneToOneField关联内置auth。这样加手机号、个性签名都方便。但坑在于有些代码里调用user.username,有些代码里调用user_profile.nickname,新手看代码会怀疑是两张表。在复现时,你要记住:登录认证走auth_user,业务数据走profile表。后面写注册登录功能时,这个区分会让你少走弯路。

3. 把商城跑起来:Django项目从解压到本地部署的完整步骤

3.1 环境准备:Python版本、虚拟环境和依赖安装

第一步先看requirements.txt。大多数毕设项目的依赖都很克制:Django、mysqlclient、pillow、有时候有django-ckeditor或haystack。如果缺了requirements.txt,也不要慌,用pipreqs也能扫出来。

我习惯先建一个干净的虚拟环境,避免和系统Python的包冲突。项目在Windows和Linux上都能跑,但依赖安装命令有些许不同。在Windows上:

python -m venv venv venv\Scripts\activate pip install -r requirements.txt

在Linux/macOS上:

python3 -m venv venv source venv/bin/activate pip install -r requirements.txt

这里有个常见翻车点:mysqlclient在Windows上安装特别容易失败,因为需要MySQL C客户端库。常见做法是改用PyMySQL,并在项目的__init__.py里加一句兼容代码。我在复现这套商城时,就在manage.py同级的目录下看到过类似:

# __init__.py import pymysql pymysql.install_as_MySQLdb()

这句话的作用是把mysqldb的引用替换成pymysql。如果你在pip install阶段卡住,用这个方案最省事。但要注意,PyMySQL和mysqlclient在连接表现上有细微差异,字符集处理需要统一,我们在后面避坑章再讲。

还需要注意Python版本兼容性。很多毕设项目是在Python 3.6或3.7时代写的,用了旧语法。如果你本机是Python 3.10以上,可能会出现一些第三方库的C扩展编译失败。一个保险做法是先在requirements里看Django版本,如果是1.x,那基本是Python 2或3.4时代,不建议直接上手;如果是2.x或3.x,Python 3.6以上都还跑得动。遇到项目代码里用了老写法,比如从collections导入Iterable,Python 3.10会报错,你需要改成collections.abc。少用全局替换,改一处测一处。

装完依赖后,先跑一下:

python manage.py check

这个命令会检查项目配置有没有问题,比如urls里引用了不存在的视图,app注册有误等。如果check通过,再继续配置数据库。

3.2 配置数据库:MySQL建库、Django迁移和初始数据导入

这套项目的数据库一般在settings.py里默认指向一个名为shop_db的库,用户名root,密码空或者123456。如果是这种配置,你最好改成自己的本地密码,避免后面各种权限报错。打开settings.py找到DATABASES部分:

# settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'shop_db', 'USER': 'root', 'PASSWORD': 'your_password', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': {'charset': 'utf8mb4'} } }

注意这里charset要写成utf8mb4,不要只写utf8。因为有些商品描述里带emoji,utf8字符集存不了4字节的emoji,写进去就会报Incorrect string value。这是血泪经验。

然后在MySQL里建库:

CREATE DATABASE shop_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

建完库后,执行迁移。迁移前,建议先把项目自带的sql文件找出来。毕设项目常常会在sql目录下放一个shop.sql或init.sql,里面是带数据的备份。有数据比空库好演示,商品和分类都有现成的。所以顺序是先做迁移生成基础表,再导入数据。如果反过来,可能会因为表结构不一致导致导入失败。

python manage.py migrate mysql -u root -p shop_db < sql/shop.sql

如果项目里没有提供sql文件,那就只能自己用后台手动录入了。migrate完成后,会看到Django自建的auth_user等一堆表。然后需要创建超级管理员:

python manage.py createsuperuser

这一步不一定非要放这里,但建议越早越好,因为后台很多功能验证都要用管理员账号。创建时,用户名、邮箱、密码按提示输入,密码至少8位,否则校验不通过。

提示:如果本机没有安装MySQL或者配置麻烦,也可以用SQLite先跑通业务,把settings里的ENGINE改成django.db.backends.sqlite3,NAME改成db.sqlite3,再把需要用的配置项注释掉即可。不过你要知道,最终提交的毕设环境大概率还是MySQL,SQLite只适合前期快速验证。

3.3 启动开发服务器并登录后台:验证用户、商品、订单链路

数据库就绪后,启动开发服务器:

python manage.py runserver 127.0.0.1:8000

浏览器打开http://127.0.0.1:8000,首页一般会显示商品列表。如果页面能正常加载,说明模板和静态文件路径基本没问题。如果图片加载不出来,通常是MEDIA_URL和STATIC_URL配置问题,留到避坑章说。

然后进入后台:

http://127.0.0.1:8000/admin

用刚才创建的超级管理员登录。登录后看三个模块即可:商品分类、商品信息、订单。点进商品信息,看是否有关键字段:标题、价格、库存、图片、销量、状态。随便点一个商品编辑,试着改价格保存,确认写入数据库。然后去前台页面刷新,看价格是否变化。这一步能验证数据库读写链路。

接着测试用户注册和登录。前台找“注册”入口,用一个新的邮箱或用户名注册,注册后登录。再走一遍加购流程:在商品详情页点“加入购物车”,然后进入购物车页面看数量是否正确。最后走下单流程:点击“结算”,进入订单确认页,填写模拟的收货地址,提交订单。如果能生成订单,并在后台订单列表里看到这笔记录,整条链路就是通的。

这一步如果卡住,大概率出在三处:一是session没有配置好,二是模板里action的url写错,三是模型里的外键字段不允许为空。我们会在第5章逐条排查。

验证时还要留意:如果支付功能对接的是支付宝沙箱,需要额外配置。很多毕设不会真的回调支付,而是把“支付”按钮做成了模拟支付,点一下直接改订单状态。你如果看到views里写的是“模拟支付成功”,就不用费劲去搞真实支付接口了。我们后面讲状态机时也会提到。

4. 商城核心模块实战:购物车、订单和支付状态机是怎么实现的

4.1 购物车是放数据库还是放Session?常见实现对比

购物车是商城模块里交通量最大的部分,毕设源代码里无非两种存法:Session存商品id和数量,或者后端数据库存cart_item表。Session的优点是免登录,用户关闭浏览器再打开还在;缺点是session默认存Cookie,商品一多Cookie会变大,而且Django默认的Cookie有大小限制。数据库存购物车可以支持多设备同步,但必须依赖登录状态,匿名用户就得加一个临时标示。

这套商城我拆下来,用的是数据库方案,cart_item表里带user外键和goods外键、数量、选中状态。登录后和用户绑定。你可以看cart_item表结构,通常是:

class CartItem(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) # 所属用户 goods = models.ForeignKey(Goods, on_delete=models.CASCADE) # 关联商品 number = models.IntegerField(default=1, verbose_name='数量') selected = models.BooleanField(default=True, verbose_name='是否选中')

ForeignKey的on_delete要留意。毕设里常写CASCADE,意味着用户删了,购物车记录也没了,这合理。但商品删了,购物车里对应记录也会消失,如果用户加购后商品被下架(一般用is_delete软删除),那这里应该用SET_NULL更合理。这就是一个可以写在答辩里的改进点。

加购的view逻辑一般是:先判断用户是否登录,未登录就跳转到登录页;然后查商品库存是否足够;如果购物车里已有该商品,数量累加,否则新建一条记录。这里有一个很小的坑:有些源码没有做“同一商品重复加购”的判断,每次点加购都新建一条记录,结算时同一个商品出现多行。你在复现时如果遇到,就在existing_item查询上加一个filter(user=request.user, goods=goods)判断。

4.2 订单状态机:从提交到发货的权限和流转控制

订单模块最值得研究的不是增删改查,而是状态机。购物商城的订单状态一般有:待付款、待发货、待收货、已完成、已取消。有些还有已退款。状态机的作用在于限制每次状态变化必须合法,比如已取消订单不能直接变为待收货,待付款不能跳过待发货直接变成已完成。

Django模型里常用的写法是给状态字段定义choices:

class Order(models.Model): STATUS_CHOICES = ( ('UNPAID', '待付款'), ('UNSHIPPED', '待发货'), ('UNRECEIVED', '待收货'), ('FINISHED', '已完成'), ('CANCELED', '已取消'), ) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='UNPAID', verbose_name='订单状态')

如果你在源码里看到的不是这类可读字符串,而是数字0/1/2/3,也别慌,只是字面量的区别。操作订单状态的地方通常在views里,比如支付成功后执行order.status = 'UNSHIPPED'。但这种直接赋值的方式容易失控,你不知道哪个方法改了状态。规范一点的做法是把状态流转写成Order模型的方法:

def pay(self): if self.status != 'UNPAID': raise ValueError('只有待付款订单可以支付') self.status = 'UNSHIPPED' self.save()

这样就把校验逻辑收敛在模型层,view只调用方法。我给不少朋友做代码审查时发现,毕设源码里十有八九是直接在view里改状态,连校验都没有。你可以在复现时顺手补上pay、ship、confirm方法,然后把这部分写进论文的“系统的可靠性设计”里,答辩老师会觉得你考虑得很全面。

订单权限也要注意:用户只能支付自己的订单,管理员才能发货。所以发货的view必须限定staff权限,否则任何登录用户都可以把订单改成待收货。用Django自带的@login_required和@user_passes_test就能搞定。这些装饰器比在view里手写if判断更清晰。

支付这块,如果项目对接了支付宝沙箱,你会看到一个通知回调地址,里面可能会做验签。验签失败时最直接的记录方式是写日志或打印在终端。如果没有对接真实支付,只是模拟支付,那状态机里的pay方法就只是把UNPAID改成UNSHIPPED,逻辑会简单很多。我的建议是:先走通模拟支付的流程,再考虑沙箱,不要一上来就啃SDK。

4.3 后台管理:Django Admin的定制项和权限配置

Django自带的后台是毕设的大杀器,因为业务逻辑还没写的时候,后台就可以手动管理数据了。这套商城的admin.py里通常注册了商品、分类、订单。你可以在admin.py里看到类似这样的结构:

@admin.register(Goods) class GoodsAdmin(admin.ModelAdmin): list_display = ['name', 'price', 'stock', 'sales', 'is_delete'] # 列表页展示字段 search_fields = ['name'] # 搜索框 list_filter = ['category', 'is_delete'] # 筛选器 list_editable = ['price', 'stock'] # 列表页直接编辑

list_editable是个常用但容易出错的配置:如果你把price放在list_display里,又放在list_editable里,注意list_editable的字段必须也在list_display中;还有,list_display里如果有方法字段,不能放到list_editable。新手把这两个配置搞混,后台保存商品时会报错。

权限这块,Django Admin天然支持给不同组分配不同权限。毕设里通常只需要超级管理员,但如果你想把管理员分成客服和运营,可以在后台添加组,然后给组勾选对应的app权限。如图片上传、订单发货这些操作,建议单独设权限。在代码层面,你需要在每个ModelAdmin里重写has_add_permission或has_change_permission来自定义。这个功能在答辩展示时非常亮眼,可以现场演示不同权限账号看到不同的菜单。

再补一点,Admin的表单可以定制。比如订单字段数量多,你可以在GoodsAdmin里加一个fieldsets把基本信息、价格库存、详情图分开。这样管理员用起来清晰很多。还有,如果商品状态有“上架/下架”,可以用list_filter,让后台人员一键筛选出下架商品。

另外,后台列表页默认每页显示50条,数据量大了以后会分页。你可以加search_fields让搜索更精准。比如商品编号、商品名,都搜出来会快很多。这些细节虽然不起眼,却能让评审老师觉得你是个真的自己动手做过的人。

5. 避坑指南:部署和复现这套商城最常见的五个坑

5.1 现象:migrate时报错,提示模块没有某个属性

原因:Django版本和源码所用版本不一致。很多毕设源码里的模型用了老版本特有的字段或写法,在新的Django版本下就会报错。解决:先在requirements.txt里确认Django版本,再严格按那个版本创建虚拟环境。如果requirements缺失,用pipreqs扫描或者看源码里settings.py的INSTALLED_APPS特征,估算出版本。我一般会先在虚拟环境里装一个Django 2.x的版本,然后跑python manage.py check,如果过不了再尝试3.0和1.11。这个过程看起来有点玄学,但实际是排查依赖最有效的方法。换版本之前记得清掉残留的pyc缓存,否则可能还会读到旧的编译文件。

5.2 现象:商品图片全挂,前端商品详情图显示不出来

原因:开发服务器没有映射media目录。settings里配了MEDIA_URL='/media/'和MEDIA_ROOT,但项目的urls.py没有加static serve。解决:在项目的urls.py里加:

from django.conf import settings from django.conf.urls.static import static urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

注意这行只适合DEBUG=True的开发环境,生产环境必须交给Nginx处理。很多新手改了settings没改urls,所以图片一直404。排错时先问自己两个问题:图片文件本身在不在MEDIA_ROOT目录里?URL前缀是不是从/admin或其它路径跳过来的?有一次我自己绕了半天,最后发现是浏览器缓存了旧页面,强制刷新就好了。这种单点排查要养成先清缓存的习惯。

5.3 现象:后台输对密码登录却一直跳回登录页,刷新几次偶尔又能进去

原因:session数据没写进数据库,或者cookie域名/路径设置不对。毕设源码里通常默认用数据库存session,如果migrate没执行,那session表不存在,登录当然存不住。解决:先确认执行过python manage.py migrate,并确保auth_user表存在。如果Django版本较新,还需要检查SESSION_COOKIE_SECURE和CSRF_COOKIE_SECURE是否为True,本地没有HTTPS时不要开启。另外有些项目会在settings里写死SESSION_COOKIE_PATH='/admin/',这会导致前台登录后cookie不生效,属于低级错误,看到直接删掉。

5.4 现象:支付回调验签失败,订单状态永远停在待付款

原因:支付网关的回调是异步的,代码里验签逻辑通常是把返回的所有参数拼接后加密,再与signature字段对比。问题往往出在参数编码或公钥格式上。毕设项目一般对接沙箱环境,如果用的是老版网关,公钥和私钥必须是字符串拼接格式,不能是文件路径。有一次我看了半天回调接口,最后发现是SDK要求把返回参数里的code和msg也参与验签,但源码只取了业务参数。解决:先打开回调日志,把收到的原始参数打印出来,对比支付文档,看少了哪个字段。实在验不过,先用模拟支付按钮把订单状态改掉,保证演示流程不中断。等答辩前再回来抠验签细节。

5.5 现象:改了模板或静态文件,刷新页面就是不生效

原因:Django的模板引擎和静态文件存在缓存。开发模式下,模板自动加载,但静态文件浏览器可能缓存;更隐蔽的是配置文件里如果开了CACHES的本地缓存,会导致部分页面短期不更新。解决:重启runserver进程;浏览器禁用缓存强制刷新;检查settings里的TEMPLATES是否设置了APP_DIRS=True。如果用了django-webpack之类的工具,那还要看dist目录有没有重新构建。最直接的办法是在urls里临时关闭缓存,或者给静态文件URL加时间戳。别迷信“改完必好”,强制刷新这一步成本最低,却总被忽略。

6. 让这套商城像作品集:把单体Django项目拆成可演示的技术亮点

6.1 给商品模块加一个Redis缓存:命中率提升的真实数据

商品详情页是流量最大的页面,如果每次都查MySQL,性能确实一般。常见做法是用Django的cache框架,加Redis作为后端。先在settings里配置:

CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.redis.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'TIMEOUT': 300 } }

然后在view里读缓存:

def goods_detail(request, goods_id): detail_key = f'goods_{goods_id}' goods = cache.get(detail_key) if goods is None: goods = get_object_or_404(Goods, pk=goods_id) cache.set(detail_key, goods, 300) return render(request, 'detail.html', {'goods': goods})

注意这里缓存的是ORM对象,Django会序列化保存。如果商品字段里面有ImageField,需要确保图片名可序列化。我在演示时,会用压测工具测一下加了缓存后同一商品接口的响应时间,对比不加缓存的数据。当你把这个真实数字写进论文的“性能测试”里,比单写“使用缓存改善性能”可信得多。

6.2 用Django REST Framework暴露API:给App端做准备

毕设如果只做Web端,答辩老师可能会问“能不能做成App”。最省力的回答是:已经把核心接口用DRF暴露出来了。只需要加一个api的app,然后写一个商品列表序列化器:

from rest_framework import serializers class GoodsSerializer(serializers.ModelSerializer): class Meta: model = Goods fields = ['id', 'name', 'price', 'stock', 'main_image']

再写一个ViewSet,用DefaultRouter注册路由。这样前端Web继续用原模板,App端可以直接调JSON接口。注意DRF要在settings的INSTALLED_APPS里注册,并且需要pip install djangorestframework。这个改动会新增两个文件,但整个项目的架构瞬间变成了「前后端可分离」的描述,适合写进论文的后期展望。

6.3 写一份部署文档,用Nginx+uWSGI模拟生产环境

很多同学答辩前只跑过runserver,面试官一问“生产部署”就卡壳。我一般会建议按这套流程走:先用uWSGI启动Django,然后再用Nginx反代。Linux下的两个关键配置:

# uwsgi.ini [uwsgi] http = 127.0.0.1:8001 module = 项目名.wsgi:application master = true processes = 2 threads = 2

Nginx转发:

location /static/ { alias /项目路径/static/; } location /media/ { alias /项目路径/media/; } location / { proxy_pass http://127.0.0.1:8001; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }

做完以后,把部署步骤整理成一份README.md,附在项目里。当你拿着这套东西去展示时,就不再只是“能跑”的毕设,而是一份有扩展思路的工程包。如果你正打算把这个包作为毕业设计底子,建议按这个顺序过一遍。我从那以后每次拿到新的毕设项目,都会强制走一遍加缓存、写API、补部署文档这三件事,确认完整才觉得真正读懂了项目。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进