十次方社交项目-项目介绍和工程搭建1

一、十次方项目需求分析

1. 项目介绍

 十次方是程序员的专属社交平台,包括头条、问答、活动、交友、吐槽、招聘六大频道。

  image

 十次方名称的由来:2的10次方为1024,程序员都懂的。

 如果你是一位技术大咖,那么赶快发布文章,增加知名度吧。

 如果你是一名技术小白,那么赶快到问答频道寻求帮助的,这里高手如云哦!

 如果你不想错过各种技术交流会,那么请经常关注活动频道吧。

 如果你还是单身,那么赶快到交友频道找到你心仪的另一半。

 如果你有太多的苦恼,那么赶快吐个槽吧。

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

2. 项目需求

二、系统设计

1. 开发模式

 十次方项目采用前后端分离的开发模式

2. 技术选型

 后端:springboot + springcloud + mybatis plus + mysql5.7

 前端:nodejs + NUXT + elementUI + vue

3. 技术架构

 采用前后端分离的系统架构

  image

4. 微服务模块划分

 image

5. 数据库表结构分析

 采用的分库分表设计,每个微服务模块为1个独立的数据库。

  tensquare_article 文章

  tensquare_base 基础

  tensquare_friend 交友

  tensquare_gathering 活动

  tensquare_qa 问答

  tensquare_recruit 招聘

  tensquare_user 用户

  tensquare_spit 吐槽

6. API文档

 课程提供了前后端开发接口文档(采用Swagger语言进行编写),并与Nginx进行了整合。

 双击Nginx执行文件启动后,在地址栏输入http://localhost:801 即可访问API文档。

 前后端约定的返回码列表:

  image

三、RESTful架构说明

1. 何为RESTful

 RESTful架构是目前最流行的一种互联网软件架构。是Roy Thomas Fielding在他2000年的博士论文中提出的;是Representational State Transfer的缩写,翻译过来是”表现层状态转化”;是所有Web应用都应该遵守的架构设计指导原则。

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

2. 接口规范

 十次方项目使用GET、POST、PUT、DELETE四种方法

 幂等性:不论你请求多少次,资源的状态是一样的

 2.1 GET

  • 安全且幂等
  • 获取表示
  • 变更时获取表示(缓存)

  ==适合查询类的接口使用==

 2.2 POST

  • 不安全且不幂等
  • 使用服务端管理的(自动产生)的实例号创建资源
  • 创建子资源
  • 部分更新资源
  • 如果没有被修改,则不过更新资源(乐观锁)

  ==适合数据提交类的接口使用==

 2.3 PUT

  • 不安全但幂等
  • 用客户端管理的实例号创建一个资源
  • 通过替换的方式更新资源
  • 如果未被修改,则更新资源(乐观锁)

  ==适合更新数据的接口使用==

 2.4 DELETE

  • 不安全但幂等
  • 删除资源

  ==适合删除数据的接口使用==

3. 请求返回响应码

 image

四、项目开发准备

1. 开发环境

  • 虚拟系统环境 VMware Workstation
  • 虚拟机系统 CentOS 7
  • 容器 docker
  • JDK1.8
  • 数据库 mysql 5.7
  • 开发工具 idea
  • 项目构建工具 maven

 所有的第三方工具如mysql等都是运行在docker容器中的

 注:虚拟机的帐户名root 密码itcast

2. mysql建库建表

 进入安装了docker的虚拟机中,按以下顺序执行命令

(1)下载镜像(此步可省略)

docker pull centos/mysql-57-centos7

  注:docker默认从国外的镜像网站拉取镜像,速度很慢。可以使用国内的阿里云镜像加速站点提升镜像拉取速度。

(2)创建容器

docker run -di --name=tensquare_mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root centos/mysql-57-centos7

(3)连接MYSQL ,并执行资料中的建表脚本,创建article数据库

3. 接口测试工具postman

 postman是一款强大网页调试工具。

  • 能够发送任何类型的HTTP 请求 (GET,HEAD, POST,PUT。。。)
  • 附带任意数量的参数

五、项目工程搭建

1. 父工程搭建

 创建项目类型为maven的父工程

  1.1 打开idea开发工具

  1.2 选择菜单file-new project ,弹出窗口中左侧菜单选择Maven ,点击next按钮

  image

  1.3 GroupId填写com.tensquare,ArtifacetId填写tensquare_parent,点击next 按钮

  image

  1.4 点击Finish 完成

  1.5 修改pom.xml文件,添加以下内容

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tensquare</groupId>
    <artifactId>tensquare_parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>tensquare_common</module>
        <module>tensquare_article</module>
        <module>mongo_demo</module>
        <module>tesquare_user</module>
        <module>tensquare_eureka</module>
        <module>tensquare_encrypt</module>
        <module>tensquare_notice</module>
        <module>netty_demo</module>
    </modules>

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

    <properties>
        <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>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <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>

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

</project>
View Code

2. 搭建公共子模块

 2.1 搭建子模块步骤

  1、右键点击父工程tensquare_parent,选择 New -> Module 弹出窗口选择 Maven ,点击next按钮

  2、ArtifacetId填写tensquare_common,点击next按钮

  image

  3、点击finish 

 2.2 创建公共实体类和工具类

  1、新建com.tensquare.entity包,包下创建Result类,用于controller返回结果

package com.tensquare.entity;

