1.前台会员系统架构

1.架构图

十.会员系统环境搭建 - 图2

2.需要创建的工程

父工程(聚合工程):crowdfunding07-member-parent
注册中心:crowdfunding08-member-eureka
实体类模块:crowdfunding09-member-entity
MySQL数据服务:crowdfunding10-member-mysql-provider
Redis数据服务:crowdfunding11-member-redis-provider
会员中心:crowdfunding12-member-authentication-consumer
项目维护:crowdfunding13-member-project-consumer
订单维护:crowdfunding14-member-order-consumer
支付功能:crowdfunding15-member-pay-consumer
网关:crowdfunding16-member-zuul
API模块:crowdfunding17-member-api

父工程下面的工程都是父工程的子工程

3.parent工程约定版本号

项目结构:
image.png
增加依赖:

  1. <!-- 在parent工程进行依赖管理 -->
  2. <dependencyManagement>
  3. <dependencies>
  4. <!-- 导入SpringCloud需要的依赖信息 -->
  5. <dependency>
  6. <groupId>org.springframework.cloud</groupId>
  7. <artifactId>spring-cloud-dependencies</artifactId>
  8. <version>Hoxton.SR8</version>
  9. <type>pom</type>
  10. <scope>import</scope>
  11. </dependency>
  12. <!-- SpringBoot依赖信息 -->
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-dependencies</artifactId>
  16. <version>2.3.3.RELEASE</version>
  17. <type>pom</type>
  18. <scope>import</scope>
  19. </dependency>
  20. <!--SpringBoot整合MyBatis的依赖-->
  21. <dependency>
  22. <groupId>org.mybatis.spring.boot</groupId>
  23. <artifactId>mybatis-spring-boot-starter</artifactId>
  24. <version>2.1.3</version>
  25. </dependency>
  26. <!--druid依赖信息-->
  27. <dependency>
  28. <groupId>com.alibaba</groupId>
  29. <artifactId>druid</artifactId>
  30. <version>1.1.17</version>
  31. </dependency>
  32. </dependencies>
  33. </dependencyManagement>


4.搭建环境约定

包名:
所有新建的包都作为com.zh.crowd的子包
主启动类类名:
CrowdMainApp
端口号:

  1. 注册中心:crowdfunding08-member-eureka 1000
  2. MySQL数据服务:crowdfunding10-member-mysql-provider 2000
  3. Redis数据服务:crowdfunding11-member-redis-provider 3000
  4. 会员中心:crowdfunding12-member-authentication-consumer 4000
  5. 项目维护:crowdfunding13-member-project-consumer 5000
  6. 订单维护:crowdfunding14-member-order-consumer 7000
  7. 支付功能:crowdfunding15-member-pay-consumer 8000
  8. 网关:crowdfunding16-member-zuul 80

5.各个组件工程


1.Eureka工程

项目结构:
image.png
依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>

主启动类:

@EnableEurekaServer
@SpringBootApplication
public class CrowdMainApp {

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

application.yml:

server:
  port: 1000
spring:
  application:
    name: crowd-eureka
eureka:
  instance:
    hostname: localhost
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

启动Eureka
通过如下地址,有如下页面表示成功。

image.png
2.Entity(实体类工程)

1.实体类的进一步细分

  • VO
  • View Object 视图对象
    • 用途 1:接收浏览器发送过来的数据
    • 用途 2:把数据发送给浏览器去显示
  • PO
  • Persistent Object 持久化对象
    • 用途 1:将数据封装到 PO 对象存入数据库
    • 用途 2:将数据库数据查询出来存入 PO 对象
    • 所以 PO 对象是和数据库表对应,一个数据库表对应一个 PO 对象
  • DO
  • Data Object 数据对象
    • 用途 1:从 Redis查询得到数据封装为DO 对象
    • 用途 2:从ElasticSearch查询得到数据封装为 DO 对象
    • 用途 3:从 Solr 查询得到数据封装为 DO 对象

……
从中间件或者第三方接口查询到的数据封装为DO对象

2.创建包

com.zh.crowd.entity.vo
com.zh.crowd.entity.po
image.png

3.使用lombok插件

为了使用lombok插件,引入lombok的依赖

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.12</version>
    </dependency>
</dependencies>

3.MySQL工程

项目结构:
image.png
1.添加依赖:

<dependencies>

    <!-- druid连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
    </dependency>

    <!-- MyBatis依赖 -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>

