
简介这份UML网上订餐系统实验报告面向软件工程、计算机相关专业学生及UML建模初学者以网上订餐系统为完整案例解决需求建模、分析模型与用例实现缺乏实操范例的问题。资源为单个doc文档压缩包约149KB内容围绕需求模型、分析模型、关键抽象、用例实现与类设计展开涵盖用例图、顺序图、序列图等核心图形。读者可从中获取游客注册、登录注销、餐品检索、订单管理等用例的事件流、前置与后置条件描述以及orderlist、order、dish、user、guest、favorite等类的属性方法与持久性、安全性分析机制并借助架构模型理解客户端、服务器端与数据库服务器的整体结构。目前已有4225人学习适合作为课程大作业、实验报告撰写或UML建模练习的参考模板。1. 从一份 UML 网上订餐系统文档说起为什么它至今仍是课程设计的高频选题如果你正在做软件工程课程设计或者毕业设计大概率绕不开「UML 网上订餐系统」这个题目。它几乎是每年高校软件工程课设的保留项目原因很直接订餐业务链条完整从用户浏览菜单、下单、支付到商家接单、后厨出餐、配送每个环节都能对应到 UML 的用例图、类图、时序图、活动图画出来不空。但很多人拿到这个题目后的第一反应是打开 Word 就开始画图画完用例图画类图画完类图不知道下一步干什么最后交上去的文档图是画了逻辑却经不起推敲——类图里没有关联关系时序图里消息顺序对不上用例图里 Actor 和用例的边界模糊。这份「UML网上订餐系统.doc」本质上是一套面向对象分析与设计的建模文档核心价值不在于图好不好看而在于它能不能把「谁在什么条件下做什么事、系统内部怎么协作」讲清楚。适合三类人一是正在做课设的学生需要一套能跑通全流程的建模思路二是刚转岗做后端或产品、想补面向对象设计基本功的从业者三是需要快速输出一份可评审设计文档的开发者。接下来我会按「先立住建模逻辑再落到每张图怎么画、参数怎么定、坑在哪」的顺序拆开讲不堆概念只讲能直接抄进文档里的做法。2. 用例图与类图先把「谁做什么」和「系统有什么」钉死2.1 用例图Actor 划分决定后面所有图的走向网上订餐系统的用例图看起来简单但 Actor 划分是最容易翻车的地方。常见做法是把「用户」当成一个 Actor然后画一堆用例连上去。问题是用户在不同场景下的身份不同浏览菜单时是游客下单时是注册用户支付时是付款方评价时是消费者。如果全揉成一个 Actor后面时序图就没法区分消息的触发条件。我一般会按「系统边界外谁与系统交互」来拆网上订餐系统典型 Actor 有四个顾客、商家、骑手、管理员。每个 Actor 对应的核心用例Actor核心用例前置条件顾客浏览菜单、加入购物车、提交订单、支付、查看订单状态、评价浏览无需登录下单需登录商家管理菜品、接单、拒单、标记出餐需商家账号登录骑手查看待配送订单、接单、更新配送状态需骑手账号登录管理员管理用户、管理商家、查看统计报表需管理员权限画用例图时include和extend别乱用。支付用例通常include提交订单因为提交订单后必然要支付除非到付那要单独建一个到付用例。评价用例用extend挂在「查看订单状态」上因为评价是可选的不是每次查看都触发。注意用例图里不要画系统内部的处理步骤比如「验证密码」「查询数据库」这类是系统内部行为不是用例。用例必须是 Actor 能感知到的完整功能单元。2.2 类图从业务名词里抽类别从代码里倒推类图是 UML 网上订餐系统文档里最核心的一张图也是软考中级 UML 建模常考的点。很多人画类图时习惯先想代码怎么写然后倒推类结果画出来的是「Service 类」「Controller 类」这种技术分层类不是业务领域类。正确的做法是从需求描述里的名词抽类。网上订餐系统的核心业务类我一般会抽这几个Customer顾客、Restaurant商家、Dish菜品、Order订单、OrderItem订单项、Payment支付记录、Delivery配送信息、Review评价。类之间的关系Customer与Order一对多关联一个顾客可以下多个订单。Order与OrderItem组合关系订单项不能脱离订单存在。OrderItem与Dish关联关系订单项引用菜品。Restaurant与Dish聚合关系菜品可以独立于商家存在比如下架后还在历史订单里。Order与Payment一对一关联。Order与Delivery一对一关联。Order与Review一对一关联评价可选。画类图时属性只写业务需要的别把数据库字段全搬上去。比如Order类写orderId、createTime、status、totalAmount就够了updateTime、isDeleted这种技术字段可以省略除非文档明确要求。方法写核心业务方法比如Order.addItem()、Order.calculateTotal()、Order.cancel()。startuml class Customer { -customerId: String -name: String -phone: String placeOrder(): Order payOrder(orderId: String): boolean } class Order { -orderId: String -createTime: Date -status: OrderStatus -totalAmount: double addItem(dish: Dish, qty: int): void calculateTotal(): double cancel(): boolean } class OrderItem { -quantity: int -unitPrice: double subtotal(): double } class Dish { -dishId: String -name: String -price: double -stock: int } class Restaurant { -restaurantId: String -name: String -address: String acceptOrder(orderId: String): boolean rejectOrder(orderId: String): boolean } Customer 1 -- * Order : places Order 1 *-- * OrderItem : contains OrderItem * -- 1 Dish : refers Restaurant 1 o-- * Dish : offers enduml上面这段 PlantUML 代码可以直接在支持 PlantUML 的工具里渲染出类图。*--表示组合o--表示聚合--表示关联。参数说明Customer与Order是一对多所以写1 -- *Order与OrderItem是组合用*--Restaurant与Dish是聚合用o--。如果你用 Visio 或 StarUML 画对应关系是组合用实心菱形聚合用空心菱形关联用普通箭头。提示类图里的箭头方向别搞反。关联关系箭头指向被引用的类比如OrderItem引用Dish箭头从OrderItem指向Dish。继承关系箭头指向父类实现关系虚线箭头指向接口。3. 时序图与活动图把「怎么一步步发生」画到能直接写代码3.1 时序图消息顺序错了后面代码逻辑全乱时序图是 UML 动态结构图里最常被考的一类也是网上订餐系统文档里最能体现设计功力的部分。很多人画时序图时只画一条「用户下单」的线从用户到系统到数据库太粗评审时会被问「支付失败怎么处理」「库存不足怎么回滚」。我一般会按核心场景拆成三张时序图下单时序图、支付时序图、取消订单时序图。以「下单」为例参与对象包括Customer、OrderController、OrderService、InventoryService、OrderRepository。消息顺序Customer发送placeOrder(cartItems)给OrderController。OrderController调用OrderService.createOrder(cartItems)。OrderService遍历cartItems对每个菜品调用InventoryService.checkStock(dishId, qty)。如果库存充足OrderService创建Order对象调用OrderRepository.save(order)。OrderRepository返回orderId。OrderService返回orderId给OrderController。OrderController返回下单成功给Customer。如果库存不足第 3 步返回 falseOrderService抛出InsufficientStockExceptionOrderController捕获后返回错误信息给Customer。这条异常路径必须在时序图里画出来用alt组合片段表示分支。startuml actor Customer participant OrderController participant OrderService participant InventoryService participant OrderRepository Customer - OrderController : placeOrder(cartItems) OrderController - OrderService : createOrder(cartItems) loop 每个菜品 OrderService - InventoryService : checkStock(dishId, qty) alt 库存充足 InventoryService -- OrderService : true else 库存不足 InventoryService -- OrderService : false OrderService -- OrderController : InsufficientStockException OrderController -- Customer : 库存不足请调整数量 end end OrderService - OrderRepository : save(order) OrderRepository -- OrderService : orderId OrderService -- OrderController : orderId OrderController -- Customer : 下单成功orderId enduml这段代码里alt是分支片段loop是循环片段。参数说明checkStock的入参是dishId和qty返回布尔值。save的入参是Order对象返回orderId。画时序图时生命线上的激活条Activation Bar要对应方法调用周期别画成一条直线从头到尾。注意时序图里的消息名要和类图里的方法名一致。类图里OrderService没有createOrder方法时序图里却出现了评审时会被挑出来。画完时序图回头补类图方法这是常规操作。3.2 活动图用泳道把「谁负责哪一步」画清楚活动图适合表达业务流程的并行和分支。网上订餐系统的订单状态流转用活动图最合适配合泳道Swimlane能清楚看出每个角色负责哪些动作。典型订单状态待支付 → 已支付 → 商家接单 → 出餐中 → 待配送 → 配送中 → 已完成。异常分支超时未支付 → 自动取消商家拒单 → 退款。画活动图时泳道按角色分顾客、系统、商家、骑手。每个动作放在对应泳道里。比如「支付」在顾客泳道「验证支付结果」在系统泳道「接单」在商家泳道「更新配送状态」在骑手泳道。分支用菱形判断框并行用同步条。startuml |顾客| start :提交订单; :支付订单; |系统| if (支付成功?) then (是) :生成待接单订单; else (否) :标记支付失败; stop endif |商家| if (接单?) then (是) :标记出餐中; else (否) :拒单并触发退款; |系统| :执行退款; stop endif |骑手| :接单配送; :更新配送状态; |系统| :标记订单完成; stop enduml这段活动图代码里|顾客|表示泳道切换if...then...else表示分支。参数说明支付成功?的判断依据是支付网关回调结果接单?的判断依据是商家在时限内的操作。活动图里每个动作节点用动词开头比如「提交订单」而不是「订单提交」。提示活动图不要画成流程图。活动图强调的是对象的活动和状态变化不是代码执行步骤。如果画出来和流程图一模一样说明抽象层级不对应该往上提一层。4. 避坑与排查UML 网上订餐系统文档里最容易翻车的 5 个地方4.1 用例图里 Actor 和用例的连线方向反了现象用例图里箭头从用例指向 Actor或者双向箭头满天飞。原因不清楚 UML 用例图的关联关系默认是 Actor 发起用例箭头方向应该从 Actor 指向用例表示 Actor 启动用例。双向箭头只在极少数双向交互场景用比如消息推送。解决统一改成 Actor → 用例的单向箭头。如果某个用例确实需要系统主动通知 Actor用extend或单独建一个「接收通知」用例别用双向箭头糊弄。4.2 类图里把关联画成了依赖现象类图里所有关系都用虚线箭头看起来像依赖关系但实际业务上是关联或聚合。原因分不清关联、聚合、组合、依赖的区别。关联是长期关系顾客和订单聚合是整体部分可分离商家和菜品组合是整体部分不可分离订单和订单项依赖是临时使用方法参数。解决按业务生命周期判断。如果部分对象能独立存在用聚合不能独立存在用组合只是方法里临时用一下用依赖。画完后检查每个关系的多重性一对多写1..*可选写0..1。4.3 时序图里消息顺序和业务逻辑对不上现象时序图里先扣库存再创建订单或者先支付再校验库存。原因画图时没按实际业务顺序推演凭感觉画。解决画时序图前先用文字把步骤写一遍标上序号再按序号画。网上订餐系统的下单顺序必须是校验库存 → 创建订单 → 扣减库存 → 返回订单号。支付顺序是创建支付记录 → 调用支付网关 → 更新支付状态 → 更新订单状态。顺序错了后面写代码时逻辑全乱。4.4 活动图泳道划分太粗或太细现象泳道只有「用户」和「系统」两条或者每个微服务一条泳道画出来几十条。原因泳道粒度没对齐文档用途。课设文档一般按角色分泳道详细设计文档才按模块分。解决课设和需求分析阶段按角色分泳道顾客、商家、骑手、管理员、系统详细设计阶段按子系统分泳道订单服务、支付服务、库存服务。别混用。4.5 文档里图之间互相矛盾现象类图里Order有cancel()方法时序图里取消订单却调的是OrderService.cancelOrder()活动图里又没有取消分支。原因每张图单独画没有交叉检查。解决画完所有图后做一次一致性检查类图里的类名和方法名是否在时序图里出现时序图里的消息是否在类图里有对应方法活动图里的分支是否覆盖了用例图里的所有用例。不一致的地方以类图为准因为类图是静态结构的核心。5. 从文档到可运行原型用 UML 类图直接生成代码骨架UML 网上订餐系统文档的最终价值不是交一份 Word而是能直接指导编码。我一般会在类图定稿后用 PlantUML 或 StarUML 的代码生成功能导出 Java 或 Python 骨架然后手动补业务逻辑。以 Python 为例类图里的Order和OrderItem可以生成如下骨架from datetime import datetime from enum import Enum class OrderStatus(Enum): PENDING_PAYMENT pending_payment PAID paid ACCEPTED accepted DELIVERING delivering COMPLETED completed CANCELLED cancelled class OrderItem: def __init__(self, dish_id: str, quantity: int, unit_price: float): self.dish_id dish_id self.quantity quantity self.unit_price unit_price def subtotal(self) - float: return self.quantity * self.unit_price class Order: def __init__(self, order_id: str, customer_id: str): self.order_id order_id self.customer_id customer_id self.create_time datetime.now() self.status OrderStatus.PENDING_PAYMENT self.items: list[OrderItem] [] def add_item(self, dish_id: str, quantity: int, unit_price: float): self.items.append(OrderItem(dish_id, quantity, unit_price)) def calculate_total(self) - float: return sum(item.subtotal() for item in self.items) def cancel(self) - bool: if self.status OrderStatus.PENDING_PAYMENT: self.status OrderStatus.CANCELLED return True return False这段代码直接对应类图里的Order和OrderItem。add_item对应类图里的addItemcalculate_total对应calculateTotalcancel对应cancel。参数说明dish_id是菜品唯一标识quantity是数量unit_price是下单时的单价不是当前菜品价格防止商家改价影响历史订单。cancel方法里加了状态判断只有待支付订单能取消已支付订单要走退款流程这个约束在类图里可以用{status PENDING_PAYMENT}的约束表达式标注。生成骨架后下一步是补 Repository 层和 Service 层。Repository 层按类图里的关联关系建表Order和OrderItem是一对多OrderItem表里加order_id外键。Service 层按时序图里的消息顺序写方法调用。活动图里的分支对应 Service 层的 if-else 和异常处理。验证方法拿一份真实的订单数据跑一遍。比如顾客点了 2 份宫保鸡丁单价 28和 1 份米饭单价 3calculate_total应该返回 59。然后调用cancel状态变成CANCELLED。再尝试对已支付订单调用cancel应该返回 False。如果对不上回头检查类图里的多重性和时序图里的消息顺序。我自己的习惯是类图定稿后先别急着画时序图先花十分钟把类图里的每个类和方法用一句话解释一遍解释不通的地方就是设计漏洞。这个习惯帮我省了很多返工时间。希望帮到你。本文还有配套的精品资源点击获取