ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue+MySQL商城系统从零开发到部署完整指南

SpringBoot+Vue+MySQL商城系统从零开发到部署完整指南 又到了毕设季每年这个时候都能在技术群里看到大量“求一个商城系统源码”的消息。电商类选题确实是Java web方向的常青树SpringBootVueMySQL这套组合更是占了半边天。但很多同学拿到一份号称“米家商城”的完整源码包之后不知道怎么消化——论文不会写、答辩答不上来、项目跑不起来反过来又来找我帮忙“脱水”。这篇东西不是给你贴一堆现成代码的那没意义我想以一个做过完整项目的Java开发者的视角把这个“SpringBootVueMySQL商城”从立项、设计、开发到部署的完整链路讲透。你要是正准备做类似选题或者刚拿到一个商城资源包不知道从哪下手这篇值得存下来慢慢看。1. 选题价值与整体方案为什么“老三样”反而是稳妥之选先说个很多人没想明白的问题每年都有人说SpringBootVueMySQL太简单、太老套、没亮点但每年最火的毕设还是它。原因不是大家偷懒而是这套技术栈能最快地覆盖一个完整Web项目应该包含的全部知识点——后端接口设计、前端页面交互、数据库建模、权限控制、文件上传、支付流程模拟、部署上线。你把这套链路走通毕业论文的“工作量”和“技术含量”其实都够了。1.1 课题拆解从“米家商城”这个壳里你能挖出多少需求“米家商城”这个选题最聪明的地方在于它有具体场景。不是泛泛的“商城系统”而是以智能家居产品为主的垂直电商平台。产品种类如手机、电视、路由器、摄像头、扫地机器人、智能灯泡等天然自带分类属性、价格区间、品牌调性、详情页素材你不用自己去编造商品数据小米官网甚至所有电商平台都能提供大量真实参考。这给论文里的“需求分析”章节提供了丰富素材也给你前端的UI设计提供了明确范本。从功能上看“商城”听上去只有几个页面但要完整覆盖至少涉及以下模块用户端注册登录、首页商品展示、商品分类筛选、商品搜索、商品详情、购物车管理、订单下单、订单支付模拟、订单列表、个人中心。管理端商品管理上架下架、库存设置、图片上传、分类管理、订单管理发货、取消、退款、用户管理、轮播图管理。公共能力JWT Token鉴权、角色权限控制、统一异常处理、分页查询、文件上传、接口文档。把这些业务模块理清楚你的论文目录就已经出来了数据库表结构也基本有了雏形。我在带学生梳理需求时经常强调一句话不要先想“我能做什么”先想“用户会怎么点”。从打开网站到下单支付用户每点一下背后就是一个接口、一张表、一个页面组件。1.2 技术选型依据SpringBoot负责稳Vue负责快MySQL负责省心很多同学选技术栈是跟风答辩老师一问“为什么选SpringBoot而不是SSH”“为什么用Vue而不是JSP”当场卡壳。这里把理由补全后端SpringBoot核心价值是“约定优于配置”。以往SSM整合要写一堆XML配置SpringBoot用自动配置来解决这个问题能让开发重心放在业务代码上。对毕设来说开发周期紧SpringBoot的生态成熟度、中文资料数量、排错信息丰富度都远超其他框架。另外它内嵌Tomcat打包成Jar就能跑部署文档能少写三分之一内容。前端Vue核心价值是组件化和数据驱动。商城首页、商品列表、购物车这些页面有大量重复UI结构商品卡片、价格标签、分页组件用Vue组件抽一次到处复用。Vue的响应式机制让你不用像JQuery那样频繁操作DOM——数据变了页面自动跟着变。对单人开发的毕设来说这套开发模型效率提升是断崖式的。数据库MySQL没什么好争论的中小型Web项目的事实标准。事务支持、ACID特性、InnoDB引擎、主外键约束这些是电商业务里订单和库存数据一致性的底线。最重要的是你毕业后找工作面试MySQL一定是必考项做毕设顺便把事务隔离级别、索引优化这些点复习了一箭双雕。2. 数据库设计购物车、订单、库存之间的联动关系是全局设计的锚点商城系统的数据库是整个项目的骨架。我见过不少翻车案例代码写得挺顺结果订单表和商品表之间没建立关联、购物车没有唯一约束导致重复数据后期一边开发一边改表改得头皮发麻。数据库设计这事儿前面多花两天后面省两周。2.1 核心表结构规划从用户到订单的字段级设计思路一个完整的商城数据库至少包含以下这些表每张表的字段设计都有讲究用户表userid、username、passwordBCrypt加密存储、nickname、phone、avatar、create_time。注意一点密码字段长度至少64BCrypt加密后的字符串长度是60你设个32长度的字段等着报错吧。手机号建议加唯一索引将来做登录名用。注册时间加default值CURRENT_TIMESTAMP少写一行业务代码。商品表productid、name、subtitle副标题、main_image、sub_images、detail、price、stock、status上下架状态、category_id、sales、create_time。价格这里有一个经典坑用Decimal而不是Double。电商系统的价格精度要求极高Double有浮点误差0.10.2这种问题在Money场景下是灾难级的。Decimal(10,2)表示总长度10位小数2位232323.23这种规模足够了。销量字段用int就行属于冗余设计——每次展示商品列表都要临时算销售量的话性能扛不住。分类表categoryid、name、parent_id父分类ID。如果只做一级分类parent_id可以忽略但电商平台几乎都有“手机-小米手机”这种层级结构预留parent_id字段能让后续迭代更顺滑。查询时用递归或者把层级控制在2级代码简单很多。购物车表cart_itemid、user_id、product_id、quantity、checked是否选中、create_time、update_time。这里必须加一个**UNIQUE(user_id, product_id)**联合唯一索引。否则用户反复添加同一商品表里出现两行重复记录购物车数量就乱套了。订单表orderid、order_no订单号、user_id、total_price、payment实付金额或status、address信息Receiver_name、receiver_phone、receiver_address、pay_type、pay_time、create_time。订单号建议用时间戳随机数来生成规格yyyyMMddHHmmss年月日时分秒6位随机数保证高并发下单不重复。唯一的坑是字符串长度要设够32为宜。订单明细表order_itemid、order_id、product_id、product_name、product_image、product_price、quantity、total_price。这里要冗余商品名称、图片、价格。为什么因为商品可能改名、下架甚至删除但历史订单必须保留下单时的快照。不冗余的话你将来查看订单商品删了订单项就变成一串数字ID没法看。2.2 MySQL表关联与事务边界保证“下单扣库存”不会超卖表之间关联其实不复杂订单表通过order_no作为唯一标识订单明细表通过order_id关联订单表购物车表关联user_id和product_id。重点是事务边界也就是“一个操作要锁哪些表、改几个字段”。以“下单”这个核心动作为例前端点击“去结算”后端接收购物车商品列表计算总金额。先生成订单主记录status待支付。再批量生成订单明细记录。扣减商品库存。清空对应购物车项。这四步必须放在一个Transactional事务里任何一个环节失败全部回滚。数据库层面库存字段扣减必须用“库存库存-1”这种原子操作而不是先查出来再算再更新——高并发下先查后改会出现超卖问题。毕设不一定会被高并发测试但答辩老师问你“如何避免超卖”你能把这条思路说出来印象分直接拉满。另外还有“支付成功后更新订单状态”这个边界先更新订单状态再更新商品销量这俩必须在一个事务里。订单表status字段用int而不是varchar0待支付、1已支付、2已发货、3已完成、4已取消代码里用枚举或者常量来控制这个状态流转。3. 后端核心链路SpringBoot里的鉴权、商品、购物车与订单实现数据库设计完后端接口设计就顺了。SpringBoot后端主要就是Controller层提供Restful接口Service层处理业务Mapper层MyBatis-Plus或者JPA跟数据库打交道。这一章节挑几个核心链路来讲实现思路和容易出问题的点。3.1 JWT登录鉴权为什么用Token方案而不是Session商城系统有用户端和管理员端你不能让普通用户访问管理接口。常见的方案就是JWT Token。流程是这样的用户登录成功后后端用用户ID、用户名、过期时间生成一个加密Token返回给前端前端每次请求都在HeaderAuthorization字段带上这个Token后端通过拦截器或过滤器校验Token的合法性解析出用户身份。为什么要用JWT而不是传统的Session两个层面讲技术上SpringBoot默认Session是单机内存存储项目部署成集群就失效了。JWT是无状态的Token自包含用户信息后端不用存任何会话数据水平扩展零成本。答辩上主动提到“无状态鉴权”和“分布式系统扩展友好”说明你不是停留在“能用”层面而是想明白“为什么”。实现时注意几点JWT秘钥不要写在代码里放application.yml配置Token过期时间建议设置2小时管理端可以更长拦截器里要做Token解析同时把userId放到ThreadLocal或Request上下文里后续下单接口直接取当前登录用户ID避免从参数里读用户ID导致越权风险——这也是很多人容易忽略的安全漏洞。3.2 商品模块分页、搜索、分类筛选怎么配合商品列表页是商城访问量最高的接口一般需要支持分页、按分类筛选、按关键字搜索、按价格/销量排序。用MyBatis-Plus的LambdaQueryWrapper就能搞定// 商品分页查询 Override public IPageProductVO getProductPage(ProductQuery query) { PageProduct page new Page(query.getPageNum(), query.getPageSize()); LambdaQueryWrapperProduct wrapper new LambdaQueryWrapper(); // 若勾选了分类按分类ID筛选 if (query.getCategoryId() ! null) { wrapper.eq(Product::getCategoryId, query.getCategoryId()); } // 关键字搜索名称或副标题模糊匹配 if (StringUtils.isNotBlank(query.getKeyword())) { wrapper.and(q - q.like(Product::getName, query.getKeyword()) .or().like(Product::getSubtitle, query.getKeyword())); } // 排序DEFAULT新品优先PRICE_ASC价格升序PRICE_DESC价格降序SALES销量排序 switch (query.getSort()) { case PRICE_ASC - wrapper.orderByAsc(Product::getPrice); case PRICE_DESC - wrapper.orderByDesc(Product::getPrice); case SALES - wrapper.orderByDesc(Product::getSales); default - wrapper.orderByDesc(Product::getCreateTime); } return productMapper.selectPage(page, wrapper).convert(product - { ProductVO vo new ProductVO(); BeanUtils.copyProperties(product, vo); return vo; }); }这套接口设计尽量注意两点分类筛选和搜索是叠加关系用户可能在手机分类下再搜“电视”所以不能用if-else排他逻辑排序字段用白名单校验避免用户传一个数据库不存在的字段导致SQL报错。商品详情页相对简单一个按ID查详情的接口但要查出来是基本信息在product表详情大图放detail字段富文本或多图拼接。同时详情接口要注意两点下架商品status0不能被普通用户查到商品浏览次数可以不做实时累加避免每次请求都触发一次update操作。3.3 购物车与订单一个典型事务场景的完整代码逻辑购物车模块的核心方法就是增删改查加勾选状态。重点看“添加购物车”的幂等处理——用户加了同一件商品三次购物车应该显示数量3而不是三行重复数据。可以先查cart_item表里是否已有该user_idproduct_id的记录有则update quantity加1没有则insert新记录。这一步也是为什么前面要强调联合唯一索引。订单流程比购物车复杂不少。我把核心方法序列写一下Transactional(rollbackFor Exception.class) public OrderVO createOrder(OrderCreateRequest request) { // 1. 获取当前登录用户 Long userId UserContext.getUserId(); // 2. 查询购物车中选中的商品项 ListCartItem cartItems cartMapper.selectByUserIdAndChecked(userId); if (CollectionUtils.isEmpty(cartItems)) { throw new BizException(请先选择商品); } // 3. 遍历计算总价并检查库存 BigDecimal totalPrice BigDecimal.ZERO; ListOrderItem orderItems new ArrayList(); for (CartItem item : cartItems) { Product product productMapper.selectById(item.getProductId()); if (product null || product.getStatus() ! 1) { throw new BizException(商品已下架 item.getProductId()); } if (product.getStock() item.getQuantity()) { throw new BizException(库存不足 product.getName()); } totalPrice totalPrice.add(product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))); // 构建订单明细快照 OrderItem orderItem new OrderItem(); orderItem.setProductId(product.getId()); orderItem.setProductName(product.getName()); orderItem.setProductImage(product.getMainImage()); orderItem.setProductPrice(product.getPrice()); orderItem.setQuantity(item.getQuantity()); orderItem.setTotalPrice(product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))); orderItems.add(orderItem); } // 4. 插入订单主表 Order order new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setTotalPrice(totalPrice); order.setStatus(0); order.setReceiverName(request.getReceiverName()); // ... 地址信息 orderMapper.insert(order); // 5. 批量插入订单明细 orderItems.forEach(item - item.setOrderId(order.getId())); orderItemMapper.batchInsert(orderItems); // 6. 扣减库存 for (CartItem item : cartItems) { productMapper.deductStock(item.getProductId(), item.getQuantity()); } // 7. 清除购物车已选商品 cartMapper.deleteByUserIdAndChecked(userId); return buildOrderVO(order, orderItems); }这套流程里最值得讲的是第6步deductStock的实现必须是SQL层面的原子操作UPDATE product SET stock stock - #{quantity} WHERE id #{id} AND stock #{quantity}。这句SQL里的stock #{quantity}条件哪怕在并发场景下也能防止超卖——如果两个请求同时扣最后一件库存只有一个人能成功另一个更新影响行数为0抛出异常触发回滚。3.4 后台管理接口RBAC权限模型和文件上传的坑管理端和用户端共用一套JWT鉴权体系但要在Token里带上角色信息user或admin并定义一个RequireAdmin注解做角色校验拦截。这样做最清晰// 自定义注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireAdmin { } // 拦截器中校验 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (handler instanceof HandlerMethod handlerMethod) { Method method handlerMethod.getMethod(); if (method.isAnnotationPresent(RequireAdmin.class)) { // 从Token解析出role非admin抛出403 } } return true; }后台的商品图片上传是另一个高发坑区。SpringBoot默认上传大小只有1MB批量传商品图很容易报错必须在application.yml里调配置spring: servlet: multipart: max-file-size: 10MB max-request-size: 50MB至于存储路径推荐存本地磁盘绝对路径数据库只存URL相对路径。为什么如果直接把图片存数据库Blob字段数据库会迅速膨胀备份慢查询慢而且没法直接给前端返回图片URL。正确做法是图片上传到服务器本地目录比如/usr/local/upload/同时配置一个静态资源映射目录让/images/**路径对应到那块磁盘路径数据库里存/images/2024/05/30/xxx.jpg。这样做最稳定也不依赖外部对象存储服务。只有一种情况你需要换掉将来部署环境不是单机而是有多台服务器那本地存储就行不通了需要OSS。但毕设场景本地存储绝对路径最省心。4. Vue前端把用户逛商店的每一步都做成顺滑动线前端大部分同学喜欢直接拿现成模板改这不丢人但至少要知道每一块页面是怎么跟后端对接的。Vue前端的主流方案是Vue CLI或者更推荐Vite创建工程搭配Vue Router做页面跳转Vuex或Pinia做状态管理Axios发HTTP请求。一个商城核心页面大概有首页、商品列表页、商品详情页、购物车页、订单确认页、支付页、个人中心页、后台管理页。4.1 前端架构与请求封装别把路径散落在几十个组件里写Vue前端第一件事不是写页面而是封装Axios。一次性统一配置基础URL、请求拦截器、响应拦截器// 前端/src/utils/request.js import axios from axios; import { message } from ant-design-vue; import router from /router; const request axios.create({ baseURL: /api, timeout: 10000 }); // 请求拦截统一携带Token request.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); // 响应拦截统一处理状态码和业务异常 request.interceptors.response.use( response { const res response.data; if (res.code 200) { return res.data; } message.error(res.message || 请求失败); return Promise.reject(new Error(res.message)); }, error { if (error.response error.response.status 401) { localStorage.removeItem(token); router.push(/login); } message.error(error.message || 网络异常); return Promise.reject(error); } ); export default request;有个容易被忽略的细节前端工程建议加上proxy代理配置把/api开头的请求代理到后端8080端口这样后端接口地址永远写/api/xxx而不是整天写localhost:8080/xxx将来部署到服务器也只需改一次Nginx代理配置。4.2 首页与商品列表组件复用是Vue的立身之本首页结构基本是顶部导航栏、搜索框、轮播图、分类快捷入口、商品瀑布流。设计时把“商品卡片”抽成一个公共组件ProductCard.vue首页、搜索页、推荐栏都用它传个商品对象内部渲染图片和价格。这就是组件化的意义——改一次样式所有页面同步生效。商品列表页要注意两个交互筛选条件改变时重新请求数据滚动到底部自动加载下一页不过常见的实现是分页组件或“加载更多”按钮。列表页的关键状态有当前分类、当前排序方式、当前页码。这些状态要同步到URL参数中通过Vue Router的query这样用户刷新页面还能保持筛选条件分享链接也能让其他人看到一样的列表。这块用watch监听路由变化重新拉数据即可。4.3 购物车与订单状态前端如何正确把握“提交按钮”购物车页面设计上前端要维护一个“购物车商品列表勾选状态”的响应式数组全选、单选、修改数量、删除商品都要处理。但“去结算”提交时不要只传价格前端算的价格只是一坨展示数据后端会重新计算一遍前端只需要把商品ID列表和数量传给后端。这也是数据一致性最基本的保障。订单确认页展示收货地址和商品清单提交订单后跳转支付页面。支付页面是模拟的场景可以直接展示“余额支付”的模拟按钮点击后调一个支付接口后端把订单状态改成已支付。如果你想多一个亮点可以引入支付宝沙箱SDK申请一个测试账号就能跑真实支付宝流程但这块会比较耗时间后期会讲优先级。支付成功后的回调跳转要注意不要用前端按钮触发改订单状态而是在后端支付接响应里直接完成订单状态更新这样才是可靠逻辑刷新页面不会出现“已支付但状态未更新”的尴尬情况。4.4 后台管理界面Element Plus表格和表单组合拳管理后台不必从零写推荐直接用开源后台模板如vue-element-admin的精简版改造。商品管理页面核心任务就是“表格弹窗表单”用Element Plus的el-table展示商品列表分页、状态筛选、关键字搜索功能一堆现成的只管绑定数据。新增/编辑商品弹窗里注意富文本组件或者图片上传组件的使用图片上传要用后端接口实现可以要求上传成功后再调用保存。加一个身份权限上的注意点管理端的路由必须做访问控制——不是管理员登录就压根看不到这个栏目而不是光靠按钮隐藏来防用户路由前置守卫里直接判断角色。5. 功能和亮点迭代让你的毕设从“平平无奇”到“有话可说”有的同学想拿高分想在项目里加一些别的组没有的亮点。这个心态我理解但落地有个顺序问题——基础闭环没跑通之前别折腾高级功能。“登录注册商品浏览下单支付”这个骨架完成了后面再往上面挂东西。以下三个方向是我觉得投入产出比最高的。5.1 秒杀备选方案Redis防超卖的设计思路如果你在简历或论文里写了“Redis分布式锁防止超卖”我建议你能把自己的思路解释清楚商品详情页上架秒杀活动后提前把秒杀商品的库存写入Rediskey为商品IDvalue为库存数量用户点击秒杀按钮时先用Redis的原子自减操作decrement判断返回值——如果值大于等于0说明扣减成功进入下单流程否则直接提示“已抢光”。库存扣减的成功是基于Redis单线程处理命令的原子性保证的。然后异步把秒杀成功的用户写进订单队列比如RabbitMQ或MongoDB/MySQL的订单表直接插入后台线程消费队列创建订单。这套方案的难度主要在于Redis环境搭建、Jedis/Spring Data Redis依赖引入、扣减代码的严谨逻辑。如果你赶时间可以在论文里把它写成“可扩展方案”在“总结与展望”章节提一嘴而不必真的把代码做出来。5.2 支付宝沙箱支付毕设答辩的加分亮点如果你有余力支付宝沙箱是目前毕设最值得调的支付方案。去蚂蚁开放平台申请一个沙箱应用拿到APP_ID、商户私钥、支付宝公钥后端引入alipay-sdk-java依赖发起下单时调用alipay.trade.page.pay接口返回一个支付表单/二维码前端跳转用户支付完成后支付宝异步通知你的回调接口你在回调里验签并修改订单状态。要说麻烦确实比你模拟支付多点步骤但这段经验写在简历里比“模仿支付”的含金量高太多。这里必须提醒一个回调安全点支付宝异步通知地址要外网能访问到GoFrame/Natapp/内网穿透且必须验签不能光凭POST参数判断。验签用支付宝提供的公钥加密不用自行实现直接用SDK的AlipaySignature.rsaCheckV1方法。5.3 技术要点如何写进论文避免“代码流水账”论文中不要粘贴大段代码而要多写“设计思路”和“关键难点解决”。比如写订单模块可以去描述“分布式事物问题”哪怕你的项目实际就是单机事务但你在方案设计时考虑过这个问题并且解释了为什么在毕设单机场景下可以使用本地事务保证一致性——这就是“思考深度”。再比如写数据库索引可以谈谈你为订单表order_no建了唯一索引为购物车的(user_id, product_id)建了联合索引各基于什么查询场景设计出来的。这些点都给答辩老师提问留下了丰富的发挥空间也提前铺好你能回答好的路。6. 部署文档与论文从“能运行”到“能交付”的最后一公里很多人以为代码写完就结束了其实“写论文部署打包”往往会花掉跟开发一样的时间。先把部署流程打通再写论文这样论文技术方案里每一句话都是验证过的不会出现错漏。6.1 本地打包与服务器部署从Jar包到Nginx反向代理本地环境用IDEIDEA启动SpringBoot和后端开发调试。需要部署到服务器是在“测试环境答辩演示”尤其验证上线环境时才有意义。步骤大致是四条后端用Maven打包成Jar包mvn clean package -DskipTests上传到服务器用java -jar xxx.jar启动前端用npm run build打包出dist静态文件目录上传到Nginx的html目录Nginx配置关键点是把/api请求反向代理到本地8080端口其余静态请求直接返回前端文件终端持久化运行用nohup命令nohup java -jar xxx.jar log.log 21 。部署文档怎么写我给个结构模板环境要求清单JDK版本、Maven版本、Node版本、MySQL版本数据库初始化步骤如何导入sql脚本、如何创建账号授权后端配置文件修改说明数据库连接、Redis地址、文件上传路径前端打包命令和Nginx配置示例常见启动报错排查端口占用、数据库密码错误、Jar包冲突把这份文档写好等于论文里的“系统部署”章节有了第一手材料而你在答辩时也能自然地回答“你的系统怎么部署的”这类问题。6.2 论文结构一个可用到毕业答辩的章节地图合理论文的章节结构通常可以安排为绪论部分着重写研究背景和意义多谈谈电商行业发展趋势、SpringBoot技术主流地位这可以显得选题“有社会价值和应用价值”。相关技术介绍部分至少给出SpringBoot、Vue、MySQL、MyBatis-Plus的简介和选型理由。系统分析部分要写可行性分析、功能需求分析通过用例图说明用户端和管理员端、非功能需求分析安全性、稳定性、性能要求。系统设计部分对应数据库设计ER图、功能架构设计。系统实现部分按模块一个一个展开配合核心代码片段和页面截图这一部分占了论文的很大篇幅不要用大段无注解的代码——用精炼的代码片段配合文字说明体现的是你明确知道这段代码在实现什么。系统测试部分重点运用测试用例表包括功能测试和兼容性测试。结论部分简要总结项目成果再说明不足之处和后续改进方向。6.3 答辩准备从源码包里“长出自己的东西”最后说说很多同学最容易露怯的地方——答辩。面试官通常会问你三个类型的问题项目架构相关“说说你的项目是怎么分层设计的”“为什么选择MyBatis-Plus”业务逻辑相关“超卖怎么解决”“用户密码怎么加密的”“订单状态如何流转”数据库相关“订单表的索引是怎么设计的”“MyISAM和InnoDB的区别”。你要做的不是背答案而是回归源码、结合自己的项目真正弄懂这三个维度的底层层设计。怎么检测自己是否真懂尝试不看源码在纸上画一遍用户下单到支付完成的整个时序流程并标出过程中涉及的表、字段、关键代码逻辑。能画出来基本就能自信走上答辩讲台了。踩过的坑里最值得提醒的也还是那句老话一份源码包只是起点不是终点。我见过太多人拿着现成代码就直接交结果连“为什么订单状态是0不是未支付”都答不上来。把每一行代码变成自己脑子里的逻辑把每一张表变成自己画过的ER图这个“项目”才真正长在你身上。而这也正是做毕设最重要的收获。
返回列表