《SOA和Web service技术》
实验及课程期末大作业
2024年 6月 13日
评分标准
序号 | 评分内容 | 得分 |
1 | 文档的规范(40分) 遵照实验和大作业模板进行规范编写。 字体,字号,行距正确;图表格式规范;文字通顺,文本清晰美观。 报告结构完整,能正确使用类图,顺序图,状态图,时序图等图形化手段对系统进行设计与描述。 采用面向服务的思想进行设计,报告中具有服务的架构图,服务之间的调用关系,服务的接口设计等。 | |
2 | 实践期间的表现(10分) 学习态度端正,按时上交完整文档和源码。 | |
3 | 实验以及大作业的完成度与正确性(40分) 完成全部实验;完成每个实验的各项要求。 完成选定的大作业,完成大作业的主要功能。 代码能正确运行。 | |
4 | 其它(10分): 1、根据项目需求合理选择及使用数据库或文件系统;并在报告中正确描述文件系统或数据库的设计与使用。 2、根据项目需求合理选择及使用其他相关技术,并在报告中正确描述相关技术的设计与使用。 3、界面设计完整。 | |
总分: |
实验题目 实验一 创建基于SOAP的Web Service
一、实验目的
1、掌握创建一个基于SOAP的Web服务的基本流程;
2、掌握查看Web服务描述文档的方法;
3、创建客户端使用服务。
二、实验内容和要求
1、使用Java创建一个基于SOAP的Web服务,查看对应的WSDL文档和创建客户端使用服务。
2、学习使用基于JWS的web service和Apache CXF的web service。
三、实验主要仪器设备和材料
1.计算机及操作系统:PC机,Windows xp/7;
2.编辑及运行环境: Eclipse,Tomcat,JDK 1.8
四、实验方法、步骤及结果测试
创建基于JWS的Web Service
1、创建基于JWS的Web Service,命名为JWSof****,其中星号部分用姓名的拼音取代(姓写全拼,名写首字母,如JWSofSongW)。该服务有一个方法string JWSHello**(string),其中星号部分用姓名取代(编码规则同上),该方法接收学号string作为输入,输出个人的基本信息string,如学院,班级,姓名,籍贯。
项目结构:
项目结构截图显示 (说明:在集成开发环境Eclipse,IntelliJ IDEA中将整个工程项目结构截图) | 项目结构说明 (说明: 说明项目中每个文件的作用,包括配置文件等。这一部分不能省略,一定要对截图中内容进行说明。) out 是项目的编译后的输出文件 src 是项目的编写代码的文件 src.WebService是基于JWS的设计的软件包 src.WebService.Client是客户的端的代码文件 src.WebService.JWSHelloZhuZ是方法的接口 src.WebService.JWSHelloZhuZImpl是方法的实现 src.WebService.ServiceMain是服务端的代码文件 |
实现代码及截图:
(说明: 1此处贴上实现的关键代码,并且中需要写上注释说明语句的作用 2 如果有多个文件需要分别说明) //ServiceMain的文件客户端方法发布 Endpoint.publish("http://localhost:8888/WebService" , new JWSHelloZhuZImpl()); | 截图显示 (说明: 截图需要将完整代码截取出来) //ServiceMain文件 //JWSHelloZhuZ文件 //JWSHelloZhuZImpl文件 |
2、服务的部署和配置
(具体的截图内容根据各自采用的实现方案自行截图,可以是代码部署,可以是配置文件部署,也可以是图形对话框部署) |
对截图内容的文字说明:
(这一部分不能省略,一定要对截图中内容进行说明) 采用代码部署的方式,通过EndPoint接口方法进行部署,地址为本地localhost,端口号为8888,发布方法为JWSHellloZhuZImpl类种的所有方法。 |
3、在浏览器中查看该Web服务的描述文档
截图:
结合自己编写的web服务,说明该服务的WSDL文档中各元素与服务之间的对应关系。
definitions>元素:这是WSDL文档的根元素,定义了整个服务的结构。 <message>元素:定义了操作所使用的消息的结构tns:HelloZhuZ。 <portType>元素:描述了服务的操作HelloZhuZ,即可用的方法。 <service>元素:定义了服务的名称JWSHelloZhuZImplService和可访问的端点JWSHelloZhuZImplPort。 |
4、产生客户端代理
截图:
对截图内容的文字说明:
(这一部分不能省略,一定要对截图中内容进行说明) 手动创建客户端代理文件 |
5、创建客户端
截图:
对截图内容的文字说明:
(这一部分不能省略,一定要对截图中内容进行说明) 通过Service.create创建客户端地址为http://localhost:8888/WebService?wsdl,获取端口的JWSHelloZhuZ的方法,最后调用HelloZhuZ方法参数为“3121004801” |
创建基于Apache CXF的web service
1、创建基于Apache CXF的web service,命名为CXFof****,其中星号部分用姓名的拼音取代(姓写全拼,名写首字母,如CXFofSongW)。该服务有一个方法string CXFHello**(string),其中星号部分用姓名取代(编码规则同上),该方法接收学号string作为输入,输出个人的基本信息string,如学院,班级,姓名,籍贯。
项目结构:
项目结构截图显示 (说明:在集成开发环境Eclipse,IntelliJ IDEA中将整个工程项目结构截图) | 项目结构说明 (说明: 说明项目中每个文件的作用,包括配置文件等。这一部分不能省略,一定要对截图中内容进行说明。) idea文件是IDEA工程自带文件 src是项目存放代码文件 taget是存放编译生成文件的文件 pom.xml是项目依赖文件 |
实现代码及截图:
实现代码 (说明: 1此处贴上实现的关键代码,并且中需要写上注释说明语句的作用 2 如果有多个文件需要分别说明) //ServiceMain文件的服务端发布 CXFHelloZhuZImpl serviceImpl = new CXFHelloZhuZImpl();
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
// 设置服务地址
factory.setAddress("http://localhost:8888/CXFofZhuZ");// 设置服务实现类
factory.setServiceBean(serviceImpl);
// 发布服务
factory.create(); | 截图显示 (说明: 截图需要将完整代码截取出来) //ServiceMain文件 //CXFHelloZhuZ文件 // CXFHelloZhuZImpl文件 |
2、服务的部署和配置
截图:
(具体的截图内容根据各自采用的实现方案自行截图,可以是代码部署,可以是配置文件部署,也可以是图形对话框部署) |
对截图内容的文字说明:
(这一部分不能省略,一定要对截图中内容进行说明) 采用代码部署,通过创建JaxWsServerFactoryBeab类来设置地址http://localhost:8888/CXFofZhuZ,并设置方法,并进一步发布。 |
3、在浏览器中查看该Web服务的描述文档
截图:
结合自己编写的web服务,说明该服务的WSDL文档中各元素与服务之间的对应关系。
4、创建客户端
截图:
对截图内容的文字说明:
(这一部分不能省略,一定要对截图中内容进行说明) 使用JaxWsProxyFactoryBean创建客户端代理对象,并创建远程服务的代理对象,然后调用远程服务的方法。 |
实验题目 实验二 创建RESTful服务端和客户端
一、实验目的
掌握使用Jersey框架创建RESTful风格的服务端,并创建客户端程序。
掌握使用spring boot创建RESTful风格的服务端,并创建客户端程序
(二选一)
二、实验内容和要求
1、创建服务端程序resourceof***,其中星号部分用姓名的拼音取代(姓写全拼,名写首字母,如resourceofSongW)。使用post注解资源接口用于增加一个学生的学号,姓名,出生年月等信息;使用get注解资源接口用于获得所有学生的信息;使用get注解的资源接口用于获得指定学号的学生信息。
2、创建客户端程序clientof***,其中星号部分用姓名的拼音取代(姓写全拼,名写首字母,如clientofSongW)。访问服务,实现增加多个学生的信息,并输出所有学生的信息,以及指定学号的学生信息。
3、根据需要创建相应的配置文件以及一些辅助类。
三、实验主要仪器设备和材料
1.计算机及操作系统:PC机,Windows xp/7;
2.编辑及运行环境: Eclipse juno ,Tomcat8.0,JDK 1.8
四、实验方法、步骤及结果测试
1、创建项目,项目命名为 REST***,其中星号部分用姓名的拼音取代(姓写全拼,名写首字母),按个各人习惯建好包。
截图展现项目文件目录结构 | 分别说明文件夹,包,类,配置文件的作用(这一部分不能省略,一定要对截图中内容进行说明) idea为IDEA项目工程配置文件 src代码文件 target为代码编译生成存放文件 |
2、创建服务端程序resourceof***。
截图展现关键代码 //ResourcefZhuZ文件 | 解释(这一部分不能省略,一定要对截图中内容进行说明,特别要列出服务接口的设计) 通过spring boot创建风格的服务端 |
3、(如果有)请给出服务端的辅助类,解释其核心代码。
截图展现关键辅助类及核心代码 | 解释(这一部分不能省略,一定要对截图中内容进行说明) |
4、请给出配置文件代码,并解释核心内容。
截图展现配置文件代码 //StudentControllar文件 | 解释(这一部分不能省略,一定要对截图中内容进行说明) 定义服务接口 @PostMapping("/AddStudents") Post请求,添加student @GetMapping("/GetStudents") Get请求。返回student数组 @GetMapping("/GetAllStudents") Get请求,输出所有student @GetMapping("/GetStudent") Get请求,输出指定的student |
5、在浏览器中访问所写的RESTful资源接口,测试RESTful资源是否能被访问。
截图展现浏览器中的运行结果 | 解释(要求分析浏览器中的URI)(这一部分不能省略,一定要对截图中内容进行说明) 测试GetStudent请求,请求学号为3121004810,数据中没有学号为3121004801的学生,输出结果 |
6、创建客户端程序clientof***。
截图展现关键代码 | 解释(这一部分不能省略,一定要对截图中内容进行说明,特别说明客户端是如何访问到服务方法的) 定义函数 public static void addMStudents 用来添加多个学生 public static void getAllStudents 输出所有学生 public static void getStudent 输出指定学生 |
截图展现运行结果 | 解释(这一部分不能省略,一定要对截图中内容进行说明) 在主函数中添加了两个学生: "3121004801", "XieBF", "2003-04-23"
"3121004805", "吉吉", "2002-08-08" 然后输出两个学生 最后在输出学号为“3121004810”的学生 |
实验题目 实验三 对SOAP消息包的操作
一、实验目的
掌握使用Java提供的handler机制对封装的SOAP消息报进行操作,加深对SOAP的理解。
二、实验内容和要求
1、使用Java提供的handler机制在客户端对SOAP请求包注入header。在实验一建立的web服务的基础上,对客户端产生的SOAP请求包加入header,header有一个子元素student,命名空间为你自己项目使用的命名空间;student有三个子元素sno和sname、time,即学号,姓名和当前系统时间。参考如下形式:
<SOAP-ENV:Header>
<student xmlns="http://hello.soapHandlerEx/">
<sno>001</sno>
<sname>你的名字</sname>
<time>2024/5/10</time>
</student>
</SOAP-ENV:Header>
输出方式可以采用命令行或文本记录。
2、在服务端对SOAP请求包进行解析,获得头部,并展示出头部的信息,展示方式可以自行设计。
三、实验主要仪器设备和材料
1.计算机及操作系统:PC机,Windows xp/7;
2.编辑及运行环境:visual studio 2010,IIS Express;Eclipse,Tomcat,JDK 1.8
四、实验方法、步骤及结果测试
1、展现项目文件目录结构,并解释每个文件的作用。
截图展现项目文件目录结构 | 分别说明文件夹,包,类,配置文件的作用 (这一部分不能省略,一定要对截图中内容进行说明) src.main.java文件是代码的仓库 target存放代码编译生成的文件 |
2、请给出关键服务端类,并解释其核心代码。
截图展现关键服务端的类及核心代码 | 解释: (这一部分不能省略,一定要对截图中内容进行说明) 采用代码部署,通过创建JaxWsServerFactoryBeab类来设置地http://localhost:8888/CXFofXieBF,设置服务端的handler并设置方法,并进一步发布。 |
3、服务端handler的设计
截图展现类及核心代码 | 解释: (这一部分不能省略,一定要对截图中内容进行说明) |
4、服务端配置文件
截图展现类及核心代码 //pom.xml文件 //ServiceMain文件 //ServiceSOAPHandler文件 | 解释: (这一部分不能省略,一定要对截图中内容进行说明) Maven工程的基本工作单元,是一个XML(可扩展标记语言)文件,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖等等。执行任务或目标时,Maven会在当前目录中查找 POM并读取从而获取所需的配置信息执行目标,属于项目级别的配置文件。 |
5、请给出关键客户端类,并解释其核心代码。
截图展现关键客户端的类及核心代码 | 解释 (这一部分不能省略,一定要对截图中内容进行说明) 首先,定义了远程服务的地址,其值为"http://localhost:8888/CXFofZhuZ"。 使用JaxWsProxyFactoryBean创建客户端代理对象。JaxWsProxyFactoryBean是Apache CXF框架提供的一个类,用于创建Web服务的客户端代理。 通过设置serviceClass属性,指定了服务接口的类型,即CXFHelloZhuZ.class。这是远程服务的接口定义。 通过设置address属性,指定了远程服务的地址。 通过factory.getHandlers().add(new InfoHandler()),添加了一个InfoHandler处理器对象。处理器可以在请求和响应之前对消息进行处理,例如添加头部信息。 使用factory.create()创建了远程服务的代理对象service,类型为CXFHelloZhuZ。 调用远程服务的方法HelloZhuZ,并传递参数"3121004805"。 将返回结果存储在result变量中。 最后,打印调用结果到控制台,输出字符串为"调用结果:"后面跟着result的值。 这段代码的主要目的是创建一个客户端代理对象,调用远程服务的方法,并处理返回结果。需要注意的是,这段代码依赖于Apache CXF框架,可能需要在项目中添加相应的依赖库才能正常运行。 |
6、客户端handler的设计
截图展现类及核心代码 | 解释: (这一部分不能省略,一定要对截图中内容进行说明)方法开始时,在控制台打印"接收信息"。 从SOAPMessageContext对象中获取MessageContext.MESSAGE_OUTBOUND_PROPERTY属性的值,并将其赋给outboundProperty变量。 如果outboundProperty为true,则代码继续修改SOAP消息。 从SOAPMessageContext对象中获取SOAP消息。 从SOAP消息中获取SOAP信封和SOAP头部。 如果SOAP头部为null,则向SOAP信封添加一个新的头部。 在控制台打印"添加头部"。 创建一个具有命名空间为"http://localhost:8888/CXFofZhuZ"的限定名称为"student"的新SOAP元素,并将其作为子元素添加到SOAP头部。 创建学生元素的子元素,分别为"sno"、"sname"和"time",并设置它们的文本内容。 使用LocalDateTime类将timeElement设置为当前日期和时间,并使用"yyyy/MM/dd"的格式进行格式化。 如果在代码块的执行过程中发生异常,它将被捕获,并将异常堆栈跟踪打印到控制台。 最后,方法返回true。 |
7、客户端配置文件,并解释核心内容。
截图展现配置文件代码 //pom.xml文件 //Client文件 //InfoHandler文件 | 解释 (这一部分不能省略,一定要对截图中内容进行说明) Maven工程的基本工作单元,是一个XML(可扩展标记语言)文件,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖等等。执行任务或目标时,Maven会在当前目录中查找 POM并读取从而获取所需的配置信息执行目标,属于项目级别的配置文件。 |
8、运行结果并解释
截图展现运行结果(客户端) | 解释 (这一部分不能省略,一定要对截图中内容进行说明) 客户端调用远程服务的方法,打印调用结果 |
截图展现运行结果(服务端) | 解释 (这一部分不能省略,一定要对截图中内容进行说明) 服务端开启,等待客户端调用远程服务的方法,打印客户端信息 |
五、实验中出现的问题及解决方案
六、参考材料
https://my.oschina.net/fhd/blog/222157
https://my.oschina.net/fhd/blog/222324
https://my.oschina.net/fhd/blog/261119
QName,SOAPHeaderElement,SOAPElement相关类和方法的使用
实验题目 实验四 基于Srping boot和Spring cloud构建微服务
一、实验目的
掌握使用Spring Cloud + Spring Boot构建微服务,建立分布式注册中心,进行分布式通信和设置分布式断路器。
二、实验内容和要求
1、具体每个服务完成的任务可以自行选择,设计的服务的关系只需能满足上图的要求,可以是订单服务,用户服务,书籍服务等,不需要使用数据库。
2、每一个服务是一个Spring boot应用程序。将所有服务在注册中心注册(可使用Spring Cloud consul)。
3、根据上述关系进行服务的调用(可使用Spring Cloud Feign)。
4、使用Spring Cloud Ribbon进行负载均衡。
5、使用Hystrix进行断路保护。
6、运行结果的展示要带有个人信息,以避免抄袭。
三、实验主要仪器设备和材料
1.计算机及操作系统:PC机,Windows xp/7;
2.编辑及运行环境: Eclipse,Tomcat,JDK 1.8,IntelliJ IDEA,Spring boot和Spring cloud
四、实验方法、步骤及结果测试
1、展现3个服务的项目文件目录结构,并解释每个文件的作用。
截图展现项目文件目录结构(A服务) (只截图A1的文件目录,与A2和A3相同) | 分别说明文件夹,包,类,配置文件的作用 (这一部分不能省略,一定要对截图中内容进行说明) pom.xml为文件的环境配置文件,application.yml为微服务的配置文件,InfoControlled是微服务的具体操作函数,OutService是调用其他服务的接口,InfoHystrix是断路保护相应的执行函数。 |
截图展现项目文件目录结构(B服务) | 分别说明文件夹,包,类,配置文件的作用 (这一部分不能省略,一定要对截图中内容进行说明) pom.xml为文件的环境配置文件,application.yml为微服务的配置文件,LoginControlled是微服务的具体操作函数,InfoService是调用其他服务的接口。UserRibbonConfig是负载均衡的具体方法的类。 |
截图展现项目文件目录结构(C服务) | 分别说明文件夹,包,类,配置文件的作用 (这一部分不能省略,一定要对截图中内容进行说明) pom.xml为文件的环境配置文件,application.yml为微服务的配置文件,FinallyControlled是微服务的具体操作函数。 |
2、说明服务A的功能设计,并解释其核心代码。
服务功能设计及核心代码 | 解释: (这一部分不能省略,一定要对截图中内容进行说明) 通过application.yml文件配置注册到consul微服务的信息。 通过info函数返回用户的年龄信息,通过OutService接口调用CloudC的微服务(为了方便测试选择GET方法进行测试) |
3、说明服务B的功能设计,并解释其核心代码。
服务功能设计及核心代码 | 解释 (这一部分不能省略,一定要对截图中内容进行说明) 通过application.yml文件配置注册到consul微服务的信息。 通过login函数进行用户的登录,通过InfoService接口调用CloudA的微服务(为了方便测试选择GET方法进行测试) |
4、说明服务C的功能设计,并解释其核心代码。
服务功能设计及核心代码 | 解释 (这一部分不能省略,一定要对截图中内容进行说明) 通过application.yml文件配置注册到consul微服务的信息。 通过out函数用户的退出。(为了方便测试选择GET方法进行测试) |
5、与服务调用相关的代码,并解释核心内容。
解释 (这一部分不能省略,一定要对截图中内容进行说明) InfoService的接口,通过Feign对CloudA微服务的调用进行封装,通过函数info对CloudA中的/info服务进行调用 OutService的接口,通过Feign对CloudC微服务的调用进行封装,通过函数out对CloudC中的/out服务进行调用 |
6、与负载均衡相关的代码,并解释核心内容
截图展现代码 | 解释 (这一部分不能省略,一定要对截图中内容进行说明) 通过@RibbonClients和@RibbonClient注解实现对CloudA微服务的负载均衡,UserRibbonConfig类是负载均衡策略,使用的是轮询的策略。 |
7、与断路保护相关的代码,并解释核心内容
截图展现代码 | 解释 (这一部分不能省略,一定要对截图中内容进行说明) 在@FeignClient注解中添加fallback和对应相应的断路保护类InfoHystrix ,InfoHystrix继承自InfoService接口,在断路时返回over time。 |
8、运行结果并解释(运行的访问入口从服务B开始,由此引发后面对其它服务的调用)
截图展现运行结果 Consul上的结果 CloudB的测试(轮询的结果不同) CloudA1的测试 CloudC的测试 断路保护 | 解释 (这一部分不能省略,一定要对截图中内容进行说明) 通过http://localhost:8511/login测试CloudB返回Login Successful是CloudB产生的微服务,age:24是CloudsA微服务返回的结果,User out是微服务CloudC返回的结果,刷新页面有三个不同的age分别是22,23,24,是通过轮询调用有coudA1,coudA2和coudA3的结果。 通过http://localhost:8514/info测试CloudA1 通过http://localhost:8513/out测试CloudC 在不开启CloudA的情况下,开启CloudB,出现time over断路保护。 |
五、实验中出现的问题及解决方案
问题:各依赖的版本对不上
描述:例子一,@EnableCircuitBreaker在不同的版本的依赖中已经由@ EnableHystrix代替
解决:使用相应的版本和相应的注解
六、参考材料
教材第2,3,4,5章内容
智慧农业管理系统的设计与实现
1 课题背景
2 需求分析
- 解决专家问答问题:专家点击解决按钮,输入问题解决方案,用来解决用户向专家提出的问题。
- 管理专家问答:管理专家问答的增删改查,设置管理专家问答数据的公开情况
- 管理科普文章:管理科普文章的增删改查,后台管理用户管理科普文章的是否推荐,是否公开,查询用户对文章点赞情况
- 向专家提问:后台管理用户向专家的提问,以及专家对问题的回答,后台管理员可以对问题和回答经行增删改查操作
- 科普文章:用户查看科普文章,可在后台对科普文章经行管理
- 店铺管理:对各个店铺信息进行管理,可以对店铺的名称、地址、介绍进行修改。
对一下管理员及用户进行用例分析:
后台管理员虽对系统的所有功能都具备操作权限,但本质上只执行与网络信息审计相关 的操作,对于不符合网络规范的相关内容进行记录和删除等操作,以保证网络信息内容 的合法性、健康性和安全性:
- 后台管理员
- 科普文章管理:修改文章,删除文章,设置文章推荐
- 问答管理:删除问答,设置问答推荐,设置问答显示
- 店铺管理:修改店铺名称,修改店铺地址,修改店铺简介,删除无效店铺
- 用户管理
专家用户对自己相关信息有操作权限,用来管理相关内容:
- 专家用户:
- 问题管理:修改、删除、输入问题及其回答
- 专家信息管理:修改专家自身信息
- 文章管理:修改、删除专家自己的文章
普通用户使用软件,可以实现查看文章,向专家提问,查看专家详情等操作,可对自己信息进行管理
3)普通用户:
1.查看文章:用户可查看文章以及对文章点赞
2.向专家提问:用户可向选定专家提问,内容包含文字、图片。
3.查看专家详情:用户可查看专家的姓名,专业,组织等信息。
4.用户信息管理:用户对自己信息进行修改和管理。
用例图:
3智慧农业管理系统的设计
系统结构图
智慧农业管理系统的总体设计架构,其中核心功能被分:科普文章、专家问答、店铺、用户管理。在科普文章子系统中包括用户查看模块,和管理模块,可对软件内的文章进行管理。专家问答子系统中包含用户问答模块,专家问答模块,问答管理模块,可以对软件内用户对专家的提问和专家的回答内容进行管理。店铺子系统包括店铺信息模块,可对店铺信息进行管理。
3.2 系统架构设计
系统架构:MVC三层架构
软件模式:B/S模式
MVC,即Model模型,View模型,Controller模型
Model:承载数据,对用户提交请求进行计算的模块。分为两类,一类称为数据承载Bean,一类称为业务处理Bean。业务处理Bean指的是Service或DAO对象,用于处理用户提出的请求;数据承载Bean指的是实体类,用于用户承载业务数据,本次软件开发中,涉及到article,expert,expertArticle,shop等类,将这些类划分到model层中。
View:视图,为用户提供直观的使用界面,与用户直接进行交互,如本课设中用到的javax.swing所完成的图形化界面。本次软件开发中,视图层包括 html,js,css的代码,所以view层由vue框架开发。
Controller:控制器,用于将用户的请求转发给相应的Model进行,并计算后给予用户反馈。本次软件开发中需要对article,expert,expertArticle,shop等类进行操作,分别需要各自的controller才操作。
(这里写上项目所采用的具体的面向服务的架构,采用的框架,技术。
并采用下图的方式描述系统面向服务的设计,这里只是参考,需要根据自己的项目来画图。)
3.3 服务的设计
3.3.1在总体设计的基础上将功能或子功能映射为服务
3.3.2每个服务的操作和协作设计
3.3.3 如果采用restful风格的服务,则需要说明API的设计。参考如下。
如果采用soap的服务,则需要描述服务接口和服务实现类(被@webservice标注的,需要被发布publish的,可列表说明,不是@service标注)。
3.4 各服务层次结构的设计
分层描述(controller service domain)
3.4.1 科普文章
3.4.2 专家问答
3.4.3 店铺管理
3.4.4 用户管理
Admin服务定义了两个接口函数,
showAdminByPwd是通过id编号来返回数据库中相应的admin对象
showAdminByNameAndPwd是通过用户名和密码编号来返回数据库中相应的admin对象
3.5 数据库的设计(如果有,可以不用数据库)
科普文章:article
id bigint auto_increment comment '文章ID' primary key,
title varchar(100) null comment '文章标题',
type varchar(50) null comment '类别',
cover_img varchar(200) null comment '封面图20*20',
source varchar(300) null comment '来源',
source_link varchar(300) null comment '来源链接',
content_type tinyint(2) null comment '内容类型: 1 图文 2 视频 ',
content longtext null comment '正文',
video_url varchar(200) null comment '视频文件链接',
video_name varchar(200) null comment '视频文件名',
video_desc varchar(500) null comment '视频描述',
read_count int null comment '阅读量',
recommend tinyint(2) null comment '推荐状态: 0 未推荐 1 推荐',
status tinyint(2) null comment '状态: 0 禁用 1 启用',
publish_time datetime null comment '发布时间',
create_time datetime null comment '创建时间',
update_time datetime null comment '修改时间',
creator_id bigint null comment '创建人',
updator_id bigint null comment '修改人',
creator_name varchar(45) null comment '创建人名称',
version varchar(45) null comment '乐观锁版本号',
del tinyint(2) default 0 null comment '删除: 0 未删除 32 已删除'
专家问答:_expert_ask
id bigint auto_increment comment '问题ID' primary key,
org_id bigint null comment '提问组织ID',
user_id bigint null comment '提问用户ID',
user_name varchar(45) null comment '提问人名称',
answer_user_id bigint null comment '回复人ID',
answer_user_name varchar(45) null comment '回复人名称',
question varchar(2000) null comment '问题内容',
images varchar(2000) null comment '图片',
answer_content varchar(2000) null comment '回答内容',
status tinyint(2) default 0 null comment '状态: 0 待解决 1 已解决',
pub_flag tinyint(2) default 0 null comment '公开: 0 不公开 1 公开',
answer_time datetime null comment '回答时间',
create_time datetime null comment '创建时间',
update_time datetime null comment '修改时间',
creator_id bigint null comment '创建人',
updator_id bigint null comment '修改人',
creator_name varchar(45) null comment '创建人名称',
version varchar(45) null comment '乐观锁版本号',
del tinyint(2) default 0 null comment '删除: 0 未删除 38 已删除'
专家:expert
id bigint auto_increment comment '专家id' primary key,
expert_name varchar(45) collate utf8mb4_bin null comment '专家名称',
profession tinyint(2) null comment '专业',
org_id bigint not null comment '所属组织id',
org varchar(100) collate utf8mb4_bin null comment '所属组织',
phone_number varchar(45) collate utf8mb4_bin null comment '联系电话',
individual_resume varchar(300) collate utf8mb4_bin null comment '个人简介',
image varchar(200) collate utf8mb4_bin null comment '图片',
phone_account varchar(45) collate utf8mb4_bin null comment '手机账号',
status tinyint(1) default 1 null comment '0禁用 1启用',
create_time datetime null comment '创建时间',
update_time datetime null comment '修改时间',
creator_id bigint null comment '创建人',
updator_id bigint null comment '修改人',
creator_name varchar(45) collate utf8mb4_bin null comment '创建人名称',
version varchar(45) collate utf8mb4_bin null comment '乐观锁版本号',
del tinyint(2) default 0 null comment '删除: 0 未删除 1 已删除'
4 智慧农业管理系统的实现
4.1项目结构及配置
4.1.1项目结构
- controller控制器:用于控制页面的跳转,调用业务逻辑层
- 编写service接口和接口的实现类,对数据访问层进行增删改查操作的组装,接收表示层的请求调用。
- 通过Spring 基于jpa标准操作数据的模块,无需写实现类,不用写sql语句直接查询数据库中的数据,实现对数据库的增删改查。
- 使用Springboot框架,使用yml文件对各类配置进行管理。
4.1.2项目配置
设置dns
vim /etc/resolv.conf
# 注释原有的,改成公共的dns
nameserver 223.5.5.5
nameserver 8.8.8.8
# 下面是为了解决重启服务器dns还原的问题
cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.bak
cat >> /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF
DNS1=114.114.114.114
DNS2=8.8.8.8
EOF
设置主机名
hostnamectl set-hostname {host名}
添加数据盘
fdisk -l #查看数据盘名
fdisk /dev/vdb
(m,n,p,回车,回车,回车,wq)
partprobe
#格式化数据盘
mkfs.xfs -f /dev/vdb
# 创建挂载目录
mkdir -p /data
# 挂载磁盘
mount /dev/vdb /data
df -T -h # 查看磁盘时候挂载成功
echo '/dev/vdb /data xfs defaults 0 0' >>/etc/fstab
# 重启测试
reboot
安装nginx
在线安装nginx
配置yum源
vim /etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
[nginx-mainline]
name=nginx mainline repobaseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
安装nginx
yum install nginx -y
启动设置开机自启
# systemctl start nginx
# systemctl enable nginx
编译安装nginx
环境准备:先安装准备环境
#yum install gcc gcc-c++ automake pcre pcre-devel zlip zlib-devel openssl openssl-devel
下载安装nginx
官方下载地址http://nginx.org/en/download.htm
# cd /usr/local/src
# wget http://nginx.org/download/nginx-1.22.2.tar.gz
# tar axf nginx-1.22.2.tar.gz
编译安装
# cd nginx-1.20.2
# ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-stream
# make && make install
nginx开机自启
配置开机自启动
# vi /lib/systemd/system/nginx.service
[Unit] Description=nginx service
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload ExecStop=/usr/local/nginx/sbin/nginx -s quit
PrivateTmp=true
[Install]
WantedBy=multi-user.target
# 启动设置开机自启
# systemctl start nginx
# systemctl enable nginx
数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://www.bunint.com:3306/ruian_zn_test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8&sslMode=disabled&nullCatalogMeansCurrent=true
# username: ruian_zn_test
# password: ruian_zn_test
url: jdbc:mysql://rm-bp1f4f79rn235buk9mo.mysql.rds.aliyuncs.com:31525/ruian_zn_test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8&sslMode=disabled&nullCatalogMeansCurrent=true
username: nhsztest
password: nhsz@testZNF
# driverClassName: dm.jdbc.driver.DmDriver
# url: jdbc:dm://120.26.98.90:52360/RUIAN_ZN_TEST?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
# username: SYSDBA
# password: SYSDBA
Redis配置
redis:
database: 5
host: 49.233.0.170
port: 6379
timeout: 10000
password: 123456
4.2 服务实现的关键代码
4.1内容管理:专家问答(网页端)
4.1.1主要功能
1)专家问答数据分页查询
进入内容管理的专家问答详情界面,界面通过表格形式,分页显示数据库内所有的专家问答记录。
2)专家问答数据多条件筛选查询
在文本框中输入查询条件,点击查询,界面对数据进行筛选,筛选结果通过表格分页显示;输入筛选条件后,点击重置按钮,输入的筛选条件清空。
3)问题解决操作(仅专家可操作)
专家点击解决按钮后,界面弹出解决问题窗口,窗口显示用户提问的问题内容和问题图片,专家可在文本框中输入问题解决内容,点击确定后,保存。
4)问答公开操作
点击空开按钮,此条问答数据将可公开,表格中词条数据的显示状态改编为公开,按钮变为取消公开,可公开的问答可在APP端显示。
5)取消公开
与公开操作相反
6)查看问答详情操作
点击查看详情按钮,界面弹出详情窗口,显示问答内容。
4.1.2程序实现截图
1)专家问答数据分页查询
问答数据通过表格形式展示:
图5-1专家问答分页查询
分页查询:
图5-2分页查询
2)专家问答数据多条件筛选查询
图5-3专家问答条件筛选
3)问题解决操作(仅专家可操作)
点击解决问题,弹出解决问题界面:
图5-4解决问题界面
解决问题界面输入内容后点击确定,数据保存,点击查看详情按钮,查看输入的内容:
图5-5查看解决问题后的数据
4)问答公开操作
点击公开按钮:
图5-6问答公开操作
5)取消公开操作
与公开操作相反:略
6)查看问答详情操作
点击查看详情按钮,弹出问答详情界面
图5-7查看详情
4.1.3主要实现代码及注释
Controller层:
@Api(tags = "专家问答")
@RestController
@RequestMapping("/web/store/expertAsk")
public class ExpertAskController extends BaseController {
@Autowired
private ExpertAskService expertAskService;
@Autowired
private MessageApiClient messageApiClient;
/**
* 专家问答分页查询
*/
@ApiOperation("专家问答分页查询")
@GetMapping("/page/list")
public Result<Page<ExpertAskVO>> queryByPage(ExpertAskQuery query){
// 判断身份,如果是专家则只能看到自己的
IPage<ExpertAsk> page = expertAskService.queryByPage(query);
List<ExpertAskVO> list = BeanUtil.copyToList(page.getRecords(), ExpertAskVO.class);
Page<ExpertAskVO> pageVO = new Page<>();
pageVO.setPages(query.getPageNum());
pageVO.setSize(query.getPageSize());
pageVO.setTotal(page.getTotal());
pageVO.setRecords(list);
return new Result().ok(pageVO);
}
/**
* 查看专家问答详情
*/
@GetMapping(value = "/{id}")
@ApiOperation("查看专家问答详情")
public Result<ExpertAskDetailVO> queryById(@ApiParam("专家问答ID") @PathVariable Long id) {
ExpertAsk expertAsk = expertAskService.getById(id);
ExpertAskDetailVO expertAskVO = BeanUtil.copyProperties(expertAsk, ExpertAskDetailVO.class);
return new Result().ok(expertAskVO);
}
/**
* 解决
*/
@PostMapping(value = "/resolve")
@ApiOperation("解决")
public Result<ExpertAskVO> resolve(@RequestBody @Valid ExpertAskDTO expertAskDTO) {
ExpertAsk expertAsk = BeanUtil.copyProperties(expertAskDTO, ExpertAsk.class);
expertAsk.setStatus(1);
expertAsk.setAnswerTime(DateUtils.getNowDate());
expertAsk.setUpdatorId(getLoginUserId());
expertAsk.setUpdateTime(LocalDateTime.now());
boolean success = expertAskService.updateExpertAsk(expertAsk);
if(success){
messageApiClient.sendMessage(expertAsk.getUserId(),"专家回复通知","您的提问已回复,回复内容:"+expertAskDTO.getAnswerContent());
}
return new Result().ok(success);
}
/**
* 新增专家问答
*/
@PostMapping("
Server层:
@Service
public class ExpertAskServiceImpl extends ServiceImpl<ExpertAskDao, ExpertAsk> implements ExpertAskService {
@Autowired
private ExpertAskDao expertAskDao;
@Autowired
private ExpertDao expertManagementDao;
public IPage<ExpertAsk> queryByPage(ExpertAskQuery query) {
LambdaQueryWrapper<ExpertAsk> wrapper = Wrappers.lambdaQuery();
if (expertManagementDao.selectById(query.getId()) != null) { //此用户是专家
wrapper.eq(ExpertAsk::getAnswerUserId, query.getId());
}
wrapper.like(query.getQuestion() != null, ExpertAsk::getQuestion, query.getQuestion());
wrapper.like(query.getUserName() != null, ExpertAsk::getUserName, query.getUserName());
wrapper.like(query.getAnswerUserName() != null, ExpertAsk::getAnswerUserName, query.getAnswerUserName());
wrapper.eq(query.getStatus() != null, ExpertAsk::getStatus, query.getStatus());
wrapper.eq(query.getPubFlag() != null, ExpertAsk::getPubFlag, query.getPubFlag());
wrapper.ge(query.getStartTime()!=null,ExpertAsk::getCreateTime,query.getStartTime());
wrapper.le(query.getEndTime()!=null,ExpertAsk::getCreateTime,query.getEndTime());
wrapper.eq(ExpertAsk::getDel, 0);
wrapper.orderByDesc(ExpertAsk::getCreateTime);
return expertAskDao.selectPage(new Page(query.getPageNum(), query.getPageSize()), wrapper);
}
@Override
public IPage<ExpertAsk> queryByPage(AppExpertAskQuery query) {
LambdaQueryWrapper<ExpertAsk> wrapper = Wrappers.lambdaQuery();
if (query.getUserId() == null) { //用户id为空,说明展示专家问答或者ta的问答
wrapper.eq(query.getAnswerUserId() != null, ExpertAsk::getAnswerUserId, query.getAnswerUserId());
wrapper.eq(ExpertAsk::getPubFlag, 1);
wrapper.eq(ExpertAsk::getStatus, 1);
} else { //
wrapper.eq(ExpertAsk::getUserId, query.getUserId());
}
wrapper.eq(ExpertAsk::getDel, 0);
wrapper.orderByDesc(ExpertAsk::getCreateTime);
return expertAskDao.selectPage(new Page(query.getPageNum(), query.getPageSize()), wrapper);
}
@Override
public boolean insertExpertAsk(ExpertAsk expertAsk) {
int ct = expertAskDao.insert(expertAsk);
return ct > 0;
}
@Override
public boolean updateExpertAsk(ExpertAsk expertAsk) {
int ct = expertAskDao.updateById(expertAsk);
return ct > 0;
}
@Override
public int getConsultationCount(Long id) {
LambdaQueryWrapper<ExpertAsk> wrapper = Wrappers.lambdaQuery();
wrapper.eq(ExpertAsk::getAnswerUserId, id);
return expertAskDao.selectCount(wrapper);
}
@Override
public int getReplyCount(Long id) {
LambdaQueryWrapper<ExpertAsk> wrapper = Wrappers.lambdaQuery();
wrapper.eq(ExpertAsk::getAnswerUserId, id);
wrapper.isNotNull(ExpertAsk::getAnswerContent);
return expertAskDao.selectCount(wrapper);
}
4.1.4对实现中所用方法和重要算法等的说明
1)分页查询接口。判断用户id是否为空,若为空,说明此接口为展示专家问答或者TA的回答,则将筛选条件设置为已公开,已解决;若id不为空,说明此接口用于展示当前登录用户的问答,则筛选条件为用户id。
4.2内容管理:科普文章(网页端)
4.2.1主要功能
1)科普文章数据分页查询
进入科普文章界面,科普文章数据通过表格的形式分页查询,显示喜好,标题,分类等信息
2)科普文章数据筛选查询
在文本框中输入查询条件,点击查询按钮,系统筛选符合条件的数据,将筛选后的数据以表格的形式分页查询。点击重置,筛选条件失效。
3)新增文章
点击新增按钮,弹出新增界面,用户输入新增文章的信息,点击保存。
4)修改科普文章
点击修改按钮,弹出文章修改界面,输入修改的内容,点击保存按钮,修改后的数据保存,点击取消,返回主界面。
5)禁用/启用科普文章
点击禁用/启用按钮,修改文章的启用状态,启用的文章将在APP端的庄稼医院中显示。
6)推荐/取消推荐科普文章
点击推荐,取消推荐按钮,修改文章的推荐状态,推荐的文章将在APP端主页显示。
4.2.2程序实现截图
1)科普文章数据分页查询
进入科普文章界面,科普文章数据通过表格显示:
图5-8科普文章分页查询
2)科普文章数据筛选查询
在文本框中输入筛选条件,系统使用表格形式展示筛选后的数据
图5-9 科普文章筛选
3)新增文章
图5-10新增科普文章
当必填项用户没有输入且点击保存时,界面提醒用户
图5-11新增科普文章(未填)
4)修改科普文章
点击修改按钮,修改界面弹出,显示被修改的文章原有数据。
图5-11修改科普文章
5)禁用/启用科普文章
图5-12设置启用
图5-13设置禁用
6)推荐/取消推荐科普文章
图5-14设置推荐
图5-15求取消推荐
4.2.3主要实现代码及注释
Controller层:
@Api(tags = "科普文章")
@RestController
@RequestMapping("/web/store/article")
public class ArticleController extends BaseController {
@Autowired
private ArticleService articleService;
/**
* 科普文章分页查询
*/
@ApiOperation("科普文章分页查询")
@GetMapping("/page/list")
public Result<Page<ArticleListVO>> queryByPage(ArticleQuery query) {
IPage<Article> page = articleService.queryByPage(query);
List<ArticleListVO> list = BeanUtil.copyToList(page.getRecords(), ArticleListVO.class);
Page<ArticleListVO> pageVO = new Page<>();
pageVO.setPages(query.getPageNum());
pageVO.setSize(query.getPageSize());
pageVO.setTotal(page.getTotal());
pageVO.setRecords(list);
return new Result().ok(pageVO);
}
/**
* 查看科普文章详情
*/
@GetMapping(value = "/{id}")
@ApiOperation("查看科普文章详情")
public Result<ArticleVO> queryById(@ApiParam("科普文章ID") @PathVariable Long id) {
Article article = articleService.getById(id);
ArticleVO articleVO = BeanUtil.copyProperties(article, ArticleVO.class);
return new Result().ok(articleVO);
}
/**
* 新增科普文章
*/
@PostMapping("/insert")
@ApiOperation("新增科普文章")
public Result insert(@RequestBody @Valid Article article) {
article.setCreatorInfo(getLoginUserId(),getLoginUserName());
article.setReadCount(1L);
article.setStatus(1);
article.setRecommend(0);
article.setPublishTime(LocalDateTime.now());
boolean success = articleService.insertArticle(article);
return new Result().ok(success);
}
/**
* 修改科普文章
*/
@PostMapping("/update")
@ApiOperation("修改科普文章")
public Result update(@RequestBody @Valid Article article) {
article.setUpdatorId(getLoginUserId());
article.setUpdateTime(LocalDateTime.now());
boolean success = articleService.updateArticle(article);
return new Result().ok(success);
}
/**
* 禁用/启用
*/
@PostMapping("/status")
@ApiOperation("禁用/启用")
public Result updateStatus(@ApiParam(value = "文章ID", required = true) @RequestParam Long id,
@ApiParam(value = "状态:0=禁用 1=启用", required = true) @RequestParam Integer status) {
Article article = new Article();
article.setId(id);
article.setStatus(status);
article.setUpdatorId(getLoginUserId());
article.setUpdateTime(LocalDateTime.now());
if (article.getStatus() != null && article.getStatus() == Constants.INT_ONE) {
article.setPublishTime(LocalDateTime.now());
}
return new Result().ok(this.articleService.updateById(article));
}
/**
* 设置/取消推荐
*/
@PostMapping("/recommend")
@ApiOperation("设置/取消推荐")
public Result updateRecommend(@ApiParam(value = "文章ID", required = true) @RequestParam Long id,
@ApiParam(value = "文章推荐:0=未推荐 1=推荐", required = true) @RequestParam Integer recommend) {
Article article = new Article();
article.setId(id);
article.setRecommend(recommend);
article.setUpdatorId(getLoginUserId());
article.setUpdateTime(LocalDateTime.now());
return new Result().ok(this.articleService.updateById(article));
}
/**
* 批量删除科普文章
*/
@PostMapping("/delete")
@ApiOperation("批量删除科普文章")
public Result delete(@ApiParam("科普文章ID列表") @RequestParam List<Long> ids) {
boolean success = articleService.removeByIds(ids);
return new Result().ok(success);
}
/**
* 删除科普文章
*/
@PostMapping("/deleteById/{id}")
@ApiOperation("删除科普文章")
public Result deleteById(@ApiParam("科普文章ID") @PathVariable Long id) {
Article article = new Article();
article.setId(id);
article.setUpdatorId(getLoginUserId());
article.setUpdateTime(LocalDateTime.now());
article.setDel(1);
boolean success = articleService.updateById(article);
return new Result().ok(success);
}
}
Server层:
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleDao, Article> implements ArticleService {
private static final long MAX_POST_SIZE = 5 * 1024l * 1024l;
@Autowired
private ArticleDao articleDao;
@Autowired
OSSHolder ossHolder;
public IPage<Article> queryByPage(ArticleQuery query) {
LambdaQueryWrapper<Article> wrapper = Wrappers.lambdaQuery();
wrapper.like(query.getTitle() != null, Article::getTitle, query.getTitle());
wrapper.eq(StrUtil.isNotBlank(query.getType()), Article::getType, query.getType());
wrapper.eq(query.getRecommend() != null, Article::getRecommend, query.getRecommend());
wrapper.eq(query.getStatus() != null, Article::getStatus, query.getStatus());
wrapper.ge(query.getStartTime() != null, Article::getCreateTime, query.getStartTime());
wrapper.le(query.getEndTime() != null, Article::getCreateTime, query.getEndTime());
wrapper.eq(Article::getDel, 0);
wrapper.orderByDesc(Article::getCreateTime);
return articleDao.selectPage(new Page(query.getPageNum(), query.getPageSize()), wrapper);
}
@Override
public IPage<Article> queryByPage(AppArticleQuery query) {
LambdaQueryWrapper<Article> wrapper = Wrappers.lambdaQuery();
wrapper.eq(ObjectUtil.isNotEmpty(query.getType()), Article::getType, query.getType());
wrapper.eq(Article::getStatus, Constants.INT_ONE);
wrapper.orderByDesc(Article::getCreateTime);
wrapper.orderByDesc(Article::getPublishTime);
return articleDao.selectPage(new Page(query.getPageNum(), query.getPageSize()), wrapper);
}
@Override
public IPage<Article> queryByPageMain(AppArticleQuery query) {
LambdaQueryWrapper<Article> wrapper = Wrappers.lambdaQuery();
wrapper.eq(ObjectUtil.isNotEmpty(query.getType()), Article::getType, query.getType());
wrapper.eq(Article::getStatus, Constants.INT_ONE);
wrapper.eq(Article::getRecommend, Constants.INT_ONE);
wrapper.orderByDesc(Article::getCreateTime);
wrapper.orderByDesc(Article::getPublishTime);
return articleDao.selectPage(new Page(query.getPageNum(), query.getPageSize()), wrapper);
}
@Override
public boolean insertArticle(Article article) {
ArticleDTO articleDTO = BeanUtil.copyProperties(article, ArticleDTO.class);
validateArticle(articleDTO);
int ct = articleDao.insert(article);
return ct > 0;
}
@Override
public boolean updateArticle(Article article) {
ArticleDTO articleDTO = BeanUtil.copyProperties(article, ArticleDTO.class);
validateArticle(articleDTO);
int ct = articleDao.updateById(article);
return ct > 0;
}
private void validateArticle(ArticleDTO articleDTO) {
Integer type = articleDTO.getContentType();
if (ArticleEnum.ArticleType.IMAGE_TEXT.getCode().equals(type)) {
validateByImage(articleDTO);
} else if (ArticleEnum.ArticleType.VIDEO.getCode().equals(type)) {
validateByVideo(articleDTO);
}
}
/**
* 图文校验
*
* @param articleDTO
*/
public void validateByImage(ArticleDTO articleDTO) {
ValidatorUtils.validate(articleDTO, Default.class, ArticleDTO.GroupImage.class);
}
/**
* 视频校验
*/
public void validateByVideo(ArticleDTO articleDTO) {
ValidatorUtils.validate(articleDTO, Default.class, ArticleDTO.GroupVideo.class);
}
}
4.2.4对实现中所用方法和重要算法等的说明
1)分类校验。当用户选择文章类型为文本时,采用文本校验;当用户选择文章类型为视频时,采用视频校验。文本校验时,需要判断文章标题是否为空,文章正文内容是否为空,文章正文内容字数是否超过2000字;视频校验时,需要判断视频文件连接是否为空,视频标题是否超过20字,视频文件是否为空。
4.4庄稼医院(APP端)
4.4.1主要功能
1)文章政策分页查询
点击主页庄稼医院按钮,进入文章政策界面,分页查询已公开的文章。
2)文章分类查询
点击窗口上方分类栏,按照文章类别分类查询。
3)查看文章详情
点击文章,跳转至文章内容界面。
4.4.2程序实现截图
1)文章政策分页查询
点击主页庄稼医院按钮,进入文章政策界面,分页查询已公开的文章。
图5-21文章政策
2)文章分类查询
点击窗口上方分类栏,按照文章类别分类查询。
图5-22文章政策分类查询
3)查看文章详情
点击文章,跳转至文章内容界面。
图5-23文章详情
4.4.3主要实现代码及注释
Controller:
@Api(tags = "文章政策")
@RestController
@RequestMapping("/app/store/article")
public class AppArticleController extends BaseController {
@Autowired
private ArticleService articleService;
/**
* 科普文章分页查询
*/
@ApiOperation("科普文章分页查询")
@GetMapping("/page/list")
public Result<Page<ArticleVO>> queryByPage(AppArticleQuery query) {
IPage<Article> page = articleService.queryByPage(query);
List<ArticleVO> list = BeanUtil.copyToList(page.getRecords(), ArticleVO.class);
Page<ArticleVO> pageVO = new Page<>();
pageVO.setPages(query.getPageNum());
pageVO.setSize(query.getPageSize());
pageVO.setTotal(page.getTotal());
pageVO.setRecords(list);
return new Result().ok(pageVO);
}
/**
* 科普文章分页查询(主页)
*/
@ApiOperation("科普文章分页查询(主页)")
@GetMapping("/page/main/list")
public Result<Page<ArticleVO>> queryByPageMain(AppArticleQuery query) {
IPage<Article> page = articleService.queryByPageMain(query);
List<ArticleVO> list = BeanUtil.copyToList(page.getRecords(), ArticleVO.class);
Page<ArticleVO> pageVO = new Page<>();
pageVO.setPages(query.getPageNum());
pageVO.setSize(query.getPageSize());
pageVO.setTotal(page.getTotal());
pageVO.setRecords(list);
return new Result().ok(pageVO);
}
/**
* 查看科普文章详情
*/
@GetMapping(value = "/{id}")
@ApiOperation("查看科普文章详情")
public Result<ArticleVO> queryById(@ApiParam("科普文章ID") @PathVariable Long id) {
Article article = articleService.getById(id);
ArticleVO articleVO = BeanUtil.copyProperties(article, ArticleVO.class);
article.setReadCount(article.getReadCount()+1L);
articleService.updateById(article);
return new Result().ok(articleVO);
}
}
4.4.4对实现中所用方法和重要算法等的说明
1)APP端专家列表接口,通过expertService.queryAll查询数据库中所有专家记录,存放在List<AppExpertListVO> expertList中,使用dictValueService.queryProfession()从数据字典中获取专家专业的字典值,采用数据流的方式,拼接专家和专家专业。
4.5店铺详情(APP端)
4.5.1主要功能
1)店铺详情,显示当前店铺的店铺名称,联系人,联系电话,地址等信息。
4.5.2程序实现截图
5-24店铺详情
4.5.3主要实现代码及注释
Controller:
@Api(tags = "01.店铺信息")
@RestController
@RequestMapping("/web/store/shop")
public class ShopController {
@Autowired
private ShopService shopService;
/**
* 店铺信息分页查询
*/
@ApiOperation("分页查询店铺列表")
@GetMapping("/page/list")
public Result<Page<ShopVO>> queryByPage(ShopQuery query){
IPage<Shop> page = shopService.queryByPage(query);
List<ShopVO> list = BeanUtil.copyToList(page.getRecords(),ShopVO.class);
Page<ShopVO> pageVO = new Page<>();
pageVO.setPages(query.getPageNum());
pageVO.setSize(query.getPageSize());
pageVO.setTotal(page.getTotal());
pageVO.setRecords(list);
return new Result().ok(pageVO);
}
}
Server:
@Service
public class ShopServiceImpl extends ServiceImpl<ShopDao, Shop> implements ShopService
{
@Autowired
private ShopDao shopDao;
@Autowired
private OrganizationService organizationService;
public IPage<Shop> queryByPage(ShopQuery query){
LambdaQueryWrapper<Shop> wrapper = Wrappers.lambdaQuery();
wrapper.like(StrUtil.isNotBlank(query.getKeyword()),Shop::getShopName, query.getKeyword());
wrapper.orderByDesc(Shop::getCreateTime);
return shopDao.selectPage(new Page(query.getPageNum(),query.getPageSize()),wrapper);
}
@Override
public List<Shop> queryShopList(List<Long> shopIds) {
if(CollectionUtil.isEmpty(shopIds)){
return Lists.newArrayList();
}
return shopDao.selectList(Wrappers.<Shop>lambdaQuery()
.eq(Shop::getDel, DelFlagEnum.NOT_DELETED.getFlag())
.in(Shop::getId,shopIds));
}
@Override
public Map<Long, Shop> queryShopMap(List<Long> shopIds) {
List<Shop> list = queryShopList(shopIds);
if(CollectionUtil.isEmpty(list)){
return MapUtil.newHashMap();
}
return list.stream().collect(Collectors.toMap(Shop::getId, Function.identity()));
}
@Override
public Shop getShopByOrgId(Long orgId) {
return shopDao.selectOne(Wrappers.<Shop>lambdaQuery()
.eq(Shop::getOrgId,orgId).last(" limit 1"));
}
@Override
public boolean insertShop(Shop shop) {
validateShop(shop);
int ct = shopDao.insert(shop);
return ct>0;
}
@Override
public boolean updateShop(Shop shop) {
validateShop(shop);
int ct = shopDao.updateById(shop);
return ct>0;
}
/**
* 只有服务商可以注册店铺
*/
private void validateShop(Shop shop) {
Long orgId = shop.getOrgId();
Organization org = organizationService.getById(orgId);
Integer orgType = Optional.ofNullable(org)
.map(Organization::getOrgType)
.orElse(null);
if(!OrganizationEnum.OrgType.SERVICE_PROVIDER.getCode().equals(orgType)) {
// 如果当前登录不是服务商
throw new BusinessException("更新店铺失败,请先切换为服务商身份");
}
}
}
总结
通过这次SOA和Web service技术课程设计,我锻炼了Navicat,idea等软件的熟练使用,掌握创建一个基于SOAP的Web服务的基本流程,掌握使用spring boot创建RESTful风格的服务端,并创建客户端程序,以及掌握使用Spring Cloud + Spring Boot构建微服务,建立分布式注册中心,进行分布式通信和设置分布式断路器。将课上所学知识与现实中的实际问题联系在一起,能够使问题简单化,通过MVC三层结构使用户能直接更改数据库的数据。我的课程设计是智慧农业管理系统,目的是使农户可以方便的管理,销售农产品,方便农户及时了解农业新闻,加强各地专家与农户交流联系,发展智慧农业。
在本次课设中依然存在不足的地方, 使用vue进行前端界面开发时不熟练;在数据库管理方面,未采用索引等方法优化数据库查询效率;后端采用springcloud等分布式开发方式时,使用各类微服务、分布式技术没有十分熟练,希望在之后的学习中,继续提高自己的能力。