定义接口方法
在itcast-haoke-manage-dubbo-server-house-resources-dubbo-interface中:
package cn.itcast.haoke.dubbo.server.api;
HouseResources queryHouseResourcesById(Long id);
实现接口
在itcast-haoke-manage-dubbo-server-house-resources-dubbo-service中:
package cn.itcast.haoke.dubbo.server.api;
@Override
public HouseResources queryHouseResourcesById(Long id) {
return this.houseResourcesService.queryHouseResourcesById(id);
}
业务Service实现
在itcast-haoke-manage-dubbo-server-house-resources-dubbo-service中:
package cn.itcast.haoke.dubbo.server.service.impl;
@Override
public HouseResources queryHouseResourcesById(Long id) {
return super.queryById(id);
}
引入graphql-java依赖
itcast-haoke-manage-api-server的pom.xml
编写haoke.graphqls文件com.graphql-java graphql-java 11.0
在resources目录下创建haoke.graphqls文件:
schema {
query: HaokeQuery
}
type HaokeQuery {
HouseResources(id:Long) : HouseResources
HouseResourcesList(page:Int, pageSize:Int) : TableResult
IndexAdList:IndexAdResult
}
scalar Long
type HouseResources {
id:Long!
title:String
estateId:Long
buildingNum:String
buildingUnit:String
buildingFloorNum:String
rent:Int
rentMethod:Int
paymentMethod:Int
houseType:String
coveredArea:String
useArea:String
floor:String
orientation:String
decoration:Int
facilities:String
pic:String
houseDesc:String
contact:String
mobile:String
time:Int
propertyCost:String
}
type TableResult{
list:[HouseResources]
pagination:Pagination
}
type Pagination{
current:Int
pageSize:Int
total:Int
}
type IndexAdResult{
list:[IndexAdResultData]
}
type IndexAdResultData{
original:String
}
编写GraphQLController
package cn.itcast.haoke.dubbo.api.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import graphql.GraphQL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RequestMapping("graphql")
@Controller
@CrossOrigin
public class GraphQLController {
@Autowired
private GraphQL graphQL;
private static final ObjectMapper MAPPER = new ObjectMapper();
@GetMapping
@ResponseBody
public Map query(@RequestParam("query") String query) {
return this.graphQL.execute(query).toSpecification();
}
}
编写GraphQLProvider
在GraphQLProvider中,需要与Spring整合,并且将GraphQL对象载入到Spring容器。
package cn.itcast.haoke.dubbo.api.graphql;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;
// 实现的功能:将GraphQL对象载入到Spring容器,并且完成GraphQL对象的初始化的功能
@Component
public class GraphQLProvider {
private GraphQL graphQL;
@Autowired
private List myDataFetchers;
//实现对GraphQL对象的初始化
@PostConstruct
public void init() throws FileNotFoundException {
File file = ResourceUtils.getFile("classpath:haoke.graphqls");
this.graphQL = GraphQL.newGraphQL(buildGraphQLSchema(file)).build();
}
private GraphQLSchema buildGraphQLSchema(File file) {
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(file);
return new SchemaGenerator().makeExecutableSchema(typeRegistry, buildWiring());
}
private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("HaokeQuery", builder -> {
for (MyDataFetcher myDataFetcher : myDataFetchers) {
builder.dataFetcher(myDataFetcher.fieldName(),
environment -> myDataFetcher.dataFetcher(environment));
}
return builder;
}
)
.build();
}
@Bean
public GraphQL graphQL() {
return this.graphQL;
}
}
优化改进GraphQLProvider逻辑
编写MyDataFetcher接口
package cn.itcast.haoke.dubbo.api.graphql;
import graphql.schema.DataFetchingEnvironment;
public interface MyDataFetcher {
String fieldName();
Object dataFetcher(DataFetchingEnvironment environment);
}
编写实现类HouseResourcesDataFetcher
package cn.itcast.haoke.dubbo.api.graphql;
import cn.itcast.haoke.dubbo.api.service.HouseResourcesService;
import graphql.schema.DataFetchingEnvironment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class HouseResourcesDataFetcher implements MyDataFetcher {
@Autowired
private HouseResourcesService houseResourcesService;
@Override
public String fieldName() {
return "HouseResources";
}
@Override
public Object dataFetcher(DataFetchingEnvironment environment) {
Long id = environment.getArgument("id");
return this.houseResourcesService.queryHouseResourcesById(id);
}
}
实现查询房源列表接口
新增HouseResourcesListDataFetcher实现
package cn.itcast.haoke.dubbo.api.graphql;
import cn.itcast.haoke.dubbo.api.service.HouseResourcesService;
import graphql.schema.DataFetchingEnvironment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class HouseResourcesListDataFetcher implements MyDataFetcher {
@Autowired
private HouseResourcesService houseResourcesService;
@Override
public String fieldName() {
return "HouseResourcesList";
}
@Override
public Object dataFetcher(DataFetchingEnvironment environment) {
Integer page = environment.getArgument("page");
if(null == page){
page = 1;
}
Integer pageSize = environment.getArgument("pageSize");
if(null == pageSize){
pageSize = 5;
}
return this.houseResourcesService.queryList(null, page, pageSize);
}
}
测试
{
HouseResourcesList(page:1, pageSize:2) {
list{
id
estateId
buildingNum
rent
contact
mobile
}
pagination{
current
pageSize
total
}
}
}
搭建前台系统
好客租房项目是采用前后端分离开发模式,前端系统由前端团队进行开发,接下来我们需要整合前端,前端是使用React+semantic-ui实现移动端web展示,后期可以将web打包成app进行发布
第一步,将资料中的haoke-web.zip解压到项目目录
第二步,导入到idea中
第三步,执行命令进行初始化和导入相关依赖包
npm install #安装依赖 npm start #启动服务
测试地址:http://localhost:9000/
搭建api工程(前台系统后端)itcast-haoke-web-api前端团队在开发时,没有采用mock的方式,而是采用了使用node.js开发服务端的方式进行了demo化开发。所以,我们也需要将该服务搭建起来,以便进行开发
第一步,将资料中的haoke-web-api.zip解压到项目目录,我的是:F:codeitcast-haokeitcast-haoke-webapi
第二步,创建数据库
创建myhome数据库,并且执行资料中的myhome.sql脚本。
第三步,修改配置文件
修改成自己的mysql配置:
第四步,输入命令进行初始化和启动服务
#脚本如下
"scripts": {
"test": "cross-env NODE_ENV=config-test node app.js",
"dev": "cross-env NODE_ENV=config-dev node app.js", #设置环境变量
"pro": "cross-env NODE_ENV=config-pro node app.js"
}
可以看出调用的配置文件是config-dev.js
npm cache clear --force npm install #安装依赖 npm run dev #启动dev脚本
测试地址:http://127.0.0.1:8086/
在users系统中查询到用户的信息如下:
首页
目录结构
加载数据流程
以首页为例,查看数据加载流程。
打开src/modules/home/home.js文件可以看到,在组件加载完成后进行加载数据:
componentDidMount = () => {
let swipe = new Promise((resolve, reject) => {
client.query({query: GET_INDEX_ADS}).then(result =>
resolve(result.data.IndexAdList.list));
})
let menu = new Promise((resolve, reject) => {
axios.post('/homes/menu').then((data)=>{
resolve(data.data.list);
});
})
let info = new Promise((resolve, reject) => {
axios.post('/homes/info').then((data)=>{
resolve(data.data.list);
});
})
let faq = new Promise((resolve, reject) => {
axios.post('/homes/faq').then((data)=>{
resolve(data.data.list);
});
})
let house = new Promise((resolve, reject) => {
axios.post('/homes/house').then((data)=>{
resolve(data.data.list);
});
})
}
通过axios进行加载数据,在App.js中对axios进行了全局的配置:
//设置全局的baseUrl配置
axios.defaults.baseURL = config.apiBaseUrl;
//设置拦截器
axios.interceptors.request.use(function (config) {
//在发送请求之前获取mytoken值
if(!config.url.endsWith('/login')){
config.headers.Authorization = localStorage.getItem('mytoken');
}
return config;
}, function (error) {
//获取数据失败的处理
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
//对响应的拦截,返回response.data数据
return response.data;
}, function (error) {
return Promise.reject(error);
});
在common.js中进行配置
export default {
// imgBaseUrl: 'http://47.96.21.88:8086/',
// apiBaseUrl: 'http://47.96.21.88:8086/',
// wsBaseUrl: 'ws://47.96.21.88:8087'
imgBaseUrl: 'http://127.0.0.1:8086/',
apiBaseUrl: 'http://127.0.0.1:8086/',
wsBaseUrl: 'ws://127.0.0.1:8087'
}
加载到数据后的处理
componentDidMount = () => {
let swipe = new Promise((resolve, reject) => {
client.query({query: GET_INDEX_ADS}).then(result =>
resolve(result.data.IndexAdList.list));
})
let menu = new Promise((resolve, reject) => {
axios.post('/homes/menu').then((data)=>{
resolve(data.data.list);
});
})
let info = new Promise((resolve, reject) => {
axios.post('/homes/info').then((data)=>{
resolve(data.data.list);
});
})
let faq = new Promise((resolve, reject) => {
axios.post('/homes/faq').then((data)=>{
resolve(data.data.list);
});
})
let house = new Promise((resolve, reject) => {
axios.post('/homes/house').then((data)=>{
resolve(data.data.list);
});
})
Promise.all([swipe, menu, info, faq, house]).then((result)=>{
this.setState({
swipeData: result[0],
menuData: result[1],
infoData: result[2],
faqData: result[3],
houseData: result[4],
menuLoading: true,
swipeLoading: true,
infoLoading: true,
faqLoading: true,
houseLoading: true,
globalLoading: false
})
// this.setState({
// globalLoading: false
// });
})
}
从代码中可以看出,通过Promise.all()方法获取到所有的异步处理的结果,并且将结果保存到this.state中。然后,在render中进行渲染:
render() {
// 轮播图渲染
const swipeLoading = this.state.swipeLoading;
const swipeData = this.state.swipeData;
let swipe = null;
if(swipeLoading) {
swipe = true}
autoPlay={true}
disableSwipe={false}
showThumbnails={false}
items={swipeData} />
}
// 菜单渲染
const menuLoading = this.state.menuLoading;
const menuData = this.state.menuData;
let menu = null;
if(menuLoading) {
let list = menuData.map(item => {
return (
this.handleMenu.bind(this,item.menu_name)} key={item.id}>
{item.menu_name}
)
})
menu = (
4}>
{list}
)
}
// 渲染资讯
let infos = null;
if(this.state.infoLoading) {
infos = this.state.infoData.map(item=>{
return (
item.id}>
限购 ●
{item.info_title}
);
})
}
// 渲染问答
let faq = null;
if(this.state.faqLoading) {
faq = this.state.faqData.map(item=>{
return (
item.question_id}>
{item.question_name}
{item.question_tag.split(',').map((tag,index)=>{return })}
{item.atime} ● {item.qnum}
);
})
}
// 渲染房屋
let newHouse = [];
let oldHouse = [];
let hireHouse = [];
if(this.state.houseLoading) {
this.state.houseData.forEach(item=>{
let listInfo = (
- item.id}>
config.imgBaseUrl+'public/home.png'}/>
{item.home_name}
{item.home_desc}
{item.home_tags.split(',').map((tag,index)=>{return })}
{item.home_price}
);
if(item.home_type === 1) {
newHouse.push(listInfo);
}else if(item.home_type === 2) {
oldHouse.push(listInfo);
}else if(item.home_type === 3) {
hireHouse.push(listInfo)
}
})
}
return (
{this.state.mapShowFlag?this.hideMap}/>:null}
{this.state.calcShowFlag?this.hideCalc}/>:null}
{this.state.searchBarFlag?this.hideSearchBar}/>:null}
this.state.globalLoading} page>
Loading
this.hideSearchBar} onFocus={this.searchHandle} fluid icon={{ name: 'search', circular: true, link: true }} placeholder='搜房源...' />
{swipe}
{menu}
-
config.imgBaseUrl+'public/zixun.png'} />
{infos}
好客问答
{faq}
最新开盘
{newHouse}
二手精选
{oldHouse}
组一个家
{hireHouse}
);
}
也就是说,我们只需要按照前端的请求以及响应数据的结构进行开发接口,即可完成前后端的整合。
在首页中,有轮播广告,需要实现在后台更新数据,前台将数据显示出来。
请求地址
响应
{
"data": {
"list": [
{
"original": "http://127.0.0.1:8086/public/1.png"
},
{
"original": "http://127.0.0.1:8086/public/2.png"
},
{
"original": "http://127.0.0.1:8086/public/3.png"
}
]
},
"meta": {
"status": 200,
"msg": "测试数据"
}
}
从数据结果中可以看出,数据只需要返回图片链接即可。
数据库表设计CREATE TABLE `tb_ad` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`type` int(10) DEFAULT NULL COMMENT '广告类型',
`title` varchar(100) DEFAULT NULL COMMENT '描述',
`url` varchar(200) DEFAULT NULL COMMENT '图片URL地址',
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='广告表'
INSERT INTO `tb_ad` (`id`, `type`, `title`, `url`, `created`, `updated`) VALUES ('1',
'1', 'UniCity万科天空之城', 'http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029097062227.jpg', '2018-11-26 11:28:49',
'2018-11-26 11:28:51');
INSERT INTO `tb_ad` (`id`, `type`, `title`, `url`, `created`, `updated`) VALUES ('2',
'1', '天和尚海庭前', 'http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/1543202958579877.jpg', '2018-11-26 11:29:27',
'2018-11-26 11:29:29');
INSERT INTO `tb_ad` (`id`, `type`, `title`, `url`, `created`, `updated`) VALUES ('3',
'1', '[奉贤 南桥] 光语著', 'http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029946721854.jpg', '2018-11-26 11:30:04',
'2018-11-26 11:30:06');
INSERT INTO `tb_ad` (`id`, `type`, `title`, `url`, `created`, `updated`) VALUES ('4',
'1', '[上海周边 嘉兴] 融创海逸长洲', 'http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029946721854.jpg', '2018-11-26 11:30:49',
'2018-11-26 11:30:53');
实现查询接口(dubbo服务)
创建common工程
创建itcast-haoke-manage-dubbo-server-common工程,将BasePojo、BaseServiceImpl移动至该工程
package cn.itcast.haoke.dubbo.server.pojo;
import lombok.Data;
import java.util.Date;
@Data
public abstract class BasePojo implements java.io.Serializable {
private Date created;
private Date updated;
}
package cn.itcast.haoke.dubbo.server.service; import cn.itcast.haoke.dubbo.server.pojo.BasePojo; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; import java.util.List; public abstract class BaseServiceImpl{ @Autowired private BaseMapper mapper; public T queryById(Long id) { return this.mapper.selectById(id); } public List queryAll() { return this.mapper.selectList(null); } public T queryOne(T record) { return this.mapper.selectOne(new QueryWrapper<>(record)); } public List queryListByWhere(T record) { return this.mapper.selectList(new QueryWrapper<>(record)); } public IPage queryPageListByWhere(T record, Integer page, Integer rows) { // 获取分页数据 return this.mapper.selectPage(new Page (page, rows), new QueryWrapper<> (record)); } public IPage queryPageList(QueryWrapper queryWrapper, Integer page, Integer rows) { // 获取分页数据 return this.mapper.selectPage(new Page (page, rows), queryWrapper); } public Integer save(T record) { record.setCreated(new Date()); record.setUpdated(record.getCreated()); return this.mapper.insert(record); } public Integer update(T record) { record.setUpdated(new Date()); return this.mapper.updateById(record); } public Integer deleteById(Long id) { return this.mapper.deleteById(id); } public Integer deleteByIds(List ids) { return this.mapper.deleteBatchIds(ids); } public Integer deleteByWhere(T record) { return this.mapper.delete(new QueryWrapper<>(record)); } }
其他工程,如itcast-haoke-manage-dubbo-server-house-resources,需要依赖此工程,并且将自己工程中的相关类删除BasePojo.java删除BaseServiceImpl.java
itcast-haoke-manage-dubbo-server-common导入公用依赖
itcast-haoke-manage-dubbo-server cn.itcast.haoke.manage 1.0-SNAPSHOT 4.0.0 itcast-haoke-manage-dubbo-server-common org.projectlombok lombok true com.baomidou mybatis-plus-boot-starter 3.0.5 true
创建三个工程
itcast-haoke-manage-dubbo-server-ad的pom.xml文件:
itcast-haoke-manage-dubbo-server cn.itcast.haoke.manage 1.0-SNAPSHOT 4.0.0 itcast-haoke-manage-dubbo-server-ad pom itcast-haoke-manage-dubbo-server-ad-interface itcast-haoke-manage-dubbo-server-ad-service cn.itcast.haoke.manage itcast-haoke-manage-dubbo-server-common 1.0-SNAPSHOT
itcast-haoke-manage-dubbo-server-ad-service的pom.xml文件
itcast-haoke-manage-dubbo-server-ad cn.itcast.haoke.manage 1.0-SNAPSHOT 4.0.0 itcast-haoke-manage-dubbo-server-ad-service cn.itcast.haoke.manage itcast-haoke-manage-dubbo-server-ad-interface 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-jdbc com.baomidou mybatis-plus-boot-starter 3.0.5 true mysql mysql-connector-java 5.1.47 true
编写pojo
itcast-haoke-manage-dubbo-server-ad-interface
package cn.itcast.haoke.dubbo.server.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@TableName("tb_ad")
public class Ad extends BasePojo {
private static final long serialVersionUID = -493439243433085768L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
//广告类型
private Integer type;
//描述
private String title;
//'图片URL地址
private String url;
}
定义dubbo接口
itcast-haoke-manage-dubbo-server-ad-interface
package cn.itcast.haoke.dubbo.server.api;
import cn.itcast.haoke.dubbo.server.pojo.Ad;
import cn.itcast.haoke.dubbo.server.vo.PageInfo;
public interface ApiAdService {
PageInfo queryAdList(Integer type, Integer page, Integer pageSize);
}
实现dubbo服务
itcast-haoke-manage-dubbo-server-ad-service
package cn.itcast.haoke.dubbo.server.api;
import cn.itcast.haoke.dubbo.server.pojo.Ad;
import cn.itcast.haoke.dubbo.server.service.AdService;
import cn.itcast.haoke.dubbo.server.vo.PageInfo;
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service(version = "1.0.0")
public class ApiAdServiceImpl implements ApiAdService {
@Autowired
private AdService adService;
@Override
public PageInfo queryAdList(Integer type, Integer page, Integer pageSize) {
Ad ad = new Ad();
ad.setType(type);
return this.adService.queryAdList(ad, page, pageSize);
}
}
在itcast-haoke-manage-dubbo-server-common项目中添加PageInfo
package cn.itcast.haoke.dubbo.server.vo; import lombok.AllArgsConstructor; import lombok.Data; import java.util.Collections; import java.util.List; @Data @AllArgsConstructor public class PageInfoimplements java.io.Serializable { private static final long serialVersionUID = -2105385689859184204L; private Integer total; private Integer pageNum; private Integer pageSize; private List records = Collections.emptyList(); }
实现AdService
package cn.itcast.haoke.dubbo.server.service;
import cn.itcast.haoke.dubbo.server.pojo.Ad;
import cn.itcast.haoke.dubbo.server.vo.PageInfo;
public interface AdService {
PageInfo queryAdList(Ad ad, Integer page, Integer pageSize);
}
package cn.itcast.haoke.dubbo.server.service.impl; import cn.itcast.haoke.dubbo.server.pojo.Ad; import cn.itcast.haoke.dubbo.server.service.AdService; import cn.itcast.haoke.dubbo.server.service.BaseServiceImpl; import cn.itcast.haoke.dubbo.server.vo.PageInfo; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import org.springframework.stereotype.Service; @Service public class AdServiceImpl extends BaseServiceImplimplements AdService { @Override public PageInfo queryAdList(Ad ad, Integer page, Integer pageSize) { QueryWrapper queryWrapper = new QueryWrapper(); // 排序 queryWrapper.orderByDesc("updated"); // 查询的条件 queryWrapper.eq("type", ad.getType()); IPage iPage = super.queryPageList(queryWrapper, page, pageSize); return new PageInfo<>(Long.valueOf(iPage.getTotal()).intValue(), page, pageSize, iPage.getRecords()); } }
创建AdMapper接口(其实AdServiceImpl extends BaseServiceImpl已经完全够了)
itcast-haoke-manage-dubbo-server-ad-service
package cn.itcast.haoke.dubbo.server.mapper; import cn.itcast.haoke.dubbo.server.pojo.Ad; import com.baomidou.mybatisplus.core.mapper.BaseMapper; public interface AdMapper extends BaseMapper{ }
编写MybatisConfig
itcast-haoke-manage-dubbo-server-ad-service
package cn.itcast.haoke.dubbo.server.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@MapperScan("cn.itcast.haoke.dubbo.server.mapper")
@Configuration
public class MybatisConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
编写application.properties配置文件
# Spring boot application spring.application.name = itcast-haoke-manage-dubbo-server-ad spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.58.136:3306/haoke?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false spring.datasource.username=root spring.datasource.password=root dubbo.scan.basePackages = cn.itcast.haoke.dubbo.server.api dubbo.application.name = dubbo-provider-ad dubbo.protocol.name = dubbo dubbo.protocol.port = 21880 dubbo.registry.address = zookeeper://192.168.58.136:2181 dubbo.registry.client = zkclient
编写启动类AdDubboProvider.java
package cn.itcast.haoke.dubbo.server;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@SpringBootApplication
public class AdDubboProvider {
public static void main(String[] args) {
new SpringApplicationBuilder(AdDubboProvider.class)
.web(WebApplicationType.NONE) // 非 Web 应用
.run(args);
}
}
启动进行测试AdDubboProvider.java
已经完成了注册。
引入依赖
cn.itcast.haoke.manage itcast-haoke-manage-dubbo-server-ad-interface 1.0-SNAPSHOT
编写Controller
package cn.itcast.haoke.dubbo.api.controller;
import cn.itcast.haoke.dubbo.api.service.AdService;
import cn.itcast.haoke.dubbo.api.vo.WebResult;
import cn.itcast.haoke.dubbo.server.pojo.Ad;
import cn.itcast.haoke.dubbo.server.vo.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RequestMapping("ad")
@RestController
@CrossOrigin
public class AdController {
@Autowired
private AdService adService;
@GetMapping
public WebResult queryIndexAd(){
PageInfo adPageInfo = this.adService.queryAdList(1, 1, 3);
List records = adPageInfo.getRecords();
List
编写Service
package cn.itcast.haoke.dubbo.api.service;
import cn.itcast.haoke.dubbo.server.api.ApiAdService;
import cn.itcast.haoke.dubbo.server.pojo.Ad;
import cn.itcast.haoke.dubbo.server.vo.PageInfo;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
@Service
public class AdService {
@Reference(version = "1.0.0")
private ApiAdService apiAdService;
public PageInfo queryAdList(Integer type, Integer page, Integer pageSize) {
return this.apiAdService.queryAdList(type, page, pageSize);
}
}
编写WebResult
WebResult用于和前端系统交互的数据结构定义。
package cn.itcast.haoke.dubbo.api.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WebResult {
@JsonIgnore
private Integer status;
@JsonIgnore
private String msg;
@JsonIgnore
private List> list;
@JsonIgnore
public static WebResult ok(List> list) {
return new WebResult(200, "成功!", list);
}
@JsonIgnore
public static WebResult ok(List> list, String msg) {
return new WebResult(200, msg, list);
}
public Map getData() {
Map map = new HashMap<>();
map.put("list", this.list);
return map;
}
public Map getMeta() {
Map map = new HashMap<>();
map.put("status", status);
map.put("msg", msg);
return map;
}
}
测试
GET:http://localhost:18080/ad
响应
{
"data": {
"list": [
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029946721854.jpg"
},
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029946721854.jpg"
},
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/1543202958579877.jpg"
}
]
},
"meta": {
"msg": "成功!",
"status": 200
}
}
整合前端系统itcast-haoke-web
修改src/modules/home/home.js文件中请求地址:
let swipe = new Promise((resolve, reject) => {
// axios.post('/homes/swipe').then((data)=>{
axios.get('http://127.0.0.1:18080/ad').then((data)=>{
resolve(data.data.list);
});
})
进行测试:
itcast-haoke-manage-dubbo-server-ad-service启动类启动
itcast-haoke-manage-api-server启动类启动
itcast-haoke-web在控制台输入
npm start
itcast-haoke-web-api项目所在根文件夹启动cmd,输入命令
npm run dev
发现,没有对CORS进行支持,所以需要在服务端进行配置:
itcast-haoke-manage-api-server的cn/itcast/haoke/dubbo/api/controller/AdController.java
添加@CrossOrigin注解的支持,表明该请求地址下都支持跨域。
之前的数据结构是这样的:
{
"data": {
"list": [
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029946721854.jpg"
},
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029946721854.jpg"
},
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/1543202958579877.jpg"
}
]
},
"meta": {
"msg": "成功!",
"status": 200
}
}
结合轮播图组件的需求,只需要返回list数组,并且每个对象包含original字段即可。
优化后的结构为:
{
"list": [
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432030275359146.jpg"
},
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029946721854.jpg"
},
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/1543202958579877.jpg"
}
]
}
编写haoke.graphqls
schema {
query: HaokeQuery
}
type HaokeQuery {
HouseResources(id:Long) : HouseResources
HouseResourcesList(page:Int, pageSize:Int) : TableResult
IndexAdList:IndexAdResult
}
scalar Long
type HouseResources {
id:Long!
title:String
estateId:Long
buildingNum:String
buildingUnit:String
buildingFloorNum:String
rent:Int
rentMethod:Int
paymentMethod:Int
houseType:String
coveredArea:String
useArea:String
floor:String
orientation:String
decoration:Int
facilities:String
pic:String
houseDesc:String
contact:String
mobile:String
time:Int
propertyCost:String
}
type TableResult{
list:[HouseResources]
pagination:Pagination
}
type Pagination{
current:Int
pageSize:Int
total:Int
}
type IndexAdResult{
list:[IndexAdResultData]
}
type IndexAdResultData{
original:String
}
根据GraphQL结构编写vo
package cn.itcast.haoke.dubbo.api.vo.ad.index;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IndexAdResult {
private List list;
}
package cn.itcast.haoke.dubbo.api.vo.ad.index;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IndexAdResultData {
private String original;
}
编写AdDataFetcher
编写cn/itcast/haoke/dubbo/api/graphql/AdDataFetcher.java用于广告数据的查询。
package cn.itcast.haoke.dubbo.api.graphql;
import cn.itcast.haoke.dubbo.api.service.AdService;
import cn.itcast.haoke.dubbo.api.vo.ad.index.IndexAdResult;
import cn.itcast.haoke.dubbo.api.vo.ad.index.IndexAdResultData;
import cn.itcast.haoke.dubbo.server.pojo.Ad;
import cn.itcast.haoke.dubbo.server.vo.PageInfo;
import graphql.schema.DataFetchingEnvironment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class AdDataFetcher implements MyDataFetcher {
@Autowired
private AdService adService;
@Override
public String fieldName() {
return "IndexAdList";
}
@Override
public Object dataFetcher(DataFetchingEnvironment environment) {
PageInfo pageInfo = this.adService.queryAdList(1, 1, 3);
List ads = pageInfo.getRecords();
List list = new ArrayList<>();
for (Ad ad : ads) {
list.add(new IndexAdResultData(ad.getUrl()));
}
return new IndexAdResult(list);
}
}
测试
//POST url
http://localhost:18080/graphql
//请求
{
IndexAdList{
list{
original
}
}
}
//响应
{
"data": {
"IndexAdList": {
"list": [
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029946721854.jpg"
},
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/15432029946721854.jpg"
},
{
"original": "http://itcast-haoke.oss-cnqingdao.aliyuncs.com/images/2018/11/26/1543202958579877.jpg"
}
]
}
}
}
GraphQL客户端Apollo Client
itcast-haoke-web安装依赖
npm install apollo-boost react-apollo graphql --save
src/modules/home/home.js创建客户端
import ApolloClient from "apollo-boost";
const client = new ApolloClient({
uri: "http://127.0.0.1:18080/graphql"
});
创建查询
import gql from "graphql-tag";
//定义查询
const GET_INDEX_ADS = gql`
{
IndexAdList{
list{
original
}
}
}
`;
let swipe = new Promise((resolve, reject) => {
client.query({query: GET_INDEX_ADS}).then(result =>
resolve(result.data.IndexAdList.list));
})
测试
发现有2个问题:
- GraphQL服务没有支持cross
- Apollo Client发起的数据请求为POST请求,现在实现的GraphQL仅仅实现了GET请求处理
解决它
package cn.itcast.haoke.dubbo.api.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import graphql.GraphQL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RequestMapping("graphql")
@Controller
@CrossOrigin
public class GraphQLController {
@Autowired
private GraphQL graphQL;
private static final ObjectMapper MAPPER = new ObjectMapper();
@GetMapping
@ResponseBody
public Map query(@RequestParam("query") String query) {
return this.graphQL.execute(query).toSpecification();
}
@PostMapping
@ResponseBody
public Map postQuery(@RequestBody String json) {
try {
JsonNode jsonNode = MAPPER.readTree(json);
if(jsonNode.has("query")){
String query = jsonNode.get("query").textValue();
return this.graphQL.execute(query).toSpecification();
}
} catch (IOException e) {
e.printStackTrace();
}
Map error = new HashMap<>();
error.put("status", 500);
error.put("msg", "查询出错");
return error;
}
}
成功获取到数据,页面效果也实现了
手机APP、管理系统后端
链接:https://pan.baidu.com/s/1UqwZMKd-rqycXSdhaMhtcA?pwd=mtce
提取码:mtce
–来自百度网盘超级会员V4的分享
手机APP前端
链接:https://pan.baidu.com/s/1gw33MAUqBpz-FjflbvupYA?pwd=mwax
提取码:mwax
–来自百度网盘超级会员V4的分享
手机APP后端(node.js开发的)
链接:https://pan.baidu.com/s/1l-FX21Cza7Gx1lEC2MrEgw?pwd=6onp
提取码:6onp
–来自百度网盘超级会员V4的分享