    <!-- mysql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!-- eureka客户端依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    <!-- web环境(为了能对外暴露接口) -->
    <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>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- 实体类依赖 -->
    <dependency>
        <groupId>com.zh.crowd</groupId>
        <artifactId>crowdfunding09-member-entity</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <!-- 工具类依赖 -->
    <dependency>
        <groupId>com.zh.crowd</groupId>
        <artifactId>crowdfunding05-common-util</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

</dependencies>

2.配置application.yml

server:
  port: 2000
spring:
  application:
    name: crowd-mysql
  datasource:
    name: mydb
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/project_rowd?serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1000/eureka/
mybatis:
  mapper-locations: classpath*:/mybatis/mapper/*Mapper.xml
logging:
  level:
    org.fall.mapper: debug
    org.fall.test: debug

3.建数据库表

CREATE TABLE t_member (
    id INT ( 11 ) NOT NULL auto_increment,
    login_acct VARCHAR ( 255 ) NOT NULL,
    user_pswd CHAR ( 200 ) NOT NULL,
    user_name VARCHAR ( 255 ),
    email VARCHAR ( 255 ),
    authstaus INT ( 4 ) COMMENT '实名认证状态 0- 未实名认证, 1- 实名认证申请中, 2- 已实名认证',
    user_type INT ( 4 ) COMMENT '0- 个人 , 1- 企业',
    real_name VARCHAR ( 255 ),
    card_num VARCHAR ( 255 ),
    acct_type INT ( 4 ) COMMENT '0- 企业, 1- 个体, 2- 个人, 3- 政府',
    PRIMARY KEY ( id ) 
)

4.逆向工程
项目结构:
image.png
修改代码:

<!-- 数据库表名与需要的实体类对应映射的指定 -->
<table tableName="t_member" domainObjectName="MemberPO"/>

5.把生成的文件放入对应的路径
image.png
6.给主启动类加上@MapperScan注解用于扫描Mapper接口

@MapperScan("com.zh.crowd.mapper")
@SpringBootApplication
public class CrowdMainApp {

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

4.MySQL对外暴露服务工程

image.png
进入到crowdfunding17-member-api工程
项目结构:
image.png
1.添加依赖

 <dependency>
            <groupId>com.zh.crowd</groupId>
            <artifactId>zhcrowdfunding01-admin-util</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.zh.crowd</groupId>
            <artifactId>crowdfunding09-member-entity</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

2.在com.zh.crowd.api包中建一个接口MySQLRemoteService
项目结构:
image.png
代码:

@FeignClient("crowd-mysql")
public interface MySQLRemoteService {

    @RequestMapping("/get/member/by/login/acct/remote")
    ResultEntity<MemberPO> getMemberPOByLoginAcctRemote(@RequestParam("loginacct") String loginacct);
}

3.与MySQL工程中的Handler中的方法对应:
项目结构:
image.png
代码1:MemberProviderHandler

@RestController
public class MemberProviderHandler {

    @Autowired
    MemberService memberService;

    @RequestMapping("/get/member/by/login/acct/remote")
    public ResultEntity<MemberPO> getMemberPOByLoginAcctRemote(@RequestParam("loginacct") String loginacct){
        try {
            // 调用本地Service完成查询
            MemberPO memberPO = memberService.getMemberPOByLoginAcct(loginacct);
            // 如果没有抛异常,就返回成功的结果
            return ResultEntity.successWithData(memberPO);
        } catch (Exception e){
            e.printStackTrace();
            // 捕获到异常,返回失败的结果
            return ResultEntity.failed(e.getMessage());
        }
    }

}

代码2:MemberService

public interface MemberService {
    MemberPO getMemberPOByLoginAcct(String loginacct);
}

代码3:MemberServiceImpl

@Transactional(readOnly = true)
@Service
public class MemberServiceImpl implements MemberService {

    @Autowired
    private MemberPOMapper memberPOMapper;

    @Override
    public MemberPO getMemberPOByLoginAcct(String loginacct) {
        // 1.创建Example对象
        MemberPOExample example = new MemberPOExample();
        // 2.创建Criteria对象
        MemberPOExample.Criteria criteria = example.createCriteria();
        // 3.封装查询条件
        criteria.andLoginAcctEqualTo(loginacct);
        // 4.执行查询
        List<MemberPO> memberPOS = memberPOMapper.selectByExample(example);
        // 5.获取结果
        return memberPOS.get(0);
    }
}

5.Redis工程

1.目标

抽取项目中所有访问Redis的操作。

2.依赖

<dependencies>

    <!-- redis依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- eureka客户端依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    <!-- web环境(为了能对外暴露接口) -->
    <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>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- 实体类依赖 -->
    <dependency>
        <groupId>org.fall</groupId>
        <artifactId>crowdfunding09-member-entity</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <!-- 工具类依赖 -->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>crowdfunding05-common-util</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

3.主启动类

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

4.application.yml

server:
  port: 3000

eureka:
  client:
    service-url:
      defaultZone: http://localhost:1000/eureka/
spring:
  application:
    name: crowd-redis
  redis:
    host: 127.0.0.1

6.Redis对外暴露服务

1.api工程创建接口

项目结构:
image.png
代码:

@FeignClient("crowd-redis")
public interface RedisRemoteService {

    @RequestMapping("/set/redis/key/value/remote")
    ResultEntity<String> setRedisKeyValueRemote(
            @RequestParam("key") String key,
            @RequestParam("value") String value
    );

    @RequestMapping("/set/redis/key/value/with/timeout/remote")
    ResultEntity<String> setRedisKeyValueWithTimeoutRemote(
            @RequestParam("key") String key,
            @RequestParam("value") String value,
            @RequestParam("time") long time,
            @RequestParam("timeUnit") TimeUnit timeUnit
    );

    @RequestMapping("/get/redis/value/by/key/remote")
    ResultEntity<String> getRedisValueByKeyRemote(
            @RequestParam("key") String key
    );

    @RequestMapping("/remove/redis/key/by/key/remote")
    ResultEntity<String> RemoveRedisKeyByKeyRemote(
            @RequestParam("key") String key
    );

}

2.编写自己handler方法

项目结构:
image.png
代码:

@RestController
public class RedisProviderHandler {

    // 自动注入StringRedisTemplate
    @Autowired
    private StringRedisTemplate redisTemplate;

    @RequestMapping("/set/redis/key/value/remote")
    ResultEntity<String> setRedisKeyValueRemote(
            @RequestParam("key") String key,
            @RequestParam("value") String value
    ){

        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            operations.set(key,value);
            return ResultEntity.successWithoutData();
        }catch (Exception e){
            e.printStackTrace();
            return ResultEntity.failed(e.getMessage());
        }

    }

    @RequestMapping("/set/redis/key/value/with/timeout/remote")
    ResultEntity<String> setRedisKeyValueWithTimeoutRemote(
            @RequestParam("key") String key,
            @RequestParam("value") String value,
            @RequestParam("time") long time,
            @RequestParam("timeUnit") TimeUnit timeUnit
    ){
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            operations.set(key,value,time,timeUnit);
            return ResultEntity.successWithoutData();
        }catch (Exception e){
            e.printStackTrace();
            return ResultEntity.failed(e.getMessage());
        }

    }


    @RequestMapping("/get/redis/value/by/key/remote")
    ResultEntity<String> getRedisValueByKeyRemote(
            @RequestParam("key") String key
    ){

        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            String value = operations.get(key);
            return ResultEntity.successWithData(value);
        }catch (Exception e){
            e.printStackTrace();
            return ResultEntity.failed(e.getMessage());
        }


    }


    @RequestMapping("/remove/redis/key/by/key/remote")
    ResultEntity<String> RemoveRedisKeyByKeyRemote(
            @RequestParam("key") String key
    ){
        try {
            redisTemplate.delete(key);
            return ResultEntity.successWithoutData();
        }catch (Exception e){
            e.printStackTrace();
            return ResultEntity.failed(e.getMessage());
        }

    }


}

7.认证页面工程

项目结构:
image.png

1.依赖

<dependencies>
        <dependency>
            <groupId>com.zh.crowd</groupId>
            <artifactId>crowdfunding17-member-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

2.主启动类

// 开启feign客户端功能
@EnableFeignClients
@SpringBootApplication
public class CrowdMainAuthApp {
    public static void main(String[] args) {
        SpringApplication.run(CrowdMainAuthApp.class, args);
    }
}

3.application.yml

server:
  port: 4000
spring:
  application:
    name: crowd-auth
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1000/eureka/

4.编写handler方法

项目结构:
image.png
代码:

@Controller
public class PortalHandler {

    // 首页,直接访问,而不用加额外的路径
    @RequestMapping("/")
    public String showPortalPage(){
        return "portal";
    }

}

5.前端页面

项目结构:
image.png
代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
HELLO
</body>
</html>

8.Zuul网关

1.依赖

项目结构:
image.png
添加依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
</dependencies>

2.主启动类

项目结构:
image.png
代码:

// 开启Zuul
@EnableZuulProxy
@SpringBootApplication
public class CrowdMainZuulApp {

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

}

3.application.yml

项目结构:
image.png
配置内容:

server:
  port: 80            # 80端口可以直接通过域名/ip访问,不用额外加端口号
spring:
  application:
    name: crowd-zuul
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1000/eureka/
zuul:
  ignored-services: "*"       # 表示忽视直接通过application-name访问微服务,必须通过route
  sensitive-headers: "*"      # 在Zuul向其他微服务重定向时,保持原本的头信息(请求头、响应头)
  routes:                     # 指定网关路由
    crowd-protal:
      service-id: crowd-auth  # 对应application-name
      path: /**               # 表示直接通过根路径访问,必须加上**,否则多层路径无法访问