基于Hadoop的物品租赁系统的设计与实现 9349a--论文

@TOC

springboot344基于Hadoop的物品租赁系统的设计与实现 9349a--论文

绪 论

1.1开发背景

随着网络的飞速发展,网络技术的应用越来越广泛,而信息技术的飞速发展,计算机管理系统的优势也逐渐体现出来,大量的计算机电子信息已经进入千家万户。物品租赁系统已跟随信息时代的重要代表,由于涉及的数据量大,以往人工管理已难以维护,因此采用信息技术进行管理。计算机系统管理模式代替了人工管理的方式,比以往人工管理的方式,采用计算机使物品租赁系统查询方便,信息准确性高,降低成本,提高效率,本系统的开发主要以物品租赁为对象,根据功能需求开发信息系统。

1.2开发目的与意义

1.2.1开发目的

近年来,物品租赁管理发展迅猛,在拥有广阔发展前景的同时,也面临着许许多多的问题。

我国线上管理起步晚,但发展快速,从业人员激增,多见于传统管理模式,管理体系不规范、不标准。且企业虽然多但规模普遍偏小,很难在市场形成有效的诚信经营的形象与品牌。

我国目前存在的物品租赁管理监管制度缺失、市场监管不规范等问题。由于缺乏专业人士的素质和服务品质的明确测量,导致物品租赁管理内部鱼龙混杂,难以获得顾客的信赖。

物品租赁系统是电子、信息技术相结合,是一种必然的发展趋势。以互联网为基础,以服务于广大用户为目的,发展整体优势,扩大规模,提升服务质量,提高物品租赁的管理效率。物品租赁系统实现了物品租赁管理向现代化和网络化的转型,为管理决策和控制提供保障,这是物品租赁管理发展中里程碑式的转型。

1.2.2开发意义

系统的建设大大节省了管理成本和人工成本,增强用户信息安全建设,是实现管理现代化的有效途径,同时促进管理上更加专业化和网络化。

对于用户来说,建立物品租赁管理体系不仅丰富了物品租赁管理的商业模式,还能刺激各部门进行自我改革,促使其专业性和规范性的提高。这是物品租赁管理良性循环发展的里程碑。

1.3本文研究内容

首先,要掌握有关系统的基本理论,如 Java技术、Hadoop平台、MySQL数据库等,并对其进行全面的分析。为了给系统打下坚实的技术基础,本系统采用了 MySQL数据库的架构,在开始这项工作前,首先要设计好要用到的数据库表。该系统的使用者有两类:管理员和用户,主要功能包括个人信息修改,对用户管理、公告类型管理、公告信息管理、物品类别管理、物品信息管理、物品租赁管理、物品归还管理、评价信息管理、物品出租管理、系统管理等功能。

2开发技术

2.1 Java介绍

Java语言正式诞生于1995年,是由SUN公司开发研制的OAK语言发展而来的。Java语言继承了OAK语言可跨平台运行的特点,融合了面向对象编程的风格,JAVA以其独有的开放性、跨平台性和面向网络的交互性席卷全球,以其安全性、易用性和开发周期短的特点,迅速从最初的编程语言发展成为全球第一大软件开发平台.广受时下程序开发人员的好评[3]。后来又为了丰富Java语言的用途,提高生产力,将它进行设计和改造,以满足更多地开发和应用场景。经过改造后,它可以满足移动端开发,桌面应用开发和企业级应用开发。Java语言还为用户提供了丰富的类库,在掌握它的特性后,学习特定的工具包,就可以满足很多场景的开发。现在来看,Java语言在这三种平台的应用开发中,都占据了举足轻重的地位。

与其他语言相比,Java语言具有很多的优点,Java语言简单比较容易理解,有c语言的基础很容易就可以学会,中文和英文的学习资源也比较多,而且Java语言经过许多年的沉淀发展,逐渐演变出很多成熟的框架技术。企业还还可以封装自己的框架,让开发变得简单。它还具有跨平台性较其他语言这一个最大的优势,这意味着它只需要一次编译就可以运行在其他平台上。

2.2 Hadoop介绍

Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。

Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,而MapReduce则为海量的数据提供了计算。主要有以下优点:

(1)高可靠性。Hadoop按位存储和处理数据的能力值得人们信赖。

(2)高扩展性。Hadoop是在可用的计算机集簇间分配数据并完成计算任务的,这些集簇可以方便地扩展到数以千计的节点中。

(3)高效性。Hadoop能够在节点之间动态地移动数据,并保证各个节点的动态平衡,因此处理速度非常快。高容错性。Hadoop能够自动保存数据的多个副本,并且能够自动将失败的任务重新分配。

(4)低成本。与一体机、商用数据仓库以及QlikView、Yonghong Z-Suite等数据集市相比,hadoop是开源的,项目的软件成本因此会大大降低。

2.3 Scrapy介绍

Scrapy是一个抓取系统数据和提取结构化数据的框架,它可以应用在广泛的应用中:Scrapy通常用于一系列应用,包括数据挖掘、信息处理或存储历史数据。使用Scrapy框架实现一个爬虫程序通常非常简单,抓取给定系统的内容或图像。

虽然Scrapy是为屏幕抓取(或者更准确地说是网页抓取)而设计的,但它也可以用于访问api以提取数据。

2.4 MySql简介

它以其优秀的性能、强大的功能以及良好的扩展性受到越来越多的关注。目前,国内已经有很多企业采用了该系统进行管理,取得了很好的效果。随着网络技术的发展。MySQL是最流行的关系型数据库管理系统之一,我的SQL是WEB应用应用程序中最好的RDBMS(Relational Database Management System)应用程序之一。MySQL是一个关系型数据库管理系统,在这个系统中,关系数据库将数据存储在不同的表中,而不是全部存储在一个大仓库中,从而提高了速度和灵活性。

