
1. 为什么选航空订票管理系统作为毕设一个性价比极高的练手项目如果你正在为毕业设计选题发愁想找一个既不算太难、又不显得太水的Web项目航空订票管理系统确实是一个很合适的选项。它在CS专业里属于经典的信息管理系统在线交易流程组合既有C端的用户操作注册登录、航班查询、在线订票又有B端的管理操作航班发布、订单管理、用户管理还天然带了一个很有意思的技术难点——余票并发控制。这套组合下来答辩时既有的讲简历上也有的写。从技术评判的角度看航空订票管理系统比学生的图书管理系统、班级管理系统这类纯增删改查项目高半档因为它涉及业务状态流转订单从待支付到已支付再到已出票航班从可售到停售。这些状态让系统更像一个真正能跑起来的业务系统而不是单纯地对着数据库做CRUD。同时它又不会像电商秒杀系统或者分布式消息队列那样复杂作为单人完成的毕业设计工作量在可控范围内。我当年做毕设时选的是类似的后台管理系统做完之后最大的感受是毕设选题的成功与否不在于题目听起来有多高大上而在于你能否把每个模块讲清楚、做扎实。航空订票系统恰好就是每个模块都能讲清楚的典型。展开来说航班查询是典型的组合条件检索涉及SQL语句动态拼接在线订票是典型的事务操作要保证下单和扣库存的原子性订单管理又涉及金额精度和状态机设计。每一个点拿出来都足够写出上千字的分析段落三个点串起来就是一篇逻辑完整的系统实现论文。再来说说项目本身的价值。航空公司票务是一个高频刚需场景业务逻辑对所有读者来说都不陌生——用户买机票、管理员放航班这种业务模型理解成本极低。相较而言如果你选一个物流配送管理系统没接触过物流业务的老师还得花时间理解你的业务建模是否合理。航空订票就不存在这个问题评委老师登机前多少都买过机票你的业务流程正不正确他们一眼就能判断出来。这也是这个题目在历届毕设中热度始终很高的原因之一。凡是被大量人做的题目意味着你可以在GitHub、码云、CSDN等渠道找到大量参考资料。遇到问题时有的是地方查不会被一个技术坑卡住好几天对毕设党来说这是非常实际的加分项。2. 系统整体架构模块划分与技术选型的底层逻辑2.1 功能模块设计站在用户和管理员两个视角看需求航空订票管理系统的功能范围核心可以拆成两个端、五个模块。我画一张清晰的功能脑图给读者看用户端面向普通旅客注册登录、航班检索、航班详情、在线下单、模拟支付、个人订单中心、行程单打印管理端面向航空公司运营人员航班信息维护、航班上下架、订单查询、退改签处理、乘客信息管理、基础数据统计先别急着动手写代码需求分析的第一步是列功能清单第二步是划清角色边界。为什么这么说因为很多新手在做毕设时拿到题目就开始建表结果做着做着发现管理员表里塞了用户字段用户功能里跳出了管理按钮整个项目逻辑混乱到无法收场。正确姿势是一张表格把角色和功能理清楚模块名称用户角色核心功能点备注说明用户认证游客/用户注册、登录、注销会话密码必须加密存储航班查询用户按起降城市、日期、舱位多条件检索列表分页展示订单系统用户创建订单、模拟支付、取消订单核心事务操作航班管理管理员新增航班、修改价格、停售启售航班状态控制订单管理管理员查看订单、处理退票、查看统计数据支撑决策五个模块之间不是孤立的而是有一条明确的数据流向管理员先发布航班用户检索到航班后下单生成订单订单状态变化后会反过来影响航班的余票数量。把这条业务链在文档里画清楚后续设计数据库表时就会顺畅很多。技术选型方面主流方案是Spring Boot MyBatis MySQL Thymeleaf或者前后端分离加Vue。这里我给一个忠告如果你的前端功底一般尽量选Thymeleaf服务端渲染方案。因为答辩时你需要能完整讲述浏览器发起请求 → Controller接收参数 → Service处理业务 → Mapper查询数据库 → 数据回填到页面这条完整链路服务端渲染的每一步都清晰可控不会牵扯跨域、Token鉴权等额外知识点。而前后端分离虽然页面更漂亮却意味着你需要额外理解和讲解CORS、Axios拦截器、Vue生命周期等一堆前端内容对毕设来说容易让重心跑偏。2.2 三层架构设计为什么Controller里不能写业务逻辑很多毕设代码的层次混乱是一大重灾区。有的同学为了省事把数据库查询代码直接写在Servlet或Controller里面StringBoot里就在Controller里注入Mapper后直接查库。短时间看代码少写了几个类可一旦进入联调debug阶段这种写法根本没有排查头绪——请求参数出错、业务逻辑出错、SQL语句出错全部搅在一起。规范的实现思路是采用经典三层架构Controller表现层→ Service业务逻辑层→ Mapper数据访问层。每一层各司其职层与层之间靠接口通信。我来说说每一层具体该做什么Controller层只负责接参、调Service、返回结果。参数校验比如用户名为空、日期格式非法应该在这一层拦截校验不通过直接返回错误提示不必进入业务层。Service层承载核心业务规则。比如创建订单时先检查余票、再扣减余票、再生成订单记录这一串动作必须在Service层完成并且外层加事务。Mapper层一对一映射数据库操作。只负责SQL执行和结果映射不做任何业务判断。三层之间通过实体类传递数据。用户在前端填写的表单通过DTO对象传入ControllerController转成实体或者直接传给ServiceService层完成逻辑后把结果封装成VO返回给页面渲染。这样做的好处是哪一层出了问题直接在对应的层排查后续如果要改动订单扣减规则只需要修改Service层的一个方法不会牵连到Controller。配套的包结构可以这样组织com.example.flight ├── controller # Web接口层接收HTTP请求 ├── service # 业务逻辑层含接口与实现类 ├── mapper # MyBatis的Mapper接口 ├── model # 实体类、DTO、VO ├── config # 配置类拦截器、跨域配置等 ├── common # 通用工具类、统一返回结果封装 └── resources ├── mapper # MyBatis的XML映射文件 └── templates # Thymeleaf模板页面这个包结构看起来很普通但它的价值在于约定大于配置。任何人接手你的毕业设计源码顺着包名就能找到对应的功能代码这在答辩演示和回答老师提问时都是隐形加分项。我记得当年答辩时老师问如果航班价格要做折扣活动你改哪里我直接演示了在Service层新增一个计价方法的过程在逻辑完整性上让问询环节顺畅很多。3. 数据库设计几张表才是核心中的核心3.1 实体模型与表关系从需求到建表的推导过程数据库设计是航空订票系统的压舱石。表设计得好不好直接决定后面写SQL和业务代码难不难。针对航空订票系统核心实体有四个用户、航班、订单、管理员。我们逐一拆开来看。**用户表user**存储注册用户的账号信息和乘客实名信息。要注意真实航空购票是强实名制的所以除了username和password还要设计real_name真实姓名、id_card身份证号、phone手机号。一个用户可以有多个常用乘机人如果把乘机人信息也塞进user表表结构就会冗余。为了控制毕设体量我建议把乘机人弱化为订单中的乘客字段也就是订单表里直接存乘客姓名和身份证号不做单独的联系人表。这符合够用就好的毕设原则。**航班表flight**是整个系统的数据中枢。字段设计我给出具体清单字段名类型说明idbigint主键flight_novarchar(20)航班号如MU5123airlinevarchar(50)航空公司departure_cityvarchar(50)出发城市arrival_cityvarchar(50)到达城市departure_timedatetime起飞时间arrival_timedatetime落地时间plane_typevarchar(50)机型total_seatsint总座位数remaining_seatsint余票数量pricedecimal(10,2)经济舱票价statustinyint0停售 1可售**订单表orders**是系统里最繁忙的表我会用表结构来说清楚业务状态流转字段名类型说明idbigint主键order_novarchar(32)订单编号唯一user_idbigint下单用户IDflight_idbigint关联航班IDpassenger_namevarchar(50)乘客姓名passenger_id_cardvarchar(20)乘客身份证seat_classvarchar(20)舱位等级pricedecimal(10,2)成交价需快照statustinyint0待支付 1已支付 2已出票 3已取消 4已退票create_timedatetime下单时间pay_timedatetime支付时间订单表中为什么要单独存一份price这涉及到业务里的快照概念——用户下单时票价是800元几天后航班涨价到1000元用户的订单金额不能被后台改价影响。所以订单创建时就要把当时的票价固化在订单表里。这个细节在答辩时提到老师会认为你具备真实业务思维。管理员表admin相对简单字段就是id、username、password、create_time。密码存储务必加密千万不能明文入库。四张表之间的关联关系是用户对订单一对多航班对订单一对多管理员对业务操作一对多。在数据库层面通过外键逻辑关联但在实际开发中不一定要物理建外键约束——使用逻辑外键即可原因是在高并发插入场景下物理外键约束会影响性能且毕设规模下逻辑外键完全够用。3.2 数据库初始化与防坑SQL写法初始化数据库时要注意三个关键点。第一个是字符集建库语句统一使用utf8mb4相比utf8它能存储一些特殊字符比如emoji实测中中文姓名和生僻字都不会乱码。第二个是排序规则推荐utf8mb4_general_ci即可。第三个是时区问题接MySQL连接串时必须加上serverTimezoneAsia/Shanghai否则Java实体里的日期字段会和数据库实际存储时间相差8小时排查起来非常痛苦。航班表初始化数据可以造一些真实感强的航班。我建议模仿国内主流航线比如北京-上海、广州-成都、深圳-杭州等每条航线放几个航班起飞时间分布在早上和下午。这些数据会在页面调试阶段给你节省大量时间数据越真实前端展示效果越好看。部分核心SQL初始化脚本示例CREATE DATABASE IF NOT EXISTS flight_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE flight_db; CREATE TABLE user ( id bigint PRIMARY KEY AUTO_INCREMENT, username varchar(50) NOT NULL UNIQUE COMMENT 登录名, password varchar(100) NOT NULL COMMENT 加密后的密码, real_name varchar(50) NOT NULL COMMENT 真实姓名, id_card varchar(20) NOT NULL COMMENT 身份证号, phone varchar(20) DEFAULT NULL COMMENT 手机号, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间 ); CREATE TABLE flight ( id bigint PRIMARY KEY AUTO_INCREMENT, flight_no varchar(20) NOT NULL UNIQUE COMMENT 航班号, airline varchar(50) NOT NULL, departure_city varchar(50) NOT NULL, arrival_city varchar(50) NOT NULL, departure_time datetime NOT NULL, arrival_time datetime NOT NULL, plane_type varchar(50) DEFAULT 空客A320, total_seats int DEFAULT 200, remaining_seats int DEFAULT 200, price decimal(10,2) NOT NULL, status tinyint DEFAULT 1 COMMENT 0停售 1可售 ); CREATE TABLE orders ( id bigint PRIMARY KEY AUTO_INCREMENT, order_no varchar(32) NOT NULL UNIQUE, user_id bigint NOT NULL, flight_id bigint NOT NULL, passenger_name varchar(50) NOT NULL, passenger_id_card varchar(20) NOT NULL, seat_class varchar(20) DEFAULT 经济舱, price decimal(10,2) NOT NULL, status tinyint DEFAULT 0 COMMENT 0待支付 1已支付 2已出票 3已取消 4已退票, create_time datetime DEFAULT CURRENT_TIMESTAMP, pay_time datetime DEFAULT NULL, KEY idx_user (user_id), KEY idx_flight (flight_id) );需要注意的是设计订单表时一定不要把表名直接用order因为order是SQL关键字在SQL语句里写起来非常麻烦要加反引号干脆起名orders避免坑。4. 核心功能实现从航班检索到订票下单的完整链路4.1 航班动态组合查询的三种实现方式航班检索是用户接触系统的第一个功能也是最典型的一个动态条件查询场景。用户可能只选出发城市也可能同时选出发城市、到达城市、出发日期甚至还会加上舱位偏好。如果为每种组合单独写一条SQL那代码会膨胀到不可维护。我在实际开发中推荐两种写法。第一种是Service层动态拼接SQL利用MyBatis自带的动态SQL能力select idsearchFlights resultTypecom.example.flight.model.Flight SELECT * FROM flight where if testdepartureCity ! null and departureCity ! AND departure_city #{departureCity} /if if testarrivalCity ! null and arrivalCity ! AND arrival_city #{arrivalCity} /if if testdepartureDate ! null AND DATE(departure_time) #{departureDate} /if if teststatus ! null AND status #{status} /if /where ORDER BY departure_time ASC /select这里用 标签的好处是它会自动处理掉第一个条件前面的多余AND避免SQL语法错误。同时如果所有条件为空 会自动退化成不带过滤条件的查询这正好符合默认展示所有航班的预期。第二种是纯Java代码判断拼接但代码可读性较差不推荐。我在做这个模块时还额外处理了一个小细节日期查询用的是日期函数DATE(departure_time)而不是BETWEEN这样可以确保用户选择某一天时不会因为时分秒的问题漏掉当天早班航班。4.2 下单扣库存事务边界与超卖预防航空订票系统最核心的业务逻辑集中在创建订单这个方法里也是答辩时老师最关注的点。围绕下单流程我要做到三个动作的原子性检查航班存在且状态为可售检查余票数量大于0扣减余票并插入订单记录。这三个动作如果分开执行中间任何一步报错都会导致数据不一致。比如扣了余票但订单插入失败用户就凭空少了一张可买的票订单插入了但没扣余票后台库存就显少。解决办法是在Service方法上添加Transactional注解让所有数据库操作在同一个事务里提交或回滚。尤其需要考虑的是并发场景。假如一个航班只剩最后一张票同时有两个用户在抢两个请求同时读到余票为1都判断可买随后都执行扣减操作结果余票变成-1订单量大于实际座位数。这就是典型的超卖问题。网上很多毕设代码根本没有处理这个问题但你一旦在答辩时主动说出来并且给出解决方案效果完全不同。低成本解决方案有两种。第一种是使用乐观锁在扣余票的SQL里加上条件UPDATE flight SET remaining_seats remaining_seats - 1 WHERE id #{flightId} AND remaining_seats 0这条SQL是原子操作数据库行锁会保证同一时间只有一个请求能成功把余票减一。如果影响行数为0说明余票已经被抢完程序抛出业务异常余票不足整个事务回滚订单也不会创建。这种方案巧妙之处在于把并发控制下沉到了数据库层面应用代码不需要加锁性能影响极小。第二种方案是悲观锁在查询航班时使用SELECT ... FOR UPDATE锁定行直到事务结束。这方案更重量级并发不高时没问题但存在锁等待风险。对于毕设简历上写一句基于乐观锁解决超卖问题已经是非常亮眼的业务要点强烈推荐这个方向。4.3 模拟支付与订单状态流转考虑到毕业设计的演示环境不可能对接真实的支付宝或微信支付所以我们要实现一个模拟支付页面。用户提交订单后跳转到收银台页面页面上显示订单号和应付金额点击确认支付按钮后向后端发送支付请求。后端接收到请求后执行以下步骤将订单状态从0待支付改为1已支付记录支付时间pay_time为当前时间判断是否满足出票条件支付成功即自动出票将订单状态从1改为2已出票。支付这个动作本身是另一个事务边界支付失败时不需要回滚订单创建。这里可以体会到订单状态机的价值状态只会按照固定方向流转不会有从已出票直接跳到待支付这种非法操作。我在代码中用一个常量类维护订单状态值避免到处写魔法数字public class OrderStatus { public static final int UNPAID 0; public static final int PAID 1; public static final int ISSUED 2; public static final int CANCELLED 3; public static final int REFUNDED 4; }用户取消订单的流程是只有待支付状态的订单可以取消取消时调用对应方法把状态置为3同时把航班表的余票数量加回来。这一个余票恢复逻辑非常容易漏掉我在调试时踩过坑专门写成一个孤立方法保证任何取消路径都能调用到。4.4 管理员后台航班CRUD与图表统计管理员端根据需求拆成航班管理和订单管理两块。航班管理就是标准的增删改查但要注意删除航班不能真的物理删除因为历史订单还关联着这个航班物理删除会导致订单页面查不到航班信息。正确的做法是逻辑删除将航班状态改为停售status0页面默认只展示可售航班管理员在列表里勾选含停售选项才能看到已停售的航班。这一个小设计就能体现你的数据保护意识。订单管理模块要支持按订单号模糊搜索、按用户ID搜索、按状态筛选。这里可以在MyBatis的动态SQL中复用之前航班搜索的写法把查询条件拼装到WHERE子句中。数据统计是可选的加戏部分。我建议使用ECharts在前端展示一个简单的近七日订单量折线图后端提供查询接口统计每天的订单数。原因是答辩演示时图表比表格更抓眼球同时ECharts的引入范围小、学习成本低。后端统计SQL可以这么写SELECT DATE(create_time) AS date, COUNT(*) AS cnt FROM orders WHERE create_time DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY DATE(create_time) ORDER BY date ASC5. 前端页面设计与布局不拖后腿就是胜利5.1 页面清单与布局方案对于一个以功能展示为核心的毕设系统前端不追求花哨但至少要做到整洁、统一、不辣眼。我推荐的页面设计是顶部导航栏 中间内容区 底部版权信息。顶部导航栏放系统Logo、用户头像下拉菜单、导航链接。中间内容区是页面主体。底部版权信息写一个固定footer即可。页面清单如下首页/航班列表页航班搜索表单 航班卡片表格航班详情页展示航班具体信息 填写乘客信息 提交订单订单支付页模拟收银台展示订单金额和确认支付按钮个人订单中心页列表展示用户的所有订单及状态后台管理页航班管理表、订单管理表两份主页面登录/注册页前端技术如果采用Thymeleaf服务端渲染页面使用Bootstrap是非常稳妥的选择。Bootstrap的栅格系统让页面天然响应式即使老师用笔记本演示也不会变形。再加上一套开源的AdminLTE后台模板管理端页面的完成度会瞬间提升一个档次不需要自己手写复杂的CSS布局。如果考虑使用前端框架如Vue增加成本主要在工程配置和跨域联调上。尤其是前后端分离后部署时你需要设置CORS跨域策略或者用Nginx反向代理这些都是额外需要向答辩老师解释的东西。5.2 权限控制与演示体验优化用户和管理员是两套完全不同的操作界面所以登录后的权限控制就很重要。常规做法是使用拦截器HandlerInterceptor拦截请求在访问受保护URL前检查Session中是否存在用户或管理员信息不存在则重定向到登录页。更细一点的权限控制可以给不同角色设置不同访问路径前缀所有管理端路径都以 /admin/** 开头拦截器对 /admin/** 路径检查管理员身份用户中心路径以 /user/** 开头检查用户身份。这种设计思路简洁、清晰且容易讲解比在数据库中引入RBAC权限表更合适毕设体量。演示体验上还有一个容易被忽视的坑空数据页面的处理。用户如果搜索的航线不存在航班航班列表页面就空荡荡的体验极差。我在Thymeleaf里用th:if th:each配合处理空列表div th:if${#lists.isEmpty(pageResult.list)} div classalert alert-warning没有找到符合条件的航班请调整搜索条件/div /div div th:unless${#lists.isEmpty(pageResult.list)} !-- 表格循环渲染 -- /div页面联调时还需要注意表单重复提交问题。用户防抖代码在客户端可以做比如点击提交订单按钮后立刻置灰按钮防止用户因为网络延迟多次点击产生多个订单。这个细节虽然简单但在演示的时候特别显眼——按钮置灰后老师会直观感受到你考虑了用户体验。6. 常见问题与调试经验那些让我差点砸电脑的坑6.1 环境与依赖层面的问题第一个高频坑就是JDK版本和Spring Boot版本不匹配。Spring Boot 2.x 适配JDK 8和11Spring Boot 3.x 强制要求JDK 17。如果你电脑装的是JDK 17却用了Spring Boot 2.3版本启动时大概率会报UnsupportedClassVersionError或者其他诡异的依赖错误。我是建议毕设直接选用JDK 8 Spring Boot 2.7.x 组合这个组合极度成熟网上资料丰富答疑成本最低。第二个坑是Maven依赖下载超时。国内网络拉取中央仓库依赖经常失败解决办法是配置阿里云镜像仓库。在maven的settings.xml里加入mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/central/url /mirror配置后重新拉依赖速度快非常多。第三个坑是MySQL连接驱动版本与数据库版本不一致。如果你用MySQL 8.x确保引入的驱动是com.mysql.cj.jdbc.Driver。如果用老版本驱动连新版MySQL会报Public Key Retrieval is not allowed错误。处理方式是在连接串中追加allowPublicKeyRetrievaltrueuseSSLfalse。6.2 业务逻辑与SQL层面排查记录在编写这个系统的时候我遇到并解决了一些典型问题整理成速查表分享给大家症状根因分析解决方案查询日期航班结果为空数据库datetime字段含时分秒DATE匹配失效使用DATE(departure_time) #{date}登录后页面跳转404拦截器放行路径配置错误在WebMvcConfig中配置excludePathPatterns订单支付后余票没减少扣减SQL被事务回滚覆盖检查Service层Transactional是否在正确方法上中文用户名乱码数据库/连接字符集不一致统一utf8mb4连接串加useUnicodetruecharacterEncodingutf8用户点击两次提交产生两单前端未做防重复提交按钮提交后立即禁用join后进入loading状态本地运行正常但部署到服务器白屏静态资源路径使用了绝对路径使用thymeleaf的{}语法生成上下文相对路径这里我要重点强调事务回滚的问题。在Spring Boot中Transactional默认只在RuntimeException时回滚如果代码捕获了异常并且吞掉Spring根本感知不到异常事务就会正常提交导致扣减余票操作和订单插入操作分开生效产生脏数据。我建议严格把事务方法设计为向上抛异常类型在Controller层统一捕获处理保证事务边界内的逻辑足够干净。SQL层面的错误还有一个高频点就是保留字冲突。我在建表阶段特意避开order关键词目的就是避免SQL里到处加反引号。如果表名用了order在XML里写SQL时每句都得写成order稍不留神漏掉反引号整个查询就崩这种繁琐非常影响开发效率。命名上主动规避SQL保留字是值得养成的习惯。6.3 答辩演示时的实操技巧除了代码调试我还想分享答辩演示的实操技巧。首先准备一套预填充数据的演示环境非常关键注册好两个测试账号普通用户与管理员各一个在航班表中录好五六条符合演示顺序的航线数据。答辩现场网络不稳定、打字容易出错用现成账号登录会节省大量时间还能避免在评委面前因为密码输入错误而尴尬。其次演示流程要有剧本。我会建议按这个顺序来管理员登录后台 → 新增一条航班临时演示 → 切换到前端用户端 → 搜索该条航班 → 下单支付 → 查看订单状态变化 → 切回后台查看对应订单 → 处理退票 → 回到前端确认余票恢复。这个闭环演示展示了一个完整的业务周期老师看到的不只是功能点而是一套业务逻辑的自洽。演示过程中最好把数据库客户端也开着让评委看到订单状态在数据库和界面上同步变化这个细节很加分。第三提前准备好如果断网了怎么办的Plan B。可以把项目部署在本地环境并录制好演示视频作为备用方案。有备无患不至于现场手忙脚乱。7. 我的几点真实体会做完它你会收获什么说真的航空订票管理系统这类题目虽然被做了无数遍但对于独立完成的开发者来说每一遍都是全新经验。我个人做完这套系统后有几个很直观的成长点想在这里多说两句。第一是真正理解了数据库设计不只是建表表结构里的状态字段、快照字段、逻辑删除字段都是在模拟真实业务约束思考这些字段的设计过程比写CRUD本身更有价值。第二是学会了如何用事务保护业务的一致性下单扣库存这一套流程里的原子性思维可以平移到很多其他项目中。第三是用三层架构组织代码之后我养成了先分层再写逻辑的编码习惯后期维护和扩展明显更顺畅。如果你决定用这个题目做毕设我的建议是不要只满足于能运行。把系统里那几个关键点吃透——尤其是并发扣库存和订单状态机这两个部分——你会发现它们在面试场景里也是高频考点。面试官问如何防止库存超卖时你能够说出乐观锁解决方案并展示代码这比背十道八股文都有说服力。希望这篇总结能够帮你减少一些踩坑时间把精力花在真正有价值的功能打磨上。最后再分享一个细节小技巧整个项目做完后把这个系统的截图和核心亮点整理成一页PDF说明文档。答辩演示前发给评委老师浏览效果远好于空口讲解。很多老师说毕设最大问题不是功能不足而是学生不会包装自己的代码。技术实现可能大家都差不多但主动展示你的思考和决策过程正是从会写代码走向会做项目的关键一步。