栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java开发全终端实战租房项目-开发GraphQL服务以及前台系统搭建

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java开发全终端实战租房项目-开发GraphQL服务以及前台系统搭建

使用GraphQL开发房源接口 实现根据id查询房源的dubbo服务

定义接口方法
在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


            com.graphql-java
            graphql-java
            11.0
        
编写haoke.graphqls文件

在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进行发布

搭建工程(前台系统前端)itcast-haoke-web

第一步,将资料中的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}
    ); }

    也就是说,我们只需要按照前端的请求以及响应数据的结构进行开发接口,即可完成前后端的整合。

    首页home.js轮播广告

    在首页中,有轮播广告,需要实现在后台更新数据,前台将数据显示出来。

    查看数据结构

    请求地址

    响应

    {
    "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 PageInfo implements 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 BaseServiceImpl implements 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

    已经完成了注册。

    实现api接口服务(RESTful接口)

    引入依赖

    
                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 result = new ArrayList<>();
            for (Ad ad : records) {
                Map map = new HashMap<>();
                map.put("original", ad.getUrl());
                result.add(map);
            }
    
            return WebResult.ok(result);
    }
    
    }
    
     
    

    编写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注解的支持,表明该请求地址下都支持跨域。

    广告服务的GraphQL接口 数据结构优化

    之前的数据结构是这样的:

    {
        "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个问题:

    1. GraphQL服务没有支持cross
    2. 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的分享

    转载请注明:文章转载自 www.mshxw.com
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号