在信息管理中,对于数据库的应用也日益广泛,不仅有效对信息管理的质量进行了提升,也进一步提升了信息管理的安全性,让信息管理更加高效。SQL Server是一种关系数据库管理系统,它能提供超大型系统所需的数据库服务。大型服务器可能有成千上万的用户同时连接到 SQL Server实例。

我的SQL使用的SQL语言是访问数据库最常用的标准化语言。它可以通过简单操作实现复杂查询功能,并且具有良好的可扩展性和可维护性等优点。随着网络技术的不断发展,ASP.NET成为目前互联网上最为流行的Web应用之一。MySQL软件采用双授权政策,分为社区版版本和商业版。由于其体积小,速度快,总体拥有成本低,特别是开放源码,MySQL用作中小型网站发展的网站数据库。

2.5 SPRINGBOOT框架

Spring Boot是由Pivotal的开发团队在2013年开发的一个免费、轻量级、开源的系统框架。SpringBoot的主要设计思想是约定大于配置,因此SpringBoot在设计时几乎达到零配置。SpringBoot集成了业界的开源框架。

SpringBoot是一个非常强大的后台框架,因为SpringBoot的开发基本上不需要写配置文件,所以利用SpringBoot来构建网站的后台环境,在SpringBoot的YML配置文件中写项目启动端口,项目就可以启动了。项目的Java和静态文件由SpringBoot管理。

2.6 B/S架构

B/S(Browser/Server,浏览器/服务器)模式,是一种架构模式,属于WEB发展后的所出现的一种网络构造,而WEB又是主要的浏览器应用商品软件。B/S架构模式不仅将系统的重新开发、维修及利用等简单化,更将其重点放到了服务器上。它使客户端得到了统一,服务器上集中了系统功能的最关键部分。

B/S架构的工作模式是浏览器发出请求后服务器进行相应的响应。Internet上文本、图片、动画等信息主要由Web服务器产生,而用户主要是通过浏览器访问这些信息。在Web服务器上下载程序时,如果在下载过程中遇到某些与数据库相关的指令,可以将这些指令交给数据库服务器来解释、进行执行。B/S架构工作流程图如2-1所示:

图2-1 B/S架构工作流程图

2.7 Tomcat简介

Tomcat是Java Servlet,JavaServerPages,Java Expression Language和JavaWebSocket(Java EE)技术的开源实现。因为可以通过HTTP提供HTML页面等静态内容的请求访问,所以是一个WEB服务器。

Tomcat 目录结构作用如表2-1所示

表2-1 tomcat目录结构表

序号 目录名称 作用
1 bin 存放启动和关闭tomcat脚本
2 conf 存放不同的配置文件(server.xml和web.xml)
3 doc 存放Tomcat文档
4 lib 存放Tomcat运行需要的库文件(JARS)
5 logs 存放Tomcat执行时的LOG文件
6 src 存放Tomcat的源代码
7 webapps Tomcat的主要Web发布目录(包括应用程序示例)
8 work 存放jsp编译后产生的class文件

3系统分析

需求分析是研发人员经过调研和分析后准确理解客户需求,确定系统需要具备的功能。然后对系统进行可行性和功能分析,确保符合物品租赁系统的实现价值,对后续的系统创建有显著的帮助。

3.1系统可行性分析

3.1.1技术可行性分析

基于B/S架构开发的物品租赁系统,技术方面应用了目前市面上比较主流的web三大框架,分别是Spring、Hibernate、struts2,数据库采用mysql,tomcat作为服务器,这些技术非常的成熟,在市面上有非常多成熟使用的案例,从技术角度是没有问题的,并且在学校的学习中对于这些技术就会有了一定的掌握,开发过类似的项目。

3.1.2经济可行性分析

物品租赁系统大都是区域性的管理,系统中维护的范围不会非常大,因此数据并发量不会非常高,在数据并发量不是很高的情况下,系统的资源配置相对较低,用户所需要的客户端普通的电脑即可胜任。并且物品租赁的管理系统还会提高效率减少纸质物品的使用,节约纸质资源。避免很多的人力消耗和资源浪费。从系统的开发角度分析,此次项目的开发软件全部都是开源且免费的。不需要在开发中投入经济成本,只需要专注于开发的内容即可,不会产生相应的开发费用。系统稳定使用后系统不会有过多的运维成本,投入使用后会在实际工作中发挥出重要的作用。

3.1.3法律可行性分析

物品租赁系统是自己独立设计的,该系统是本人开发出来做毕业设计之用,并不会侵犯他人、集体和国家的利益。该系统使用正版软件开发,所有参考资料都是正规网站查询分析得出,开发的技术完全是开源免费的工具,百分百遵守国家法律法规。不会出现任何违反国家的政策和法律的。

3.2系统性能分析

  1. 系统安全性

物品租赁系统中,系统的安全性要有一定的保障,不仅要保证系统数据存储足够安全,还要保障数据传输过程安全,还要保证对用户权限管理是合理的。保证一些意外情况发生,导致系统数据缺损时,会有历史数据备份对数据进行还原。

  1. 可维护性和适应性

世界是在不断进步的,互联网也在不断发展,随着行业发展,对物品租赁系统可能会产生新的需求,好的系统应该具有可扩展性,无论在现在还是未来,都能够满足用户需求,可以长期使用本物品租赁系统。

  1. 可靠性

物品租赁管理在发展进步,影院会越做越大,到时候系统的访问量就会比现在多很多,物品租赁系统要足够可靠,能够在并发量高的情况下,依旧保持优越的运行速度、容错能力。

3.3功能需求分析

系统的目标是为管理员和用户搭建一个网上沟通平台,保证双方的安全,并使双方的利益最大化。

3.3.1管理员需求分析

