ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

基于SpringBoot和微信小程序的民宿预订毕设全攻略

基于SpringBoot和微信小程序的民宿预订毕设全攻略 每年毕业设计榜单上民宿预订、酒店预订这类题目总能占一个席位我当年选“基于Springboot的民宿预订小程序LW”也纯粹是因为它看起来不偏门、业务链路完整、技术栈主流。真做下来才发现这个题目远不止“建个表、写个增删改查”那么简单用户端要处理房源搜索、预订下单、微信登录管理端要折腾房源审核、订单统计论文还得写需求分析、数据库设计、系统测试演示的时候更不能翻车。标题里那个“LW”其实就是“论文演示文档”的拼音缩写不少同学因为不懂这个词去百度结果越搜越慌。这篇文章我会把这套民宿预订小程序的完整技术方案、核心代码实现、部署和答辩经验全部摊开讲适合正在做这个题目或者想拿SpringBoot小程序练手的同学直接照做。先说清楚这篇内容能帮你解决什么问题第一搞清楚前后端该选什么技术、如何分层、接口怎么设计第二数据库表和核心业务逻辑该怎么设计才不会被导师挑刺第三微信登录、JWT鉴权、订单状态机这些关键环节的代码思路第四部署到服务器、配置HTTPS、准备答辩时最容易踩的坑。无论你是第一次写SpringBoot项目还是已经有点基础想看看完整方案这篇文章都可以当作一份能落地的参考手册。1. 项目概述与核心需求拆解1.1 民宿预订业务到底长什么样别小看“民宿预订”这四个字它背后是一条非常标准的电商交易链路用户浏览房源、查看详情、收藏心仪的民宿、选定入住日期、提交订单、支付、入住最后还可以评价。房东这边需要发布房源、管理房态、处理订单、统计收入。平台方作为管理者要对房源进行审核、对用户和房东进行管理还要能实时看到订单数据和营收情况。这条链路一旦拆清楚至少能划分出三种角色游客/用户、民宿房东、系统管理员。三种角色对应三种完全不同权限的功能集合这是毕设论文“需求分析”章节最好写、也最能体现你思路的地方。一个常见的误区是一上来就堆功能比如做一堆用户根本用不上的积分系统结果核心的预订流程反而没做扎实。我的建议是把主干环节先做通周边功能作为扩展项写进论文“后续展望”就行。1.2 为什么用SpringBoot加微信小程序这个组合在毕设里火不是没道理。SpringBoot解决的是后端开发效率问题内置Tomcat、自动配置、生态成熟哪怕你之前只写过Servlet花半天时间就能把工程跑起来。微信小程序解决的则是传播和获客问题用户扫码即用不用下载App房东不用去维护一个独立的App端用户在微信里搜完就能下单。站在毕业设计的角度这个组合还有个隐性的好处论文的“国内外研究现状”好写技术参考文档丰富出问题随便一搜就有解决方案不像有些冷门框架遇到Bug连问都没处问。而且SpringBoot本身面试问得多小程序开发现在也是各行业获客的标配手段做完这个题你简历上能写的内容会扎实不少。1.3 论文和演示文档该怎么配合做毕设最忌讳“代码写完了论文还没开始”因为论文需要的东西在开发过程中如果没留痕迹后面补起来特别痛苦。我建议在你设计数据库表的时候就把ER图存下来写接口的时候就把接口清单保持一份测试功能的时候顺手截图。后面写LW的时候把这些资料往论文框架里一填效率至少提升一倍。经典的论文结构一般是绪论与背景、相关技术介绍、需求分析、系统设计、系统实现、系统测试、总结与展望。其中系统设计必须有架构图、功能模块图、数据库ER图和关键表设计说明系统实现必须有页面截图和核心代码片段。可以说论文写得好不好七成取决于你开发过程中有没有意识地收集这些素材。2. 技术选型与整体架构方案2.1 后端依赖清单与版本避坑我用的后端版本是SpringBoot 2.7.x不是3.x。为什么因为很多教材和网上教程都基于2.x导入3.x之后会遇到javax到jakarta包名迁移的问题处理起来很膈应。别小看这个真到联调阶段报错你可能会浪费一整天排查。后面第7章我会专门讲这个坑的来龙去脉。pom.xml里我最终保留的核心依赖如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdcom.auth0/groupId artifactIdjava-jwt/artifactId version4.4.0/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies注意MyBatis-Plus版本要和SpringBoot 2.x匹配别直接用4.x否则会出现MyBatis的SqlSessionFactory初始化失败。这个组合我实测过3.5.3.1配2.7.18很稳。2.2 小程序端技术选型原生还是uni-app这几乎是每个做毕设的人都会纠结的问题。原生微信小程序用的是WXML、WXSS、JS官方工具链完整微信开发者工具一打开就能调试uni-app则是一套代码多端发布用Vue语法能同时出小程序、H5、App。我最后选的是原生微信小程序。原因很简单这个项目的业务复杂度没有高到需要跨端复用的程度原生小程序调试最直接网络请求在开发者工具里看得明明白白而uni-app如果只是拿来写一个微信小程序等于多包了一层遇到组件兼容问题你未必处理得了。如果你后期想把项目扩展成多端应用再考虑uni-app也不迟。两种方案的对比可以简单列一下对比项原生微信小程序uni-app开发语言WXML/WXSS/JSVue语法调试工具微信开发者工具HBuilderX多端支持仅微信生态小程序/H5/App学习成本低中等在毕设里的可控性更可控依赖的配置较多2.3 整体调用链路设计整套系统的调用链路我建议画成一条线就能讲清楚微信小程序发送HTTPS请求经过Nginx反向代理转发到SpringBoot后端接口后端再操作MySQL数据库和Redis缓存结果逐层返回。如果不想用Nginx开发阶段直接让小程序请求后端IP加端口也可以但上线阶段必须走域名加HTTPS。这个链路里每个节点都是论文里可以展开讲的技术点。Nginx承担了静态资源服务和反向代理SpringBoot承担业务逻辑Redis缓存房源热门数据、防止订单重复提交MySQL是最终的数据落点。我在答辩时就被问到“为什么中间要加Redis”这个问题其实很好答房源列表和详情页是高频读操作引入缓存能降低数据库压力同时下单防重也能依托Redis的原子操作来实现。3. 功能模块、数据库设计与接口规划3.1 三个角色的功能地图我最后实现的功能模块可以概括为“三端一后台”三个端分别对应三类用户后台只给管理员用。用户端功能包括微信一键登录、首页民宿轮播与推荐、民宿列表搜索筛选、民宿详情、收藏民宿、创建订单、模拟支付、我的订单、发表评价。房东端功能包括房东入驻申请、房源发布与管理、房源上下架、订单处理、查看收入概览。管理员端功能包括用户管理、房东审核、房源审核、订单管理、数据统计。这些功能看着多本质就是围绕一张订单表做的增删改查加状态流转。搭建的时候建议按“用户管理为基础、房源管理为中坚、订单管理为核心、评价管理为辅助”的顺序来做先打通主线再补齐周边。3.2 数据库表设计核心数据库表我设计了7张核心表这里直接给出清单和职责说明表名说明关键字段示例user用户表openid、昵称、头像、角色标识landlord房东申请表用户id、真实姓名、手机号、审核状态house房源表房东id、标题、封面图、价格、状态house_image房源图片表房源id、图片url、排序favorite收藏表用户id、房源id、创建时间orders订单表订单号、用户id、房源id、入住时间、金额、状态comment评价表订单id、用户id、房源id、评分、内容这里重点说一下订单表。订单号的生成我用了“时间戳加随机数”的方式避免用数据库自增ID直接暴露订单量也方便后续对接支付时作为商户订单号。订单状态我用整型存储0待支付、1已支付、2已入住、3已完成、4已取消、5退款中这样状态流转在代码里用常量判断逻辑清晰且数据库存储量小。房源表里还有一个容易被忽略的字段deleted。这是一张逻辑删除标记MyBatis-Plus默认支持。民宿可能会被房东下架或者被管理员封禁直接用物理删除会把订单表、收藏表关联的数据一起搞乱所以用逻辑删除最稳妥。3.3 统一返回结果与接口设计规范接口设计如果没有统一规范小程序端解析数据就会很难受。这里强烈建议做一个统一返回对象和分页返回对象代码类似这样Data public class ResultT { private Integer code; private String msg; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.code 200; result.msg success; result.data data; return result; } public static T ResultT error(Integer code, String msg) { ResultT result new Result(); result.code code; result.msg msg; return result; } }接口路径我统一采用“/api/模块/动作”的规则比如“/api/house/list”“/api/order/create”“/api/order/pay”。每个接口必须写明请求方式、参数、返回值这个接口清单我直接生成成了表格用在了论文的“接口设计”一节算是一份材料两次利用。小程序端所有请求都通过一个封装好的request函数调用统一带token、统一处理错误码减少大量重复代码。4. SpringBoot后端实现要点4.1 工程骨架与关键配置后端工程我采用的还是经典分层结构Controller负责接收请求和参数校验Service负责业务逻辑Mapper负责数据库操作Entity对应表结构DTO/VO负责前后端数据交互。目录结构如下src/main/java/com/example/homestay ├── controller ├── service │ └── impl ├── mapper ├── entity ├── dto ├── vo ├── config ├── interceptor ├── common │ ├── Result.java │ ├── PageResult.java │ └── BusinessException.javaapplication.yml中有几个配置值得注意。端口我固定在8080上下文路径设置了“/api”这样Controller里写“/house/list”时实际访问路径就是“/api/house/list”前端和小程序在开发阶段少写一段前缀。数据库连接串里要加“useUnicodetruecharacterEncodingutf8”避免中文乱码。上传文件大小限制建议调大一点因为民宿实拍图经常有好几MBserver: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/homestay?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 servlet: multipart: max-file-size: 10MB max-request-size: 50MB mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 04.2 微信登录与JWT鉴权微信小程序的登录逻辑并不复杂小程序端调用wx.login拿到临时code发给后端后端拿着code再加小程序的AppId和AppSecret去微信接口换取openid和session_key。数据库里用openid作为用户在系统里的唯一标识第一次登录自动注册不是第一次就直接返回用户信息。换取session_key的核心代码简化如下String url https://api.weixin.qq.com/sns/jscode2session?appid appId secret appSecret js_code code grant_typeauthorization_code; RestTemplate restTemplate new RestTemplate(); String result restTemplate.getForObject(url, String.class); JSONObject json JSON.parseObject(result); String openid json.getString(openid);拿到openid之后后端生成一个JWT令牌返回给小程序。小程序后续每次请求都在请求头里带上“Authorization: Bearer token”后端用一个拦截器统一拦截请求解析token并把用户信息放入请求上下文。这样做的好处是所有需要登录的接口不用在每个方法里重复取session代码干净很多。4.3 房源检索、订单状态机与并发处理房源列表页最核心的是分页和筛选。小程序端滚动到底部就触发“加载更多”请求下一页后端用MyBatis-Plus的Page对象接收页码和每页数量。筛选条件包括城市、入住日期、价格区间、关键词搜索。这里有一个细节入住日期筛选不能直接查数据库字段而是要先查订单表把已经被预订的房源ID查出来再在查询房源时排除掉。这样筛选结果才准确。订单状态机是这道题的一个核心难点。订单从创建到完成状态流转路径非常明确待支付-已支付-已入住-已完成期间任何一步都可能变成已取消。我写这段逻辑时没有让每个业务方法都直接去改状态字段而是封装了一个OrderStatusChangeService每次变更都做一次前置状态校验防止出现“已取消的订单还能被支付”这种脏数据。关于并发问题最典型的是同一间房在同一时间段被两个人同时下单。解决思路是在确定房态空闲后先向订单表插入一条待支付订单同时在Redis里写入一个“房态预占”标记有效期为15分钟其他用户在规定时间内看到该房源不可订。这样既保证了数据一致性又避免了用数据库锁带来的复杂性对毕设来说完全够用。4.4 支付和文件上传的实现思路支付如果走正规微信支付需要商户号、证书、密钥等一系列资质对在校生来说非常麻烦。我的方案是做“模拟支付”提交订单后跳转到一个收银台页面点击“确认支付”就把订单状态从待支付改为已支付。但这个逻辑不能是前端直接调接口改状态否则没有安全性。后端在支付接口里模拟了整个支付流程并生成了流水号论文里我写的是“本章节采用模拟支付方式实际生产环境可替换为微信支付V3接口”。文件上传方面我最初把图片传到本地服务器目录后来发现小程序端访问本地路径很不方便就改成了配置一个静态资源映射把上传目录映射到“/upload/**”下。如果想让效果更接近真实项目也可以接入阿里云OSS毕设阶段用本地存储足够答辩时我建议直接说明“为降低部署成本当前采用服务器本地存储”。5. 小程序端实现与交互细节5.1 页面清单与tabBar配置小程序端我做了10个页面核心页面包括首页、房源列表、房源详情、预订确认、订单列表、订单详情、我的页面、房东工作台、房源管理、评价页面。tabBar设置了三项首页、订单、我的这符合民宿类产品的主要用户路径。tabBar的配置在app.json中{ pages: [ pages/index/index, pages/order/list, pages/my/my, pages/house/detail, pages/house/list, pages/booking/confirm, pages/comment/create ], tabBar: { list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/order/list, text: 订单 }, { pagePath: pages/my/my, text: 我的 } ] } }这里有一个新手容易踩的坑tabBar的pagePath必须是app.json的pages数组里已经声明的页面路径而且tabBar最多支持5项。如果你临时新增了一个tab页一定要在所有出现“页面路径”的地方同步修改否则运行时会白屏报错。5.2 网络请求封装与加载更多原生小程序里调用接口没有axios可以用必须用wx.request。做了几次重复调用之后我封装了一个request.js统一处理基础路径、token注入、错误弹窗和登录态失效const BASE_URL https://yourdomain.com/api function request(path, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: ${BASE_URL}${path}, method, data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) || }, success: (res) { if (res.data.code 200) { resolve(res.data.data) } else if (res.data.code 401) { wx.navigateTo({ url: /pages/login/login }) reject(res.data) } else { wx.showToast({ title: res.data.msg, icon: none }) reject(res.data) } }, fail: (err) { wx.showToast({ title: 网络异常, icon: none }) reject(err) } }) }) }分页加载更多也是一个必做的交互点。民宿列表页的初始数据量只有10条用户滚动到底部时再加载下一页直到没有更多数据为止。这个逻辑需要在页面里维护一个pageNum和一个hasMore标记具体实现如下onReachBottom() { if (!this.data.hasMore || this.data.loading) return this.setData({ pageNum: this.data.pageNum 1 }) this.loadHouseList() }5.3 预订流程与交互细节预订流程是小程序端最关键的交互链路。用户从房源详情页点击“立即预订”后需要先选择入住日期和离店日期系统自动计算晚数和总价确认后创建订单并跳转模拟支付。这里日期选择我建议直接用原生picker组件模式设为date然后在确认按钮里做校验离店日期必须晚于入住日期否则弹窗提示。千万不要自己在页面上画日历组件那是一个巨大的工期黑洞。还有一个小细节用户选择日期后房源的剩余可订状态最好实时提示。比如某房源在选定日期区间已经被订了详情页的价格旁边就要显示“该日期已被占用”避免用户走到下单环节才发现订不了。这个校验在后端也要做一遍前端只是提示后端才是安全底线。评价页面上传图片同样值得留意。wx.chooseMedia可以选图片上传时建议先调wx.uploadFile把图片传到服务器拿到图片路径后再和其他表单一起提交不要把文件和文本混在一次请求里。我最初图省事把base64直接塞进JSON结果图片一大请求直接超时白白折腾了一个晚上。5.4 真机调试与网络排查小程序开发比普通网页开发的特殊性在于真机上很多问题只能在手机上复现后台接口返回的报错不能像浏览器那样直接在Network面板里看。所以一定要学会用微信开发者工具里的“模拟器”和“真机调试”两个模式交叉排查。开发阶段关掉域名校验就能连本地接口这个选项在微信开发者工具的“详情-本地设置-不校验合法域名”里。上线前必须把请求域名配置到小程序管理后台的“request合法域名”里而且域名必须是HTTPS。真机上如果看到“不在以下request合法域名列表中”十有八九就是这里忘了配置。有些同学想知道小程序实际发的请求长什么样可以装一个代理工具观察也可以直接在开发者工具里勾选“显示请求详细信息”在Network标签页看请求数据和响应结果。用代理抓包的意义在于能看到HTTPS解密后的明文但作为毕设阶段开发者工具自带的Network查看能力已经足够定位问题我这里就不展开讲代理的配置细节了等你真需要再深入研究不迟。6. 部署上线与论文答辩准备6.1 本地到服务器的最短路径部署其实没有想象中那么复杂。你只需要一台最低配的云服务器、一个域名、一个HTTPS证书剩下的就是按步骤操作。先把后端打成jar包mvn clean package -DskipTests然后把生成的jar包传到服务器上安装JDK、MySQL、Redis导入数据库脚本再用nohup启动nohup java -jar homestay-platform-0.0.1-SNAPSHOT.jar app.log 21 这里有个小经验先用一个简单的curl命令测一下后端接口通不通比如“curl http://localhost:8080/api/house/list”通了再配置Nginx反向代理否则你根本分不清是后端挂了还是Nginx配置写错了。6.2 HTTPS与小程序的合法域名小程序上线必须具备两个条件域名备案、HTTPS证书。域名备案通常需要几个工作日建议提前申请别等到最后几天才弄。证书可以在云平台申请免费证书期限为一年到期续签一次就好。使用Nginx代理后端接口时配置核心就两段server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /etc/nginx/ssl/cert.pem; ssl_certificate_key /etc/nginx/ssl/key.pem; location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这里有一个顺序容易搞错的问题Nginx监听443端口然后把“/api/”路径的请求代理到后端的8080端口。HTTPS是在Nginx这一层加密解密的后端自己不需要再配置SSL数据库连接仍然是本机内网访问不暴露公网端口安全性上更稳妥。6.3 论文结构、图表与答辩高频问题LW论文我建议采用这种模块化的写法第1章绪论重点写研究背景、国内外现状不要过度吹嘘自己做了什么第2章相关技术介绍写SpringBoot、微信小程序、MySQL、Redis注意技术介绍要对应到后面用到的功能上第3章需求分析画用例图、功能结构图、业务流程描述第4章系统设计包括架构设计、模块设计、数据库设计、部分关键接口设计第5章系统实现附上页面截图和核心代码代码不要整段贴挑重点逻辑讲第6章系统测试写测试环境、测试用例表、功能与性能测试结果。答辩高频问题我提前准备了一份清单基本都能覆盖导师的提问问题方向怎么回答为什么用SpringBoot自动配置、内嵌容器、生态成熟、开发效率高小程序登录原理wx.login获取code后端换openid生成JWT返回订单状态怎么管理状态机字段、前置校验、状态流转图并发重复下单怎么办Redis预占标记加订单状态限制数据库为什么用逻辑删除保留业务关联数据、便于审计恢复支付是真实的吗模拟支付真实接入需商户资质7. 常见问题与排查技巧实录7.1 SpringBoot版本太高导致的javax/jakarta问题标题里的热搜词有一条是“springboot版本太高”这绝对是我见过最高频的项目报错。SpringBoot 3.x从Java EE标准迁移到了Jakarta EE包名从javax.改成了jakarta.。很多旧教程里的“import javax.servlet.*”在3.x里直接编译不过原因是整个包都改名了。比如拦截器、过滤器相关的类在2.x里是javax.servlet.Filter在3.x里变成了jakarta.servlet.Filter。这个问题最经典的报错就是“程序包javax.servlet不存在”。解决办法倒也不是不能升级而是如果你用的MyBatis-Plus、Lombok等依赖没有适配3.x就会冒出各种莫名其妙的兼容错误。我最终选择直接退回SpringBoot 2.7把版本和依赖统一到同一个时代让问题从源头消失。7.2 Maven构建和打包的那些坑Maven构建最常见的坑有两个一是依赖下载慢二是打包后的jar没有主清单属性。下载慢可以通过配置阿里云镜像解决在maven的settings.xml里加mirror节点就行。没有主清单属性的原因通常是漏了spring-boot-maven-plugin插件加了之后重新打包就能解决问题build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build还有一个经常被忽略的小问题多模块项目里子模块依赖版本不一致。我这个项目虽然只有一个模块但当时为了设计方便分过一段时间的多模块结构结果每次打包都要确认父pom的版本控制。毕设阶段建议直接单模块没必要给论文增加额外的复杂度。7.3 小程序端常见报错速查现象可能原因解决办法页面白屏app.json路径写错或页面未注册检查pages数组是否包含当前页面请求返回404后端context-path与前端请求路径不一致统一前端BASE_URL和后端接口前缀真机连不上接口未配置合法域名或未开启HTTPS在管理后台配置request合法域名图片加载不出来服务器上传目录没做静态资源映射SpringBoot增加WebMvcConfigurer映射加载更多重复触发loading状态没有防重在onReachBottom里加loading判断这个表里的问题我几乎全踩过一遍最耗时间的其实是图片加载不出来。原因是SpringBoot默认不会把本地磁盘目录当作静态资源对外暴露小程序里拿“http://服务器IP/upload/xxx.jpg”自然访问不到。解决办法是在配置类里实现WebMvcConfigurer重写addResourceHandlers方法把“/upload/**”映射到本地盘符的路径上。7.4 联调与演示前的最后检查演示当天翻车是最要命的。我给自己列了一个演示前检查清单第一准备好3个测试账号分别属于用户、房东、管理员第二提前准备10条左右的房源演示数据详情页要有清晰的图片和完整的介绍第三支付流程走一遍确保模拟支付后订单状态能正确变化第四网络环境检查一下不要在现场等接口响应等半天第五如果现场突然出Bug不要慌先把报错信息截图然后直接切换到已经准备好的备用路径。8. 写在最后的一点个人经验这个项目做完我最大的感受是毕设题目的价值不在于它有多新而在于你能不能把一个常见需求真正做透。民宿预订小程序就是这样技术上没有特别花哨的算法但它把SpringBoot、微信小程序、MySQL、Redis、HTTPS、服务器部署这些实际开发中最常用的东西串在了一起。做完之后你再去面实习面试官问你“会不会SpringBoot”“有没有做过小程序项目”你就有一整套完整的东西可以聊包括踩过的坑和解决问题的过程这比背一百道面试题都有说服力。如果你时间充裕这个项目还可以往几个方向扩展一是把模拟支付替换成微信支付V3二是接入地图API做房源位置展示三是给房东端做一个简单的月度营收报表。这些内容不用都做完挑一个写进论文的“展望与改进”章节反而比堆砌功能显得更有思考深度。最后再分享一个我个人的小习惯写代码的时候同步在文档里记录每一个接口的请求、响应和状态码含义。这个习惯让我写论文、调小程序、准备答辩时都省了很多功夫。毕设不是一个只追求“跑起来”的任务它更是你第一次完整体验从需求到设计、从编码到部署、从测试到展示的工程过程把每一步都走完整收获会比你想象中大得多。
RELATED READING

延伸阅读

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