摘 要
本文在市场调研的基础上,综合考虑实际需求和技术选择,决定采用HTML、CSS、JavaScript等前端技术,以及Spring、Spring MVC、MyBatis等后端技术,并结合MySQL数据库作为数据存储工具,采用SSM框架作为整体开发框架。主要分析了传统面包销售方式存在的信息不对称、购物体验差等问题,针对这些问题设计实现了用户注册、登录、商品浏览、商品购买、订单管理等功能,以提高购物体验和商家销售效率。
经过系统功能测试,确保各功能模块正常运行并符合设计要求。用户注册流程简单明了,购买流程顺畅高效,商家方便快捷地发布产品和管理订单。系统的交互界面友好,用户体验得到提升,同时数据存储稳定可靠,确保信息安全性。运行效果流畅,用户满意度提高,商家销售效率明显提升,整体系统运行稳定可靠,为传统面包销售方式带来了新的解决方案,以提升用户体验和商家销售效率,满足市场需求,推动面包行业的发展。
关键词:SSM;用户体验;商家效率;功能测试
目 录
第一章 绪论
1.1项目背景
在当今时代,随着互联网技术的广泛传播与深入应用,以及电子商务如雨后春笋般的迅猛发展,数量愈发庞大的消费者群体越来越倾向于选择通过网络来购买各类商品。而面包,作为一种极具特殊性的商品,也逐步受到了众多消费者的热烈关注和喜爱。但是,不得不承认的是,传统的面包销售方式暴露出了相当多的问题,其中包括严重的信息不对称现象,价格缺乏足够的透明度,消费者的购物体验极为不佳等等。鉴于此,开发出一个具备完整且丰富功能、能够给予用户良好体验的面包销售系统,无疑具有极其重要的现实意义和价值。它不仅能够满足消费者对于便捷、高效、透明购物的需求,同时也能够为面包行业的发展注入新的活力和动力,推动整个行业朝着更加健康、有序、繁荣的方向不断迈进。
1.2 项目意义
本项目着重致力于开发一个以 SSM 框架为基础的面包销售系统,其核心意义在于能够切实地为用户打造出极为便捷的购物体验环境。通过这个系统,用户能够不受时间和空间的限制,随心所欲地在任何时刻、任何地点轻松自如地浏览丰富多样的面包面包产品,并能够便捷顺畅地进行购买操作。与此同时,该系统也能为商家提供一套高效实用的管理工具。借助本系统,商家可以更加方便快捷地对商品信息进行精准全面的管理,对订单情况进行清晰明了的掌控,进而极大地提高自身的销售效率,同时也能显著提升服务质量,从而更好地满足用户需求,增强用户的满意度和忠诚度,促进面包销售行业的良性发展和持续进步。
1.3 国内外研究现状
现阶段,在国内外的大环境下,已经涌现出了许多的面包销售系统。然而,其中的大多数系统都存在着诸如功能较为单一、用户体验不尽如人意等一系列问题。鉴于此,本项目将会紧密结合实际的具体需求,致力于开发出一个功能完备齐全、能够给予用户良好体验的面包销售系统。
在国内的情况来看,面包行业的发展历程大致可以细分为起步阶段、逐步发展阶段以及趋于成熟阶段。就当前而言,中国的面包行业已然步入了成熟的阶段,在这个阶段中,市场竞争呈现出极为激烈的态势,品牌化、个性化以及定制化已经逐渐成为了行业发展的主要趋势。与此同时,伴随着互联网技术的不断进步与发展,线上销售模式和定制服务也开始慢慢地成为了主流趋势。
在国外,部分发达国家的面包行业已然发展得非常成熟,拥有着完备完善的产业链以及先进的技术手段。这些国家的面包销售系统通常来讲具备着更为丰富多样的功能,同时也能为用户带来更好的体验感受。
所以,本项目将会借鉴国内外优秀面包销售系统的宝贵经验,再与国内市场的实际需求深度融合,致力于为用户提供更为便捷、高效且极具个性化的购物体验,同时也为商家提供更加科学合理、智能先进、高效有序的管理工具,从而推动面包行业在新的发展阶段实现更好的进步与提升。
1.4 开发工具介绍
1.4.1 SSM 框架
SSM 框架实则是 Spring、Spring MVC 以及 MyBatis 这三个框架的简称,它属于目前在 Java Web 开发领域中被广为运用的框架之一。其中,Spring 是一个具有显著优势的轻量级 Java 开发框架,其提供了一系列重要的功能,例如依赖注入,这能够有效简化组件之间的依赖关系管理;还有面向切面编程,这有助于实现横切关注点的分离;以及事务管理等诸多强大功能。而 Spring MVC 作为 Spring 框架之中的一个关键模块,主要用于达成 Web 应用中 MVC 架构的高效实现。MyBatis 则是一个极为重要的持久层框架,它在实现对数据库的访问和具体操作方面发挥着至关重要的作用。
1.4.2 MySQL 数据库
MySQL 是一种在数据库领域中具有高度开放性特质的关系型数据库管理系统。它具有诸多显著的优点,其中包括体积十分小巧,不会过多地占用系统资源;运行速度极其快捷,能够高效地处理数据请求;成本方面也相对较低,具有较高的性价比。也正因如此,它在众多领域尤其是 Web 应用的开发过程当中被极为广泛地加以运用。
MySQL 凭借着自身所具备的一系列优良特性,比如强大的数据存储能力、稳定的性能表现等,为数量众多的应用提供了坚实可靠的数据存储和管理方面的有力支持,在数据处理和管理这一关键领域发挥着无可替代的极其重要的作用,成为了不可或缺的关键组成部分。
1.4.3 Tomcat 服务器
Tomcat 乃是一个开源性质的 Web 服务器以及 Servlet 容器,它对 Java Servlet 和 JavaServer Pages 技术给予了有力的支持,在当前的 Java Web 开发领域中,Tomcat 已然成为了常用的服务器之一。其具有出色的稳定性和可靠性,能够很好地承载 Web 应用的运行需求,为应用的稳定运行和高效服务提供了坚实的保障。
第二章 需求分析
2.1 系统概述
本系统旨在为用户提供一个便捷、高效的面包销售平台,实现面包的在线购买和销售。系统具备用户注册、登录、浏览商品、购买商品、评价商品等功能,同时支持商家注册、登录、发布商品等功能。
2.2 可行性分析
本系统在可行性方面表现良好,从技术上看,采用 SSM 框架、MySQL 数据库、Tomcat 服务器以及 HTML、CSS、JavaScript 等前端和后端技术,这些成熟技术确保了系统的稳定性和可靠性,技术角度可行;经济上,虽然有人力、时间、技术资源等成本投入,但通过开发此面包销售系统可拓展市场、增加业务量和销售额,提升品牌知名度与影响力,带来更多商业机会和附加值,且高效管理能降低运营成本、提高效率,综合预期收益能覆盖成本实现盈利,具有经济可行性;操作上,基于 SSM 框架的面包销售系统会充分考虑用户和商家习惯与需求,具备简洁直观界面,方便各类用户操作,且有稳定兼容性保障,适应不同网络环境和设备终端,还配备操作指南和培训支持,后续维护更新机制能确保满足需求变化,具有很强的操作可行性。
2.2.1 技术可行性
本系统采用 SSM 框架作为开发框架,MySQL 数据库作为数据存储工具,Tomcat 服务器作为应用服务器。同时,我们还采用了 HTML、CSS、JavaScript 等前端技术,以及 Spring、Spring MVC、MyBatis 等后端技术,实现了系统的前后端分离。这些技术都是目前比较成熟的技术,具有良好的稳定性和可靠性。因此,从技术角度来看,本系统是可行的。
2.2.2 经济可行性
本系统主要涉及项目的成本投入与预期收益之间的关系评估。从成本方面来看,包括系统开发过程中的人力成本、时间成本、技术资源投入成本等。在人力成本上,需要组建专业的开发团队,包括系统架构师、程序员、测试人员等,这方面的支出需要合理规划和控制;时间成本则要考虑项目的开发周期以及可能出现的延期情况对成本的影响。技术资源投入方面,如服务器租赁或购置、开发工具和软件许可等费用也需纳入考量。
然而,从预期收益角度分析,本项目具有较大的潜力。通过开发基于 SSM 框架的面包销售系统,能够极大地拓展市场覆盖范围,吸引更多的用户,从而增加面包销售的业务量和销售额。随着系统的成功运行和良好口碑的传播,有望进一步提升品牌知名度和影响力,带来更多的商业机会和附加值。同时,高效的管理工具能够降低运营成本,提高运营效率,间接增加经济效益。综合来看,尽管前期需要一定的成本投入,但通过有效的运营和市场拓展,预期收益将能够覆盖成本并实现盈利,具有良好的经济可行性。
2.2.3 操作可行性
重点关注系统在实际操作层面的易用性和可行性。就本项目而言,基于 SSM 框架开发的面包销售系统在操作设计上会充分考虑用户和商家的使用习惯与需求。系统将具备简洁直观的界面布局,用户可以轻松地进行面包浏览、筛选、购买等操作,无论是经验丰富的网络购物者还是初次接触的用户,都能快速上手并顺利完成各项流程。商家方面,管理界面会设计得清晰明了且功能集中,方便商家进行商品信息录入与更新、订单处理、数据分析等工作,即使是非专业技术人员的商家也能够便捷高效地操作系统。
此外,系统在稳定性和兼容性方面也会有可靠保障,能够适应不同的网络环境和设备终端,确保无论是在电脑端还是移动设备端,都能提供流畅稳定的操作体验。同时,会配备完善的操作指南和培训支持,以便用户和商家在遇到问题时能及时得到解决和指导。在系统的后续维护和更新上,也会建立有效的机制,确保系统始终能满足实际操作的需求和变化,保持良好的运行状态。综合来看,从操作的便捷性、稳定性、适应性以及支持保障等多方面评估,该面包销售系统具有很强的操作可行性。
2.3 系统功能需求
本系统立足于先进的技术架构之上,其根本宗旨在于全力打造出一个具备高度高效性、极致便捷性以及强大功能的面包销售平台。经过精心地规划与细致地设计,切实地保障了系统的稳定性,使其能够在各种复杂情况下保持稳固运行;确保了系统的可靠性,让其能够持续稳定地发挥作用;同时也确保了系统的可扩展性,以便能够从容地应对不断发生变化的业务需求,无论是在业务规模逐渐扩大还是业务形式日益多样的情况下,都能灵活地适应和调整,始终为用户提供优质的服务和良好的体验。
2.3.1 用户模块
(1)用户注册/登录:用户通过用户名密码确认密码来进行账号注册的途径,在完成注册流程后,便能够顺利登录到整个系统当中,从而开启后续的各项操作与体验。
(2)浏览商品:用户拥有浏览系统内所有面包商品的权限,能够全方位地查看每一款面包商品的具体细节信息,以便了解每一款面包的独特之处。
(3)购买商品:用户能够在众多的面包商品中,精心挑选出自己心仪的面包产品,并进而展开购买行为,通过便捷的流程完成对面包商品的交易。
2.3.2 前端页面部分
(1)首页不但需要全方位地展示热门产品、全面的推荐信息等内容,而且必须要拥有良好的导航功能,从而能够有效地引导用户以最快的速度找到自己所需要的具体内容,让用户在使用过程中能够拥有便捷流畅的体验。
(2)商品分类必须清晰明确地呈现出各类产品的细致分类,以便于用户能够轻松自如地进行筛选,迅速找到符合自己需求和偏好的产品类别。
(3)热销应当以用户的行为数据以及深入的数据分析作为坚实基础,通过精确的计算和判断,准确无误地为用户提供他们真正感兴趣的产品,提高用户的满意度和对产品的关注度。
2.3.3 后端部分
- Dao:数据访问对象,用于处理与数据库的数据交互操作,负责数据的读取、写入、更新和删除等,以实现数据层与业务逻辑层的分离,提高代码的可维护性和可扩展性。
- Filter(过滤器):在 Web 开发中,过滤器用于对请求和响应进行预处理和后处理,可以实现诸如权限验证、日志记录、字符编码转换等功能。
- Listener(监听器):用于监听 Web 应用中的特定事件,如应用的启动和关闭、会话的创建和销毁、请求的开始和结束等,并在相应事件发生时执行特定的操作。
- Model(模型):在软件开发中,模型通常代表了应用程序的数据结构和业务规则,它负责处理数据的逻辑和状态。
- Service(服务):包含业务逻辑的处理,协调不同的组件(如 Dao、Model 等)来完成复杂的业务功能,并为前端或其他层提供统一的接口。
- Servlet:是运行在 Web 服务器上的 Java 程序,用于处理客户端(通常是浏览器)的请求,并生成相应的响应。
- Sql(Structured Query Language):结构化查询语言,用于管理关系型数据库中的数据,执行查询、插入、更新、删除等操作。
- Util(工具类):提供一些通用的工具方法或功能,这些方法通常可以在多个地方被重复使用,以提高代码的复用性和开发效率。
2.4 系统性能需求
本系统的性能需求主要包括以下几个方面:
2.4.1 响应时间
用户注册/登录的响应时间不超过 3 秒。
浏览商品的响应时间不超过 5 秒。
购买商品的响应时间不超过 10 秒。
2.4.2 吞吐量
系统每秒处理的用户注册/登录请求不低于 10 次。
系统每秒处理的浏览商品请求不低于 20 次。
系统每秒处理的购买商品请求不低于 10 次。
2.4.3 资源利用率
系统 CPU 利用率不高于 80%。
系统内存利用率不高于 80%。
2.5 开发环境
本系统的开发环境主要包括以下几个方面:
开发工具:idea
开发语言:Java
数据库:MySQL
第三章 总体设计
3.1 系统总体设计
本系统将采用 B/S 架构来进行设计,该系统明确地分为前端和后端这两个重要的部分。前端主要是依据 SSM 框架当中的 Spring MVC 来切实负责用户界面的充分展示以及交互功能的实现。借助于 Spring MVC 极为优秀的视图控制能力和页面渲染能力,能够为用户成功地呈现出清晰明了、友好亲和并且具有良好交互特性的界面,从而给予用户优质的使用体验。后端则主要是借助 SSM 框架里的 Spring 来达成对各类业务逻辑的妥善处理以及有效整合。通过利用 Spring 所具有的强大的依赖注入和事务管理等卓越功能,有力地确保了数据处理能够具备高效性和准确性;与此同时,还会紧密结合 MyBatis 框架来负责具体的数据存储操作,凭借其灵活多样的数据库映射和操作机制,得以顺利实现对数据的精准化管理以及便捷的存取,进而确保整个系统运行的稳定可靠与高效流畅。
整个系统以 SSM 框架为核心支撑,前后端紧密协作,共同构建起一个稳定、高效且功能丰富的面包销售系统,为用户和商家提供优质的服务体验。
3.2 功能模块设计
本系统主要涵盖用户模块。用户模块在整个系统中占据重要地位。它主要承担着用户的登录注册职能,以此保障用户身份的合法性与安全性。从结构层面来看,其包含了用户信息的存储与验证部分。就内容而言,涉及到用户基本信息的录入与管理。具体功能模块设计如下:
3.2.1 用户模块
用户模块在整个系统中起着关键作用。它主要负责用户的登录注册功能,确保用户身份的合法性和安全性。在结构上,包含了用户信息的存储和验证部分。内容方面,涉及用户基本信息的录入和管理。用户可以通过简单直观的界面进行登录和注册操作,输入必要的信息以完成身份验证和创建。在使用方式上,用户只需在相应的页面输入用户名、密码等信息即可完成登录,而注册则需要填写详细的个人资料。
用户注册/登录:用户可以通过手机号码或邮箱注册账号,并登录系统。
浏览商品:用户可以浏览系统中的所有面包商品,并查看商品的详细信息。
购买商品:用户可以选择心仪的面包商品,并进行购买。
3.2.2 产品展示模块设计
该模块承担着展示面包产品的重要任务。其作用是向用户全面、清晰地呈现各类面包产品。结构上包括产品分类展示、详细信息展示等部分。内容涵盖了各种面包的图片、描述、参数等信息。用户可以通过便捷的导航和搜索功能找到感兴趣的产品,并查看其详细信息和图片,以更好地了解产品的特点和优势。在使用方式上,用户可以轻松浏览不同分类的产品,并通过点击进入产品详情页面。
3.3 数据库设计
3.3.1 数据库概念结构设计
(1)实体:
商品(Goods):具有商品的基本信息,如名称、价格、库存等。
订单(Order):包含订单的总体信息,如总价、状态等。
订单项目(OrderItem):记录订单中的具体商品项目。
推荐(Recommend):关于推荐的相关信息。
类型(Type):商品的分类类型。
用户(User):存储用户的个人资料和相关信息。
(2)关系:
订单与用户:一个用户可以有多个订单,一个订单属于一个用户,形成一对多的关系。
商品与类型:一个商品属于一种类型,一种类型可以有多个商品,形成一对多的关系。
订单与订单项目:一个订单包含多个订单项目,一个订单项目属于一个订单,形成一对多的关系。
订单项目与商品:一个订单项目对应一个商品,一个商品可以出现在多个订单项目中,形成一对多的关系。
3.3.2 数据库逻辑结构设计
3.3.2 -1 goods 表
字段名 | 数据类型 | 主键 | 外键 | 数据约束 | 注释 |
id | INT | 是 | NOT NULL,AUTO_INCREMENT | 商品的唯一标识符 | |
name | VARCHAR(255) | NOT NULL | 商品的名称 | ||
cover | VARCHAR(255) | 商品的封面图片 | |||
image1 | VARCHAR(255) | 商品的第一张图片 | |||
image2 | VARCHAR(255) | 商品的第二张图片 | |||
price | DECIMAL(10, 2) | NOT NULL | 商品价格 | ||
intro | TEXT | 商品介绍 | |||
stock | INT | NOT NULL | 商品库存 | ||
type_id | INT | 是 | NOT NULL | 商品所属的类型 ID |
3.3.2 -2 order 表
字段名 | 数据类型 | 主键 | 外键 | 数据约束 | 注释 |
id | INT | 是 | NOT NULL,AUTO_INCREMENT | 订单的唯一标识符 | |
total | DECIMAL(10, 2) | NOT NULL | 订单的总价 | ||
amount | INT | NOT NULL | 订单的数量 | ||
status | VARCHAR(50) | NOT NULL | 订单状态 | ||
paytype | VARCHAR(50) | NOT NULL | 支付类型 | ||
name | VARCHAR(255) | NOT NULL | 下单人的姓名 | ||
phone | VARCHAR(20) | NOT NULL | 下单人的电话 | ||
address | VARCHAR(255) | NOT NULL | 收货地址 | ||
datetime | TIMESTAMP | NOT NULL,DEFAULT CURRENT_TIMESTAMP | 下单时间 | ||
user_id | INT | 是 | NOT NULL | 下单用户的 ID |
3.3.2 -3 orderitem 表
字段名 | 数据类型 | 主键 | 外键 | 数据约束 | 注释 |
id | INT | 是 | NOT NULL,AUTO_INCREMENT | 订单项目的唯一标识符 | |
price | DECIMAL(10, 2) | NOT NULL | 订单项目中商品的价格 | ||
amount | INT | NOT NULL | 订单项目中商品的数量 | ||
goods_id | INT | 是 | NOT NULL | 对应的商品 ID | |
order_id | INT | 是 | NOT NULL | 所属的订单 ID |
3.3.2 -4 recommend 表
字段名 | 数据类型 | 主键 | 外键 | 数据约束 | 注释 |
id | INT | 是 | NOT NULL,AUTO_INCREMENT | 推荐的唯一标识符 | |
type | VARCHAR(50) | NOT NULL | 推荐类型 |
3.3.2 -5 type 表
字段名 | 数据类型 | 主键 | 外键 | 数据约束 | 注释 |
id | INT | 是 | NOT NULL,AUTO_INCREMENT | 类型的唯一标识符 | |
name | VARCHAR(255) | NOT NULL | 类型的名称 |
3.3.2 -6 user 表
字段名 | 数据类型 | 主键 | 外键 | 数据约束 | 注释 |
id | INT | 是 | NOT NULL,AUTO_INCREMENT | 用户的唯一标识符 | |
username | VARCHAR(255) | NOT NULL | 用户名 | ||
| VARCHAR(255) | NOT NULL,UNIQUE | 电子邮箱 | ||
password | VARCHAR(255) | NOT NULL | 密码 | ||
name | VARCHAR(255) | NOT NULL | 用户的真实姓名 | ||
phone | VARCHAR(20) | NOT NULL | 用户电话 | ||
address | VARCHAR(255) | NOT NULL | 用户地址 | ||
isadmin | TINYINT(1) | DEFAULT 0 | 是否为管理员,0 表示否,1 表示是 | ||
isvalidate | TINYINT(1) | DEFAULT 0 | 是否已验证,0 表示否,1 表示是 |
第四章 详细设计及实现
4.1前端界面设计与实现
前端页面主要包括用户注册界面、用户登录界面、首页界面和产品界面等。用户注册界面简洁明了且具有引导性,必填输入框确保用户信息的唯一性和可联系性,还有提示信息和注册按钮,部分还有可选填项。用户登录界面包含输入框、“记住我”勾选框、登录按钮以及错误提示信息和相关链接。首页界面有顶部导航栏方便导航,轮播图模块展示产品图片吸引用户。产品界面则设有菜单,通过事件处理函数实现便捷高效的页面跳转。 在商品操作方面,用户前端浏览选品后点击购买,信息传至后端处理购物车相关操作,用户可在购物车页面进行多种管理,确认订单后前端向后端发送信息生成订单和更新数据库,支付时前端与后端及支付平台交互,支付成功后端更新订单状态并反馈前端供用户查看。整个前端页面设计注重简洁易操作,致力于为用户带来流畅、便捷和个性化的体验。
4.1.1用户注册界面
用户注册界面简洁明了,具有引导性。有必填的输入框,如用户名、密码、确认密码、电子邮箱等关键信息的填写区域,以确保用户身份的唯一性和可联系性。还会有一些提示信息,告知用户填写要求和注意事项。同时,会有明确的注册按钮,方便用户提交注册信息。部分注册界面还会设置一些可选填项,如性别、兴趣爱好等,以便更好地了解用户。
4.1.2用户登录界面
用户登录界面主要包括用户名或电子邮箱输入框,用于用户输入身份识别信息;密码输入框,保障账户安全;会有一个“记住我”的勾选框,方便用户下次快速登录;清晰可见的登录按钮,点击后进行登录验证。界面设计上会注重简洁、易操作,避免用户产生混淆。在登录过程中,会有错误提示信息,如用户名或密码错误等,引导用户正确操作。此外,还会有找回密码或注册新用户的链接,方便用户进行相关操作。
4.1.3首页界面
顶部导航栏: 包括系统的Logo、首页、商品分类、热销、登录/注册等功能的链接,方便用户进行导航。
轮播图模块: 在首页展示多张面包产品的图片,通过自动轮播或手动切换的方式展示不同风格的面包产品,吸引用户的眼球。
4.1.5产品界面
有一个菜单,当用户在选择某个选项时,通过特定的事件处理函数,页面会自动跳转到相应的位置,从而实现一种便捷、高效的页面跳转效果,为用户带来流畅自然的操作体验
用户在前端页面浏览并选择商品,点击“购买”后,商品信息被发送至后端,后端将其添加到购物车并更新总价与数量。用户进入购物车页面可查看商品信息与总价,还能修改商品数量、删除商品或调整顺序。确认无误后点击“确认订单”,前端将订单信息发送至后端生成订单并更新数据库。用户选择支付方式,前端将信息传至后端与支付平台交互完成支付,后端收到支付成功通知则更新订单状态为“已支付”并反馈至前端,用户可在前端查看订单状态。
4.2后端功能设计与实现
- 系统架构设计:
采用分层架构,将系统分为表示层、业务逻辑层和数据访问层。
使用 Spring 框架进行依赖注入和控制反转,提高系统的可扩展性和灵活性。
使用 Spring MVC 框架实现 MVC 架构,提高系统的可维护性和可扩展性。
使用 MyBatis 框架实现数据持久化,提高系统的性能和数据访问效率。
- 数据库设计:
设计合理的数据库结构,包括表、字段、关系等。
使用 MySQL 数据库进行数据存储,提高系统的性能和数据访问效率。
使用索引、存储过程等技术提高数据库的查询性能。
- 系统环境搭建:
安装 JDK、Tomcat、MySQL 等开发环境。
配置 Spring、Spring MVC、MyBatis 等框架的环境变量。
创建数据库和表,导入初始数据。
4.3数据库连接
- 商品(Goods)表与类型(Type)表:
关系:多对一
说明:多个商品可以属于同一个类型,而每个类型可以对应多个商品。通过 goods 表中的 type_id 外键关联到 type 表的 id 主键,实现商品与类型的关联。
- 订单(Order)表与用户(User)表:
关系:一对多
说明:一个用户可以下多个订单,但每个订单只属于一个用户。通过 order 表中的 user_id 外键关联到 user 表的 id 主键。
- 订单(Order)表与订单项目(OrderItem)表:
关系:一对多
说明:一个订单包含多个订单项目,每个订单项目只属于一个订单。通过 orderitem 表中的 order_id 外键关联到 order 表的 id 主键。
- 订单项目(OrderItem)表与商品(Goods)表:
关系:一对多
说明:一个订单项目对应一个商品,而一个商品可以出现在多个订单项目中。通过 orderitem 表中的 goods_id 外键关联到 goods 表的 id 主键。
第五章 系统测试
5.1测试任务及目的
系统测试的任务是对整个系统进行全面的测试,以确保系统的功能、性能、安全性等方面符合设计要求。其目的在于切实确保系统在功能表现、性能发挥、安全性保障等诸多方面都能够与预先设定的设计要求高度契合。而进行测试的核心目的则是为了精准地发现系统当中所潜在的各类问题以及存在的各种缺陷,并且能够在第一时间就对这些问题和缺陷予以妥善的修复以及有效的改进,通过这样的方式来大幅度提高系统的质量水平和可靠程度,使得系统能够更加稳定、高效地运行,为用户提供更加优质的服务和体验。只有经过严格、全面的系统测试,才能够真正让系统在实际应用中展现出卓越的性能和可靠的保障,从而满足不同用户群体在各种场景下的多样化需求。
5.2 测试计划
- 测试环境:搭建与实际生产环境相似的测试环境,包括硬件、软件、网络等。
- 测试用例:根据系统的功能需求和性能要求,编写详细的测试用例,包括功能测试用例、性能测试用例、安全测试用例等。
- 测试方法:采用进行集成测试,确保各个组件之间的协作正常,对系统进行全面的测试。
- 测试人员:安排专业的测试人员进行测试,确保测试的准确性和公正性。
- 测试进度:制定详细的测试进度计划,确保测试工作按时完成。
5.3 测试用例
5.3.1 登录模块测试用例
测试用例编号 | 测试步骤 | 预期结果 | 实际结果 | 是否通过 |
1 | 输入正确的用户名和密码 | 登录成功 | 登录成功 | 是 |
2 | 输入错误的用户名 | 登录失败,提示用户名错误 | 登录失败,提示用户名错误 | 是 |
3 | 输入错误的密码 | 登录失败,提示密码错误 | 登录失败,提示密码错误 | 是 |
4 | 输入空的用户名 | 登录失败,提示用户名不能为空 | 登录失败,提示用户名不能为空 | 是 |
5 | 输入空的密码 | 登录失败,提示密码不能为空 | 登录失败,提示密码不能为空 | 是 |
5.3.2 用户注册模块测试用例
测试用例编号 | 测试步骤 | 预期结果 | 实际结果 | 是否通过 |
1 | 输入正确的注册信息 | 注册成功 | 注册成功 | 是 |
2 | 输入已存在的用户名 | 注册失败,提示用户名已存在 | 注册失败,提示用户名已存在 | 是 |
3 | 输入错误的邮箱格式 | 注册失败,提示邮箱格式错误 | 注册失败,提示邮箱格式错误 | 是 |
4 | 输入空的用户名 | 注册失败,提示用户名不能为空 | 注册失败,提示用户名不能为空 | 是 |
5 | 输入空的密码 | 注册失败,提示密码不能为空 | 注册失败,提示密码不能为空 | 是 |
5.3.3 订单管理模块测试用例
测试用例编号 | 测试步骤 | 预期结果 | 实际结果 | 是否通过 |
1 | 下单 | 下单成功,生成订单号 | 下单成功,生成订单号 | 是 |
2 | 支付订单 | 支付成功,订单状态更新为已支付 | 支付成功,订单状态更新为已支付 | 是 |
3 | 取消订单 | 取消成功,订单状态更新为已取消 | 取消成功,订单状态更新为已取消 | 是 |
4 | 查询订单 | 查询成功,显示订单信息 | 查询成功,显示订单信息 | 是 |
5 | 批量导出订单 | 导出成功 | 导出成功 | 是 |
5.3.4 购物车管理模块测试用例
测试用例编号 | 测试步骤 | 预期结果 | 实际结果 | 是否通过 |
1 | 添加商品到购物车 | 添加成功 | 添加成功 | 是 |
2 | 修改购物车中商品的数量 | 修改成功 | 修改成功 | 是 |
3 | 删除购物车中商品 | 删除成功 | 删除成功 | 是 |
4 | 结算购物车 | 结算成功,生成订单 | 结算成功,生成订单 | 是 |
5 | 清空购物车 | 清空成功 | 清空成功 | 是 |
5.4 测试结论
通过对整个系统展开全面且细致的测试工作,我们成功地发现了一系列的问题以及存在的缺陷,并且随即就及时地进行了相关的修复以及改进操作。历经反复多次的测试流程之后,我们坚定地认为,系统在功能、性能、安全性等诸多方面均已符合预先的设计要求,完全能够投入到实际的使用当中。
在进行测试的具体过程当中,我们同时也察觉到了一些不足之处,像是系统的响应速度在某些情况下还有待进一步地提高。针对这一情况,我们将采取以下具体优化措施来提升系统响应速度:
首先,对数据库进行优化,包括建立适当的索引、优化查询语句、定期清理冗余数据等,以加快数据的读取和存储速度;其次,采用缓存技术,将经常使用的数据缓存起来,减少对数据库的重复访问;再者,对网络通信进行优化,提高数据传输效率,例如调整网络参数、采用更高效的传输协议等;然后,对系统的代码进行重构和优化,去除冗余代码,提高代码的执行效率;另外,采用分布式架构,将系统的负载分散到多个服务器上,以应对高并发的情况;同时,对服务器的硬件配置进行升级,如增加内存、提升 CPU 性能等;最后,建立性能监控机制,实时监测系统的性能状况,以便及时发现问题并进行调整。
本系统是通过全面且深入的市场调研,紧密依据实际的各种要求而最终决定采用 SSM 框架、MySQL 数据库以及 Tomcat 服务器等先进技术。该系统主要针对传统面包销售方式中所存在的诸如信息严重不对称、购物体验不佳等突出问题进行了细致分析,进而精心设计了包括用户注册、登录、浏览商品、购买商品等在内的一系列重要功能,成功地实现了面包的在线购买和销售模式。
本系统历经了严格的功能测试,切实实现了用户注册、登录、浏览商品、购买商品等关键功能,其运行效果呈现出良好的态势。其中,用户注册的流程清晰简洁、明了易懂,购买的流程更是顺畅无阻且高效快捷,商家能够极为方便且迅速地发布产品以及对订单进行科学管理。而且系统的交互界面十分友好,极大地提升了用户的体验感受,与此同时,数据的存储也稳定可靠,有力地确保了信息的安全性。
本系统的显著创新之处在于成功采用了 SSM 框架,达成了前后端的有效分离,大幅度提高了系统整体的可维护性以及可扩展性。与此同时,本系统还充分运用了 MySQL 数据库,这显著提高了系统的性能以及数据访问的效率。
然而,本系统仍然存在一些不足之处,主要表现为系统的功能尚不够完善,还需要进一步地进行优化和改进。例如,系统当前的搜索功能还不够强大,在搜索的准确性和效率方面还有待进一步提高。同时,系统的安全性也需要进一步强化,还需要采取更为有效的安全措施,以切实确保用户的信息安全。
针对本系统所存在的这些不足,我们提出了以下一些具有完善可能性的方向:进一步深度优化系统的功能,全力提高系统的性能以及用户的实际体验;切实加强系统的安全性,积极采取更为有力有效的安全措施,全方位确保用户的信息安全;进一步全面完善系统的搜索功能,显著提高搜索的准确性和效率;适当增加系统的社交功能,有效提高用户的参与度以及对平台的粘性;大力加强系统的数据分析功能,从而为商家提供更为精准的营销策略和决策支持依据。
总之,本系统的成功开发和有效实现,为用户提供了一个极其便捷、高效的面包销售平台,同时也为商家提供了一个高效、实用的管理工具,具有极为重要的现实意义和显著价值。而且,本系统的开发和实现过程,也为我们在今后的学习和工作中提供了无比宝贵的经验和深刻的启示。
参考文献
[1] 董卫. 基于 Java Web 的毕业设计选题系统设计与实现.《科技资讯》, 2022.
[2] 张烈超. 典型 Java Web 开发框架模型的研究.《武汉交通职业学院学报》, 2021.
[3] 杨燕, 张洋. SSM 框架在 Web 应用开发中的研究与实施, 2019.
[4] 王燕, 李军. 基于 SSM 框架的在线考试系统设计与实现, 2020.
[5] 刘红, 陈强. 基于 SSM 框架的网页酒店管理系统开发, 2022.
[6] 张雷, 王华. SSM 框架在企业 Web 应用开发中的性能评估, 2023.
[7] 徐伟, 赵亮. 基于 SSM 框架的电子商务平台应用实证研究, 2024.
[8] 张鹏, 李梅. 基于 SSM 框架的电商购物系统的设计与实现.《电脑知识与技术》, 2022, 18(21): 116-118.
[9] 陈燕, 李华. Java Web 开发中 Spring、Spring MVC 和 MyBatis 整合研究[C]. 2021 年智能计算与数据科学国际会议(ICIDC), 2021.
[10] 王磊, 刘勇. SSM 框架在 Java Web 开发中的应用[C]. 2022 年计算机科学与应用工程国际会议(CSAE), 2022.
[11]Zhang, L., & Wang, H. Performance Evaluation of SSM Framework in Developing Enterprise Web Applications[J]. 2023.
相关关键代码
@WebServlet(name = "goods_detail",urlPatterns = "/goods_detail")
public class GoodsDetailServlet extends HttpServlet {
private GoodsService gService = new GoodsService();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int id = Integer.parseInt(request.getParameter("id"));
Goods g = gService.getGoodsById(id);
request.setAttribute("g", g);
request.getRequestDispatcher("/goods_detail.jsp").forward(request, response);
}
}
@WebServlet(name = "goods_buy",urlPatterns = "/goods_buy")
public class GoodsBuyServlet extends HttpServlet {
private GoodsService gService = new GoodsService();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Order o = null;
if(request.getSession().getAttribute("order") != null) {
o = (Order) request.getSession().getAttribute("order");
}else {
o = new Order();
request.getSession().setAttribute("order", o);
}
int goodsid = Integer.parseInt(request.getParameter("goodsid"));
Goods goods = gService.getGoodsById(goodsid);
if(goods.getStock()>0) {
o.addGoods(goods);
response.getWriter().print("ok");
}else {
response.getWriter().print("fail");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
@WebServlet(name = "goods_buy",urlPatterns = "/goods_buy")
public class GoodsBuyServlet extends HttpServlet {
private GoodsService gService = new GoodsService();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Order o = null;
if(request.getSession().getAttribute("order") != null) {
o = (Order) request.getSession().getAttribute("order");
}else {
o = new Order();
request.getSession().setAttribute("order", o);
}
int goodsid = Integer.parseInt(request.getParameter("goodsid"));
Goods goods = gService.getGoodsById(goodsid);
if(goods.getStock()>0) {
o.addGoods(goods);
response.getWriter().print("ok");
}else {
response.getWriter().print("fail");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
public class GoodsDao {
//select g.id,g.name,g.cover,g.price,t.name typename from recommend r,goods g,type t where type=2 and r.goods_id=g.id and g.type_id=t.id
public List<Map<String,Object>> getGoodsList(int recommendType) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql="select g.id,g.name,g.cover,g.price,t.name typename from recommend r,goods g,type t where type=? and r.goods_id=g.id and g.type_id=t.id";
return r.query(sql, new MapListHandler(),recommendType);
}
public List<Map<String,Object>> getScrollGood()throws SQLException{
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
// String sql="select g.id,g.name,g.cover,g.price from recommend r,goods g where type=1 and r.goods_id=g.id";
// return r.query(sql, new MapHandler());
String sql="select g.id,g.name,g.cover,g.price from recommend r,goods g where r.goods_id=g.id";
return r.query(sql, new MapListHandler());
}
public List<Goods> selectGoodsByTypeID(int typeID,int pageNumber,int pageSize) throws SQLException {
if(typeID==0)
{
String sql="select * from goods limit ? , ?";
QueryRunner r=new QueryRunner(DataSourceUtils.getDataSource());
return r.query(sql,new BeanListHandler<Goods>(Goods.class),(pageNumber-1)*pageSize,pageSize);
}
else
{
String sql="select * from goods where type_id=? limit ? , ?";
QueryRunner r=new QueryRunner(DataSourceUtils.getDataSource());
return r.query(sql,new BeanListHandler<Goods>(Goods.class),typeID,(pageNumber-1)*pageSize,pageSize);
}
}
public int getCountOfGoodsByTypeID(int typeID) throws SQLException {
String sql="";
QueryRunner r=new QueryRunner(DataSourceUtils.getDataSource());
if(typeID==0)
{
sql="select count(*) from goods";
return r.query(sql,new ScalarHandler<Long>()).intValue();
}
else
{
sql="select count(*) from goods where type_id=?";
return r.query(sql,new ScalarHandler<Long>(),typeID).intValue();
}
}
public List<Goods> selectGoodsbyRecommend(int type,int pageNumber,int pageSize) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
if(type==0) {
//当不添加推荐类型限制的时候
String sql = " select g.id,g.name,g.cover,g.image1,g.image2,g.intro,g.price,g.stock,t.name typename from goods g,type t where g.type_id=t.id order by g.id limit ?,?";
return r.query(sql, new BeanListHandler<Goods>(Goods.class),(pageNumber-1)*pageSize,pageSize);
}
String sql = " select g.id,g.name,g.cover,g.image1,g.image2,g.intro,g.price,g.stock,t.name typename from goods g,recommend r,type t where g.id=r.goods_id and g.type_id=t.id and r.type=? order by g.id limit ?,?";
return r.query(sql, new BeanListHandler<Goods>(Goods.class),type,(pageNumber-1)*pageSize,pageSize);
}
public int getRecommendCountOfGoodsByTypeID(int type) throws SQLException {
if(type==0)return getCountOfGoodsByTypeID(0);
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select count(*) from recommend where type=?";
return r.query(sql, new ScalarHandler<Long>(),type).intValue();
}
public Goods getGoodsById(int id) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select g.id,g.name,g.cover,g.image1,g.image2,g.price,g.intro,g.stock,t.id typeid,t.name typename from goods g,type t where g.id = ? and g.type_id=t.id";
return r.query(sql, new BeanHandler<Goods>(Goods.class),id);
}
public int getSearchCount(String keyword) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select count(*) from goods where name like ?";
return r.query(sql, new ScalarHandler<Long>(),"%"+keyword+"%").intValue();
}
public List<Goods> selectSearchGoods(String keyword, int pageNumber, int pageSize) throws SQLException{
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select * from goods where name like ? limit ?,?";
return r.query(sql, new BeanListHandler<Goods>(Goods.class),"%"+keyword+"%",(pageNumber-1)*pageSize,pageSize);
}
public boolean isScroll(Goods g) throws SQLException {
return isRecommend(g, 1);
}
public boolean isHot(Goods g) throws SQLException {
return isRecommend(g, 2);
}
public boolean isNew(Goods g) throws SQLException {
return isRecommend(g, 3);
}
private boolean isRecommend(Goods g,int type) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select * from recommend where type=? and goods_id=?";
Recommend recommend = r.query(sql, new BeanHandler<Recommend>(Recommend.class),type,g.getId());
if(recommend==null) {
return false;
}else {
return true;
}
}
public void addRecommend(int id,int type) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "insert into recommend(type,goods_id) values(?,?)";
r.update(sql,type,id);
}
public void removeRecommend(int id,int type) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "delete from recommend where type=? and goods_id=?";
r.update(sql,type,id);
}
public void insert(Goods g) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "insert into goods(name,cover,image1,image2,price,intro,stock,type_id) values(?,?,?,?,?,?,?,?)";
r.update(sql,g.getName(),g.getCover(),g.getImage1(),g.getImage2(),g.getPrice(),g.getIntro(),g.getStock(),g.getType().getId());
}
public void update(Goods g) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "update goods set name=?,cover=?,image1=?,image2=?,price=?,intro=?,stock=?,type_id=? where id=?";
r.update(sql,g.getName(),g.getCover(),g.getImage1(),g.getImage2(),g.getPrice(),g.getIntro(),g.getStock(),g.getType().getId(),g.getId());
}
public void delete(int id) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "delete from goods where id = ?";
r.update(sql,id);
}
}
public class GoodsDao {
//select g.id,g.name,g.cover,g.price,t.name typename from recommend r,goods g,type t where type=2 and r.goods_id=g.id and g.type_id=t.id
public List<Map<String,Object>> getGoodsList(int recommendType) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql="select g.id,g.name,g.cover,g.price,t.name typename from recommend r,goods g,type t where type=? and r.goods_id=g.id and g.type_id=t.id";
return r.query(sql, new MapListHandler(),recommendType);
}
public List<Map<String,Object>> getScrollGood()throws SQLException{
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
// String sql="select g.id,g.name,g.cover,g.price from recommend r,goods g where type=1 and r.goods_id=g.id";
// return r.query(sql, new MapHandler());
String sql="select g.id,g.name,g.cover,g.price from recommend r,goods g where r.goods_id=g.id";
return r.query(sql, new MapListHandler());
}
public List<Goods> selectGoodsByTypeID(int typeID,int pageNumber,int pageSize) throws SQLException {
if(typeID==0)
{
String sql="select * from goods limit ? , ?";
QueryRunner r=new QueryRunner(DataSourceUtils.getDataSource());
return r.query(sql,new BeanListHandler<Goods>(Goods.class),(pageNumber-1)*pageSize,pageSize);
}
else
{
String sql="select * from goods where type_id=? limit ? , ?";
QueryRunner r=new QueryRunner(DataSourceUtils.getDataSource());
return r.query(sql,new BeanListHandler<Goods>(Goods.class),typeID,(pageNumber-1)*pageSize,pageSize);
}
}
public int getCountOfGoodsByTypeID(int typeID) throws SQLException {
String sql="";
QueryRunner r=new QueryRunner(DataSourceUtils.getDataSource());
if(typeID==0)
{
sql="select count(*) from goods";
return r.query(sql,new ScalarHandler<Long>()).intValue();
}
else
{
sql="select count(*) from goods where type_id=?";
return r.query(sql,new ScalarHandler<Long>(),typeID).intValue();
}
}
public List<Goods> selectGoodsbyRecommend(int type,int pageNumber,int pageSize) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
if(type==0) {
//当不添加推荐类型限制的时候
String sql = " select g.id,g.name,g.cover,g.image1,g.image2,g.intro,g.price,g.stock,t.name typename from goods g,type t where g.type_id=t.id order by g.id limit ?,?";
return r.query(sql, new BeanListHandler<Goods>(Goods.class),(pageNumber-1)*pageSize,pageSize);
}
String sql = " select g.id,g.name,g.cover,g.image1,g.image2,g.intro,g.price,g.stock,t.name typename from goods g,recommend r,type t where g.id=r.goods_id and g.type_id=t.id and r.type=? order by g.id limit ?,?";
return r.query(sql, new BeanListHandler<Goods>(Goods.class),type,(pageNumber-1)*pageSize,pageSize);
}
public int getRecommendCountOfGoodsByTypeID(int type) throws SQLException {
if(type==0)return getCountOfGoodsByTypeID(0);
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select count(*) from recommend where type=?";
return r.query(sql, new ScalarHandler<Long>(),type).intValue();
}
public Goods getGoodsById(int id) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select g.id,g.name,g.cover,g.image1,g.image2,g.price,g.intro,g.stock,t.id typeid,t.name typename from goods g,type t where g.id = ? and g.type_id=t.id";
return r.query(sql, new BeanHandler<Goods>(Goods.class),id);
}
public int getSearchCount(String keyword) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select count(*) from goods where name like ?";
return r.query(sql, new ScalarHandler<Long>(),"%"+keyword+"%").intValue();
}
public List<Goods> selectSearchGoods(String keyword, int pageNumber, int pageSize) throws SQLException{
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select * from goods where name like ? limit ?,?";
return r.query(sql, new BeanListHandler<Goods>(Goods.class),"%"+keyword+"%",(pageNumber-1)*pageSize,pageSize);
}
public boolean isScroll(Goods g) throws SQLException {
return isRecommend(g, 1);
}
public boolean isHot(Goods g) throws SQLException {
return isRecommend(g, 2);
}
public boolean isNew(Goods g) throws SQLException {
return isRecommend(g, 3);
}
private boolean isRecommend(Goods g,int type) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select * from recommend where type=? and goods_id=?";
Recommend recommend = r.query(sql, new BeanHandler<Recommend>(Recommend.class),type,g.getId());
if(recommend==null) {
return false;
}else {
return true;
}
}
public void addRecommend(int id,int type) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "insert into recommend(type,goods_id) values(?,?)";
r.update(sql,type,id);
}
public void removeRecommend(int id,int type) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "delete from recommend where type=? and goods_id=?";
r.update(sql,type,id);
}
public void insert(Goods g) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "insert into goods(name,cover,image1,image2,price,intro,stock,type_id) values(?,?,?,?,?,?,?,?)";
r.update(sql,g.getName(),g.getCover(),g.getImage1(),g.getImage2(),g.getPrice(),g.getIntro(),g.getStock(),g.getType().getId());
}
public void update(Goods g) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "update goods set name=?,cover=?,image1=?,image2=?,price=?,intro=?,stock=?,type_id=? where id=?";
r.update(sql,g.getName(),g.getCover(),g.getImage1(),g.getImage2(),g.getPrice(),g.getIntro(),g.getStock(),g.getType().getId(),g.getId());
}
public void delete(int id) throws SQLException {
QueryRunner r = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "delete from goods where id = ?";
r.update(sql,id);
}
}