管理员端的功能主要是开放给系统的管理人员使用,能够对用户的进行管理,包括对他们的账号管理、对用户管理、公告类型管理、公告信息管理、物品类别管理、物品信息管理、物品租赁管理、物品归还管理、评价信息管理、物品出租管理、系统管理进行查看,修改和删除等,对系统整体运行情况进行了解。管理员用例分析图,如图3-1所示。

图3-1管理员用例分析图

3.3.2用户需求分析

用户的功能主要是对个人中心、物品租赁管理、物品归还管理、评价信息管理、我的收藏管理进行详细操作等。用户用例分析图,如图3-2所示。

图3-2用户用例分析图

3.4系统流程分析

在本系统,非本系统的用户要想进行物品租赁管理就要注册本系统,登录时需要填写相应的资料,如有使用者,则会显示使用者名称已经存在,请再次键入使用者名称的提示框,若使用者不存在,则填写密码、确认密码等资料,并由系统判定密码与确认密码相符,确认无误后,填写使用者所填写的资料,即可进行登记。而且,为了保证系统的安全,只有在登录了本系统以后,才能进入系统后台操作。该系统的工作流程见图3-3。

图3-3 程序流程图

4系统设计

4.1功能模块设计

对本系统进行全面的系统功能的分析,可以得出基于Hadoop的物品租赁系统的功能模块图,如图4-1所示。

图4-1 系统功能模块图

4.2数据库设计

4.2.1数据库设计原则

要学习程序设计,如果你想了解数据库管理系统或根据要求开发的系统接口,你必须创建一个数据库管理系统模型来存储数据。这样,当您在应用程序编程过程中,就不需要将信息加载到操作系统页面,从而提高整个系统的工作效率。信息库管理系统中存储着许多数据,应该说是管理信息系统建设的中心和基础。信息库管理系统还为管理信息系统的建设提供了添加、删除、更改和搜索的操作功能,使管理信息系统建设能够快速查询所需的数据,而不是直接从程序代码中查找。信息库管理系统通过按照特定的方法将信息表的各个组成部分组合起来,准确地组合、分类并构成信息库管理体系。

4.2.2系统E-R图

本毕业设计的E-R图描述了在系统中各个实体之间的联系,以下是对部分主要的关键实体如下:

物品信息实体属性图如下图4-2所示。

图4-2物品信息实体属性图

评价信息实体属性图如下图4-3所示。

图4-3评价信息实体属性图

物品出租实体属性图如下图4-4所示。

图4-4物品出租实体属性图

关于我们实体属性图如下图4-5所示。

图4-5关于我们实体属性图

公告信息实体属性图如下图4-6所示。

图4-6公告信息实体属性图

4.2.3数据库表设计

物品租赁系统的数据以一个个数据表的方式存储在数据库中,这一个个数据表示系统调取数据的基础,在进行系统数据库搭建时,会根据这些设计好的数据表进行优化搭建。系统选用MySQL数据库,对各表进行了详细的说明如下:

表名4-1:物品归还

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
wupinbianhao varchar 200 物品编号
wupinmingcheng varchar 200 物品名称
pinpai varchar 200 品牌
wupinleibie varchar 200 物品类别
wupintupian longtext 4294967295 物品图片
wupinzhuangtai longtext 4294967295 物品状态
wupinshuliang int 归还数量
zujin int 租金
zulinshijian varchar 200 租赁时间
yajin int 押金
zongjiage int 总价格
guihaishijian date 归还时间
wodezhanghao varchar 200 我的账号
wodexingming varchar 200 我的姓名
wodedianhua varchar 200 我的电话
yonghuzhanghao varchar 200 出租账号
yonghuxingming varchar 200 出租姓名
yonghudianhua varchar 200 出租电话
crossuserid bigint 跨表用户id
crossrefid bigint 跨表主键id
ispay varchar 200 是否支付 未支付

表名4-2:用户

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
yonghuzhanghao varchar 200 用户账号
mima varchar 200 密码
yonghuxingming varchar 200 用户姓名
xingbie varchar 200 性别
touxiang longtext 4294967295 头像
yonghudianhua varchar 200 用户电话
shenfenzhenghao varchar 200 身份证号

表名4-3:物品信息评论表

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
refid bigint 关联表id
userid bigint 用户id
avatarurl longtext 4294967295 头像
nickname varchar 200 用户名
content longtext 4294967295 评论内容
reply longtext 4294967295 回复内容

表名4-4:物品租赁

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
wupinbianhao varchar 200 物品编号
wupinmingcheng varchar 200 物品名称
pinpai varchar 200 品牌
wupinleibie varchar 200 物品类别
wupintupian longtext 4294967295 物品图片
xinjiuchengdu varchar 200 新旧程度
zujin int 租金
yajin int 押金
wupinshuliang int 租赁数量
zulinshijian date 租赁时间
wodezhanghao varchar 200 我的账号
wodexingming varchar 200 我的姓名
wodedianhua varchar 200 我的电话
yonghuzhanghao varchar 200 出租账号
yonghuxingming varchar 200 出租姓名
yonghudianhua varchar 200 出租电话
ispay varchar 200 是否支付 未支付

表名4-5:物品出租

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
biaoti varchar 200 标题
fengmian longtext 4294967295 封面
leibie varchar 200 类别
fuwuquyu varchar 200 服务区域
lianxiren varchar 200 联系人
gongsi varchar 200 卖方公司
sjdz varchar 200 商家地址
fatie varchar 200 发帖
laiyuan longtext 4294967295 来源
detail longtext 4294967295 店铺介绍
clicktime datetime 最近点击时间
clicknum int 点击次数 0

表名4-6:配置文件

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
name varchar 100 配置参数名称
value varchar 100 配置参数值

