1. 程式人生 > 實用技巧 >第1章 - 專案介紹和工程搭建

第1章 - 專案介紹和工程搭建

第1章 - 專案介紹和工程搭建
學習目標:

瞭解十次方專案需求;
瞭解前後端分離開發模式
理解RESTful
完成專案開發環境搭建
完成父工程、公共模組和文章微服務的搭建
掌握mybatis plus的使用,並開發完成文章微服務中文章的增刪改查功能
掌握公共異常處理類的使用

1 十次方專案需求分析
1.1 專案介紹
十次方是程式設計師的專屬社交平臺,包括頭條、問答、活動、交友、吐槽、招聘六大頻道。

1557476206791

十次方名稱的由來:2的10次方為1024,程式設計師都懂的。

如果你是一位技術大咖,那麼趕快釋出文章,增加知名度吧。

如果你是一名技術小白,那麼趕快到問答頻道尋求幫助的,這裡高手如雲哦!

如果你不想錯過各種技術交流會,那麼請經常關注活動頻道吧。

如果你還是單身,那麼趕快到交友頻道找到你心儀的另一半。

如果你有太多的苦惱,那麼趕快吐個槽吧。

如果你正在找工作或是想跳槽拿高薪,那麼來招聘頻道淘金吧。

1.2 專案需求
詳見: 資源\文件\十次方需求規格說明書.docx

2 系統設計
2.1 開發模式
十次方專案採用前後端分離的開發模式

2.2 技術選型
後端:springboot + springcloud + mybatis plus + mysql5.7

前端:nodejs + NUXT + elementUI + vue

2.3 技術架構
採用前後端分離的系統架構

1557635014480

2.4 微服務模組劃分
模組名稱 模組中文名稱
tensquare_common 公共模組
tensquare_base 基礎微服務
tensquare_article 文章微服務
tensquare_friend 交友微服務
tensquare_gathering 活動微服務
tensquare_qa 問答微服務
tensquare_recruit 招聘微服務
tensquare_user 使用者微服務
tensquare_spit 吐槽微服務
tensquare_search 搜尋微服務
tensquare_web 前臺微服務閘道器
tensquare_manager 後臺微服務閘道器
tensquare_eureka 註冊中心
tensquare_config 配置中心
tensquare_sms 簡訊微服務
tensquare_notice 訊息通知微服務

2.5 資料庫表結構分析
採用的分庫分表設計,每個微服務模組為1個獨立的資料庫。

tensquare_article 文章

tensquare_base 基礎

tensquare_friend 交友

tensquare_gathering 活動

tensquare_qa 問答

tensquare_recruit 招聘

tensquare_user 使用者

tensquare_spit 吐槽

詳見 資源\文件\十次方資料庫文件.xlsx

2.6 API文件
課程提供了前後端開發介面文件(採用Swagger語言進行編寫),並與Nginx進行了整 合。雙擊Nginx執行檔案啟動後,在位址列輸入http://localhost:801 即可訪問API文件

前後端約定的返回碼列表:

狀態描述 返回碼
成功 20000
失敗 20001
使用者名稱或密碼錯誤 20002
許可權不足 20003
遠端呼叫失敗 20004
重複操作 20005

3 RESTful架構說明
3.1 何為RESTful
RESTful架構是目前最流行的一種網際網路軟體架構

是Roy Thomas Fielding在他2000年的博士論文中提出的

是Representational State Transfer的縮寫,翻譯過來是”表現層狀態轉化”

是所有Web應用都應該遵守的架構設計指導原則

7個HTTP方法:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS

3.2 介面規範
十次方專案使用GET、POST、PUT、DELETE四種方法

冪等性:不論你請求多少次,資源的狀態是一樣的。

3.2.1 GET
安全且冪等
獲取表示
變更時獲取表示(快取)
適合查詢類的介面使用

3.2.2 POST
不安全且不冪等
使用服務端管理的(自動產生)的例項號建立資源
建立子資源
部分更新資源
如果沒有被修改,則不過更新資源(樂觀鎖)
適合資料提交類的介面使用

3.2.3 PUT
不安全但冪等
用客戶端管理的例項號建立一個資源
通過替換的方式更新資源
如果未被修改,則更新資源(樂觀鎖)
適合更新資料的介面使用

3.2.4 DELETE
不安全但冪等
刪除資源
適合刪除資料的介面使用

請求返回響應碼:

程式碼 含義
200 (OK)- 如果現有資源已被更改
201 (created)- 如果新資源被建立
202 (accepted)- 已接受處理請求但尚未完成(非同步處理)
301 (Moved Permanently)- 資源的URI被更新
303 (See Other)- 其他(如,負載均衡)
400 (bad request)- 指代壞請求
404 (not found)- 資源不存在
406 (not acceptable)- 服務端不支援所需表示
409 (conflict)- 通用衝突
412 (Precondition Failed)- 前置條件失敗(如執行條件更新時的衝突)
415 (unsupported media type)- 接受到的表示不受支援
500 (internal server error)- 通用錯誤響應
503 (Service Unavailable)- 服務當前無法處理請求

4 專案開發準備
4.1 開發環境
虛擬系統環境 VMware Workstation
虛擬機器系統 CentOS 7
容器 docker
JDK1.8
資料庫 mysql 5.7
開發工具 idea
專案構建工具 maven
所有的第三方工具如mysql等都是執行在docker容器中的

注:虛擬機器的帳戶名root 密碼itcast

4.2 mysql建庫建表
進入安裝了docker的虛擬機器中,按以下順序執行命令

(1)下載映象(此步可省略)

docker pull centos/mysql‐57‐centos7
注:docker預設從國外的映象網站拉取映象,速度很慢。可以使用國內的阿里雲映象加速站點提升映象拉取速度。具體步驟可以參考文件

docker配置國內映象加速站點.pdf

(2)建立容器

docker run -di --name=tensquare_mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root centos/mysql-57-centos7
(3)連線MYSQL ,並執行資料中的建表指令碼,建立article資料庫

4.3 介面測試工具postman
postman是一款強大網頁除錯工具

能夠傳送任何型別的HTTP 請求 (GET,HEAD, POST,PUT。。。)
附帶任意數量的引數

5 專案工程搭建
5.1 父工程搭建
建立專案型別為maven的父工程

開啟idea開發工具

選擇選單file-new project ,彈出視窗中左側選單選擇Maven ,點選next按鈕

1557482311840

GroupId填寫com.tensquare,ArtifacetId填寫tensquare_parent,點選next按鈕

1557482444471

點選Finish 完成

修改pom.xml檔案,新增以下內容

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
    <relativePath/>
</parent>



<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<mybatisplus-spring-boot-starter.version>1.0.5</mybatisplus-spring-boot-starter.version>
<mybatisplus.version>2.2.0</mybatisplus.version>
<fastjson.version>1.2.39</fastjson.version>
<gson.version>2.8.0</gson.version>




org.springframework.boot
spring-boot-starter-web


org.springframework.boot
spring-boot-starter-test
test

<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>




spring-snapshots
Spring Snapshots
https://repo.spring.io/snapshot

true



spring-milestones
Spring Milestones
https://repo.spring.io/milestone

false


5.2 搭建公共子模組
5.2.1 搭建子模組步驟
右鍵點選父工程tensquare_parent,選擇 New -> Module 彈出視窗選擇Maven ,點選next按鈕

ArtifacetId填寫tensquare_common,點選next按鈕

1557487423299

點選finish

5.2.2 建立公共實體類和工具類
新建com.tensquare.entity包,包下建立Result類,用於controller返回結果

public class Result {
private boolean flag;//是否成功
private Integer code;// 返回碼
private String message;//返回資訊
private Object data;// 返回資料

public Result(boolean flag, Integer code, String message, Object data) {

super();
this.flag = flag;
this.code = code;
this.message = message;
this.data = data;
}

public Result() { }

public Result(boolean flag, Integer code, String message) {    
    super();        
    this.flag = flag;        
    this.code = code;        
    this.message = message;        
}    

public boolean isFlag() {    
    return flag;        
}    


public void setFlag(boolean flag) {
this.flag = flag;
}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public Object getData() {
return data;
}

public void setData(Object data) {
this.data = data;
}
}

建立類PageResult ,用於返回分頁結果

public class PageResult {
private Long total;
private List rows;

public PageResult(Long  total,  List<T>  rows)  {
    super();
    this.total  =  total;
    this.rows  =  rows;
}

//getter  and  setter  ....

}

返回碼定義類

public class StatusCode {
public static final int OK=20000;//成功
public static final int ERROR =20001;//失敗
public static final int LOGINERROR =20002;//使用者名稱或密碼錯誤
public static final int ACCESSERROR =20003;//許可權不足
public static final int REMOTEERROR =20004;//遠端呼叫失敗
public static final int REPERROR =20005;//重複操作
}

分散式ID生成器

課程中已經提供了分散式ID生成器

資源\微服務相關\工具類\IdWorker.java

tensquare_common工程建立util包,將IdWorker.java直接拷貝到tensquare_common工程的util包中。

不能使用資料庫本身的自增功能來產生主鍵值,原因是生產環境為分片部署的。

使用snowflake (雪花)演算法(twitter出品)生成唯一的主鍵值

1557488319373

