做毕设选什么题目一直是件挺纠结的事。既要能体现技术栈的掌握程度又要有实际业务场景可以展开讲还得保证工作量够、后期能答上辩。如果你看到了“基于SpringBoot的药店药品管理系统”这个题说明方向已经比较明确了这个题目很适合作为Java方向的计算机毕设它麻雀虽小五脏俱全覆盖了常规Web系统开发的完整链路。我前后带过不少同学做类似的进销存管理系统也帮人调试过好多版代码。药店管理系统相比通用的库存管理系统多出来的业务关键点很明显批次管理、效期预警、药品分类规则、销售与入库的单位换算。这些细节才是答辩时能从一堆CRUD项目里脱颖而出的地方。这篇文章我就从需求设计、技术选型、数据库建模、核心模块实现、前后端联调再到最后的常见坑位排查把整个项目完整拆一遍给你一份靠谱的“抄作业指南”。1. 项目整体设计与思路拆解1.1 核心需求从哪来先说清楚这个系统到底要管理什么。药店不是超市药品也不是普通商品。药店日常经营有几个绕不开的场景采购入库从供应商进药要记录批号、生产日期、有效期、进价、数量。销售出库顾客买药要记录销售流水库存要扣减而且同一药品不同批次的效期不同理论上先进先出的逻辑要考虑。库存管理哪些药品快卖完了要提醒补货哪些药品快过期了要提醒处理这是药店最核心的痛点。用户与权限老板、店长、收银员、采购员不同角色看到和能操作的功能不一样。所以这个系统的功能模块其实是可以很清晰列出来的功能本身不复杂但模块之间的数据关系需要理清楚。这也是我在帮同学梳理需求的时候一定会先让人把所有业务场景用一两句话描述清楚的原因——需求不明后面做表就是在给自己挖坑。1.2 技术栈为什么这样选题目里明确写的是SpringBoot这个选择很聪明它基本是当前Java后端就业和毕设的“默认选项”。我建议的具体技术组合如下后端SpringBoot 2.7.x MyBatis-Plus MySQL 8.0 Maven前端Vue 2或Vue 3 Element UI/Element Plus Axios权限认证JWT前后端分离项目比Session更顺手也更好解释接口测试Postman或Apifox为什么要选MyBatis-Plus而不是纯MyBatis对于一个管理系统来说80%以上的操作都是单表CRUDMyBatis-Plus自带的BaseMapper能省掉大量重复写SQL的时间而且它的分页插件非常好用。剩下那20%涉及多表关联查询的就自己写SQL这个度拿捏得刚刚好。前端为什么推荐Vue而不是JSP虽然SpringBoot原生集成JSP很简单但“前后端分离”在现在的毕业设计题目和要求里出现频率非常高用Vue做前端接口全部走JSON交互前后端并行开发互不影响演示效果也更接近企业真实开发状态。1.3 系统模块划分我建议把系统切成下面几个模块模块核心功能涉及角色登录与权限登录认证、Token校验、角色区分所有用户药品管理药品信息CRUD、药品分类、多条件检索管理员、采购员批次与库存入库批次管理、库存查询、效期预警、低库存预警采购员、管理员销售管理前台开单、销售记录、销售退货收银员、管理员供应商管理供应商信息维护、供应商检索采购员、管理员统计看板今日销售额、库存总览、预警列表管理员每个模块拿出来都能单独写一个“核心功能”小节工作量也够答辩的时候每个模块都有的可讲。这样划分还有一个好处角色权限能自然落到模块上不用刻意去设计复杂的权限表降低实现难度。2. 数据库设计整个项目的定海神针2.1 表结构设计思路数据库设计的好坏直接决定项目写起来顺不顺。不少同学一上来就建表建完发现业务逻辑要加字段、字段间关联拆不清越写越别扭。我建议先画ER图至少脑子里要清楚实体间的关系再落表。这个药店的系统建议设计下面几张核心表用户表sys_user角色表sys_role药品表drug_info药品分类表drug_category供应商表supplier采购入库表purchase_order purchase_order_detail销售表sale_order sale_order_detail库存表drug_stock药品表、供应商、分类属于基础数据采购、销售是业务流水库存表可以看作是“药品批次”当前剩余量的快照。这里有个必须强调的点药品表不要把库存数量直接放在drug_info里。我在帮别人调代码的时候见过一份代码直接把库存数写在药品表的stock字段上入库就update一次销售就update一次。初看没问题但一旦做批次管理或者要查“某批次还剩多少”或者做进销存报表这个设计就废了。合理做法是库存单独表按“药品批次号供应商”确定一条记录包含当前库存量。2.2 核心表字段设计要点药品信息表drug_info核心字段要这样设计字段名类型说明idbigint主键drug_codevarchar药品编码唯一且不可为空这个要做唯一索引drug_namevarchar药品通用名category_idbigint分类ID关联分类表manufacturervarchar生产厂家approval_numbervarchar批准文号国药准字号这个字段在药店系统里必须要有specificationvarchar规格比如“0.25g*24片/盒”dosage_formvarchar剂型片剂、胶囊、口服液等sale_pricedecimal(10,2)销售单价purchase_pricedecimal(10,2)采购单价可以不公开展示prescription_flagtinyint是否处方药1是 0否statustinyint状态1上架 0下架库存表drug_stock字段要注意这里字段名类型说明idbigint主键drug_idbigint药品IDbatch_numbervarchar批次号对应本次入库的批号supplier_idbigint供应商IDproduction_datedate生产日期expire_datedate有效期至quantityint当前库存数量min_stockint库存预警下限可选放这里或放药品表为什么批号、效期要放在库存表因为同一款药可能分两次进货批号不同、效期不同卖的时候理论上是先出效期近的批次这样库存表按批次记数量才能支持“效期预警”和“先进先出”的逻辑。光在药品表里记一个总数这俩核心业务场景全做不了。2.3 表关系与事务边界表之间的关系是典型的“主从结构”采购订单主表对采购订单明细从表是1对N一张单可以包含多种药品。销售订单主表对销售订单明细从表是1对N。主表记录整单信息总金额、操作人、时间、供应商/顾客从表记录每行药品的单价、数量、批次。这样设计的好处是业务上有据可查统计报表也很好做而且“订单总价”可以直接用SQL的SUM聚合出来不用另外维护冗余字段。事务边界要控制好一次采购入库动作 插入采购主表 插入采购明细表 更新库存表这三件事必须在同一个事务里执行。同理一次销售出库动作也要事务包裹。如果少做了库存更新过两天就会发现订单流水和库存对不上这是调试时最容易碰到的数据一致性问题。3. 核心功能模块解析与实操要点3.1 药品信息管理不是简单CRUD药品信息管理看起来就是增删改查但有几个实操细节需要注意。第一是药品编码的唯一性校验除了数据库层面建唯一索引前端提交时后端也要先查一遍避免重复数据。我建议在后端Service层做“编码是否存在”的校验不能只靠前端因为接口可以被人绕过。第二是查询列表要支持多条件组合检索。药品名模糊查询、分类筛选、处方药/非处方药筛选、状态筛选这些放在一个页面里非常常见。用MyBatis-Plus的LambdaQueryWrapper来实现很方便动态拼接条件代码非常简洁。查询结果要分页MyBatis-Plus分页插件一开写起来就几行。还有一个容易忽略的点药品的上下架状态改了之后在销售端要立刻生效。也就是说库存查询的SQL里要join药品表并且过滤status1这个在写销售开单页面时要注意。3.2 入库管理结合批次的采购流程入库这个环节是药店管理系统里最有业务含量的。前端页面选择供应商然后添加药品列表每个药品录入采购数量、批次号、生产日期、有效期、采购单价后台一次性提交。后端处理流程是这样的校验供应商存在。生成采购单号可以用日期随机数或直接雪花ID。保存采购主表信息。循环保存每条药品明细明细里带上批次号和生产信息。把每行药品写入库存表如果同一药品同批次已存在则累加数量否则新增库存记录。整个流程在一个事务里。这里有个实用的细节库存累加时要注意并发。药店场景并发量其实不高但如果使用了MyBatis-Plus的乐观锁插件或者直接写UPDATE语句“SET quantity quantity #{num} WHERE id ...”就不会出现覆盖更新的问题。直接用先SELECT再UPDATE的做法在并发下可能会丢数量虽然概率低但出于好习惯我会建议用原子更新。3.3 销售出库库存扣减与批次选择销售开单是收银员每天用的功能核心就是“选药-加入购物车-结账-扣库存”。批次选择这块医院药房或连锁药店系统会做得很复杂要考虑锁库存、先进先出等但毕设项目不用过度开发。我建议在销售页面上显示药品当前所有批次的汇总库存和价格扣减时优先扣效期最近的批次代码上实现起来就是在库存表里按批次分组查询然后遍历扣减到满足数量。这一步要放在事务中扣减顺序固定避免并发冲突。销售开单还需要考虑一个边界情况库存不足时要有明确提示。如果顾客要买5盒当前库存只有3盒要提示“库存不足剩余3盒”而不是直接把3盒给人开单成功。校验逻辑写在后端这是业务底线。我记得就有学生在这个地方偷懒前端页面禁用数量框但接口直接传数量的时候完全不校验最后演示翻车。3.4 库存预警与效期管理这个模块是系统里最能体现“药店专业度”的地方强烈建议重点做。要做两件事低库存预警当库存表里的quantity小于等于min_stock预警下限时在首页或库存列表里标红显示“补货提醒”。效期预警当expire_date距当前日期小于90天时提示“近效期药品”小于30天时更紧急。这个功能对药店的实际意义非常大滞销药品如果过期了损失只能药店自己承担。对应实现的SQL条件也不复杂低库存就是 where quantity min_stock效期预警是 where expire_date between now() and date_add(now(), interval 90 day)。建议做一个统一的“预警看板”页面用数据统计卡片或者列表的方式把两类预警汇总展示。答辩时这两块讲出来老师通常会认同你对业务的理解。3.5 登录与权限控制登录模块虽然每个项目都有但很多同学在权限这块写得一言难尽。我不推荐引入过于沉重的Spring Security除非你要拿这个做亮点否则简单方案够用JWT 拦截器 角色判断。流程是这样的用户输入账号密码后端查库并用MD5加盐或BCrypt加密后比对。比对成功签发一个JWT Token里面带上用户ID和角色标识。前端把Token存到localStorage每次请求在Axios拦截器里放Authorization头。后端写一个拦截器或AOP切面对每个请求验证Token的有效期。在需要角色控制的接口比如只有管理员能访问新增药品就在方法上加个自定义注解或者直接判断角色。这样做的好处是逻辑清晰、代码量适中答辩时也能把原理讲明白。另外有个细节密码不要明文存数据库起码要加个盐做哈希这是非常重要安全习惯哪怕只是一个毕设项目也要养成这个习惯。4. 实操过程与核心代码实现4.1 工程搭建与目录结构我以实际项目为例工程名称可以叫pharmacy-system。后端用Maven构建存放到类似下面这样的分层结构里com.example.pharmacy ├── PharmacyApplication.java ├── common │ ├── Result.java │ ├── ResultCode.java │ ├── PageResult.java │ └── GlobalExceptionHandler.java ├── config │ ├── MybatisPlusConfig.java │ ├── WebMvcConfig.java │ └── JwtInterceptor.java ├── controller │ ├── DrugController.java │ ├── StockController.java │ ├── PurchaseController.java │ └── SaleController.java ├── service │ ├── DrugService.java │ ├── PurchaseService.java │ └── SaleService.java ├── mapper │ ├── DrugMapper.java │ ├── StockMapper.java │ └── SaleMapper.java ├── entity │ ├── Drug.java │ ├── Stock.java │ └── SaleOrder.java └── dto ├── PurchaseOrderDTO.java └── SaleOrderDTO.java这个分层结构即使用来作为模板也非常便于后续功能扩展。要是你项目里还需要加客户管理、会员积分等功能直接加对应的Controller和Service层就行了。4.2 配置文件数据源与MyBatis-Plusapplication.yml 的关键配置如下我直接写出来server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/pharmacy_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0map-underscore-to-camel-case这个配置一定要开数据库字段的snake_case就能自动映射到实体类的camelCase少写很多TableField注解。另外建议加上逻辑删除配置。药品、供应商这些基础数据不建议物理删除万一删除后又想恢复或者之后要查历史流水逻辑删除都能兜住。这是企业开发里很常用的设计思路。4.3 分页配置与统一响应MyBatis-Plus分页插件配置放在配置类里Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }统一响应结果类这是前后端联调的基础我简单写一个Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }前端统一处理这个结构后端的异常全局捕获后再返回错误信息会让整个联调过程非常顺畅。这里有个很实用的小经验后端千万不要直接返回Map或裸数据来取代统一结果类。一开始走捷径后面前端处理每个接口都要单独判断会非常痛苦。4.4 核心代码药品多条件查询药品分页列表的查询很典型代码长这样Override public PageResultDrugVO pageDrug(DrugQueryDTO dto) { PageDrug page new Page(dto.getPageNum(), dto.getPageSize()); LambdaQueryWrapperDrug wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(dto.getDrugName()), Drug::getDrugName, dto.getDrugName()) .eq(dto.getCategoryId() ! null, Drug::getCategoryId, dto.getCategoryId()) .eq(dto.getPrescriptionFlag() ! null, Drug::getPrescriptionFlag, dto.getPrescriptionFlag()) .eq(dto.getStatus() ! null, Drug::getStatus, dto.getStatus()) .orderByDesc(Drug::getCreateTime); PageDrug result this.page(page, wrapper); // 转VO、填充分类名称等字段 return PageResult.of(result); }LambdaQueryWrapper的条件构造很优雅条件为null的时候自动不拼接直接就实现了动态SQL的效果。不需要写XML文件里的if test对新手非常友好。4.5 核心代码采购入库的事务处理采购入库的后端代码是核心中的核心Service实现部分我贴出来Transactional(rollbackFor Exception.class) Override public void createPurchaseOrder(PurchaseOrderDTO dto) { // 1. 构建主表 PurchaseOrder order new PurchaseOrder(); order.setOrderNo(generateOrderNo()); order.setSupplierId(dto.getSupplierId()); order.setOperatorId(getCurrentUserId()); order.setTotalAmount(dto.getItems().stream() .mapToDecimal(item - item.getPrice().multiply(new BigDecimal(item.getQuantity()))) .sum()); purchaseOrderMapper.insert(order); // 2. 保存明细并更新库存 for (PurchaseItemDTO item : dto.getItems()) { PurchaseOrderDetail detail new PurchaseOrderDetail(); detail.setOrderId(order.getId()); detail.setDrugId(item.getDrugId()); detail.setBatchNumber(item.getBatchNumber()); detail.setQuantity(item.getQuantity()); detail.setPurchasePrice(item.getPrice()); purchaseOrderDetailMapper.insert(detail); // 3. 如果同一药品同一批次已存在累加数量否则插入新记录 Stock stock stockMapper.selectOne(new LambdaQueryWrapperStock() .eq(Stock::getDrugId, item.getDrugId()) .eq(Stock::getBatchNumber, item.getBatchNumber())); if (stock ! null) { stockMapper.increaseQuantity(stock.getId(), item.getQuantity()); } else { Stock newStock new Stock(); newStock.setDrugId(item.getDrugId()); newStock.setBatchNumber(item.getBatchNumber()); newStock.setSupplierId(dto.getSupplierId()); newStock.setProductionDate(item.getProductionDate()); newStock.setExpireDate(item.getExpireDate()); newStock.setQuantity(item.getQuantity()); newStock.setMinStock(item.getMinStock() ! null ? item.getMinStock() : 10); stockMapper.insert(newStock); } } }Transactional注解一定要加这个没得商量。一旦中间某条明细插失败了主表还在库存却没更新数据就脏了。另外这里的increaseQuantity建议用自定义的UPDATE SQL而不是先查出来再set再update原因前面讲过是防并发覆盖。对应的Mapper方法可以是Update(UPDATE drug_stock SET quantity quantity #{quantity} WHERE id #{id}) int increaseQuantity(Param(id) Long id, Param(quantity) Integer quantity);4.6 核心代码销售出库与库存扣减销售开单的Service层逻辑更复杂一点因为要处理多批次的扣减顺序Transactional(rollbackFor Exception.class) Override public void createSaleOrder(SaleOrderDTO dto) { SaleOrder saleOrder new SaleOrder(); saleOrder.setOrderNo(generateOrderNo()); saleOrder.setCashierId(getCurrentUserId()); saleOrder.setTotalAmount(calculateTotal(dto.getItems())); saleOrderMapper.insert(saleOrder); for (SaleItemDTO item : dto.getItems()) { // 查询该药品所有有效批次按有效期排序 ListStock stocks stockMapper.selectList(new LambdaQueryWrapperStock() .eq(Stock::getDrugId, item.getDrugId()) .gt(Stock::getQuantity, 0) .orderByAsc(Stock::getExpireDate)); int remain item.getQuantity(); for (Stock stock : stocks) { if (remain 0) break; int deduct Math.min(remain, stock.getQuantity()); stockMapper.decreaseQuantity(stock.getId(), deduct); remain - deduct; // 记录销售明细包含批次信息 SaleOrderDetail detail new SaleOrderDetail(); detail.setOrderId(saleOrder.getId()); detail.setDrugId(item.getDrugId()); detail.setBatchNumber(stock.getBatchNumber()); detail.setQuantity(deduct); detail.setPrice(item.getPrice()); saleOrderDetailMapper.insert(detail); } if (remain 0) { throw new BizException(药品 item.getDrugId() 库存不足缺货数量 remain); } } }这里的核心思想就是“按有效期从小到大扣减”也就是先进先出。前端的“购物车”把选中的药品、数量、单价传到后端后端在事务里一次性扣减。如果中途发现库存不够直接抛异常让事务回滚前面扣减的、插入的明细全部撤销保证数据一致性。这里有一个值得注意的点销售明细里记录批次号。很多新手做销售单的时候不记批次只记录药品ID和数量这样一旦要做退货或者批次追溯完全无从下手。记批次号虽然让代码多了一行但对系统来说价值非常大。4.7 前端页面与联调要点前端我用Vue Element Plus举例页面级别大概分这么几个登录页布局页侧边栏菜单 顶部用户信息 主内容区药品管理页表格 搜索栏 新增/编辑对话框入库页面选择供应商 动态添加药品行销售开单页左侧药品列表/搜索右侧购物车底部结算库存预警页两个统计卡片 两个预警列表供应商管理页路由守卫要做一下未登录用户访问任何页面时跳转回登录页这个用Vue Router的beforeEach来实现。Axios请求拦截器里带上Token响应拦截器里如果拿到401直接跳转登录页这两个是前后端分离项目的基本功。4.8 说明文档的写法建议题目里提到了“说明文档”这也是很多同学头疼的地方。我的建议是说明文档按这条线写课题背景与意义、国内外研究现状、需求分析角色分析 用例图 功能列表、系统设计架构图 模块设计 数据库设计、系统实现每个页面截图 关键代码讲解、系统测试测试用例表 测试结果、总结与展望。不用过度堆字数重点是“需求分析”和“系统设计”要写得有逻辑截图和代码要对得上。答辩老师翻到需求分析看到用例图 角色场景描述就能快速建立起“这个学生真的有认真思考业务”的印象。5. 常见问题与排查技巧实录5.1 数据库连接与驱动问题最常见的问题是MySQL 8.0的驱动名跟MySQL 5.x不一样很多同学还在用com.mysql.jdbc.Driver导致启动就报错。MySQL 8.0要用com.mysql.cj.jdbc.Driver。同时url里一定要带上serverTimezoneAsia/Shanghai否则也会报时区错误。另一个坑是字符集。数据库创建的时候如果你没有指定utf8mb4插入中文时可能变成问号。建库语句顺手就带一句CREATE DATABASE pharmacy_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;这个建库细节比你想的重要别等系统跑起来才发现中文乱码排查半天最后是库的问题。5.2 MyBatis-Plus的Mapper扫描不到如果启动时就报“Invalid bound statement (not found)”或者自动注入失败多半是两件事没做对一是启动类上没有加MapperScan注解二是XML文件的位置和Mapper接口不一致。解决方式很简单在启动类上加MapperScan(com.example.pharmacy.mapper)如果有的Mapper写了自定义XML在application.yml里把XML路径指对默认的规则是放在resources/mapper/目录下。5.3 前后端联调跨域问题Vue项目开发服务器默认跑在8081或5173后端在8080跨域是绕不开的。可以在后端写一个跨域配置类也可以在前后端用代理解决。我更推荐在后端做全局跨域配置比如实现WebMvcConfigurer接口重写addCorsMappings简单粗暴且对前端零要求。要注意的是跨域配置要允许Authorization请求头不要用allowedOrigins( * )要指定具体的前端地址否则带上Token也会被浏览器拦下来。这是一个非常容易踩的细节。5.4 日期格式与时间戳对不上后端返回日期默认可能是时间戳前端展示出来就是一串数字。解决方式很简单在实体类或者统一的Jackson配置里指定格式。更稳妥的办法是在全局配置文件里统一设置返回日期格式这样前端收到的就是“2024-05-20 14:30:00”这样的字符串。特别注意LocalDateTime类型的格式化除了spring.jackson.date-format之外可能还要单独配置LocalDateTime的序列化器记不住就统一在实体类的日期字段加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。5.5 库存扣减的并发与一致性问题很多同学一开始都是“先select再update”的写法这在单用户测试时永远没问题一旦用JMeter压一下或者两个人同时下单就露馅。解决办法一个是前面说的原子UPDATE语句一个是给库存表加上乐观锁版本号字段。毕设项目不要求做分布式锁但把“为什么用原子更新”写在系统设计里答辩反而是加分项。5.6 答辩时被问到“有什么亮点”怎么答这块其实是很多同学面试或者答辩前最焦虑的。我提供几个方向供参考批次管理和效期预警说明你考虑了药品这种特殊商品的实际管理难点。近效期优先出库你实现了先进先出逻辑而不是无脑从总量里扣。JWT无状态认证授权对比Session方案说明对前后端分离的理解。事务一致性设计采购、销售均包裹在事务中说明了你对数据安全的理解。逻辑删除基础数据不物理删除便于追溯。这几个亮点讲出去整个项目的深度一下就上去了。6. 前后端调试习惯与项目扩展建议6.1 调试时建议开启SQL日志MyBatis-Plus配置里把日志打开开发阶段能直接看到每一条SQL语句。排查“查不到数据”“更新影响到0行”“条件不对”等问题时直接看SQL是最快的方式。上线或交付前再把日志级别改回warn就好这是一个非常实用的开发习惯。6.2 项目后续可以做哪些扩展如果时间和精力允许哪怕只是把扩展方向写在说明文档的“展望”部分也会让项目显得有前瞻性。扩展方向不外乎这几个增加供应商对账功能按时间段拉取采购流水生成应付账单。增加会员与积分销售单关联会员ID按消费金额累计积分。增加图形报表统计每周/每月的销售趋势使用ECharts画折线图和饼图。增加药品图片上传用本地存储或MinIO存文件路径。这些扩展功能对你个人能力的提升也很有帮助特别是ECharts报表答辩现场展示图表永远比展示一张表格有效果。建议优先做这个。这个项目做完你会发现SpringBoot Vue这套组合的使用感很顺而且在药店这个业务场景里你能接触到很多在通用的图书管理、新闻发布系统里学不到的知识点比如批次、效期、进销存逻辑。这些都是实际的业务逻辑毕业之后进公司写业务代码本质上也就是这些东西。最后再分享一个我个人的习惯写代码前先把整个数据库的表结构和每个表的字段列表手写一遍卡壳了就说明需求还没想清楚。把表设计搞定这个项目基本就完成一半了剩下的就是按模块一个一个填代码过程其实是比较愉快的。希望这份拆解对你的毕设有所帮助。