表名4-7:物品信息

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
wupinbianhao varchar 200 物品编号
wupinmingcheng varchar 200 物品名称
pinpai varchar 200 品牌
wupinleibie varchar 200 物品类别
wupintupian longtext 4294967295 物品图片
xinjiuchengdu varchar 200 新旧程度
wupinshuliang int 出租数量
wupinxiangqing longtext 4294967295 物品详情
zujin varchar 200 租金
yajin varchar 200 押金
fabushijian date 发布时间
yonghuzhanghao varchar 200 出租账号
yonghuxingming varchar 200 出租姓名
yonghudianhua varchar 200 出租电话
clicktime datetime 最近点击时间
clicknum int 点击次数 0

表名4-8:用户表

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
username varchar 100 用户名
password varchar 100 密码
role varchar 100 角色 管理员
addtime timestamp 新增时间 CURRENT_TIMESTAMP

表名4-9:关于我们

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
title varchar 200 标题
subtitle varchar 200 副标题
content longtext 4294967295 内容
picture1 longtext 4294967295 图片1
picture2 longtext 4294967295 图片2
picture3 longtext 4294967295 图片3

表名4-10:收藏表

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
userid bigint 用户id
refid bigint 商品id
tablename varchar 200 表名
name varchar 200 名称
picture longtext 4294967295 图片
type varchar 200 类型(1:收藏,21:赞,22:踩,31:竞拍参与,41:关注) 1
inteltype varchar 200 推荐类型
remark varchar 200 备注

表名4-11:评价信息

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
wupinbianhao varchar 200 物品编号
wupinmingcheng varchar 200 物品名称
pinpai varchar 200 品牌
wupinleibie varchar 200 物品类别
wupintupian longtext 4294967295 物品图片
xinyongpingjia varchar 200 信用评价
pingjiashijian datetime 评价时间
wodezhanghao varchar 200 我的账号
wodexingming varchar 200 我的姓名
wodedianhua varchar 200 我的电话
yonghuzhanghao varchar 200 出租账号
yonghuxingming varchar 200 出租姓名
yonghudianhua varchar 200 出租电话

表名4-12:闲置资讯

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
title varchar 200 标题
introduction longtext 4294967295 简介
picture longtext 4294967295 图片
content longtext 4294967295 内容

表名4-13:公告信息

字段名称 类型 长度 字段说明 主键 默认值
id bigint 主键 主键
addtime timestamp 创建时间 CURRENT_TIMESTAMP
biaoti varchar 200 标题
jianjie longtext 4294967295 简介
fengmian longtext 4294967295 封面
neirong longtext 4294967295 内容
faburen varchar 200 发布人
fabushijian datetime 发布时间

5系统实现

在物品租赁系统的生命周期中,经过了系统分析、系统设计等阶段之后,便开始了系统实施阶段。系统的实现主要对管理员和用户功能的实现,通过实现的过程对代码和逻辑进行相应的修改和完善。该模块也是直接面对使用者的,不仅功能要齐全,而且要做到页面美观。

5.1 系统功能模块

当人们打开系统的网址后,首先看到的就是首页界面。在这里,人们能够看到物品租赁系统的导航条,通过导航条进入各功能信息展示页面进行操作。系统首页界面如图5-1所示:

图5-1 系统首页界面

用户注册:在用户注册页面输入用户注册信息进行注册操作,用户注册页面如图5-2所示:

图5-2用户注册页面

物品信息:在物品信息页面的输入栏中输入物品名称和选择物品类别进行查询,可以查看到物品详细信息,并进行租赁、评论或收藏操作;物品信息页面如图5-3所示:

图5-3物品信息详细页面

物品出租:在物品出租页面的输入栏中输入标题、类别和联系人进行查询,可以查看到物品出租详情,并进行收藏操作;物品出租页面如图5-4所示:

图5-4物品出租详细页面

后台管理:用户注册登录系统后,在首页页面点击后台管理可以对个人中心、物品租赁管理、物品归还管理、评价信息管理、我的收藏管理等进行详细操作;用户后台管理页面如图5-5所示:

图5-5用户后台管理详细页面

5.2 管理员功能模块实现

管理员登录,在登录页面正确输入用户名和密码后,点击登录进入操作系统进行操作;如图5-6所示。

图5-6 管理员登录界面

管理员进入主页面,主要功能包括对个人中心、用户管理、公告类型管理、公告信息管理、物品类别管理、物品信息管理、物品租赁管理、物品归还管理、评价信息管理、物品出租管理、系统管理等进行操作。管理员主页面如图5-7所示:

图5-7 管理员主界面

管理员点击用户管理。进入用户页面输入用户账号可以查询,新增或删除用户列表,并根据需要对用户详细信息进行查看详情,修改或删除操作。如图5-8所示:

图5-8用户管理界面

管理员点击公告信息管理。进入公告信息页面输入标题、简介和发布人可以查询,新增或删除公告信息列表,并根据需要对公告详细信息进行查看详情,修改或删除操作。如图5-9所示:

图5-9公告信息管理界面

管理员点击物品信息管理。进入物品信息页面输入物品名称和选择物品类别可以查询或删除物品信息列表,并根据需要对物品详细信息进行查看详情、修改、查看评论或删除操作。如图5-10所示:

图5-10物品信息管理界面

管理员点击物品租赁管理。进入物品租赁页面输入物品名称和物品类别可以查询或删除物品租赁列表,并根据需要对物品租赁详细信息进行查看详情、修改或删除操作。如图5-11所示:

图5-11物品租赁管理界面

管理员点击评价信息管理。进入评价信息页面输入物品名称和物品类别可以查询或删除评价信息列表,并根据需要对评价详细信息进行查看详情、修改或删除操作。如图5-12所示:

图5-12评价信息管理界面

管理员点击物品出租管理。进入物品出租页面输入标题、类别和联系人可以查询、爬取数据、新增或删除物品出租列表,并根据需要对物品出租详细信息进行修改或删除操作。如图5-13所示:

图5-13物品出租管理界面

