应用Dubbo框架打造仿猫眼项目 理解微服务核心思想

第2章 演示环境构建

基本的概念:

微服务的privodier

 

 服务环境的搭建,第一步先建立一个maven的project

第二章环境搭建的内容请参考尚硅谷dubbo视频教程,和上面讲的一样

第3章 业务基础环境构建

dubbo中不需要进行动态路由的设置,但是spring cloud需要进行动态路由的测试

 这里要因人gun工程

Guns基于Spring Boot2,致力于做更简洁的后台管理系统。包含系统管理,代码生成,多数据库适配,SSO单点登录,工作流,短信,邮件发送,OAuth2登录,任务调度,持续集成,docker部署等功。支持Spring Cloud Alibaba微服务。社区活跃,版本迭代快,加群免费技术支持。

将naan1993-guns-master.zip解压,elipse导入maven工程

导入之后整个工程如下所示

 运行gun环境需要创建数据库,脚本位于目录下

 

 

/*
 Navicat MySQL Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50721
 Source Host           : localhost:3306
 Source Schema         : guns_rest

 Target Server Type    : MySQL
 Target Server Version : 50721
 File Encoding         : 65001

 Date: 26/01/2018 21:16:47
*/

DROP DATABASE IF EXISTS guns_rest;
CREATE DATABASE IF NOT EXISTS guns_rest DEFAULT CHARSET utf8 COLLATE utf8_general_ci;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `userName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'admin');

SET FOREIGN_KEY_CHECKS = 1;

 接下来需要修改guns-rest中数据库的配置

application.yml

 

接下来运行guns-rest

运行的时候报错

 需要在guns-rest的pom.xml中添加log4j的依赖

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.13</version>
</dependency>

 再次运行guns-rest还是会报错,需要修改application.yml中的数据库的url连接为下面的形式

rest:
  auth-open: true #jwt鉴权机制是否开启(true或者false)
  sign-open: true #签名机制是否开启(true或false)

jwt:
  header: Authorization   #http请求头所需要的字段
  secret: mySecret        #jwt秘钥
  expiration: 604800      #7天 单位:秒
  auth-path: auth         #认证请求的路径
  md5-key: randomKey      #md5加密混淆key

server:
  port: 80 #项目端口

mybatis-plus:
  mapper-locations: classpath*:com/stylefeng/guns/rest/**/mapping/*.xml
  typeAliasesPackage: com.stylefeng.guns.rest.common.persistence.model
  global-config:
    id-type: 0  #0:数据库ID自增   1:用户输入id  2:全局唯一id(IdWorker)  3:全局唯一ID(uuid)
    db-column-underline: false
    refresh-mapper: true
  configuration:
    map-underscore-to-camel-case: false
    cache-enabled: true #配置的缓存的全局开关
    lazyLoadingEnabled: true #延时加载的开关
    multipleResultSetsEnabled: true #开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句,调试用

spring:
  datasource:
      url: jdbc:mysql://127.0.0.1:3306/guns_rest?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
      password: 123456
      filters: log4j,wall,mergeStat

logging:
  level.root: info
  level.com.stylefeng: debug
  path: logs/
  file: guns-rest.log

 再次运行程序运行成功了,在浏览器输入http://localhost/auth?userName=admin&password=admin会返回对应的token信息

 

 

 现在guns环境已经搭建完成了,接下来我们要在guns项目的基础上新建立一个api网关的工程

我们直接将工程guns-rest直接复制一份,重新命名为

接下来,我们要修改对于的guns-gateway的pom.xml文件


<groupId>com.stylefeng.guns</groupId>
<artifactId>guns-rest</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>


<name>guns-rest</name>
<description>guns REST服务器</description>



 

 需要将guns-rest修改为


<groupId>com.stylefeng.guns</groupId>
<artifactId>guns-gateway</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>


<name>guns-gateway</name>
<description>Meeting院线</description>

 

 

接下来还需要修改guns-parent的pom.xml文件,将<module>guns-gateway</module>添加进去

 

    <modules>
        <module>guns-admin</module>
        <module>guns-core</module>
        <module>guns-rest</module>
        <module>guns-gateway</module>
        <module>guns-generator</module>
    </modules>

 整个工程的目录如下

接下来,我们要将guns-gateway和dubbo进行整合

首先需要在pom.xml中添加对于的依赖包

 

        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>
        
         <!-- https://mvnrepository.com/artifact/com.alibaba.boot/dubbo-spring-boot-starter -->
<dependency>
    <groupId>com.alibaba.spring.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>

 

整个文件的依赖为

<?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.stylefeng.guns</groupId>
    <artifactId>guns-gateway</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <name>guns-gateway</name>
    <description>Meeting院线的api网关</description>

    <parent>
        <groupId>com.stylefeng</groupId>
        <artifactId>guns-parent</artifactId>
        <version>1.0.0</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <dependencies>
        <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>
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.13</version>
</dependency>
        
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
        <dependency>
            <groupId>com.stylefeng</groupId>
            <artifactId>guns-core</artifactId>
        </dependency>
        

  

        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>
        
         <!-- https://mvnrepository.com/artifact/com.alibaba.boot/dubbo-spring-boot-starter -->
<dependency>
    <groupId>com.alibaba.spring.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
         
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

 

接下来我们需要在application.yml配置dubbo相关的属性

我们新建立一个application.properties
#WEB\u670D\u52A1\u7AEF\u53E3
server.port=8081

## dubbo \u914D\u7F6E
spring.dubbo.application.name=meeting-gateway-provider
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20880

 

 

 

package com.stylefeng.guns.rest.modular.user;

public interface UserAPI {

    boolean login(String username,String password);
}

 

package com.stylefeng.guns.rest.modular.user;

import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Service;

@Component
@Service(interfaceClass=UserAPI.class)
public class UserImpl  implements UserAPI{

    @Override
    public boolean login(String username, String password) {
        // TODO Auto-generated method stub
        return true;
    }

}

 

这里首先要将UserImpl  添加到spring容器中,@Service是dubbo的不是spring的com.alibaba.dubbo.config.annotation.Service;

这样我们就将服务暴露出去了,同时需要在启动类中添加

@EnableDubbo注解
package com.stylefeng.guns.rest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableLoadTimeWeaving;

import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;

@SpringBootApplication(scanBasePackages = {"com.stylefeng.guns"})
@EnableDubbo
public class GunsRestApplication {

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

 上面写成@EnableDubbo会报错

改成下面的形式@EnableDubboConfiguration就可以了,初步定为是版本问题

package com.stylefeng.guns.rest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;

@SpringBootApplication(scanBasePackages = {"com.stylefeng.guns"})
@EnableDubboConfiguration
public class GunsRestApplication {

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

 

在实际的开发过程中,所有的接口都应该放在一个单独的模块中,我们应该单独建立一个独立的api模块,我们将guns-core复制一份为guns-api

接下来需要修改guns-api的pom文件

  <artifactId>guns-core</artifactId>
    <packaging>jar</packaging>

    <name>guns-core</name>
    <url>http://maven.apache.org</url>

 

修改为

<artifactId>guns-api</artifactId>
    <packaging>jar</packaging>

    <name>guns-api</name>
    <url>http://maven.apache.org</url>

 

接下来需要修改guns-parent的pom文件,增加子模块依赖 <module>guns-api</module>

 <modules>
        <module>guns-admin</module>
        <module>guns-core</module>
        <module>guns-rest</module>
        <module>guns-generator</module>
         <module>guns-gateway</module>
         <module>guns-api</module>
    </modules>

 接下来需要让guns-rest和guns-gateway依赖我们创建的guns-api模块,在pom.xml中增加下面的依赖

 

   <dependency>
            <groupId>com.stylefeng</groupId>
            <artifactId>guns-api</artifactId>
             <version>1.0.0</version>
        </dependency>

 

第4章 Dubbo基本特性:用户模块开发

用户模块首先要有对于的建表语句

DROP TABLE IF EXISTS mooc_user_t;
CREATE TABLE mooc_user_t(
   UUID INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键编号',
   user_name VARCHAR(50) COMMENT '用户账号',
   user_pwd VARCHAR(50) COMMENT '用户密码',
   nick_name VARCHAR(50) COMMENT '用户昵称',
   user_sex INT COMMENT '用户性别 0-男,1-女',
   birthday VARCHAR(50) COMMENT '出生日期',
   email VARCHAR(50) COMMENT '用户邮箱',
   user_phone VARCHAR(50) COMMENT '用户手机号',
   address VARCHAR(50) COMMENT '用户住址',
   head_url VARCHAR(50) COMMENT '头像URL',
   biography VARCHAR(200) COMMENT '个人介绍',
   life_state INT COMMENT '生活状态 0-单身,1-热恋中,2-已婚,3-为人父母',
   begin_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
   update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'
) COMMENT '用户表' ENGINE = INNODB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

insert into mooc_user_t(user_name,user_pwd,nick_name,user_sex,birthday,email,user_phone,address,head_url,life_state,biography) values('admin','0192023a7bbd73250516f069df18b500','隔壁泰山',0,'2018-07-31','admin@mooc.com','13888888888','北京市海淀区朝阳北路中南海国宾馆','films/img/head-img.jpg',0,'没有合适的伞,我宁可淋着雨');
insert into mooc_user_t(user_name,user_pwd,nick_name,user_sex,birthday,email,user_phone,address,head_url,life_state,biography) values('jiangzh','5e2de6bd1c9b50f6e27d4e55da43b9

17
','阿里郎',0,'2018-08-20','jiangzh@mooc.com','13866666666','北京市朝阳区大望路万达广场','films/img/head-img.jpg',1,'我喜欢隔壁泰山');

 

接下来是用户对应的接口文档

 

目录
一、    用户模块接口    2
1、    用户注册接口    2
    请求地址    2
    请求方式    2
    请求字段    2
    应答报文    3
    业务异常    3
    系统异常    3
2、    用户名验证接口    3
    请求地址    3
    请求方式    3
    请求字段    3
    应答报文    4
    业务异常    4
    系统异常    4
3、    用户登陆接口    4
    请求地址    4
    请求方式    4
    请求字段    4
    应答报文    5
    业务异常    5
    系统异常    5
4、    用户退出接口    5
    请求地址    5
    请求方式    6
    请求字段    6
    应答报文    6
    业务异常    6
    系统异常    6
5、    用户信息查询接口    6
    请求地址    6
    请求方式    7
    请求字段    7
    应答报文    7
    业务异常    7
    系统异常    7
6、    用户信息修改接口    8
    请求地址    8
    请求方式    8
    请求字段    8
    应答报文    8
    业务异常    9
    系统异常    9



















一、    用户模块接口
1、    用户注册接口
    请求地址
/user/register
    请求方式
post
    请求字段
请求字段    字段含义    是否必填
username    用户名    是
password    用户密码    是
email    用户邮箱    否
phone    用户电话    否
address    用户住址    否

    应答报文
{
“status” :0,
“msg”  :“注册成功”
}
    业务异常
{
“status” :1,
  “msg”   :“用户已存在”
}
    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}
2、    用户名验证接口
    请求地址
/user/check
    请求方式
post
    请求字段
请求字段    字段含义    是否必填
username    用户名    是

    应答报文
{
“status” :0,
“msg”  :“验证成功”
}

    业务异常
{
“status” :1,
“msg”  :“用户已存在”
}

    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}

3、    用户登陆接口
    请求地址
/auth
    请求方式
post
    请求字段
请求字段    字段含义    是否必填
username    用户登陆账号    是
password    用户登陆密码    是

    应答报文
{
“status” : 0,
  “data”:{
    "randomKey":"nv0958",
"token":"eyJhbGciOiJIUzUxMiJ9.eyJyYW5kb21LZXkiOiJudjA5NTgiLCJzdWIiOiJhZG1pbiIsImV4cCI6MTUzMjQ0MDY1MCwiaWF0IjoxNTMxODM1ODUwfQ.miIl1JXpb1ztkd-RHks1OPD6KQ53I-C4ESwhOywU0O7KDWu9SodHP0HMct0Di3BO2qtuc9EpVtSZcgTnnudrHA"
    }
}

    业务异常
{
“status” : 1,
  “msg”   :“用户名或密码错误”
}

    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}
4、    用户退出接口
    请求地址
/user/logout
    请求方式
get

    请求字段
无,但是要带上Authorization的头信息
    应答报文
{
“status” : 0,
  “msg”   :“成功退出”
}

    业务异常
{
“status” : 1,
  “msg”   :“退出失败,用户尚未登陆”
}

    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}
5、    用户信息查询接口
    请求地址
/user/getUserInfo
    请求方式
get
    请求字段
无,但是要带上Authorization的头信息

    应答报文
{
    "status": 0,
    "data": {
        "uuid": 12,
        "username": "aaa",
        “nickname”: ”咫尺天涯”,
"email": "aaa@163.com",
        "phone": null,
        “sex” : 0, [0-男,1-女],
        “birthday”:“2018-12-12”,
        “lifeState” :0, [0-单身,1-热恋中,2-已婚,3-为人父母],
        “biography” :“没有合适的伞,我宁可淋着雨”,
        “address”:”北京市东城区中南海12号楼主席办公室”,
        “headAddress” : “http://img.meetingshop.cn/12324123.png“
        "createTime": 1479048325000,
        "updateTime": 1479048325000
    }
}
    业务异常
{
“status” : 1,
  “msg”   :“查询失败,用户尚未登陆”
}

    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}
6、    用户信息修改接口
    请求地址
/user/updateUserInfo
    请求方式
post
    请求字段
请求字段    字段含义    是否必填
uuid    用户编号    是
nickname    用户昵称    否
email    用户邮箱地址    否
phone    用户电话    否
sex    用户性别 0代表男,1代表女    否
birthday    用户出生年月日    否
lifeState    用户生活状态 
0-单身,1-热恋中,2-已婚,3-为人父母    否
biography    个性标签    否
address    用户住址    否

    应答报文
{
   "status": 0,
   "data": {
        "id": 12,
        "username": "aaa",
        “nickname”: ”咫尺天涯”,
"email": "aaa@163.com",
        "phone": null,
        “sex” : 0, [0-男,1-女],
        “birthday”:“2018-12-12”,
        “lifeState” :0, [0-单身,1-热恋中,2-已婚,3-为人父母],
        “biography” :“没有合适的伞,我宁可淋着雨”,
        “address”:”北京市东城区中南海12号楼主席办公室”,
        “headAddress” : “http://img.meetingshop.cn/12324123.png“
        "createTime": 1479048325000,
        "updateTime": 1479048325000
    }
}

    业务异常
{
“status” : 1,
  “msg”   :“用户信息修改失败”
}

    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}

 

接下来,guns-gateway模块已经配置完成,接下来我们要创建一个用户的模块guns-user模块,通过gateway能够访问user模块

我们复制guns-gateway修改名称为guns-user,进入到guns-user修改起pom.xml的配置文件

 <groupId>com.stylefeng.guns</groupId>
    <artifactId>guns-gateway</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <name>guns-gateway</name>
    <description>Meeting院线的api网关</description>

 

修改为

    <groupId>com.stylefeng.guns</groupId>
    <artifactId>guns-users</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <name>guns-users</name>
    <description>用户管理模块</description>

 

同时需要在guns-parent中添加子模块guns-user的依赖

   <modules>
        <module>guns-admin</module>
        <module>guns-core</module>
        <module>guns-rest</module>
        <module>guns-generator</module>
        <module>guns-gateway</module>
        <module>guns-api</module>
        <module>guns-users</module>
    </modules>

 

整个项目的结构如下

接下来要让guns-gateway和guns-users之间集成dubbo,二者相互调用,二者通信的接口要定义在guns-api中

guns-users是网关来进行调用的,所以不需要进行jwt安全的校验,安全的校验都是在网关中实现

我们修改guns-users,关闭jwt的校验,修改application.yml

rest:
  auth-open: false #jwt鉴权机制是否开启(true或者false)
  sign-open: false #签名机制是否开启(true或false)

jwt:
  header: Authorization   #http请求头所需要的字段
  secret: mySecret        #jwt秘钥
  expiration: 604800      #7天 单位:秒
  auth-path: auth         #认证请求的路径
  md5-key: randomKey      #md5加密混淆key

server:
  port: 8083 #项目端口

mybatis-plus:
  mapper-locations: classpath*:com/stylefeng/guns/rest/**/mapping/*.xml
  typeAliasesPackage: com.stylefeng.guns.rest.common.persistence.model
  global-config:
    id-type: 0  #0:数据库ID自增   1:用户输入id  2:全局唯一id(IdWorker)  3:全局唯一ID(uuid)
    db-column-underline: false
    refresh-mapper: true
  configuration:
    map-underscore-to-camel-case: false
    cache-enabled: true #配置的缓存的全局开关
    lazyLoadingEnabled: true #延时加载的开关
    multipleResultSetsEnabled: true #开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句,调试用

spring:
  datasource:
      url: jdbc:mysql://127.0.0.1:3306/guns_rest?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
      username: root
      password: 123456
      filters: log4j,wall,mergeStat

logging:
  level.root: info
  level.com.stylefeng: debug
  path: logs/
  file: guns-rest.log

 

auth-open: false #jwt鉴权机制是否开启(true或者false)
sign-open: false #签名机制是否开启(true或false)

都设置为false关闭jwt校验,应用的端口设置为8083

接下来在application.properties中配置guns-user的dubbo集成参数

#WEB\u670D\u52A1\u7AEF\u53E3
server.port=8083

## dubbo \u914D\u7F6E
spring.dubbo.application.name=meeting-user
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20881

这里guns-users集成dubbo还需要保证在pom.xml中引入dubbo的依赖,因为guns-user是通过guns-gateway复制的,guns-gateway中已经引入了dubbo的依赖,这里guns-user就不需要在做单独的配置了

这样guns-user和guns-gateway相关的dubbo的集成已经完成了,后面二者就可以通过dubbo相互调用了

接下来,在guns-gateway中我们要配置,某些http请求,我们不进行jwt的校验,进入到guns-gatawy的application.yml

jwt:
  header: Authorization   #http请求头所需要的字段
  secret: mySecret        #jwt秘钥
  expiration: 604800      #7天 单位:秒
  auth-path: auth         #认证请求的路径
  md5-key: randomKey      #md5加密混淆key
  ignore-url: /usr/,/film/   #对改类请求的url不进行jwt模块的校验

我们增加一个属性ignore-url: /usr/,/film/   #对改类请求的url不进行jwt模块的校验

在guns-gateway中存在一个类JwtProperties就是读取application.yml中以jwt开头的配置,我们要在该类中增加一个   private  String ignoreUrl;属性并且生成get set方法,属性名称ignoreUrl必须和

  ignore-url相互对应
package com.stylefeng.guns.rest.config.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * jwt相关配置
 *
 * @author fengshuonan
 * @date 2017-08-23 9:23
 */
@Configuration
@ConfigurationProperties(prefix = JwtProperties.JWT_PREFIX)
public class JwtProperties {

    public static final String JWT_PREFIX = "jwt";

    private String header = "Authorization";

    private String secret = "defaultSecret";

    private Long expiration = 604800L;

    private String authPath = "auth";

    private String md5Key = "randomKey";
    
    private  String ignoreUrl;
    

    public String getIgnoreUrl() {
        return ignoreUrl;
    }

    public void setIgnoreUrl(String ignoreUrl) {
        this.ignoreUrl = ignoreUrl;
    }

    public static String getJwtPrefix() {
        return JWT_PREFIX;
    }

    public String getHeader() {
        return header;
    }

    public void setHeader(String header) {
        this.header = header;
    }

    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

    public Long getExpiration() {
        return expiration;
    }

    public void setExpiration(Long expiration) {
        this.expiration = expiration;
    }

    public String getAuthPath() {
        return authPath;
    }

    public void setAuthPath(String authPath) {
        this.authPath = authPath;
    }

    public String getMd5Key() {
        return md5Key;
    }

    public void setMd5Key(String md5Key) {
        this.md5Key = md5Key;
    }
}

 配置完成之后,接下来我们要在AuthFilter中对配置的ignore-url不进行拦截

package com.stylefeng.guns.rest.modular.auth.filter;

import com.stylefeng.guns.core.base.tips.ErrorTip;
import com.stylefeng.guns.core.util.RenderUtil;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import com.stylefeng.guns.rest.config.properties.JwtProperties;
import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import io.jsonwebtoken.JwtException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 对客户端请求的jwt token验证过滤器
 *
 * @author fengshuonan
 * @Date 2017/8/24 14:04
 */
public class AuthFilter extends OncePerRequestFilter {

    private final Log logger = LogFactory.getLog(this.getClass());

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private JwtProperties jwtProperties;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request.getServletPath().equals("/" + jwtProperties.getAuthPath())) {
            chain.doFilter(request, response);
            return;
        }
        
        //对ignore-url不进行拦截
        String ignoreUrl = jwtProperties.getIgnoreUrl();
        String[] ignoreUrls = ignoreUrl.split(",");
        
        for(int i=0; i< ignoreUrls.length;i++){
            if(request.getServletPath().equals(ignoreUrls[i])){
                chain.doFilter(request, response);
                return;
            }
            
        }
        
        final String requestHeader = request.getHeader(jwtProperties.getHeader());
        String authToken = null;
        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            authToken = requestHeader.substring(7);

            //验证token是否过期,包含了验证jwt是否正确
            try {
                boolean flag = jwtTokenUtil.isTokenExpired(authToken);
                if (flag) {
                    RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_EXPIRED.getCode(), BizExceptionEnum.TOKEN_EXPIRED.getMessage()));
                    return;
                }
            } catch (JwtException e) {
                //有异常就是token解析失败
                RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
                return;
            }
        } else {
            //header没有带Bearer字段
            RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
            return;
        }
        chain.doFilter(request, response);
    }
}


 接下来我们需要在guns-api中实现我们的用户查询的业务接口

package com.stylefeng.guns.api.user;

public interface UserAPI {
    
    boolean login(String username,String password);
    
    boolean register(UserModel userModel);
    
    boolean checkUsername(String username);
    
    
    UserInfoModel getUserInfo(int uuid);
    
    UserInfoModel updateUserInfo(UserInfoModel userInfoModel);

    
    
    
}

 

package com.stylefeng.guns.api.user;

import java.io.Serializable;

public class UserInfoModel  implements Serializable{
    
    private String username;
    private String nickname;
    private String email;
    
    private String phone;
    private int sex;
    private String birthday;
    
    private String lifeState;
    private String biography;
    private String address;
    private String headAddress;
    private long beginTime;
    private long updateTime;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getNickname() {
        return nickname;
    }
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public int getSex() {
        return sex;
    }
    public void setSex(int sex) {
        this.sex = sex;
    }
    public String getBirthday() {
        return birthday;
    }
    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }
    public String getLifeState() {
        return lifeState;
    }
    public void setLifeState(String lifeState) {
        this.lifeState = lifeState;
    }
    public String getBiography() {
        return biography;
    }
    public void setBiography(String biography) {
        this.biography = biography;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getHeadAddress() {
        return headAddress;
    }
    public void setHeadAddress(String headAddress) {
        this.headAddress = headAddress;
    }
    public long getBeginTime() {
        return beginTime;
    }
    public void setBeginTime(long beginTime) {
        this.beginTime = beginTime;
    }
    public long getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(long updateTime) {
        this.updateTime = updateTime;
    }
    
    

}

 

package com.stylefeng.guns.api.user;

import java.io.Serializable;

public class UserModel  implements Serializable{
    
    private String username;
    private String password;
    private String email;
    private String phone;
    private String address;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "UserModel [username=" + username + ", password=" + password + ", email=" + email + ", phone=" + phone
                + ", address=" + address + "]";
    }
    
    

}

 

这里强调下三个类:userModel和UserInfoModel是guns-gateway和guns-user进行交互的类

后面MoocUserT是guns-user内部使用的是表mock_user_t自动代码生产的类

 

 

接下来,我们在guns-gateway中要实现对应的校验功能

AuthController类中我们要做如下的修改

package com.stylefeng.guns.rest.modular.auth.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.stylefeng.guns.api.user.UserAPI;
import com.stylefeng.guns.core.exception.GunsException;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import com.stylefeng.guns.rest.modular.auth.VO.ReponseVO;
import com.stylefeng.guns.rest.modular.auth.controller.dto.AuthRequest;
import com.stylefeng.guns.rest.modular.auth.controller.dto.AuthResponse;
import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import com.stylefeng.guns.rest.modular.auth.validator.IReqValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * 请求验证的
 *
 * @author fengshuonan
 * @Date 2017/8/24 14:22
 */
@RestController
public class AuthController {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Resource(name = "simpleValidator")
    private IReqValidator reqValidator;
    
    @Reference(interfaceClass=UserAPI.class)
    private UserAPI userApi;

    @RequestMapping(value = "${jwt.auth-path}")
    public ReponseVO createAuthenticationToken(AuthRequest authRequest) {

        //        boolean validate = reqValidator.validate(authRequest);
        boolean validate = true;
       int userId = userApi.login(authRequest.getUserName(), authRequest.getPassword());
       
       if(userId ==0){
           validate = false;
       }
        

        if (validate) {
            final String randomKey = jwtTokenUtil.getRandomKey();
            final String token = jwtTokenUtil.generateToken(userId+"", randomKey);
            return ReponseVO.success(new AuthResponse(token, randomKey));
        } else {
            return ReponseVO.serviceFail("用户名或者密码错误");
        }
    }
}

 用户登录的接口,返回的结果是

Ø  应答报文

{

“status” : 0,

  “data”:{

      "randomKey":"nv0958",

"token":"eyJhbGciOiJIUzUxMiJ9.eyJyYW5kb21LZXkiOiJudjA5NTgiLCJzdWIiOiJhZG1pbiIsImV4cCI6MTUzMjQ0MDY1MCwiaWF0IjoxNTMxODM1ODUwfQ.miIl1JXpb1ztkd-RHks1OPD6KQ53I-C4ESwhOywU0O7KDWu9SodHP0HMct0Di3BO2qtuc9EpVtSZcgTnnudrHA"

    }

}

我们要新增加一个对于接口的返回类

package com.stylefeng.guns.rest.modular.auth.VO;

public class ReponseVO<T> {

    
    private  int status;
    
    
    private String msg;
    
    private T data;
    
    private ReponseVO(){
        
    }
    
    
    
    public int getStatus() {
        return status;
    }



    public void setStatus(int status) {
        this.status = status;
    }



    public String getMsg() {
        return msg;
    }



    public void setMsg(String msg) {
        this.msg = msg;
    }



    public T getData() {
        return data;
    }



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

    
public static<T> ReponseVO serviceFail(String msg){
        
        ReponseVO reponseVO =new ReponseVO();
        reponseVO.setStatus(1);
        reponseVO.setMsg(msg);
        return reponseVO;
    }


    public static<T> ReponseVO success(T data){
        
        ReponseVO reponseVO =new ReponseVO();
        reponseVO.setStatus(0);
        reponseVO.setData(data);
        return reponseVO;
    }
    
public static<T> ReponseVO appFail(String msg){
        
        ReponseVO reponseVO =new ReponseVO();
        reponseVO.setStatus(999);
        reponseVO.setMsg(msg);
        return reponseVO;
    }
}

 

 接下来我们在网关中要定义一个ThreadLocal变量来保存当前用户的信息

我们新建立一个CurrentUser类,将当前用户的用户ID存储在ThreadLocal中,这里不要存储UserInfoModel整个对象,当用户过多的时候会导致内存溢出,在ThreadLocal中我们尽量存储少一点的内容

package com.stylefeng.guns.rest.common;

import com.stylefeng.guns.api.user.UserInfoModel;

public class CurrentUser {
    
    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
    
    //将用户信息放入存储空间中
    public static void saveUserInfo(String userId){
        threadLocal.set(userId);
    }
        
    
    
    //从线程中取出当前用户的信息
    public static String getUserInfo(){
        return threadLocal.get();
    }

}

 

接下来,我们要修改AuthFilter

1、改类主要的作用如下:第一判断当前那些http请求是否要做jwt的校验

2、对当前的请求做jwt的校验

我们在jwt校验的时候,如果jwt校验成功,我们需要将用户请求头中携带的用户ID存储到ThreadLocal中,便于后续的请求使用

当用户登录的时候,会访问AuthController类的createAuthenticationToken方法后台会产生一个token,token中携带了当前用户的id

final String token = jwtTokenUtil.generateToken(userId+"", randomKey);

然后,后面用户所有的请求中都会携带对应的token信息,所用的请求都会被authFilter拦截,我们可以在jwt校验的时候,从请求头中获得对于的用户ID存储到ThreadLocal中

package com.stylefeng.guns.rest.modular.auth.filter;

import com.stylefeng.guns.core.base.tips.ErrorTip;
import com.stylefeng.guns.core.util.RenderUtil;
import com.stylefeng.guns.rest.common.CurrentUser;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import com.stylefeng.guns.rest.config.properties.JwtProperties;
import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import io.jsonwebtoken.JwtException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 对客户端请求的jwt token验证过滤器
 *
 * @author fengshuonan
 * @Date 2017/8/24 14:04
 */
public class AuthFilter extends OncePerRequestFilter {

    private final Log logger = LogFactory.getLog(this.getClass());

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private JwtProperties jwtProperties;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request.getServletPath().equals("/" + jwtProperties.getAuthPath())) {
            chain.doFilter(request, response);
            return;
        }
        
        //对ignore-url不进行拦截
        String ignoreUrl = jwtProperties.getIgnoreUrl();
        String[] ignoreUrls = ignoreUrl.split(",");
        
        for(int i=0; i< ignoreUrl.length();i++){
            if(request.getServletPath().equals(ignoreUrls[i])){
                chain.doFilter(request, response);
                return;
            }
            
        }
        
        final String requestHeader = request.getHeader(jwtProperties.getHeader());
        String authToken = null;
        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            authToken = requestHeader.substring(7);

            //获得jwt中请求头中携带的userid
           String userId = jwtTokenUtil.getUsernameFromToken(authToken);
            //将userID存储到ThreadLocal中
           CurrentUser.saveUserInfo(userId);
            //验证token是否过期,包含了验证jwt是否正确
            try {
                boolean flag = jwtTokenUtil.isTokenExpired(authToken);
                if (flag) {
                    RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_EXPIRED.getCode(), BizExceptionEnum.TOKEN_EXPIRED.getMessage()));
                    return;
                }
            } catch (JwtException e) {
                //有异常就是token解析失败
                RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
                return;
            }
        } else {
            //header没有带Bearer字段
            RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
            return;
        }
        chain.doFilter(request, response);
    }
}


 以上整个用户的jwt校验已经完成了,接下来我们要开始用户业务操作的逻辑开发,我们要mooc_user_t表自动生产对于的实体类,我们利用guns提供的自动代码生成类

我们进入到guns-user模块

EntityGenerator类是guns给我们提供的自动代码生产类,能够将数据库表自动生成对于的实体类

package com.stylefeng.guns.generator;

import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.DbType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;

/**
 * 实体生成
 *
 * @author fengshuonan
 * @date 2017-08-23 12:15
 */
public class EntityGenerator {

    @Test
    public void entityGenerator() {
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        //D:\work_project\naan1993-guns-master\guns\guns-user\src\main\java
        gc.setOutputDir("D:\\work_project\\naan1993-guns-master\\guns\\guns-user\\src\\main\\java");//这里写你自己的java目录
        gc.setFileOverride(true);//是否覆盖
        gc.setActiveRecord(true);
        gc.setEnableCache(false);// XML 二级缓存
        gc.setBaseResultMap(true);// XML ResultMap
        gc.setBaseColumnList(false);// XML columList
        gc.setAuthor("weiyuan");
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MYSQL);
        dsc.setTypeConvert(new MySqlTypeConvert() {
            // 自定义数据库表字段类型转换【可选】
            @Override
            public DbColumnType processTypeConvert(String fieldType) {
                return super.processTypeConvert(fieldType);
            }
        });
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setUrl("jdbc:mysql://127.0.0.1:3306/guns_rest?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
        mpg.setDataSource(dsc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //strategy.setTablePrefix(new String[]{"_"});// 此处可以修改为您的表前缀
        strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
        strategy.setInclude(new String[]{"mooc_user_t"});
        mpg.setStrategy(strategy);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent(null);
        pc.setEntity("com.stylefeng.guns.rest.common.persistence.model");
        pc.setMapper("com.stylefeng.guns.rest.common.persistence.dao");
        pc.setXml("com.stylefeng.guns.rest.common.persistence.dao.mapping");
        pc.setService("TTT");       //本项目没用,生成之后删掉
        pc.setServiceImpl("TTT");   //本项目没用,生成之后删掉
        pc.setController("TTT");    //本项目没用,生成之后删掉
        mpg.setPackageInfo(pc);

        // 注入自定义配置,可以在 VM 中使用 cfg.abc 设置的值
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                this.setMap(map);
            }
        };
        mpg.setCfg(cfg);

        // 执行生成
        mpg.execute();

        // 打印注入设置
        System.err.println(mpg.getCfg().getMap().get("abc"));
    }
}


上面要设置生产的文件存储的位置,生成文件对于的包名,生产文件对于的数据库的驱动,将那个mooc_user_t表自动生成 

生成之后会在guns-users下面生成对应的类

 

现在就已经存在了三个业务实体类:UserModel和UserInfoModel是guns-gateway和guns-user模块中dubbo服务通信的实例类

MockUserT是mooc-user-t表自动代码生成的实体类,在guns-user模块中内部使用

接下来我们要在guns-user实现用户业务操作的业务类

我们要建立一个用户操作的业务类UserServiceImpl

package com.stylefeng.guns.rest.modular.user;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Service;
import com.stylefeng.guns.api.user.UserAPI;
import com.stylefeng.guns.api.user.UserInfoModel;
import com.stylefeng.guns.api.user.UserModel;
import com.stylefeng.guns.core.util.MD5Util;
import com.stylefeng.guns.rest.common.persistence.dao.MoocUserTMapper;
import com.stylefeng.guns.rest.common.persistence.dao.UserMapper;
import com.stylefeng.guns.rest.common.persistence.model.MoocUserT;

@Component
@Service(interfaceClass=UserAPI.class)
public class UserServiceImpl implements UserAPI{

    //注入数据库的自动扫描类
    @Autowired
    private MoocUserTMapper userMapper;
    
    @Override
    public int login(String username, String password) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public boolean register(UserModel userModel) {
        // TODO Auto-generated method stub
        //userModel是用户在页面上输入的数据,密码为123456,存储在数据库中需要进行加密处理
        //将注册实体转换成数据实体
        MoocUserT moocUserT = new MoocUserT();
        moocUserT.setUserName(userModel.getUsername());
        String md5Passwd= MD5Util.encrypt(userModel.getPassword());
        moocUserT.setUserPwd(md5Passwd);
        moocUserT.setEmail(userModel.getEmail());
        moocUserT.setAddress(userModel.getAddress());
        moocUserT.setUserPhone(userModel.getPhone());
        
        //将实体存储到数据库中
        Integer insert = userMapper.insert(moocUserT);
        if(insert>0){
            return true;
        }else{
            return false;
        }
        
    }

    @Override
    public boolean checkUsername(String username) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public UserInfoModel getUserInfo(int uuid) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public UserInfoModel updateUserInfo(UserInfoModel userInfoModel) {
        // TODO Auto-generated method stub
        return null;
    }

}

 1、首先改业务类要实现UserAPI的接口

2、改类要使用@Compent添加到spring容器中,使用@Service暴露成dubbo服务接口,让guns-gateway调用

3.要注入操作数据库的接口类MoocUserTMapper 

注册功能,需要将用户从页面封装到UserModel对象转换成数据库对应的MoocUser对象,存储到数据库中

@Override
    public boolean register(UserModel userModel) {
        // TODO Auto-generated method stub
        //userModel是用户在页面上输入的数据,密码为123456,存储在数据库中需要进行加密处理
        //将注册实体转换成数据实体
        MoocUserT moocUserT = new MoocUserT();
        moocUserT.setUserName(userModel.getUsername());
        String md5Passwd= MD5Util.encrypt(userModel.getPassword());
        moocUserT.setUserPwd(md5Passwd);
        moocUserT.setEmail(userModel.getEmail());
        moocUserT.setAddress(userModel.getAddress());
        moocUserT.setUserPhone(userModel.getPhone());
        
        //将实体存储到数据库中
        Integer insert = userMapper.insert(moocUserT);
        if(insert>0){
            return true;
        }else{
            return false;
        }
        
    }

接下来讲解下登录和检查用户是否存在的功能

package com.stylefeng.guns.rest.modular.user;

import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Service;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.stylefeng.guns.api.user.UserAPI;
import com.stylefeng.guns.api.user.UserInfoModel;
import com.stylefeng.guns.api.user.UserModel;
import com.stylefeng.guns.core.util.MD5Util;
import com.stylefeng.guns.rest.common.persistence.dao.MoocUserTMapper;
import com.stylefeng.guns.rest.common.persistence.dao.UserMapper;
import com.stylefeng.guns.rest.common.persistence.model.MoocUserT;

@Component
@Service(interfaceClass=UserAPI.class)
public class UserServiceImpl implements UserAPI{

    //注入数据库的自动扫描类
    @Autowired
    private MoocUserTMapper userMapper;
    
    @Override
    public int login(String username, String password) {
        // TODO Auto-generated method stub
        MoocUserT moocUserT = new MoocUserT();
        moocUserT.setUserName(username);
        
        //到数据库中查询
        MoocUserT result = userMapper.selectOne(moocUserT);
        //判断密码是否相等
        if(result !=null && result.getUuid() > 0){
            String md5Passwd = MD5Util.encrypt(password);
            if(result.getUserPwd().endsWith(md5Passwd)){
                return result.getUuid();
            }
            
        }
        
        
        
        
        return 0;
    }

    @Override
    public boolean register(UserModel userModel) {
        // TODO Auto-generated method stub
        //userModel是用户在页面上输入的数据,密码为123456,存储在数据库中需要进行加密处理
        //将注册实体转换成数据实体
        MoocUserT moocUserT = new MoocUserT();
        moocUserT.setUserName(userModel.getUsername());
        String md5Passwd= MD5Util.encrypt(userModel.getPassword());
        moocUserT.setUserPwd(md5Passwd);
        moocUserT.setEmail(userModel.getEmail());
        moocUserT.setAddress(userModel.getAddress());
        moocUserT.setUserPhone(userModel.getPhone());
        
        //将实体存储到数据库中
        Integer insert = userMapper.insert(moocUserT);
        if(insert>0){
            return true;
        }else{
            return false;
        }
        
    }

    @Override
    public boolean checkUsername(String username) {
        // TODO Auto-generated method stub
        EntityWrapper<MoocUserT> entityWrapper = new  EntityWrapper<>();
        entityWrapper.eq("user_name", username);        
        Integer result = userMapper.selectCount(entityWrapper);
        if(result!=null && result>0){
            return false;
        }else{
            return  true;
            
        }
        
        
    }

    @Override
    public UserInfoModel getUserInfo(int uuid) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public UserInfoModel updateUserInfo(UserInfoModel userInfoModel) {
        // TODO Auto-generated method stub
        return null;
    }

}

接下来讲解下查询用户的信息,通过uuid获得当前用户的信息

 整个业务实现类的代码如下

package com.stylefeng.guns.rest.modular.user;

import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Service;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.stylefeng.guns.api.user.UserAPI;
import com.stylefeng.guns.api.user.UserInfoModel;
import com.stylefeng.guns.api.user.UserModel;
import com.stylefeng.guns.core.util.MD5Util;
import com.stylefeng.guns.rest.common.persistence.dao.MoocUserTMapper;
import com.stylefeng.guns.rest.common.persistence.dao.UserMapper;
import com.stylefeng.guns.rest.common.persistence.model.MoocUserT;

@Component
@Service(interfaceClass=UserAPI.class)
public class UserServiceImpl implements UserAPI{

    //注入数据库的自动扫描类
    @Autowired
    private MoocUserTMapper userMapper;
    
    @Override
    public int login(String username, String password) {
        // TODO Auto-generated method stub
        MoocUserT moocUserT = new MoocUserT();
        moocUserT.setUserName(username);
        
        //到数据库中查询
        MoocUserT result = userMapper.selectOne(moocUserT);
        //判断密码是否相等
        if(result !=null && result.getUuid() > 0){
            String md5Passwd = MD5Util.encrypt(password);
            if(result.getUserPwd().equals(md5Passwd)){
                return result.getUuid();
            }
            
        }
        
        
        
        
        return 0;
    }

    @Override
    public boolean register(UserModel userModel) {
        // TODO Auto-generated method stub
        //userModel是用户在页面上输入的数据,密码为123456,存储在数据库中需要进行加密处理
        //将注册实体转换成数据实体
        MoocUserT moocUserT = new MoocUserT();
        moocUserT.setUserName(userModel.getUsername());
        String md5Passwd= MD5Util.encrypt(userModel.getPassword());
        moocUserT.setUserPwd(md5Passwd);
        moocUserT.setEmail(userModel.getEmail());
        moocUserT.setAddress(userModel.getAddress());
        moocUserT.setUserPhone(userModel.getPhone());
        
        //将实体存储到数据库中
        Integer insert = userMapper.insert(moocUserT);
        if(insert>0){
            return true;
        }else{
            return false;
        }
        
    }

    @Override
    public boolean checkUsername(String username) {
        // TODO Auto-generated method stub
        EntityWrapper<MoocUserT> entityWrapper = new  EntityWrapper<>();
        entityWrapper.eq("user_name", username);        
        Integer result = userMapper.selectCount(entityWrapper);
        if(result!=null && result>0){
            return false;
        }else{
            return  true;
            
        }
        
        
    }

    private UserInfoModel  do2UserInfoModel(MoocUserT moocUserT){
        
        UserInfoModel userInfoModel = new UserInfoModel();
        userInfoModel.setUsername(moocUserT.getUserName());
        userInfoModel.setUpdateTime(moocUserT.getUpdateTime().getTime());
        userInfoModel.setSex(moocUserT.getUserSex());
        userInfoModel.setPhone(moocUserT.getUserPhone());
        userInfoModel.setNickname(moocUserT.getNickName());
        userInfoModel.setLifeState(moocUserT.getLifeState()+"");
        userInfoModel.setHeadAddress(moocUserT.getHeadUrl());
        userInfoModel.setEmail(moocUserT.getEmail());
        userInfoModel.setBirthday(moocUserT.getBirthday());
        userInfoModel.setBiography(moocUserT.getBiography());
        userInfoModel.setAddress(moocUserT.getAddress());
        
        return userInfoModel;
    }

    @Override
    public UserInfoModel getUserInfo(int uuid) {
        // TODO Auto-generated method stub
        MoocUserT moocUserT = userMapper.selectById(uuid);
        UserInfoModel userInfoModel = do2UserInfoModel(moocUserT);
        return userInfoModel;
    }

    @Override
    public UserInfoModel updateUserInfo(UserInfoModel userInfoModel) {
        // TODO Auto-generated method stub
        return null;
    }

}

 

接下来业务层实现完成之后,需要在guns-gateway中使用对远程服务的调用,我们编写一个UserController类,来实现guns-gateway对guns-user的访问

UserController

package com.stylefeng.guns.rest.modular.user;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.dubbo.config.annotation.Reference;
import com.stylefeng.guns.api.user.UserAPI;
import com.stylefeng.guns.api.user.UserInfoModel;
import com.stylefeng.guns.api.user.UserModel;
import com.stylefeng.guns.rest.common.CurrentUser;
import com.stylefeng.guns.rest.modular.auth.VO.ReponseVO;

@RestController
@RequestMapping("/user/")
public class UserController {
    
    
    @Reference(interfaceClass= UserAPI.class)
    private UserAPI userAPI;
    
    @RequestMapping(value="register",method=RequestMethod.POST)
    public ReponseVO register(UserModel userModel){
        
        
        if(userModel.getUsername() == null || userModel.getUsername().equals("") ){
            return ReponseVO.serviceFail("用户名不能为空");
        }
        if(userModel.getPassword() == null || userModel.getPassword().equals("")){
            
            return ReponseVO.serviceFail("密码不能为空");
        }
        boolean result = userAPI.register(userModel);
        if(result){
            return ReponseVO.success(new String("用户注册成功"));
        }else{
            return ReponseVO.appFail("用户注册异常");
        }
        
    }
    
    //检查用户是否存在
    @RequestMapping(value="check",method=RequestMethod.POST)
    public ReponseVO check(String username){
        
        
        if(username == null || username.equals("") ){
            return ReponseVO.serviceFail("用户名不能为空");
        }
        
         boolean notExits = userAPI.checkUsername(username);
         if(notExits){
             
             return ReponseVO.success(new String("用户不存在,可以注册"));         
             }else{
                 
                 return ReponseVO.serviceFail("改用户已经存在"); 
             }
        
        
    }
    
    //获得当前用户的信息
    @RequestMapping(value="getUserInfo",method=RequestMethod.GET)
    public ReponseVO getUserInfo(){
        
        //首先从ThreadLocal中获得当前用户的id
        String userId = CurrentUser.getUserInfo();
        if(userId==null ||"".equals(userId)){
            return ReponseVO.serviceFail("当前用户没有登录");
        }else{
            
            int uuid = Integer.parseInt(userId);
            UserInfoModel userInfoModel = userAPI.getUserInfo(uuid);
            if(userInfoModel!=null){
                return ReponseVO.success(userInfoModel);
            }else{
                
                return ReponseVO.appFail("用户信息查询失败");
            }
        }
        
        
    }

}

 

完成之后,我们需要进行测试,通过guns-gateway来访问guns-user

业务测试:

 

1、访问gateway的注册、查询用户是否存在不需要进行url的校验

 我们在guns-gateway中配置不进行jwt检查的url

rest:
  auth-open: true #jwt鉴权机制是否开启(true或者false)
  sign-open: true #签名机制是否开启(true或false)

jwt:
  header: Authorization   #http请求头所需要的字段
  secret: mySecret        #jwt秘钥
  expiration: 604800      #7天 单位:秒
  auth-path: auth         #认证请求的路径
  md5-key: randomKey      #md5加密混淆key
  ignore-url: /user/register,/user/check   #对改类请求的url不进行jwt模块的校验

server:
  port: 80 #项目端口

mybatis-plus:
  mapper-locations: classpath*:com/stylefeng/guns/rest/**/mapping/*.xml
  typeAliasesPackage: com.stylefeng.guns.rest.common.persistence.model
  global-config:
    id-type: 0  #0:数据库ID自增   1:用户输入id  2:全局唯一id(IdWorker)  3:全局唯一ID(uuid)
    db-column-underline: false
    refresh-mapper: true
  configuration:
    map-underscore-to-camel-case: false
    cache-enabled: true #配置的缓存的全局开关
    lazyLoadingEnabled: true #延时加载的开关
    multipleResultSetsEnabled: true #开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句,调试用

spring:
  datasource:
      url: jdbc:mysql://127.0.0.1:3306/guns_rest?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
      username: root
      password: 123456
      filters: log4j,wall,mergeStat

logging:
  level.root: info
  level.com.stylefeng: debug
  path: logs/
  file: guns-rest.log

 接下来我们启动工程,因为guns-gateway通过dubbo访问了guns-user,我们要先启动zookeeper,在启动guns-user,最后在启动guns-gateway

 我们可以使用postman对上面的接口进行验证

接下来我们来验证获得当前用户详情的接口,要获得用户的详细信息,需要用户已经登录,必须要有jwt接口

这里很关键:

第一步使用接口获得对于的jwt信息

http://localhost:8081/auth?userName=admin&password=admin123

这里后台做了几件事情访问AuthController的createAuthenticationToken方法,在改方法中首先调用guns-user的login登录接口,判断当前输入的用户名和密码是否正确

,如果当前的用户名和密码正确,会 final String token = jwtTokenUtil.generateToken(userId+"", randomKey);会使用当前用户的用户id生成一个token信息

 

接下来第二步:

访问用户详情的接口,在请求的请求头中携带了jwt的信息,所有的http请求都会被AuthFilter拦截,在业务处理中会从请求头中获得jwt的信息,验证当前的//验证token是否过期,包含了验证jwt是否正确,然后从jwt中获得

当前用户的id,然后将当前用户的id存储到ThreadLocal变量中,filter拦截器通过之后,接下来回访问controller的业务接口,从ThreadLocal从获得当前用户的id,通过dubbo远程

调用guns-user中获得用户详情的接口,返回用户详情信息

这里有几点需要注意的,head中请求头的名称是在中指定的

jwt:
header: Authorization #http请求头所需要的字段

第二点:请求头携带的内容是以Bearer 开头加上第一步中返回的jwt的token信息

启动检查的时候,例如guns-gateway要远程调用guns-user的服务,当guns-gateway启动的时候,会检查guns-user是否启动正确,如果guns-user没有启动,整个guns-gateway就会启动

失败,如果我们要取消启动检查,需要在服务消费者方配置一个check=false的属性,这样即使guns-user没有启动,guns-gateway启动的时候也不会报错

package com.stylefeng.guns.rest.modular.auth.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.stylefeng.guns.api.user.UserAPI;
import com.stylefeng.guns.core.exception.GunsException;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import com.stylefeng.guns.rest.modular.auth.VO.ReponseVO;
import com.stylefeng.guns.rest.modular.auth.controller.dto.AuthRequest;
import com.stylefeng.guns.rest.modular.auth.controller.dto.AuthResponse;
import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import com.stylefeng.guns.rest.modular.auth.validator.IReqValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * 请求验证的
 *
 * @author fengshuonan
 * @Date 2017/8/24 14:22
 */
@RestController
public class AuthController {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Resource(name = "simpleValidator")
    private IReqValidator reqValidator;
    
    @Reference(interfaceClass=UserAPI.class,check=false)
    private UserAPI userApi;

    @RequestMapping(value = "${jwt.auth-path}")
    public ReponseVO createAuthenticationToken(AuthRequest authRequest) {

        //        boolean validate = reqValidator.validate(authRequest);
        boolean validate = true;
      int userId = userApi.login(authRequest.getUserName(), authRequest.getPassword());
       
        //int userId = 8;
       if(userId ==0){
           validate = false;
       }
        

        if (validate) {
            final String randomKey = jwtTokenUtil.getRandomKey();
            final String token = jwtTokenUtil.generateToken(userId+"", randomKey);
            return ReponseVO.success(new AuthResponse(token, randomKey));
        } else {
            return ReponseVO.serviceFail("用户名或者密码错误");
        }
    }
}

 

LeastActive:记录用户活跃数,第一个服务调用需要2秒,第二个服务需要调用3秒,那么调用的时候回转发到2秒那个服务上调用

 

 

 

 dubbo框架支持下面的协议:

dubbo支持的dubbo协议的话,最大的报文数据包不能超过100K

上面就是将协议配置成了RMI协议

F:\第5章 Dubbo服务开发:影片模块开发\第5章 Dubbo服务开发:影片模块开发

 

 接下来我们要构建一个guns-film模块,我们直接将guns-user复制一份,变成guns-film

进入到guns-film,修改起pom.xml文件

    <groupId>com.stylefeng.guns</groupId>
    <artifactId>guns-users</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <name>guns-users</name>
    <description>用户管理模块</description>

 

修改为

    <groupId>com.stylefeng.guns</groupId>
    <artifactId>guns-film</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <name>guns-film</name>
    <description>电影模块</description>

 

接下来进入到guns-parent模块,添加子模块guns-film的依赖

  <modules>
        <module>guns-admin</module>
        <module>guns-core</module>
        <module>guns-rest</module>
        <module>guns-generator</module>
        <module>guns-gateway</module>
        <module>guns-api</module>
        <module>guns-user</module>
        <module>guns-film</module>
    </modules>

 

接下来我们来看看影片的业务接口文档

目录
一、    影片模块接口    2
1、    首页接口    2
    请求地址    2
    请求方式    2
    请求字段    2
    应答报文    2
    业务异常    5
    系统异常    5
2、    影片条件列表查询接口    5
    请求地址    5
    请求方式    5
    请求字段    5
    应答报文    5
    业务异常    7
    系统异常    7
3、    影片查询接口    7
    请求地址    7
    请求方式    7
    请求字段    7
    应答报文    8
    业务异常    8
    系统异常    8
4、    影片详情查询接口    9
    请求地址    9
    请求方式    9
    请求字段    9
    应答报文    9
    业务异常    10
    系统异常    10



















一、    影片模块接口
1、    首页接口
    请求地址
/film/getIndex
    请求方式
get
    请求字段
无
    应答报文
{
status: 0,
imgPre:’http://img.meetingshop.cn/’,
data: {
    banners : [
        {
            bannerId:’001’,
            bannerAddress:‘img/banner/001.jpg’
            bannerUrl:‘http://www.meetingshop.cn/002.html’
},
{
            bannerId:’002’,
            bannerAddress:‘img/banner/002.jpg’
            bannerUrl:‘http://www.meetingshop.cn/002.html’
}
],
hotFilms : {
    filmNum : 28,
    filmInfo : [
        {
            filmId:’001’,
            filmType:1, [0-2D,1-3D,2-3DIMAX,4-无]
            imgAddress:‘img/film/001.jpg’
            filmName:‘我不是药神’,
            filmScore:‘8.3’
},
{
            filmId:’002’,
            filmType:4, [0-2D,1-3D,2-3DIMAX,4-无]
            imgAddress:‘img/film/002.jpg’
            filmName:‘摩天营救’,
            filmScore:‘9.0’
}
]
},
soonFilms : {
    filmNum : 295,
    filmInfo : [
        {
            filmId:’001’,
            filmType:1, [0-2D,1-3D,2-3DIMAX,4-无]
            imgAddress:‘img/film/001.jpg’
            filmName:‘我不是药神’,
            expectNum:283000,
            showTime : ‘2018-08-04’
},
{
            filmId:’002’,
            filmType:0, [0-2D,1-3D,2-3DIMAX,4-无]
            imgAddress:‘img/film/002.jpg’
            filmName:‘狄仁杰之四大天王’,
            expectNum:283000,
            showTime : ‘2018-09-04’
}
    ]
},
boxRanking : [
{
            filmId:’002’,
            imgAddress:‘img/film/002.jpg’
            filmName:‘狄仁杰之四大天王’,
            boxNum:231
},
{
            filmId:’003’,
            imgAddress:‘img/film/003.jpg’
            filmName:‘小猪佩奇’,
            boxNum:200
}
],
expectRanking : [
    {
            filmId:’002’,
            imgAddress:‘img/film/002.jpg’
            filmName:‘狄仁杰之四大天王’,
            expectNum:231850
},
{
            filmId:’003’,
            imgAddress:‘img/film/003.jpg’
            filmName:‘小猪佩奇’,
            expectNum:200321
}
].
top100 : [
    {
            filmId:’002’,
            imgAddress:‘img/film/002.jpg’
            filmName:‘狄仁杰之四大天王’,
            score:’9.3’
},
{
            filmId:’003’,
            imgAddress:‘img/film/003.jpg’
            filmName:‘小猪佩奇’,
            score:’9.0’
}
]
}

}
    业务异常
{
“status” : 1,
  “msg”   :“查询失败,无banner可加载”
}

    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}


2、    影片条件列表查询接口
    请求地址
/film/getConditionList
    请求方式
get
    请求字段
字段名称    字段含义    是否必填
catId    类型编号    否,默认为99
sourceId    片源编号    否,默认为99
yearId    年代编号    否,默认为99

    应答报文
{
status: 0,
data: {
    catInfo:[
    {
        catId:”001”,
        catName:”爱情”,
isActive: true
},
{
        catId:”002”,
        catName:”喜剧”
isActive: false
}

],
sourceInfo:[
{
        sourceId:”001”,
        sourceName:”大陆”,
        isActive: false
},
{
        sourceId:”002”,
        sourceName:”美国”
        isActive: true
}
],
yearInfo:[
{
        yearId:”001”,
        yearName:”2018年”,
        isActive: true
},
{
        yearId:”002”,
        yearName:”2018年以后”
        isActive: false
}

]
}
}
    业务异常
{
“status” : 1,
  “msg”   :“查询失败,无条件可加载”
}

    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}


3、    影片查询接口
    请求地址
/film/getFilms
    请求方式
get
    请求字段
字段名称    字段含义    是否必填
showType    查询类型,1-正在热映,2-即将上映,3-经典影片    否,默认为1
sortId    排序方式,1-按热门搜索,2-按时间搜索,3-按评价搜索    否,默认为1
catId    类型编号    否,默认为99
sourceId    区域编号    否,默认为99
yearId    年代编号    否,默认为99
nowPage    影片列表当前页,翻页使用    否,默认为1
pageSize    每页显示条数    否,默认为18

    应答报文
{
status: 0,
imgPre:’http://img.meetingshop.cn/’,
nowPage : 1,
totalPage : 3,
data: [
    {
        filmId:’001’,
        filmType:1, [0-2D,1-3D,2-3DIMAX,4-无],
        imgAddress:‘img/film/001.jpg’,
        filmName:‘我不是药神’,
        filmScore:‘8.3’
},
{
        filmId:’002’,
        filmType:4, [0-2D,1-3D,2-3DIMAX,4-无],
        imgAddress:‘img/film/002.jpg’,
        filmName:‘摩天营救’,
        filmScore:‘9.0’
}
]

}
    业务异常
{
“status” : 1,
  “msg”   :“查询失败,无banner可加载”
}

    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}

4、    影片详情查询接口
    请求地址
/film/films/{影片编号或影片名称}
    请求方式
get
    请求字段
searchType : ‘0表示按照编号查找,1表示按照名称查找’
    应答报文
{
status: 0,
imgPre:’http://img.meetingshop.cn/’,
data: {
    filmName : ‘动物世界’,
    filmEnName : ‘Animal World’,
    imgAddress : ‘img/films/23412.jpg’,
score : ‘8.5’,
scoreNum : ‘43万人评分’,
totalBox : ‘5.07亿’,
info01 : ‘动作,悬疑,冒险’,
info02 : ‘中国大陆,美国 / 132分钟’,
info03 : ‘2018-06-29大陆上映’,
info04 : {
biography : ‘男主角郑开司(李易峰 饰)因被朋友欺骗而背负上数百万的债务,面对重病的母亲和痴心等待的青梅竹马,他决心登上“命运号”游轮,改变自己一事无成的人生。只要能在渡轮上的游戏中获胜,他就将有机会将债务一笔勾销,并给家人带来更好的生活。这场游戏看似简单,参与者只需以标着“石头,剪刀,布”的扑克为道具,赢取对手身上的星星标志。但游轮上的亡命徒们毫无底线的欺诈争夺,却让人性的自私与残酷暴露无遗,局中局、计中计,让游戏场最终沦为“动物世界”斗兽场。面对绝境的郑开司,能否坚守自我底线保持善良本性?能否凭借自己的智慧和坚韧摆脱困境?这是一场自我救赎的残酷游戏,多重考验也将接踵而至。’,
actors : {
    director : {
        imgAddress : ‘actors/02134.jpg’,
        directorName : ‘韩延’
},
actors : [
    {
            imgAddress : ‘actors/02136.jpg’,
            directorName : ‘李易峰’,
            roleName : ‘郑开司’
},
{
            imgAddress : ‘actors/02332.jpg’,
            directorName : ‘周冬雨’,
            roleName : ‘刘青’
}
]
}
},
        imgs : {
            mainImg : ‘fims/123123.png’,
            img01 : ‘fims/12312.jpg’
img02 : ‘films/16345.png’,
img03 : ‘films/12938.jpg’,
img04 : ‘films/21365.jpg’
},
filmId : ‘12’
}
}
    业务异常
{
“status” : 1,
  “msg”   :“查询失败,无影片可加载”
}

    系统异常
{
“status” : 999,
  “msg”   :“系统出现异常,请联系管理员”
}

 

我们现在guns-gateway中网关中先建立一个film模块

影片模块需要下面的6类数据

 

 1、首先获得横幅的数据

2、获得当前正在热映的数据

3、获得即将上演的数据

4、票房排行榜的数据

5、获取受欢迎榜单的数据

6.获取前一百的数据

在首页面展示的时候需要获得上面6个模块的数据,这里如果前段页面分别发起6次http请求去获得对于的数据,比较麻烦

我们可以在guns-gateway中提供一个接口,当前段查询整个接口的时候,把上面6个接口的数据返回给前段,在网关层对业务进行聚合操作

接下来我们创建对于的实体类对象BannerVO为横幅对于的实体类

package com.stylefeng.guns.rest.modular.film.VO;

public class BannerVO {

    
    private String bannerID;
    private String bannerAddress;
    private String bannerUrl;
    public String getBannerID() {
        return bannerID;
    }
    public void setBannerID(String bannerID) {
        this.bannerID = bannerID;
    }
    public String getBannerAddress() {
        return bannerAddress;
    }
    public void setBannerAddress(String bannerAddress) {
        this.bannerAddress = bannerAddress;
    }
    public String getBannerUrl() {
        return bannerUrl;
    }
    public void setBannerUrl(String bannerUrl) {
        this.bannerUrl = bannerUrl;
    }
    @Override
    public String toString() {
        return "BannerVO [bannerID=" + bannerID + ", bannerAddress=" + bannerAddress + ", bannerUrl=" + bannerUrl + "]";
    }
    
    
    
}

 

我们创建实体类的时候,每次都要给实体类编写getter和setter方法,还有toString方法,非常的麻烦,lombok框架已经帮我们完成这个功能了

我们只需要添加下面的依赖

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.2</version>
    <scope>provided</scope>
</dependency>

 

我们在编写实体类的时候使用@Data注解就会自动帮实体类生成getter和setter 还有toString等方法

package com.stylefeng.guns.rest.modular.film.VO;

import lombok.Data;

@Data
public class BannerVO {

    
    private String bannerID;
    private String bannerAddress;
    private String bannerUrl;
    
    
    
    
}


 上面的代码

package com.stylefeng.guns.rest.modular.film.VO;

public class BannerVO {

    
    private String bannerID;
    private String bannerAddress;
    private String bannerUrl;
    public String getBannerID() {
        return bannerID;
    }
    public void setBannerID(String bannerID) {
        this.bannerID = bannerID;
    }
    public String getBannerAddress() {
        return bannerAddress;
    }
    public void setBannerAddress(String bannerAddress) {
        this.bannerAddress = bannerAddress;
    }
    public String getBannerUrl() {
        return bannerUrl;
    }
    public void setBannerUrl(String bannerUrl) {
        this.bannerUrl = bannerUrl;
    }
    @Override
    public String toString() {
        return "BannerVO [bannerID=" + bannerID + ", bannerAddress=" + bannerAddress + ", bannerUrl=" + bannerUrl + "]";
    }
    
    
    
}

接下来按照业务接口

 

 

 

创建hotFilm的对象中包含了filmNum,和fileinfo对象

我们先创建filmInfo对象

package com.stylefeng.guns.rest.modular.film.VO;

public class FilmInfo  implements Serializable{
    
    private String filmId;
    private int filmType;
    private String imgAddress;
    private String filmName;
    private String filmScore;
    private int expectNum;
    private String showTime ;
    public String getFilmId() {
        return filmId;
    }
    public void setFilmId(String filmId) {
        this.filmId = filmId;
    }
    @Override
    public String toString() {
        return "FilmInfo [filmId=" + filmId + ", filmType=" + filmType + ", imgAddress=" + imgAddress + ", filmName="
                + filmName + ", filmScore=" + filmScore + ", expectNum=" + expectNum + ", showTime=" + showTime + "]";
    }
    public int getFilmType() {
        return filmType;
    }
    public void setFilmType(int filmType) {
        this.filmType = filmType;
    }
    public String getImgAddress() {
        return imgAddress;
    }
    public void setImgAddress(String imgAddress) {
        this.imgAddress = imgAddress;
    }
    public String getFilmName() {
        return filmName;
    }
    public void setFilmName(String filmName) {
        this.filmName = filmName;
    }
    public String getFilmScore() {
        return filmScore;
    }
    public void setFilmScore(String filmScore) {
        this.filmScore = filmScore;
    }
    public int getExpectNum() {
        return expectNum;
    }
    public void setExpectNum(int expectNum) {
        this.expectNum = expectNum;
    }
    public String getShowTime() {
        return showTime;
    }
    public void setShowTime(String showTime) {
        this.showTime = showTime;
    }

}

 一个完整的FileInfo对象包括下面的内容:

package com.stylefeng.guns.rest.modular.film.VO;

public class FilmInfo implements Serializable {
    
    private String filmId;
    private int filmType;
    private String imgAddress;
    private String filmName;
    private String filmScore;
    private int expectNum;
    private String showTime ;
    private int boxNum;
    private String score;
    
    
    public String getScore() {
        return score;
    }
    public void setScore(String score) {
        this.score = score;
    }
    public int getBoxNum() {
        return boxNum;
    }
    public void setBoxNum(int boxNum) {
        this.boxNum = boxNum;
    }
    public String getFilmId() {
        return filmId;
    }
    public void setFilmId(String filmId) {
        this.filmId = filmId;
    }
    @Override
    public String toString() {
        return "FilmInfo [filmId=" + filmId + ", filmType=" + filmType + ", imgAddress=" + imgAddress + ", filmName="
                + filmName + ", filmScore=" + filmScore + ", expectNum=" + expectNum + ", showTime=" + showTime + "]";
    }
    public int getFilmType() {
        return filmType;
    }
    public void setFilmType(int filmType) {
        this.filmType = filmType;
    }
    public String getImgAddress() {
        return imgAddress;
    }
    public void setImgAddress(String imgAddress) {
        this.imgAddress = imgAddress;
    }
    public String getFilmName() {
        return filmName;
    }
    public void setFilmName(String filmName) {
        this.filmName = filmName;
    }
    public String getFilmScore() {
        return filmScore;
    }
    public void setFilmScore(String filmScore) {
        this.filmScore = filmScore;
    }
    public int getExpectNum() {
        return expectNum;
    }
    public void setExpectNum(int expectNum) {
        this.expectNum = expectNum;
    }
    public String getShowTime() {
        return showTime;
    }
    public void setShowTime(String showTime) {
        this.showTime = showTime;
    }

}

 

package com.stylefeng.guns.rest.modular.film.VO;

public class BannerVO {

    
    private String bannerID;
    private String bannerAddress;
    private String bannerUrl;
    public String getBannerID() {
        return bannerID;
    }
    public void setBannerID(String bannerID) {
        this.bannerID = bannerID;
    }
    public String getBannerAddress() {
        return bannerAddress;
    }
    public void setBannerAddress(String bannerAddress) {
        this.bannerAddress = bannerAddress;
    }
    public String getBannerUrl() {
        return bannerUrl;
    }
    public void setBannerUrl(String bannerUrl) {
        this.bannerUrl = bannerUrl;
    }
    @Override
    public String toString() {
        return "BannerVO [bannerID=" + bannerID + ", bannerAddress=" + bannerAddress + ", bannerUrl=" + bannerUrl + "]";
    }
    
    
    
}

 

接下来我们定义一个首页对象,改对象包含上面的6个对象性质

package com.stylefeng.guns.rest.modular.film.VO;

import java.util.List;

public class FilmIndexVO  implements Serializable{
    private List<BannerVO> bannners;
    private FilmVO hotFilms;
    private FilmVO  soonFils;
    private List<FilmInfo> boxRanking ;
    private List<FilmInfo> expectRanking ;
    private List<FilmInfo> top100 ;
    public List<BannerVO> getBannners() {
        return bannners;
    }
    public void setBannners(List<BannerVO> bannners) {
        this.bannners = bannners;
    }
    public FilmVO getHotFilms() {
        return hotFilms;
    }
    public void setHotFilms(FilmVO hotFilms) {
        this.hotFilms = hotFilms;
    }
    public FilmVO getSoonFils() {
        return soonFils;
    }
    public void setSoonFils(FilmVO soonFils) {
        this.soonFils = soonFils;
    }
    public List<FilmInfo> getBoxRanking() {
        return boxRanking;
    }
    public void setBoxRanking(List<FilmInfo> boxRanking) {
        this.boxRanking = boxRanking;
    }
    public List<FilmInfo> getExpectRanking() {
        return expectRanking;
    }
    public void setExpectRanking(List<FilmInfo> expectRanking) {
        this.expectRanking = expectRanking;
    }
    public List<FilmInfo> getTop100() {
        return top100;
    }
    public void setTop100(List<FilmInfo> top100) {
        this.top100 = top100;
    }
    
    
    

}

 

package com.stylefeng.guns.rest.modular.film.VO;

public class BannerVO implements Serializable {

    
    private String bannerID;
    private String bannerAddress;
    private String bannerUrl;
    public String getBannerID() {
        return bannerID;
    }
    public void setBannerID(String bannerID) {
        this.bannerID = bannerID;
    }
    public String getBannerAddress() {
        return bannerAddress;
    }
    public void setBannerAddress(String bannerAddress) {
        this.bannerAddress = bannerAddress;
    }
    public String getBannerUrl() {
        return bannerUrl;
    }
    public void setBannerUrl(String bannerUrl) {
        this.bannerUrl = bannerUrl;
    }
    @Override
    public String toString() {
        return "BannerVO [bannerID=" + bannerID + ", bannerAddress=" + bannerAddress + ", bannerUrl=" + bannerUrl + "]";
    }
    
    
    
}

 

整个影片模块的实体类对象就已经创建完成了

 

 接下来我们就开始进行影片接口模块的实现,我们要到guns-api中实现对于的影片接口模块的业务实现类

因为guns-gateway和guns-film都要使用到上面创建的FilmVO,FilmInfo,以及BannerVO对象,我们把三个对象存储在公共的guns-api中,FilmIndexVO仅仅是guns-film模块使用的

 

 并且上面的几个类一定要实现序列号接口,接下来我们编写影片的业务接口

FillmServiceAPI.java

package com.stylefeng.guns.api.film;

import java.util.List;

import com.stylefeng.guns.rest.modular.film.VO.BannerVO;
import com.stylefeng.guns.rest.modular.film.VO.FilmInfo;
import com.stylefeng.guns.rest.modular.film.VO.FilmVO;

public interface FillmServiceAPI {
    
/*    1、首先获得横幅的数据

    2、获得当前正在热映的数据

    3、获得即将上演的数据

    4、票房排行榜的数据

    5、获取受欢迎榜单的数据

    6.获取前一百的数据*/
    
    
    //1、首先获得横幅的数据
    
    List<BannerVO> getBanners();
    
    //2、获得当前正在热映的数据 isLimit是否对影片的数目做限制,nums表示最大的数目是多少
    FilmVO getHotFilms(boolean isLimit ,int nums);
    
    
    //3、获得即将上演的数据
    FilmVO getSoonFilms(boolean isLimit ,int nums);
    
    //4、票房排行榜的数据
    
    List<FilmInfo> getboxRanking();
    
    //5、获取受欢迎榜单的数据
    List<FilmInfo> getexpectRanking();
    
    
    //6.获取前一百的数据*/
    List<FilmInfo> getTop();
    
}

 

接下来我们开始编写我们的业务功能,guns-film模块也是通过网关来调用的mguns-film中也不需要进行jwt的校验,所以在配置文件中application.yml,需要关闭jwt的校验

rest:
  auth-open: false #jwt鉴权机制是否开启(true或者false)
  sign-open: false #签名机制是否开启(true或false)

 

 接来下我们在guns-films模块中来实现具体的业务操作,我们新建两个模块dao,存储影片模块的dao,Service来实现具体的业务操作功能

 我们首先要在数据库中建立下面的表,采用下面的建表语句进行操作

-- ----------------------------
-- Table structure for mooc_banner_t
-- ----------------------------
DROP TABLE IF EXISTS `mooc_banner_t`;
CREATE TABLE mooc_banner_t(
  UUID INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键编号',
  banner_address VARCHAR(50) COMMENT 'banner图存放路径',
  banner_url VARCHAR(200) COMMENT 'banner点击跳转url',
  is_valid INT DEFAULT 0 COMMENT '是否弃用 0-失效,1-失效'
) COMMENT 'banner信息表' ENGINE = INNODB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


-- ----------------------------
-- Records of mooc_banner_t
-- ----------------------------
INSERT INTO mooc_banner_t(banner_address,banner_url) VALUES('banners/9d75708ae91d5fc918369b76e9e2edba197666.jpg','www.meetingshop.cn');
INSERT INTO mooc_banner_t(banner_address,banner_url) VALUES('banners/15b3730838f35d56a76d88a1787aaaa5186414.jpg','www.meetingshop.cn');
INSERT INTO mooc_banner_t(banner_address,banner_url) VALUES('banners/51afa73f0347e9b98957c53fa971d41491652.jpg','www.meetingshop.cn');
INSERT INTO mooc_banner_t(banner_address,banner_url) VALUES('banners/6605d3518e2bba10f29a6f9ae32b361987015.jpg','www.meetingshop.cn');
INSERT INTO mooc_banner_t(banner_address,banner_url) VALUES('banners/c1a713981cabef02c88ae5f42888de70183835.jpg','www.meetingshop.cn');



-- ----------------------------
-- Table structure for mooc_cat_dict_t
-- ----------------------------
DROP TABLE IF EXISTS `mooc_cat_dict_t`;
CREATE TABLE mooc_cat_dict_t(
  UUID INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键编号',
  show_name VARCHAR(100) COMMENT '显示名称'
) COMMENT '类型信息表' ENGINE = INNODB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


-- ----------------------------
-- Records of mooc_cat_dict_t
-- ----------------------------
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(99,'全部');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(1,'爱情');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(2,'喜剧');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(3,'动画');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(4,'剧情');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(5,'恐怖');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(6,'惊悚');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(7,'科幻');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(8,'动作');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(9,'悬疑');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(10,'犯罪');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(11,'冒险');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(12,'战争');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(13,'奇幻');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(14,'运动');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(15,'家庭');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(16,'古装');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(17,'武侠');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(18,'西部');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(19,'历史');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(20,'传记');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(21,'歌舞');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(22,'短片');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(23,'纪录片');
INSERT INTO mooc_cat_dict_t(uuid,show_name) values(24,'黑色电影');





-- ----------------------------
-- Table structure for mooc_source_dict_t
-- ----------------------------
DROP TABLE IF EXISTS `mooc_source_dict_t`;
CREATE TABLE mooc_source_dict_t(
  UUID INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键编号',
  show_name VARCHAR(100) COMMENT '显示名称'
) COMMENT '区域信息表' ENGINE = INNODB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


-- ----------------------------
-- Records of mooc_source_dict_t
-- ----------------------------
INSERT INTO mooc_source_dict_t(uuid,show_name) values(99,'全部');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(1,'大陆');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(2,'美国');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(3,'韩国');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(4,'日本');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(5,'中国香港');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(6,'中国台湾');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(7,'印度');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(8,'法国');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(9,'英国');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(10,'俄罗斯');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(11,'意大利');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(12,'西班牙');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(13,'德国');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(14,'波兰');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(15,'澳大利亚');
INSERT INTO mooc_source_dict_t(uuid,show_name) values(16,'伊朗');


-- ----------------------------
-- Table structure for mooc_year_dict_t
-- ----------------------------
DROP TABLE IF EXISTS `mooc_year_dict_t`;
CREATE TABLE mooc_year_dict_t(
  UUID INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键编号',
  show_name VARCHAR(100) COMMENT '显示名称'
) COMMENT '年代信息表' ENGINE = INNODB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


-- ----------------------------
-- Records of mooc_year_dict_t
-- ----------------------------
INSERT INTO mooc_year_dict_t(uuid,show_name) values(99,'全部');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(1,'更早');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(2,'70年代');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(3,'80年代');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(4,'90年代');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(5,'2000-2010');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(6,'2011');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(7,'2012');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(8,'2013');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(9,'2014');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(10,'2015');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(11,'2016');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(12,'2017');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(13,'2018');
INSERT INTO mooc_year_dict_t(uuid,show_name) values(14,'2018以后');



-- ----------------------------
-- Table structure for mooc_film_t
-- ----------------------------
DROP TABLE IF EXISTS `mooc_film_t`;
CREATE TABLE mooc_film_t(
  UUID INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键编号',
  film_name VARCHAR(100) COMMENT '影片名称',
  film_type INT COMMENT '片源类型: 0-2D,1-3D,2-3DIMAX,4-无',
  img_address VARCHAR(200) COMMENT '影片主图地址',
  film_score VARCHAR(20) COMMENT '影片评分',
  film_preSaleNum INT COMMENT '影片预售数量',
  film_box_office INT COMMENT '影片票房:每日更新,以万为单位',
  film_source INT COMMENT '影片片源,参照片源字典表',
  film_cats VARCHAR(50) COMMENT '影片分类,参照分类表,多个分类以#分割',
  film_area INT COMMENT '影片区域,参照区域表',
  film_date INT COMMENT '影片上映年代,参照年代表',
  film_time TIMESTAMP COMMENT '影片上映时间',
  film_status INT COMMENT '影片状态,1-正在热映,2-即将上映,3-经典影片'
) COMMENT '影片主表' ENGINE = INNODB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


-- ----------------------------
-- Records of mooc_film_t
-- ----------------------------
INSERT INTO mooc_film_t(uuid,film_name,film_source,film_type,film_cats,film_area,film_date,film_time,film_preSaleNum,film_box_office,film_score,film_status,img_address) 
            values(2,'我不是药神',1,0,'#2#4#22#',1,13,'2018-07-05',231432491,309600,'9.7',1,'films/238e2dc36beae55a71cabfc14069fe78236351.jpg');
                    
            

-- ----------------------------
-- Table structure for mooc_film_info_t
-- ----------------------------
DROP TABLE IF EXISTS `mooc_film_info_t`;
CREATE TABLE mooc_film_info_t(
  UUID INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键编号',
  film_id VARCHAR(100) COMMENT '影片编号',
  film_en_name VARCHAR(50) COMMENT '影片英文名称',
  film_score VARCHAR(20) COMMENT '影片评分',
  film_score_num INT COMMENT '评分人数,以万为单位',
  film_length INT COMMENT '播放时长,以分钟为单位,不足取整',
  biography TEXT COMMENT '影片介绍',
  director_id INT COMMENT '导演编号',
  film_imgs TEXT COMMENT '影片图片集地址,多个图片以逗号分隔'
) COMMENT '影片主表' ENGINE = INNODB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


-- ----------------------------
-- Records of mooc_film_info_t
-- ----------------------------
INSERT INTO mooc_film_info_t(film_id,film_en_name,film_score,film_score_num,film_length,director_id,film_imgs,biography)
    values(2,'Dying To Survive','9.7',225,117,1,
        'films/3065271341357040f5f5dd988550951e586199.jpg,films/6b2b3fd6260ac37e5ad44d00ea474ea3651419.jpg,films/4633dd44c51ff15fc7e939679d7cdb67561602.jpg,films/df2d30b1a3bd58fb1d38b978662ae844648169.jpg,films/c845f6b04aa49059951fd55e6b0eddac454036.jpg',
        '一位不速之客的意外到访,打破了神油店老板程勇(徐峥 饰)的平凡人生,他从一个交不起房租的男性保健品商贩,一跃成为印度仿制药“格列宁”的独家代理商。收获巨额利润的他,生活剧烈变化,被病患们冠以“药神”的称号。但是,一场关于救赎的拉锯战也在波涛暗涌中慢慢展开......'
    );


-- ----------------------------
-- Table structure for mooc_actor_t
-- ----------------------------
DROP TABLE IF EXISTS `mooc_actor_t`;
CREATE TABLE mooc_actor_t(
  UUID INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键编号',
  actor_name VARCHAR(50) COMMENT '演员名称',
  actor_img  VARCHAR(200) COMMENT '演员图片位置'
) COMMENT '演员表' ENGINE = INNODB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


-- ----------------------------
-- Records of mooc_actor_t
-- ----------------------------
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(1,'徐峥','actors/2b98c9d2e6d23a7eff25dcac8b584b0136045.jpg');
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(2,'王传君','actors/b782d497577baffb5ed14de52841dcb164365.jpg');
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(3,'谭卓','actors/acf7db57456cb1aed1a42f7ebffedaa842002.jpg');
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(4,'黄渤','actors/c6594ef2705dcaf7d9df857d228b5e1645712.jpg');
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(5,'舒淇','actors/6b32a489467283bb739a2bac3b2b929742175.jpg');
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(6,'张艺兴','actors/b738d5e78a1f5c3379d9d42a9b18286f32246.jpeg');
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(7,'强森','actors/7e3067d066c1e285b0cc17bfd5f1b34e108474.jpg');
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(8,'杰森·斯坦森','actors/7ec0c90aec03c7904c1db3af1153162f77864.jpg');
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(9,'李冰冰','actors/d2258cd0529950cf5099206519d91d0e51803.jpg');
INSERT INTO mooc_actor_t(uuid,actor_name,actor_img) value(10,'汤姆·克鲁斯','actors/6afaea1cb4ca2b346e86e265347c78b833970.jpg');

 

-- ----------------------------
-- Table structure for mooc_film_actor_t
-- ----------------------------
DROP TABLE IF EXISTS `mooc_film_actor_t`;
CREATE TABLE mooc_film_actor_t(
  UUID INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键编号',
  film_id INT COMMENT '影片编号,对应mooc_film_t',
  actor_id INT COMMENT '演员编号,对应mooc_actor_t',
  role_name VARCHAR(100) COMMENT '角色名称'
) COMMENT '影片与演员映射表' ENGINE = INNODB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;


-- ----------------------------
-- Records of mooc_film_actor_t
-- ----------------------------
INSERT INTO mooc_film_actor_t(UUID,film_id,actor_id,role_name)
    values(1,2,1,'演员1');
INSERT INTO mooc_film_actor_t(UUID,film_id,actor_id,role_name)
    values(2,2,2,'演员2');
INSERT INTO mooc_film_actor_t(UUID,film_id,actor_id,role_name)
    values(3,2,3,'演员3');
INSERT INTO mooc_film_actor_t(UUID,film_id,actor_id,role_name)
    values(4,2,4,'演员4');

 

表建立完成之后,接下来我们采用自动代码生成的方式帮助我们生成对于的代码,我们使用EntityGenerator帮助我们自动生成对于的代码类

package com.stylefeng.guns.generator;

import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.DbType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;

/**
 * 实体生成
 *
 * @author fengshuonan
 * @date 2017-08-23 12:15
 */
public class EntityGenerator {

    @Test
    public void entityGenerator() {
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        //D:\work_project\naan1993-guns-master\guns\guns-user\src\main\java
        gc.setOutputDir("D:\\work_project\\naan1993-guns-master\\guns\\guns-film\\src\\main\\java");//这里写你自己的java目录
        gc.setFileOverride(true);//是否覆盖
        gc.setActiveRecord(true);
        gc.setEnableCache(false);// XML 二级缓存
        gc.setBaseResultMap(true);// XML ResultMap
        gc.setBaseColumnList(false);// XML columList
        gc.setAuthor("weiyuan");
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MYSQL);
        dsc.setTypeConvert(new MySqlTypeConvert() {
            // 自定义数据库表字段类型转换【可选】
            @Override
            public DbColumnType processTypeConvert(String fieldType) {
                return super.processTypeConvert(fieldType);
            }
        });
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setUrl("jdbc:mysql://127.0.0.1:3306/guns_rest?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
        mpg.setDataSource(dsc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //strategy.setTablePrefix(new String[]{"_"});// 此处可以修改为您的表前缀
        strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
        strategy.setInclude(new String[]{"mooc_actor_t","mooc_banner_t","mooc_cat_dict_t","mooc_film_actor_t","mooc_film_info_t","mooc_film_t",
                "mooc_source_dict_t","mooc_year_dict_t"});
        mpg.setStrategy(strategy);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent(null);
        pc.setEntity("com.stylefeng.guns.rest.common.persistence.model");
        pc.setMapper("com.stylefeng.guns.rest.common.persistence.dao");
        pc.setXml("com.stylefeng.guns.rest.common.persistence.dao.mapping");
        pc.setService("TTT");       //本项目没用,生成之后删掉
        pc.setServiceImpl("TTT");   //本项目没用,生成之后删掉
        pc.setController("TTT");    //本项目没用,生成之后删掉
        mpg.setPackageInfo(pc);

        // 注入自定义配置,可以在 VM 中使用 cfg.abc 设置的值
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                this.setMap(map);
            }
        };
        mpg.setCfg(cfg);

        // 执行生成
        mpg.execute();

        // 打印注入设置
        System.err.println(mpg.getCfg().getMap().get("abc"));
    }
}

 

我们运行EntityGenerator,就能够帮助我们生成mybatis对于的dao文件

dao层生成好了,接下来我们来实现具体的业务操作

接下来实现我们的banners横幅的业务代码

package com.stylefeng.guns.rest.modular.film.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Service;
import com.stylefeng.guns.api.film.FillmServiceAPI;
import com.stylefeng.guns.rest.common.persistence.dao.MoocBannerTMapper;
import com.stylefeng.guns.rest.common.persistence.model.MoocBannerT;
import com.stylefeng.guns.rest.modular.film.VO.BannerVO;
import com.stylefeng.guns.rest.modular.film.VO.FilmInfo;
import com.stylefeng.guns.rest.modular.film.VO.FilmVO;

@Component
@Service(interfaceClass=FillmServiceAPI.class)
public class DefaultFilmServiceImpl  implements FillmServiceAPI{

    @Autowired
    private MoocBannerTMapper moocBannerTMapper;
    
    @Override
    public List<BannerVO> getBanners() {
        // TODO Auto-generated method stub
        //查询出所有的banners
        List<MoocBannerT> selectList = moocBannerTMapper.selectList(null);
        List<BannerVO> datas = new ArrayList<>();
        for( MoocBannerT moocBannerT:selectList){
            BannerVO bannerVO = new BannerVO();
            bannerVO.setBannerID(moocBannerT.getUuid()+"");
            bannerVO.setBannerUrl(moocBannerT.getBannerUrl());
            bannerVO.setBannerAddress(moocBannerT.getBannerAddress());
            datas.add(bannerVO);
        }
        return datas;
    }

    @Override
    public FilmVO getHotFilms(boolean isLimit, int nums) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public FilmVO getSoonFilms(boolean isLimit, int nums) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<FilmInfo> getboxRanking() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<FilmInfo> getexpectRanking() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<FilmInfo> getTop() {
        // TODO Auto-generated method stub
        return null;
    }

}

 

接下来实现热映电影的业务代码

首页具有有热映的电影,默认8个电影,在电影菜单下也存在正在热映的电影

所有在热映的实现类中加入了一个来判断当前是否在首页的热映

整个业务层的代码如下

package com.stylefeng.guns.rest.modular.film.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Service;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.stylefeng.guns.api.film.FillmServiceAPI;
import com.stylefeng.guns.core.util.DateUtil;
import com.stylefeng.guns.rest.common.persistence.dao.MoocBannerTMapper;
import com.stylefeng.guns.rest.common.persistence.dao.MoocFilmTMapper;
import com.stylefeng.guns.rest.common.persistence.model.MoocBannerT;
import com.stylefeng.guns.rest.common.persistence.model.MoocFilmT;
import com.stylefeng.guns.rest.modular.film.VO.BannerVO;
import com.stylefeng.guns.rest.modular.film.VO.FilmInfo;
import com.stylefeng.guns.rest.modular.film.VO.FilmVO;

@Component
@Service(interfaceClass=FillmServiceAPI.class)
public class DefaultFilmServiceImpl  implements FillmServiceAPI{

    @Autowired
    private MoocBannerTMapper moocBannerTMapper;
    
    @Autowired
    private MoocFilmTMapper moocFilmMapper;
    
    @Override
    public List<BannerVO> getBanners() {
        // TODO Auto-generated method stub
        //查询出所有的banners
        List<MoocBannerT> selectList = moocBannerTMapper.selectList(null);
        List<BannerVO> datas = new ArrayList<>();
        for( MoocBannerT moocBannerT:selectList){
            BannerVO bannerVO = new BannerVO();
            bannerVO.setBannerID(moocBannerT.getUuid()+"");
            bannerVO.setBannerUrl(moocBannerT.getBannerUrl());
            bannerVO.setBannerAddress(moocBannerT.getBannerAddress());
            datas.add(bannerVO);
        }
        return datas;
    }

    @Override
    public FilmVO getHotFilms(boolean isLimit, int nums) {
        // TODO Auto-generated method stub
         FilmVO filmVO = new FilmVO();
         List<FilmInfo> filmInfos = new ArrayList<>();
         //按照影片的限制条件进行查找,热映影片对于的film_state为1
         EntityWrapper<MoocFilmT> entityWrapper = new EntityWrapper<>();
         entityWrapper.eq("film_status", "1");
         //判断当前是否是首页加载热映影片
         if(isLimit){
             //这里首页也要进行分页查询,这里使用mybatis-plus插件提供的分页查询
             //1表示第一页,nums表示当前页要显示的数目
             Page<MoocFilmT> page = new Page<>(1, nums);
             //接下来执行查询操作
             List<MoocFilmT> moocFilmTs = moocFilmMapper.selectPage(page, entityWrapper);
             filmInfos = getFilmInfos(moocFilmTs);
             filmVO.setFilmNum(filmInfos.size()+"");
             filmVO.setFilminfo(filmInfos);
         }
         
         
        return filmVO;
    }
    
    List<FilmInfo> getFilmInfos(List<MoocFilmT> moocFilmTs){
        
        List<FilmInfo> filmInfos =  new ArrayList<>();
        for(MoocFilmT data:moocFilmTs){
            FilmInfo filmInfo = new FilmInfo();
            filmInfo.setScore(data.getFilmScore());
            filmInfo.setImgAddress(data.getImgAddress());
            filmInfo.setFilmType(data.getFilmType());
            filmInfo.setFilmScore(data.getFilmScore());
            filmInfo.setFilmName(data.getFilmName());
            filmInfo.setFilmId(data.getUuid()+"");
            filmInfo.setExpectNum(data.getFilmPresalenum());
            filmInfo.setBoxNum(data.getFilmBoxOffice());
            filmInfo.setShowTime(DateUtil.getDay( data.getFilmTime()));
            filmInfos.add(filmInfo);
            
        }
        
        
        return filmInfos;
        
    }

    @Override
    public FilmVO getSoonFilms(boolean isLimit, int nums) {
        // TODO Auto-generated method stub
                 FilmVO filmVO = new FilmVO();
                 List<FilmInfo> filmInfos = new ArrayList<>();
                 //按照影片的限制条件进行查找,热映影片对于的film_state为1
                 EntityWrapper<MoocFilmT> entityWrapper = new EntityWrapper<>();
                 entityWrapper.eq("film_status", "2");
                 //判断当前是否是首页加载热映影片
                 if(isLimit){
                     //这里首页也要进行分页查询,这里使用mybatis-plus插件提供的分页查询
                     //1表示第一页,nums表示当前页要显示的数目
                     Page<MoocFilmT> page = new Page<>(1, nums);
                     //接下来执行查询操作
                     List<MoocFilmT> moocFilmTs = moocFilmMapper.selectPage(page, entityWrapper);
                     filmInfos = getFilmInfos(moocFilmTs);
                     filmVO.setFilmNum(filmInfos.size()+"");
                     filmVO.setFilminfo(filmInfos);
                 }
                 
                 
                return filmVO;
    }

    @Override
    public List<FilmInfo> getboxRanking() {
        // TODO Auto-generated method stub
        //正在上映的票房前十名
        EntityWrapper<MoocFilmT> entityWrapper = new EntityWrapper<>();
         entityWrapper.eq("film_status", "1");
         
         //封装一个Page分析对象进行分页查询,安装票房进行排序
         //1表示第一页,10表示当前页显示10条记录
         //film_box_office表示当前排序的字段
         Page<MoocFilmT> page = new Page<>(1, 10, "film_box_office");
         List<MoocFilmT> moocFilmTs = moocFilmMapper.selectPage(page,entityWrapper);
         List<FilmInfo> filmInfos = getFilmInfos(moocFilmTs);
        
        return filmInfos;
    }

    @Override
    public List<FilmInfo> getexpectRanking() {
        //即将上映的 ,提前售票的票房前十秒
        EntityWrapper<MoocFilmT> entityWrapper = new EntityWrapper<>();
         entityWrapper.eq("film_status", "2");
        //封装一个Page分析对象进行分页查询,安装票房进行排序
         //1表示第一页,10表示当前页显示10条记录
         //film_box_office表示当前排序的字段
         Page<MoocFilmT> page = new Page<>(1, 10, "film_preSaleNum");
         List<MoocFilmT> moocFilmTs = moocFilmMapper.selectPage(page,entityWrapper);
         List<FilmInfo> filmInfos = getFilmInfos(moocFilmTs);
        
        return filmInfos;
    }

    @Override
    public List<FilmInfo> getTop() {
        //已经上映的,打分的前十名
        EntityWrapper<MoocFilmT> entityWrapper = new EntityWrapper<>();
         entityWrapper.eq("film_status", "1");
        //封装一个Page分析对象进行分页查询,安装票房进行排序
         //1表示第一页,10表示当前页显示10条记录
         //film_box_office表示当前排序的字段
         Page<MoocFilmT> page = new Page<>(1, 10, "film_score");
         List<MoocFilmT> moocFilmTs = moocFilmMapper.selectPage(page,entityWrapper);
         List<FilmInfo> filmInfos = getFilmInfos(moocFilmTs);
        
        return filmInfos;
    }

}

 

 业务层需要注意几点:

1、业务层要使用@compent添加到spring的容器中

2、业务层代码要使用dubbo的@Service暴露服务,让guns-gateway网关可以来调用

3、具体的业务操作,例如即将上映的提前预售的前十名的电影信息

首先限定的条件是即将上映的,film_state的状态是2表示即将上映的,我们创建一个EntityWrapper对象,改类作为查询的限定条件
然后要分页查询,,我们使用mybatis-plus插件提供的分页查询Page类

current表示当前第几页,size表示当页显示的数目,oderByFileld表示按照那个字段进行排序,isAsc表示升序还是降序

 

guns-film的业务层代码实现完成之后,接下来要实现guns-gateway来调用业务层的代码,我们在guns-gateway中新建立一个controller来调用业务层的代码

package com.stylefeng.guns.rest.modular.film.VO;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.dubbo.config.annotation.Reference;
import com.stylefeng.guns.api.film.FillmServiceAPI;
import com.stylefeng.guns.rest.modular.auth.VO.ReponseVO;

@RestController
@RequestMapping("/film/")
public class FilmController {
    
    @Reference(interfaceClass=FillmServiceAPI.class)
    private FillmServiceAPI fillmServiceAPI;
    
    @RequestMapping(value="/getIndex",method=RequestMethod.GET)
    public ReponseVO<FilmIndexVO> getIndex(){
        FilmIndexVO filmIndexVO = new FilmIndexVO();
        //获得横幅的信息
        filmIndexVO.setBannners(fillmServiceAPI.getBanners());
        //获得票房的信息
        filmIndexVO.setBoxRanking(fillmServiceAPI.getboxRanking());
        filmIndexVO.setExpectRanking(fillmServiceAPI.getexpectRanking());
        filmIndexVO.setHotFilms(fillmServiceAPI.getHotFilms(true, 8));
        filmIndexVO.setSoonFils(fillmServiceAPI.getSoonFilms(true, 10));
        filmIndexVO.setTop100(fillmServiceAPI.getTop());
        
        return ReponseVO.success(filmIndexVO);
    }

}

 

首页的需要展示横幅的内容,票房前十的内容,我们在网关的地方将全部首页要展示的数据通过一个getIndex就可以获得首先要展示的全部内容,不需要各个模块分别发起请求

接下来我们来进行测试通过guns-gateway来访问guns-film,来获得首页展示的内容,在测试之前要配置下guns-film的dubbo配置文件

application.properties

#WEB\u670D\u52A1\u7AEF\u53E3
server.port=8084

## dubbo \u914D\u7F6E
spring.dubbo.application.name=meeting-film
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20882

 

我们修改下应用启动的端口为8084,和远程消费者通信的协议是dubbo协议,通信的端口为20882,不能和之前的通信端口20881重复,这样就可以启动dubbo了,启动类要使用@EnableDubboConfiguration注解

package com.stylefeng.guns.rest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;

@SpringBootApplication(scanBasePackages = {"com.stylefeng.guns"})
@EnableDubboConfiguration
public class GunsFilmApplication {

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

 

我们来测试下

 

 演示的时候,先启动guns-film模块,在启动guns-gateway,因为之前guns-gateway可以远程访问guns-user,所有启动guns-gateway之前,也要先把guns-user先启动起来,否则会报错

在测试的时候,获得首页不需要进行jwt的AuthFilter的拦截,我们需要在application.yml中进行配置

jwt:
  header: Authorization   #http请求头所需要的字段
  secret: mySecret        #jwt秘钥
  expiration: 604800      #7天 单位:秒
  auth-path: auth         #认证请求的路径
  md5-key: randomKey      #md5加密混淆key
  ignore-url: /user/register,/user/check,/film/getIndex  #对改类请求的url不进行jwt模块的校验

 

 

这样上面的业务接口我们就接口调通了,这样首页的接口我们已经完成了

接下来,我们来实现第二个接口2、 影片条件列表查询接口

 F:\第9章 分布式事务\第9章 分布式事务

事务是用来保证一组数据操作的完整性和一致性,注意是保证数据一组操作的正确性和完整性的

例如购买一个东西,需要三个操作扣减库存,下订单,和给商户打钱,这三个动作要不同时成功,要不同时失败

 

 事务的原子性:在物理上最小的物理可分割单元是原子性,原子是最小不可分割单元,事务所有的内部操作就是原子操作是一个完整的不可再分割的整体,不可能再进行分割了

事务的一致性:事务的操作要不是全部成功,要不就是全部失败

 事务的隔离性:不同事务的操作是相互隔离的,例如你给小明打款,和晓东给小明打款是分开的

事务的持久性:事务一万完成就是持久性的,不可逆恢复的

分布式事务:例如现在有10个机器,每个机器上部署了不同的应用,在10个机器上部署了独立的数据库,例如扣减库存应用安装在一台机器上,下订单服务在一台机器上,支付服务在一台机器上,如果其中的

例如下订单服务失败了,其他两个服务如何进行处理了,这就是分布式事务

分布式事务一般由事务的参与者、资源服务器、事务管理器三个部分构成。上面的扣减库存服务、订单服务、支付服务就是事务的参与者,资源管理器一般指数据库底层对事务的支持指的就是数据库,

事务管理器指当一个服务失败了,如何通知其他两个服务,一般需要事务管理器来通知

现在使用最多的是基于TCC编程式的补偿性事务

两阶段式事务

事务管理器会告诉事务的参与者,现在可以开始事务了,发出预备的指令通知事务参赛者

如果事务参与者已经准备就绪,就会想事务管理器提交预备的指令

事务管理者必须等待所有的就绪指令都收到之后,然后向事务参与者发出提交指令

事务参与者开始做提交的动作,然后向事务管理器发送已提交的质量,如果2个事务参与者,事务管理器只收到一个已提交指令,说明发生了异常

三段式事务在两段式基础上引入了一个预状态。但是两段式和三段式事务在生产上都很少使用,我们通过一个XA的案例来看下两段式事务

基于消息的一致性方案的使用场景

我们假定支付系统是A系统,下单之后会存在一个支付的操作,当你扫描支付操作完成之后,钱就会到支付宝

 

 

基于TCC的事务补偿性方案

 

 

 接下来我们来搭建TCC-transaction的环境下载对于的1.2版本

 

我们把整个框架导入到eclipse中

我们选择导入存在的maven工程

tcc的核心模块主要是上面的几个部分,第一个是api的包,第二个是tcc的核心模块,第三个是tcc对dubbo的支持,server是tcc提供的对事务的监控,spring的tcc对事务的支持

在运行tcc之前我们先初始化我们的数据库脚本

在tcc-transaction-tutorial-sample中给我们提供了集成tcc的一些案例,接下来我们要初始化我们的数据库

这里给我们提供了初始化数据库的脚本

create_db_cap.sql

CREATE DATABASE `TCC_CAP` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC_CAP;
CREATE TABLE `CAP_CAPITAL_ACCOUNT` (
  `CAPITAL_ACCOUNT_ID` int(11) NOT NULL AUTO_INCREMENT,
  `BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,
  `USER_ID` int(11) DEFAULT NULL,
  PRIMARY KEY (`CAPITAL_ACCOUNT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

CREATE TABLE `CAP_TRADE_ORDER` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `SELF_USER_ID` bigint(11) DEFAULT NULL,
  `OPPOSITE_USER_ID` bigint(11) DEFAULT NULL,
  `MERCHANT_ORDER_NO` varchar(45) NOT NULL,
  `AMOUNT` decimal(10,0) DEFAULT NULL,
  `STATUS` varchar(45) DEFAULT NULL,
  `VERSION` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `UX_MERCHANT_ORDER_NO` (`MERCHANT_ORDER_NO`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (1,10000,1000);
INSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (2,10000,2000);

create_db_ord.sql

CREATE DATABASE `TCC_ORD` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC_ORD;
CREATE TABLE `ORD_ORDER` (
  `ORDER_ID` int(11) NOT NULL AUTO_INCREMENT,
  `PAYER_USER_ID` int(11) DEFAULT NULL,
  `PAYEE_USER_ID` int(11) DEFAULT NULL,
  `RED_PACKET_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,
  `CAPITAL_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,
  `STATUS` varchar(45) DEFAULT NULL,
  `MERCHANT_ORDER_NO` varchar(45) NOT NULL,
  `VERSION` int(11) DEFAULT NULL,
  PRIMARY KEY (`ORDER_ID`),
  UNIQUE KEY `MERCHANT_ORDER_NO_UNIQUE` (`MERCHANT_ORDER_NO`)
) ENGINE=InnoDB AUTO_INCREMENT=1188 DEFAULT CHARSET=utf8;

CREATE TABLE `ORD_ORDER_LINE` (
  `ORDER_LINE_ID` int(11) NOT NULL AUTO_INCREMENT,
  `PRODUCT_ID` int(11) DEFAULT NULL,
  `QUANTITY` decimal(10,0) DEFAULT NULL,
  `UNIT_PRICE` decimal(10,0) DEFAULT NULL,
  PRIMARY KEY (`ORDER_LINE_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `ORD_SHOP` (
  `SHOP_ID` int(11) NOT NULL,
  `OWNER_USER_ID` int(11) DEFAULT NULL,
  PRIMARY KEY (`SHOP_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `ORD_PRODUCT`(
  `PRODUCT_ID` int(11) NOT NULL,
  `SHOP_ID` int(11) NOT NULL,
  `PRODUCT_NAME` VARCHAR(64) DEFAULT NULL ,
  `PRICE` DECIMAL(10,0) DEFAULT NULL,
  PRIMARY KEY (`PRODUCT_ID`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;


INSERT INTO `ORD_SHOP` (`SHOP_ID`,`OWNER_USER_ID`) VALUES (1,1000);

INSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (1,1,'IPhone6S',5288);
INSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (2,1,'MAC Pro',10288);
INSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (3,1,'IWatch',2288);

create_db_red.sql

CREATE DATABASE `TCC_RED` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC_RED;
CREATE TABLE `RED_RED_PACKET_ACCOUNT` (
  `RED_PACKET_ACCOUNT_ID` int(11) NOT NULL,
  `BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,
  `USER_ID` int(11) DEFAULT NULL,
  PRIMARY KEY (`RED_PACKET_ACCOUNT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `RED_TRADE_ORDER` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `SELF_USER_ID` bigint(11) DEFAULT NULL,
  `OPPOSITE_USER_ID` bigint(11) DEFAULT NULL,
  `MERCHANT_ORDER_NO` varchar(45) NOT NULL,
  `AMOUNT` decimal(10,0) DEFAULT NULL,
  `STATUS` varchar(45) DEFAULT NULL,
  `VERSION` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `MERCHANT_ORDER_NO_UNIQUE` (`MERCHANT_ORDER_NO`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (1,950,1000);
INSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (2,500,2000);

create_db_tcc.sql

CREATE DATABASE `TCC` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC;
CREATE TABLE `TCC_TRANSACTION_CAP` (
  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
  `DOMAIN` varchar(100) DEFAULT NULL,
  `GLOBAL_TX_ID` varbinary(32) NOT NULL,
  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,
  `CONTENT` varbinary(8000) DEFAULT NULL,
  `STATUS` int(11) DEFAULT NULL,
  `TRANSACTION_TYPE` int(11) DEFAULT NULL,
  `RETRIED_COUNT` int(11) DEFAULT NULL,
  `CREATE_TIME` datetime DEFAULT NULL,
  `LAST_UPDATE_TIME` datetime DEFAULT NULL,
  `VERSION` int(11) DEFAULT NULL,
  PRIMARY KEY (`TRANSACTION_ID`),
  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `TCC_TRANSACTION_CAP` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;

CREATE TABLE `TCC_TRANSACTION_ORD` (
  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
  `DOMAIN` varchar(100) DEFAULT NULL,
  `GLOBAL_TX_ID` varbinary(32) NOT NULL,
  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,
  `CONTENT` varbinary(8000) DEFAULT NULL,
  `STATUS` int(11) DEFAULT NULL,
  `TRANSACTION_TYPE` int(11) DEFAULT NULL,
  `RETRIED_COUNT` int(11) DEFAULT NULL,
  `CREATE_TIME` datetime DEFAULT NULL,
  `LAST_UPDATE_TIME` datetime DEFAULT NULL,
  `VERSION` int(11) DEFAULT NULL,
  PRIMARY KEY (`TRANSACTION_ID`),
  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `TCC_TRANSACTION_ORD` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;

CREATE TABLE `TCC_TRANSACTION_RED` (
  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
  `DOMAIN` varchar(100) DEFAULT NULL,
  `GLOBAL_TX_ID` varbinary(32) NOT NULL,
  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,
  `CONTENT` varbinary(8000) DEFAULT NULL,
  `STATUS` int(11) DEFAULT NULL,
  `TRANSACTION_TYPE` int(11) DEFAULT NULL,
  `RETRIED_COUNT` int(11) DEFAULT NULL,
  `CREATE_TIME` datetime DEFAULT NULL,
  `LAST_UPDATE_TIME` datetime DEFAULT NULL,
  `VERSION` int(11) DEFAULT NULL,
  PRIMARY KEY (`TRANSACTION_ID`),
  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `TCC_TRANSACTION_RED` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;

CREATE TABLE `TCC_TRANSACTION_UT` (
  `TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
  `DOMAIN` varchar(100) DEFAULT NULL,
  `GLOBAL_TX_ID` varbinary(32) NOT NULL,
  `BRANCH_QUALIFIER` varbinary(32) NOT NULL,
  `CONTENT` varbinary(8000) DEFAULT NULL,
  `STATUS` int(11) DEFAULT NULL,
  `TRANSACTION_TYPE` int(11) DEFAULT NULL,
  `RETRIED_COUNT` int(11) DEFAULT NULL,
  `CREATE_TIME` datetime DEFAULT NULL,
  `LAST_UPDATE_TIME` datetime DEFAULT NULL,
  `VERSION` int(11) DEFAULT NULL,
  PRIMARY KEY (`TRANSACTION_ID`),
  UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `TCC_TRANSACTION_UT` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;

 

这几个库的作用我们后面会详细进行讲解

接下来我们启动应用,来做我们的数据库的测试,

 

posted on 2019-07-22 17:56  luzhouxiaoshuai  阅读(1690)  评论(0编辑  收藏  举报

导航