public class Result {
    private boolean flag;//是否成功
    private Integer code;//返回码
    private String message;//返回信息
    private Object data;//返回数据

    public Result() {
    }

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

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

    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;
    }
}
View Code

  2、创建类PageResult ,用于返回分页结果

package com.tensquare.entity;

import java.util.List;

/**
 * 返回分页结果
 * @param <T>
 */
public class PageResult<T> {

    private Long total;
    private List<T> rows;

    public PageResult() {
    }

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

    public Long getTotal() {
        return total;
    }

    public void setTotal(Long total) {
        this.total = total;
    }

    public List<T> getRows() {
        return rows;
    }

    public void setRows(List<T> rows) {
        this.rows = rows;
    }

    @Override
    public String toString() {
        return "PageResult{" +
                "total=" + total +
                ", rows=" + rows +
                '}';
    }
}
View Code

  3、返回码定义类

package com.tensquare.entity;

/**
 * 返回码定义类
 */
public class StatusCode {
    public static final int OK=200;//成功
    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;//重复操作
}
View Code

  4、分布式ID生成器 

   tensquare_common工程创建util包,将IdWorker.java直接拷贝到tensquare_common工程的util包中。

package com.tensquare.util;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;

public class IdWorker {
    // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
    private final static long twepoch = 1288834974657L;
    // 机器标识位数
    private final static long workerIdBits = 5L;
    // 数据中心标识位数
    private final static long datacenterIdBits = 5L;
    // 机器ID最大值
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    // 数据中心ID最大值
    private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    // 毫秒内自增位
    private final static long sequenceBits = 12L;
    // 机器ID偏左移12位
    private final static long workerIdShift = sequenceBits;
    // 数据中心ID左移17位
    private final static long datacenterIdShift = sequenceBits + workerIdBits;
    // 时间毫秒左移22位
    private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    /* 上次生产id时间戳 */
    private static long lastTimestamp = -1L;
    // 0,并发控制
    private long sequence = 0L;

    private final long workerId;
    // 数据标识id部分
    private final long datacenterId;

    public IdWorker(){
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
    }
    /**
     * @param workerId
     *            工作机器ID
     * @param datacenterId
     *            序列号
     */
    public IdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    /**
     * 获取下一个ID
     *
     * @return
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            // 当前毫秒内,则+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了,则等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // ID偏移组合生成最终的ID,并返回ID
        long nextId = ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | sequence;

        return nextId;
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * <p>
     * 获取 maxWorkerId
     * </p>
     */
    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
        StringBuffer mpid = new StringBuffer();
        mpid.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty()) {
            /*
             * GET jvmPid
             */
            mpid.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * <p>
     * 数据标识id部分
     * </p>
     */
    protected static long getDatacenterId(long maxDatacenterId) {
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                id = ((0x000000FF & (long) mac[mac.length - 1])
                        | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                id = id % (maxDatacenterId + 1);
            }
        } catch (Exception e) {
            System.out.println(" getDatacenterId: " + e.getMessage());
        }
        return id;
    }
}
View Code

   不能使用数据库本身的自增功能来产生主键值,原因是生产环境为分片部署的。

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

    image

  • 41bit的时间戳可以支持该算法使用到2082年
  • 10bit的工作机器id可以支持1024台机器
  • 序列号支持1毫秒产生4096个自增序列id
  • 整体上按照时间自增排序
  • 整个分布式系统内不会产生ID碰撞
  • 每秒能够产生26万ID左右

3. 搭建Eureka微服务

 基于tensquare_parent父工程,创建子工程tensquare_eureka

 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>tensquare_parent</artifactId>
        <groupId>com.tensquare</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>tensquare_eureka</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.1.1.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>
View Code

 application.yml

server:
  port: 6868
spring:
  application:
    name: tensquare-eureka
eureka:
  client:
    register-with-eureka: false   #是否将自己注册到eureka中
    fetch-registry: false         #是否从eureka中获取信息
    service-url:
      defaultZone: http://127.0.0.1:${server.port}/eureka/
View Code

 EurekaApplication

package com.tensquare.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class,args);
    }
}
View Code

4. 搭建用户微服务

 基于tensquare_parent父工程,创建子工程tesquare_user

 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>tensquare_parent</artifactId>
        <groupId>com.tensquare</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>tesquare_user</artifactId>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.tensquare</groupId>
            <artifactId>tensquare_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <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>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
    </dependencies>

</project>
View Code

 application.yml

server:
  port: 9008
spring:
  application:
    name: tensquare-user
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.240.134:3306/tensquare_user?characterEncoding=utf-8
    username: root
    password: root
mybatis-plus:
  type-aliases-package: com.tensquare.user.pojo
  global-config:
    id-type: 1
    db-column-underline: false
    refresh-mapper: true
    configuration:
      map-underscore-to-camel-case: true
      cache-enabled: true              #配置的缓存的全局开关
      lazyLoadingEnable: true           #延时加载的开关,开启延时加载,否则按需加载属性
      multipleResultSetsEnabled: true  #允许单语句返回多结果集
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #打印sql语句,调试用
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    prefer-ip-address: true
View Code

 UserApplication

package com.tensquare.user;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@MapperScan("com.tensquare.user.dao")
@EnableEurekaClient
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class,args);
    }
}
View Code

 

posted on 2025-12-03 02:05  花溪月影  阅读(4)  评论(0)    收藏  举报