管理员进行爬取数据后,点击主页面右上角的看板,可以查看到系统简介、用户总数、物品信息总数、物品租赁总数、物品出租总数、类别统计、联系人统计、商家地址统计、发贴统计、租赁数量统计、出租量统计、物品出租等实时的分析图进行可视化管理;如图5-14所示:

图5-14看板界面

管理员点击系统管理,进入系统管理页面可以对轮播图管理、关于我们和系统简介进行详情或修改操作,在闲置资讯页面输入标题可以查询,新增或删除闲置资讯列表,并根据需要对闲置资讯详细信息进行查看详情,修改或删除操作。如图5-15所示:

图5-15系统管理界面

PingjiaxinxiController.java
package com.controller;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;

import com.utils.ValidatorUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.annotation.IgnoreAuth;

import com.entity.PingjiaxinxiEntity;
import com.entity.view.PingjiaxinxiView;

import com.service.PingjiaxinxiService;
import com.service.TokenService;
import com.utils.PageUtils;
import com.utils.R;
import com.utils.MD5Util;
import com.utils.MPUtil;
import com.utils.CommonUtil;
import java.io.IOException;

/**
 * 评价信息
 * 后端接口
 * @author 
 * @email 
 * @date 2023-05-01 12:14:50
 */
@RestController
@RequestMapping("/pingjiaxinxi")
public class PingjiaxinxiController {
    @Autowired
    private PingjiaxinxiService pingjiaxinxiService;


    


    /**
     * 后端列表
     */
    @RequestMapping("/page")
    public R page(@RequestParam Map<String, Object> params,PingjiaxinxiEntity pingjiaxinxi,
		HttpServletRequest request){
        EntityWrapper<PingjiaxinxiEntity> ew = new EntityWrapper<PingjiaxinxiEntity>();

        String tableName = request.getSession().getAttribute("tableName").toString();
        ew.andNew();
        if(tableName.equals("yonghu")) {
            ew.eq("wodezhanghao", (String)request.getSession().getAttribute("username"));
        }
        if(tableName.equals("yonghu")) {
            ew.or();
            ew.eq("yonghuzhanghao", (String)request.getSession().getAttribute("username"));
        }
		PageUtils page = pingjiaxinxiService.queryPage(params, MPUtil.sort(MPUtil.between(MPUtil.likeOrEq(ew, pingjiaxinxi), params), params));

        return R.ok().put("data", page);
    }
    
    /**
     * 前端列表
     */
	@IgnoreAuth
    @RequestMapping("/list")
    public R list(@RequestParam Map<String, Object> params,PingjiaxinxiEntity pingjiaxinxi, 
		HttpServletRequest request){
        EntityWrapper<PingjiaxinxiEntity> ew = new EntityWrapper<PingjiaxinxiEntity>();

		PageUtils page = pingjiaxinxiService.queryPage(params, MPUtil.sort(MPUtil.between(MPUtil.likeOrEq(ew, pingjiaxinxi), params), params));
        return R.ok().put("data", page);
    }

	/**
     * 列表
     */
    @RequestMapping("/lists")
    public R list( PingjiaxinxiEntity pingjiaxinxi){
       	EntityWrapper<PingjiaxinxiEntity> ew = new EntityWrapper<PingjiaxinxiEntity>();
      	ew.allEq(MPUtil.allEQMapPre( pingjiaxinxi, "pingjiaxinxi")); 
        return R.ok().put("data", pingjiaxinxiService.selectListView(ew));
    }

	 /**
     * 查询
     */
    @RequestMapping("/query")
    public R query(PingjiaxinxiEntity pingjiaxinxi){
        EntityWrapper< PingjiaxinxiEntity> ew = new EntityWrapper< PingjiaxinxiEntity>();
 		ew.allEq(MPUtil.allEQMapPre( pingjiaxinxi, "pingjiaxinxi")); 
		PingjiaxinxiView pingjiaxinxiView =  pingjiaxinxiService.selectView(ew);
		return R.ok("查询评价信息成功").put("data", pingjiaxinxiView);
    }
	
    /**
     * 后端详情
     */
    @RequestMapping("/info/{id}")
    public R info(@PathVariable("id") Long id){
        PingjiaxinxiEntity pingjiaxinxi = pingjiaxinxiService.selectById(id);
        return R.ok().put("data", pingjiaxinxi);
    }

    /**
     * 前端详情
     */
	@IgnoreAuth
    @RequestMapping("/detail/{id}")
    public R detail(@PathVariable("id") Long id){
        PingjiaxinxiEntity pingjiaxinxi = pingjiaxinxiService.selectById(id);
        return R.ok().put("data", pingjiaxinxi);
    }
    



    /**
     * 后端保存
     */
    @RequestMapping("/save")
    public R save(@RequestBody PingjiaxinxiEntity pingjiaxinxi, HttpServletRequest request){
    	pingjiaxinxi.setId(new Date().getTime()+new Double(Math.floor(Math.random()*1000)).longValue());
    	//ValidatorUtils.validateEntity(pingjiaxinxi);
        pingjiaxinxiService.insert(pingjiaxinxi);
        return R.ok();
    }
    
    /**
     * 前端保存
     */
    @RequestMapping("/add")
    public R add(@RequestBody PingjiaxinxiEntity pingjiaxinxi, HttpServletRequest request){
    	pingjiaxinxi.setId(new Date().getTime()+new Double(Math.floor(Math.random()*1000)).longValue());
    	//ValidatorUtils.validateEntity(pingjiaxinxi);
        pingjiaxinxiService.insert(pingjiaxinxi);
        return R.ok();
    }



    /**
     * 修改
     */
    @RequestMapping("/update")
    @Transactional
    public R update(@RequestBody PingjiaxinxiEntity pingjiaxinxi, HttpServletRequest request){
        //ValidatorUtils.validateEntity(pingjiaxinxi);
        pingjiaxinxiService.updateById(pingjiaxinxi);//全部更新
        return R.ok();
    }



    