41bit的時間戳可以支援該演算法使用到2082年
10bit的工作機器id可以支援1024臺機器
序列號支援1毫秒產生4096個自增序列id
整體上按照時間自增排序
整個分散式系統內不會產生ID碰撞
每秒能夠產生26萬ID左右

6 文章微服務-文章管理
6.1 模組搭建
在tensquare_parent專案下建立tensquare_article模組,建立過程參考第4.2節公共子模組的建立過程

修改tensquare_article模組的pom.xml檔案,新增以下依賴

mysql mysql-connector-java com.tensquare tensquare_common 1.0-SNAPSHOT 建立com.tensquare.article包,並建立BaseApplication啟動類

package com.tensquare.article;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import util.IdWorker;

@SpringBootApplication
public class ArticleApplication {
public static void main(String[] args) {
SpringApplication.run(ArticleApplication.class, args);
}

@Bean
public IdWorker idWorker(){
        return new IdWorker(1,1);
}

}
在resources資料夾下建立application.yml,並新增以下內容

server:
port: 9004
spring:
application:
name: tensquare-article #指定服務名
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.200.129:3306/tensquare_article?characterEncoding=utf-8
username: root
password: root

6.2 文章管理-CRUD
6.2.1 表結構分析
tensquare_article資料庫,tb_article表

文章表 tb_article
欄位名稱 欄位含義 欄位型別 備註
id ID 文字
columnid 專欄ID 文字
userid 使用者ID 文字
title 文章標題 文字
content 文章內容 文字
image 文章封面 文字
createtime 發表日期 日期
updatetime 修改日期 日期
ispublic 是否公開 文字 0:不公開
istop 是否置頂 文字 0:不置頂
visits 瀏覽量 整型
thumbup 點贊數 整型
comment 評論數 整型
state 稽核狀態 文字 0:未稽核 1:已稽核
channelid 所屬頻道 整型 關聯頻道表ID
url URL地址 文字
type 文章型別 文字 0:分享

6.2.2 整合mybatis plus
mybatis plus概述

是對Mybatis框架的二次封裝和擴充套件
純正血統:完全繼承原生 Mybatis 的所有特性
最少依賴:僅僅依賴Mybatis以及Mybatis-Spring
效能損耗小:啟動即會自動注入基本CURD ,效能無損耗,直接面向物件操作
自動熱載入:Mapper對應的xml可以熱載入,大大減少重啟Web伺服器時間,提升開發效率
效能分析:自帶Sql效能分析外掛,開發測試時,能有效解決慢查詢
全域性攔截:提供全表delete、update操作智慧分析阻斷
避免Sql注入:內建Sql注入內容剝離器,預防Sql注入攻擊
在pom.xml檔案中引入相關依賴

    <!-- mybatis-plus begin -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatisplus-spring-boot-starter</artifactId>
        <version>${mybatisplus-spring-boot-starter.version}</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus</artifactId>
        <version>${mybatisplus.version}</version>
    </dependency>
    <!-- mybatis-plus end -->

在配置檔案application.yml中新增相關配置

Mybatis-Plus 配置

mybatis-plus:

mapper-locations: classpath:/mapper/*Mapper.xml

實體掃描,多個package用逗號或者分號分隔

typeAliasesPackage: com.tensquare.article.pojo
global-config:
id-type: 1 #0:資料庫ID自增 1:使用者輸入id
db-column-underline: false
refresh-mapper: true
configuration:
map-underscore-to-camel-case: true
cache-enabled: true #配置的快取的全域性開關
lazyLoadingEnabled: true #延時載入的開關
multipleResultSetsEnabled: true #開啟延時載入,否則按需載入屬性
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #列印sql語句,除錯用

修改啟動類,增加Mapper掃描註解

@SpringBootApplication
//Mapper掃描註解
@MapperScan("com.tensquare.article.dao")
public class ArticleApplication {
public static void main(String[] args) {
SpringApplication.run(ArticleApplication.class, args);
}

@Bean
public IdWorker idWorker() {
return new IdWorker(1, 1);
}
}

6.2.3 實現查詢所有文章和根據id號查詢文章功能
在com.tensquare.article包下面建立pojo包,並建立Article實體類

package com.tensquare.article.pojo;

import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;

import java.io.Serializable;
import java.util.Date;

@TableName("tb_article")
public class Article implements Serializable {

@TableId(type = IdType.INPUT)
private String id;//ID

private String columnid; //專欄ID
private String userid; //使用者ID
private String title; //標題
private String content; //文章正文
private String image; //文章封面
private Date createtime; //發表日期
private Date updatetime; //修改日期
private String ispublic; //是否公開
private String istop; //是否置頂
private Integer visits; //瀏覽量
private Integer thumbup; //點贊數
private Integer comment; //評論數
private String state; //稽核狀態
private String channelid; //所屬頻道
private String url; //URL
private String type; //型別

//getters and setters
}

編寫資料訪問介面dao

public interface ArticleDao extends BaseMapper

{
}

編寫service

@Service
public class ArticleService {

@Autowired
private ArticleDao articleDao;

public List

findAll() {
return articleDao.selectList(null);
}

public Article findById(String id) {
return articleDao.selectById(id);
}
}
編寫controller

@RestController
@RequestMapping("/article")
public class ArticleController {

@Autowired
private ArticleService articleService;

@RequestMapping(method = RequestMethod.GET)
public Result findAll() {
List list = articleService.findAll();
return new Result(true, StatusCode.OK, "查詢成功", list);
}

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Result findById(@PathVariable String id) {
Article Article = articleService.findById(id);
return new Result(true, StatusCode.OK, "查詢成功", Article);
}
}

6.2.4 新增文章、修改文章和刪除文章
新增文章 ArticleController中新增程式碼

//新增標籤資料介面
@RequestMapping(method = RequestMethod.POST)
public Result add(@RequestBody Article article) {
articleService.add(article);

return new Result(true, StatusCode.OK, "新增成功");
}
ArticleService中新增程式碼

@Autowired
private IdWorker idWorker;

public void add(Article article) {
article.setId(idWorker.nextId() + "");
articleDao.insert(article);
}

修改文章

ArticleController中新增程式碼

//修改標籤資料介面
@RequestMapping(value = "{id}", method = RequestMethod.PUT)
public Result update(@PathVariable String id, @RequestBody Article article) {
article.setId(id);
articleService.update(article);
return new Result(true, StatusCode.OK, "修改成功");
}
ArticleService中新增程式碼

public void update(Article article) {
//根據id號更新
//方法1
articleDao.updateById(article);

//方法2
EntityWrapper wrapper = new EntityWrapper

();
wrapper.eq("id", article.getId());
articleDao.update(article, wrapper);
}

刪除文章

ArticleController中新增程式碼

//刪除文章資料介面
@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public Result delete(@PathVariable String id) {
articleService.delete(id);

return new Result(true, StatusCode.OK, "刪除成功");
}
ArticleService中新增程式碼

public void delete(String id) {
articleDao.deleteById(id);
}
6.2.5 條件查詢和分頁
條件查詢

使用Mybatis Plus 提供的EntityWrapper物件封裝where查詢條件,例如以下使用方式:

EntityWrapper wrapper = new EntityWrapper

();
wrapper.eq("id", article.getId());

//動態sql,例如 and field='xxx'
wrapper.eq(null != map.get(field), field, map.get(field));
分頁

使用 Mybatis Plus 提供的Page物件
向Mybatis Plus中注入PaginationInterceptor外掛
新建config包,建立MybatisPlusConfig物件,新增下面的程式碼
@Configuration
public class MybatisPlusConfig {

@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
完整程式碼

ArticleController中新增程式碼

 @RequestMapping(value="/search/{page}/{size}", method = RequestMethod.POST)
public Result search(@RequestBody Map map, @PathVariable int page, @PathVariable int size) {
    Page page1 = articleService.search(map, page, size);
    return new Result(true, StatusCode.OK, "查詢成功", new PageResult((int) page1.getTotal(), page1.getRecords()));
}

ArticleService中新增程式碼

public Page search(Map map, int page, int size) {
EntityWrapper wrapper = new EntityWrapper

();
Set fieldSet = map.keySet();
for(String field : fieldSet) {
//wrapper.eq(field, map.get(field));
wrapper.eq(null != map.get(field), field, map.get(field));
}

Page page1 = new Page(page, size);
List list = articleDao.selectPage(page1, wrapper);
page1.setRecords(list);
return page1;
}

6.3 公共異常處理類
為了使程式碼容易維護,減少冗餘,我們建立一個類集中處理異常

在com.tensquare.user.controller包下建立公共異常處理類BaseExceptionHandler,並新增程式碼

@ControllerAdvice
public class BaseExceptionHandler {

@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result error(Exception e) {
e.printStackTrace();
return new Result(false, StatusCode.ERROR, e.getMessage());
}
}
ArticleController中新增測試程式碼

@RequestMapping(value="/exception", method = RequestMethod.GET)
public Result exception() throws Exception {
throw new Exception("測試統一異常處理");
}

6.4 跨域處理
何謂跨域

瀏覽器從一個域名的網頁去請求另一個域名的資源時,域名、埠、協議任一不同,都是跨域 。

十次方專案是採用前後端分離開發的,也是前後端分離部署的,必然會存在跨域問題。

如何解決跨域

只需要在controller類上添加註解@CrossOrigin即可!