    /**
     * 删除
     */
    @RequestMapping("/delete")
    public R delete(@RequestBody Long[] ids){
        pingjiaxinxiService.deleteBatchIds(Arrays.asList(ids));
        return R.ok();
    }
    
	









}
WupinxinxiServiceImpl.java
package com.service.impl;

import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.List;

import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.utils.PageUtils;
import com.utils.Query;


import com.dao.WupinxinxiDao;
import com.entity.WupinxinxiEntity;
import com.service.WupinxinxiService;
import com.entity.vo.WupinxinxiVO;
import com.entity.view.WupinxinxiView;

@Service("wupinxinxiService")
public class WupinxinxiServiceImpl extends ServiceImpl<WupinxinxiDao, WupinxinxiEntity> implements WupinxinxiService {
	
	
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        Page<WupinxinxiEntity> page = this.selectPage(
                new Query<WupinxinxiEntity>(params).getPage(),
                new EntityWrapper<WupinxinxiEntity>()
        );
        return new PageUtils(page);
    }
    
    @Override
	public PageUtils queryPage(Map<String, Object> params, Wrapper<WupinxinxiEntity> wrapper) {
		  Page<WupinxinxiView> page =new Query<WupinxinxiView>(params).getPage();
	        page.setRecords(baseMapper.selectListView(page,wrapper));
	    	PageUtils pageUtil = new PageUtils(page);
	    	return pageUtil;
 	}
    
    @Override
	public List<WupinxinxiVO> selectListVO(Wrapper<WupinxinxiEntity> wrapper) {
 		return baseMapper.selectListVO(wrapper);
	}
	
	@Override
	public WupinxinxiVO selectVO(Wrapper<WupinxinxiEntity> wrapper) {
 		return baseMapper.selectVO(wrapper);
	}
	
	@Override
	public List<WupinxinxiView> selectListView(Wrapper<WupinxinxiEntity> wrapper) {
		return baseMapper.selectListView(wrapper);
	}

	@Override
	public WupinxinxiView selectView(Wrapper<WupinxinxiEntity> wrapper) {
		return baseMapper.selectView(wrapper);
	}

    @Override
    public List<Map<String, Object>> selectValue(Map<String, Object> params, Wrapper<WupinxinxiEntity> wrapper) {
        return baseMapper.selectValue(params, wrapper);
    }

    @Override
    public List<Map<String, Object>> selectTimeStatValue(Map<String, Object> params, Wrapper<WupinxinxiEntity> wrapper) {
        return baseMapper.selectTimeStatValue(params, wrapper);
    }

    @Override
    public List<Map<String, Object>> selectGroup(Map<String, Object> params, Wrapper<WupinxinxiEntity> wrapper) {
        return baseMapper.selectGroup(params, wrapper);
    }




}
GonggaoxinxiController.java
package com.controller;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;

import com.utils.ValidatorUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.annotation.IgnoreAuth;

import com.entity.GonggaoxinxiEntity;
import com.entity.view.GonggaoxinxiView;

import com.service.GonggaoxinxiService;
import com.service.TokenService;
import com.utils.PageUtils;
import com.utils.R;
import com.utils.MD5Util;
import com.utils.MPUtil;
import com.utils.CommonUtil;
import java.io.IOException;

/**
 * 公告信息
 * 后端接口
 * @author 
 * @email 
 * @date 2023-05-01 12:14:49
 */
@RestController
@RequestMapping("/gonggaoxinxi")
public class GonggaoxinxiController {
    @Autowired
    private GonggaoxinxiService gonggaoxinxiService;


    


    /**
     * 后端列表
     */
    @RequestMapping("/page")
    public R page(@RequestParam Map<String, Object> params,GonggaoxinxiEntity gonggaoxinxi,
		HttpServletRequest request){
        EntityWrapper<GonggaoxinxiEntity> ew = new EntityWrapper<GonggaoxinxiEntity>();

		PageUtils page = gonggaoxinxiService.queryPage(params, MPUtil.sort(MPUtil.between(MPUtil.likeOrEq(ew, gonggaoxinxi), params), params));

        return R.ok().put("data", page);
    }
    
    /**
     * 前端列表
     */
	@IgnoreAuth
    @RequestMapping("/list")
    public R list(@RequestParam Map<String, Object> params,GonggaoxinxiEntity gonggaoxinxi, 
		HttpServletRequest request){
        EntityWrapper<GonggaoxinxiEntity> ew = new EntityWrapper<GonggaoxinxiEntity>();

		PageUtils page = gonggaoxinxiService.queryPage(params, MPUtil.sort(MPUtil.between(MPUtil.likeOrEq(ew, gonggaoxinxi), params), params));
        return R.ok().put("data", page);
    }

	/**
     * 列表
     */
    @RequestMapping("/lists")
    public R list( GonggaoxinxiEntity gonggaoxinxi){
       	EntityWrapper<GonggaoxinxiEntity> ew = new EntityWrapper<GonggaoxinxiEntity>();
      	ew.allEq(MPUtil.allEQMapPre( gonggaoxinxi, "gonggaoxinxi")); 
        return R.ok().put("data", gonggaoxinxiService.selectListView(ew));
    }

	 /**
     * 查询
     */
    @RequestMapping("/query")
    public R query(GonggaoxinxiEntity gonggaoxinxi){
        EntityWrapper< GonggaoxinxiEntity> ew = new EntityWrapper< GonggaoxinxiEntity>();
 		ew.allEq(MPUtil.allEQMapPre( gonggaoxinxi, "gonggaoxinxi")); 
		GonggaoxinxiView gonggaoxinxiView =  gonggaoxinxiService.selectView(ew);
		return R.ok("查询公告信息成功").put("data", gonggaoxinxiView);
    }
	
    /**
     * 后端详情
     */
    @RequestMapping("/info/{id}")
    public R info(@PathVariable("id") Long id){
        GonggaoxinxiEntity gonggaoxinxi = gonggaoxinxiService.selectById(id);
        return R.ok().put("data", gonggaoxinxi);
    }

    /**
     * 前端详情
     */
	@IgnoreAuth
    @RequestMapping("/detail/{id}")
    public R detail(@PathVariable("id") Long id){
        GonggaoxinxiEntity gonggaoxinxi = gonggaoxinxiService.selectById(id);
        return R.ok().put("data", gonggaoxinxi);
    }
    



    /**
     * 后端保存
     */
    @RequestMapping("/save")
    public R save(@RequestBody GonggaoxinxiEntity gonggaoxinxi, HttpServletRequest request){
    	gonggaoxinxi.setId(new Date().getTime()+new Double(Math.floor(Math.random()*1000)).longValue());
    	//ValidatorUtils.validateEntity(gonggaoxinxi);
        gonggaoxinxiService.insert(gonggaoxinxi);
        return R.ok();
    }
    
    /**
     * 前端保存
     */
    @RequestMapping("/add")
    public R add(@RequestBody GonggaoxinxiEntity gonggaoxinxi, HttpServletRequest request){
    	gonggaoxinxi.setId(new Date().getTime()+new Double(Math.floor(Math.random()*1000)).longValue());
    	//ValidatorUtils.validateEntity(gonggaoxinxi);
        gonggaoxinxiService.insert(gonggaoxinxi);
        return R.ok();
    }



    /**
     * 修改
     */
    @RequestMapping("/update")
    @Transactional
    public R update(@RequestBody GonggaoxinxiEntity gonggaoxinxi, HttpServletRequest request){
        //ValidatorUtils.validateEntity(gonggaoxinxi);
        gonggaoxinxiService.updateById(gonggaoxinxi);//全部更新
        return R.ok();
    }



    

    /**
     * 删除
     */
    @RequestMapping("/delete")
    public R delete(@RequestBody Long[] ids){
        gonggaoxinxiService.deleteBatchIds(Arrays.asList(ids));
        return R.ok();
    }
    
	









}
canvas-bg-2.js
export default function canvasBg(){
	var w = window.innerWidth;
	var h = window.innerHeight;
	var ctx = document.getElementById('canvas');
	
	window.addEventListener('load', resize);
	window.addEventListener('resize', resize, false);
	
	function resize() {
		ctx.width = w = window.innerWidth;
		ctx.height = h = window.innerHeight;
	}
	
	resize();
	
	let ctxfr = ctx.getContext('2d');
	
	// min and max radius, radius threshold and percentage of filled circles
	var radMin = 5,
		radMax = 125,
		filledCircle = 60, //percentage of filled circles
		concentricCircle = 30, //percentage of concentric circles
		radThreshold = 25; //IFF special, over this radius concentric, otherwise filled
	
	//min and max speed to move
	var speedMin = 0.3,
		speedMax = 2.5;
	
	//max reachable opacity for every circle and blur effect
	var maxOpacity = 0.6;
	
	//default palette choice
	var colors = ['52,168,83', '117,95,147', '199,108,23', '194,62,55', '0,172,212', '120,120,120'],
		bgColors = ['52,168,83', '117,95,147', '199,108,23', '194,62,55', '0,172,212', '120,120,120'],
		circleBorder = 10,
		backgroundLine = bgColors[0];
	var backgroundMlt = 0.85;
	
	//min distance for links
	var linkDist = Math.min(canvas.width, canvas.height) / 2.4,
		lineBorder = 2.5;
	
	//most importantly: number of overall circles and arrays containing them
	var maxCircles = 12,
		points = [],
		pointsBack = [];
	
	//populating the screen
	for (var i = 0; i < maxCircles * 2; i++) points.push(new Circle());
	for (var i = 0; i < maxCircles; i++) pointsBack.push(new Circle(true));
	
	//experimental vars
	var circleExp = 1,
		circleExpMax = 1.003,
		circleExpMin = 0.997,
		circleExpSp = 0.00004,
		circlePulse = false;
	
	//circle class
	function Circle(background) {
		//if background, it has different rules
		this.background = (background || false);
		this.x = randRange(-canvas.width / 2, canvas.width / 2);
		this.y = randRange(-canvas.height / 2, canvas.height / 2);
		this.radius = background ? hyperRange(radMin, radMax) * backgroundMlt : hyperRange(radMin, radMax);
		this.filled = this.radius < radThreshold ? (randint(0, 100) > filledCircle ? false : 'full') : (randint(0, 100) >
			concentricCircle ? false : 'concentric');
		this.color = background ? bgColors[randint(0, bgColors.length - 1)] : colors[randint(0, colors.length - 1)];
		this.borderColor = background ? bgColors[randint(0, bgColors.length - 1)] : colors[randint(0, colors.length - 1)];
		this.opacity = 0.05;
		this.speed = (background ? randRange(speedMin, speedMax) / backgroundMlt : randRange(speedMin, speedMax)); // * (radMin / this.radius);
		this.speedAngle = Math.random() * 2 * Math.PI;
		this.speedx = Math.cos(this.speedAngle) * this.speed;
		this.speedy = Math.sin(this.speedAngle) * this.speed;
		var spacex = Math.abs((this.x - (this.speedx < 0 ? -1 : 1) * (canvas.width / 2 + this.radius)) / this.speedx),
			spacey = Math.abs((this.y - (this.speedy < 0 ? -1 : 1) * (canvas.height / 2 + this.radius)) / this.speedy);
		this.ttl = Math.min(spacex, spacey);
	};
	
	Circle.prototype.init = function() {
		Circle.call(this, this.background);
	}
	
	//support functions
	//generate random int a<=x<=b
	function randint(a, b) {
		return Math.floor(Math.random() * (b - a + 1) + a);
	}
	//generate random float
	function randRange(a, b) {
		return Math.random() * (b - a) + a;
	}
	//generate random float more likely to be close to a
	function hyperRange(a, b) {
		return Math.random() * Math.random() * Math.random() * (b - a) + a;
	}
	
	//rendering function
	function drawCircle(ctx, circle) {
		//circle.radius *= circleExp;
		var radius = circle.background ? circle.radius *= circleExp : circle.radius /= circleExp;
		ctx.beginPath();
		ctx.arc(circle.x, circle.y, radius * circleExp, 0, 2 * Math.PI, false);
		ctx.lineWidth = Math.max(1, circleBorder * (radMin - circle.radius) / (radMin - radMax));
		ctx.strokeStyle = ['rgba(', circle.borderColor, ',', circle.opacity, ')'].join('');
		if (circle.filled == 'full') {
			ctx.fillStyle = ['rgba(', circle.borderColor, ',', circle.background ? circle.opacity * 0.8 : circle.opacity, ')']
				.join('');
			ctx.fill();
			ctx.lineWidth = 0;
			ctx.strokeStyle = ['rgba(', circle.borderColor, ',', 0, ')'].join('');
		}
		ctx.stroke();
		if (circle.filled == 'concentric') {
			ctx.beginPath();
			ctx.arc(circle.x, circle.y, radius / 2, 0, 2 * Math.PI, false);
			ctx.lineWidth = Math.max(1, circleBorder * (radMin - circle.radius) / (radMin - radMax));
			ctx.strokeStyle = ['rgba(', circle.color, ',', circle.opacity, ')'].join('');
			ctx.stroke();
		}
		circle.x += circle.speedx;
		circle.y += circle.speedy;
		if (circle.opacity < (circle.background ? maxOpacity : 1)) circle.opacity += 0.01;
		circle.ttl--;
	}
	
	//initializing function
	function init() {
		window.requestAnimationFrame(draw);
	}
	
	//rendering function
	function draw() {
	
		if (circlePulse) {
			if (circleExp < circleExpMin || circleExp > circleExpMax) circleExpSp *= -1;
			circleExp += circleExpSp;
		}
	
		ctxfr.globalCompositeOperation = 'destination-over';
		ctxfr.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
	
		ctxfr.save();
		ctxfr.translate(canvas.width / 2, canvas.height / 2);
	
		//function to render each single circle, its connections and to manage its out of boundaries replacement
		function renderPoints(ctx, arr) {
			for (var i = 0; i < arr.length; i++) {
				var circle = arr[i];
				//checking if out of boundaries
				if (circle.ttl < 0) {}
				var xEscape = canvas.width / 2 + circle.radius,
					yEscape = canvas.height / 2 + circle.radius;
				if (circle.ttl < -20) arr[i].init(arr[i].background);
				//if (Math.abs(circle.y) > yEscape || Math.abs(circle.x) > xEscape) arr[i].init(arr[i].background);
				drawCircle(ctx, circle);
			}
			for (var i = 0; i < arr.length - 1; i++) {
				for (var j = i + 1; j < arr.length; j++) {
					var deltax = arr[i].x - arr[j].x;
					var deltay = arr[i].y - arr[j].y;
					var dist = Math.pow(Math.pow(deltax, 2) + Math.pow(deltay, 2), 0.5);
					//if the circles are overlapping, no laser connecting them
					if (dist <= arr[i].radius + arr[j].radius) continue;
					//otherwise we connect them only if the dist is < linkDist
					if (dist < linkDist) {
						var xi = (arr[i].x < arr[j].x ? 1 : -1) * Math.abs(arr[i].radius * deltax / dist);
						var yi = (arr[i].y < arr[j].y ? 1 : -1) * Math.abs(arr[i].radius * deltay / dist);
						var xj = (arr[i].x < arr[j].x ? -1 : 1) * Math.abs(arr[j].radius * deltax / dist);
						var yj = (arr[i].y < arr[j].y ? -1 : 1) * Math.abs(arr[j].radius * deltay / dist);
						ctx.beginPath();
						ctx.moveTo(arr[i].x + xi, arr[i].y + yi);
						ctx.lineTo(arr[j].x + xj, arr[j].y + yj);
						var samecolor = arr[i].color == arr[j].color;
						ctx.strokeStyle = ["rgba(", arr[i].borderColor, ",", Math.min(arr[i].opacity, arr[j].opacity) * ((linkDist -
							dist) / linkDist), ")"].join("");
						ctx.lineWidth = (arr[i].background ? lineBorder * backgroundMlt : lineBorder) * ((linkDist - dist) / linkDist); //*((linkDist-dist)/linkDist);
						ctx.stroke();
					}
				}
			}
		}
	
		renderPoints(ctxfr, points);
		ctxfr.restore();
	
		window.requestAnimationFrame(draw);
	}
	
	init();
}

声明

本博客适用于广泛的学术和教育用途,包括但不限于个人学习、开发设计,产品设计。仅供学习参考,旨在为读者提供深入理解和学术研究的材料。

相关推荐
IT咖啡馆6 分钟前
35K star!生产环境的Java诊断工具,阿里开源神器
java·github
杰克尼10 分钟前
Java五子棋
java·开发语言
March€10 分钟前
常见算法复习
java·算法·排序算法
小松学前端1 小时前
第六章 6.1 字符串常用方法
java·开发语言
启山智软1 小时前
Java微服务商城系统的特点有哪些
java·大数据·开发语言·人工智能·微服务·架构·ux
一撮不知名的呆毛1 小时前
Lambda表达式(Java)
java
RW~1 小时前
Java 远程url文件sha256加密
java·开发语言
林小果12 小时前
建造者模式
java·开发语言·设计模式·建造者模式
吾爱星辰2 小时前
【解密 Kotlin 扩展函数】扩展函数的创建(十六)
java·开发语言·jvm·kotlin
v199498395632 小时前
会议室预约系统源码开发
java