TH9-搭建后台系统

1、项目架构

1.1 概述

API网关有很多实现方式,我们通过SpringCloud Gateway实现

使用Nacos作为配置中心
在这里插入图片描述

1.2 API网关

1.2.1 搭建网关

依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    <!-- 监控检查-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!-- nacos配置中心依赖支持
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    -->
    <dependency>
        <groupId>com.itheima</groupId>
        <artifactId>tanhua-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
引导类
@SpringBootApplication
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}
跨域问题配置类
package com.itheima.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * 跨域支持
 */
@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source =
                new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}
配置文件
server:
  port: 8888
spring:
  profiles:
    active: prod
  application:
    name: tanhua-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.136.160:8848
    gateway:
      globalcors:
        add-to-simple-url-handler-mapping: true
        corsConfigurations:
          '[/**]':
            allowedHeaders: "*"
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION


server:
  port: 8888
spring:
  profiles:
    active: prod
  application:
    name: tanhua-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.136.160:8848
    gateway:
      globalcors:
        add-to-simple-url-handler-mapping: true
        corsConfigurations:
          '[/**]':
            allowedHeaders: "*"
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION
      routes:
        # 探花系统
        - id: tanhua-app-server
          uri: lb://tanhua-app-server
          predicates:
            - Path=/app/**
          filters:
            - StripPrefix= 1
        # 后台系统
        - id: tanhua-admin
          uri: lb://tanhua-admin
          predicates:
            - Path=/admin/**
          filters:
            - StripPrefix= 1
gateway:
  excludedUrls: /user/login,/user/loginVerification,/system/users/verification,/system/users/login

1.2.2 配置鉴权管理器

@Component
public class AuthFilter implements GlobalFilter, Ordered {

    //排除的链接
    @Value("#{'${gateway.excludedUrls}'.split(',')}")
    private List<String> excludedUrls;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String url = exchange.getRequest().getURI().getPath();

        System.out.println( "url:"+ url);
        //排除特殊接口 不校验
        if(excludedUrls.contains(url)){
            return chain.filter(exchange);
        }
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        if(!StringUtils.isEmpty(token)){
            token = token.replace("Bearer ", "");
        }
        ServerHttpResponse response = exchange.getResponse();

        //2、使用工具类,判断token是否有效
        boolean verifyToken = JwtUtils.verifyToken(token);
        //3、如果token失效,返回状态码401,拦截
        if(!verifyToken) {
            Map<String, Object> responseData = new HashMap<>();
            responseData.put("errCode", 401);
            responseData.put("errMessage", "用户未登录");
            return responseError(response,responseData);
        }
        return chain.filter(exchange);
    }

    //响应错误数据
    private Mono<Void> responseError(ServerHttpResponse response,Map<String, Object> responseData){
        // 将信息转换为 JSON
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] data = new byte[0];
        try {
            data = objectMapper.writeValueAsBytes(responseData);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        // 输出错误信息到页面
        DataBuffer buffer = response.bufferFactory().wrap(data);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * 设置过滤器的执行顺序
     */
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

1.3 Nacos配置中心

Nacos提供了注册中心和配置管理的能力,使用Nacos可以快速实现服务发现、服务配置管理等需求

在这里插入图片描述

1.3.1 添加依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
1.3.2 添加bootstrap.yml配置
server:
  port: 8888
spring:
  profiles:
    active: prod
  application:
    name: tanhua-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.136.160:8848
      config:
        server-addr: 192.168.136.160:8848
        file-extension: yml
1.3.3 nacos添加配置
server:
  port: 9997
spring:
  cloud:
    gateway:
      globalcors:
        add-to-simple-url-handler-mapping: true
        corsConfigurations:
          '[/**]':
            allowedHeaders: "*"
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION
      routes:
        # 用户微服务
        - id: tanhua-app-server
          uri: lb://tanhua-app-server
          predicates:
            - Path=/app/**
          filters:
            - StripPrefix= 1
        # 文章微服务
        - id: tanhua-admin
          uri: lb://tanhua-admin
          predicates:
            - Path=/admin/**
          filters:
            - StripPrefix= 1
gateway:
  excludedUrls: /user/login,/user/loginVerification,/system/users/verification,/system/users/login

2、后台系统

2.1 概述

探花交友APP建立的后台管理系统,目的是完成探花交友项目的业务闭环,主要功能包括:用户管理、动态管理、审核管理以及系统管理。

在这里插入图片描述

课程中实现的功能有:登录、首页、用户管理、动态审核。

2.2 环境前端搭建

2.2.1 导入数据库

将资料中的tanhua-admin.sql引入到mysql数据库中

在这里插入图片描述

2.2.2 导入静态页面

后台系统也是采用前后端分离的方式,前端采用Vue.js实现,关于前端系统我们不进行实现,拿来直接使用。

在这里插入图片描述

nginx安装

将资料中提供的nginx解压到合适的位置

在这里插入图片描述

其中html目录中为,vue编译后的所有页面。

修改Nginx的/conf/nginx.conf配置文件:

    server {
        listen       8088;  #请求端口
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;
		

		location / {
            root   html;
            index  index.html index.htm;
        }
		
		location  /management {
			proxy_pass http://127.0.0.1:18083/;  #转发后台地址
		}
		#....略
	}
  • 访问vue页面的路径:localhost:8088
  • 其中内部调用java服务器的路径 : http://127.0.0.1:18083/
测试

双击nginx.exe,待启动完成后访问:http://127.0.0.1:8088即可访问后台项目

2.3 搭建后端环境

2.3.1 Admin实体类
//后台系统的管理员对象
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Admin implements Serializable {
    /**
     * id
     */
    private Long id;
    /**
     * 用户名
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 头像
     */
    private String avatar;
}
2.3.2 导入项目

将今日资料中的tanhua-admin模块导入到探花项目中,完成开发。

在这里插入图片描述

2.3.3 nacos中加入配置

DataID:tanhua-admin-prod.yml

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/tanhua-admin?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
    username: root
    password: root
  rabbitmq:
    host: 192.168.136.160
    port: 5672
    username: guest
    password: guest    
  redis:
    host: 192.168.136.160
    port: 6379
  cloud:  #nacos配置
    nacos:
      discovery:
        server-addr: 192.168.136.160:8848
dubbo:    #dubbo配置
  registry:
    address: spring-cloud://localhost
  consumer:
    check: false
    retries: 0
  protocols:
    dubbo:
      port: -1

#配置短信平台信息
tanhua:  #手机验证码,咱们自己注册(不要白嫖)
  sms:
    signName: 物流云商
    templateCode: SMS_106590012
    accessKey: LTAI4GKgob9vZ53k2SZdyAC7
    secret: LHLBvXmILRoyw0niRSBuXBZewQ30la
  oss:
    accessKey: LTAI4GKgob9vZ53k2SZdyAC7
    secret: LHLBvXmILRoyw0niRSBuXBZewQ30la
    endpoint: oss-cn-beijing.aliyuncs.com
    bucketName: tanhua143
    url: https://tanhua143.oss-cn-beijing.aliyuncs.com
  aip:
    appId: 22837663
    apiKey: nA43galrxfUZTGtYRVK8F8tb
    secretKey: MQp567q4nGnIKfniURa2XAw8bT1SlPE3
  huanxin:
    appkey: 1110201018107234#tanhua
    clientId: YXA6nxJJ_pdEQ_eYUlqcRicS4w
    clientSecret: YXA6GMUxVEZhAvxlMn4OvHSXbWuEUTE
#mybaits-plus
mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_    #数据库表前缀
      id-type: auto        #数据库表主键的策略

2.4 管理员登录

后台系统的登录模块独立于APP端的登录。

2.4.1 生成验证码

验证码:页面端发送请求到服务端。服务端生成一个验证码的图片,已流的形式返回

SystemController
/**
 * 生成图片验证码
 */
@GetMapping("/verification")
public void verification(String uuid, HttpServletResponse response) throws IOException {
    //设置响应参数
    response.setDateHeader("Expires", 0);
    // Set standard HTTP/1.1 no-cache headers.
    response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
    // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
    response.addHeader("Cache-Control", "post-check=0, pre-check=0");
    // Set standard HTTP/1.0 no-cache header.
    response.setHeader("Pragma", "no-cache");
    response.setContentType("image/jpeg");
    //1、通过工具类生成验证码对象(图片数据和验证码信息)
    LineCaptcha captcha = CaptchaUtil.createLineCaptcha(299, 97);
    String code = captcha.getCode();  //1234
    //2、调用service,将验证码存入到redis
    redisTemplate.opsForValue().set(Constants.CAP_CODE+uuid,code);
    //3、通过输出流输出验证码
    captcha.write(response.getOutputStream());
}
2.4.2 用户登录
SystemController
/**
 * 用户登录:
 */
@PostMapping("/login")
public ResponseEntity login(@RequestBody Map map) {
    Map retMap = adminService.login(map);
    return ResponseEntity.ok(retMap);
}
SystemService
//用户登录
public ResponseEntity login(Map map) {
    //1、获取请求的参数(username,password,verificationCode(验证码),uuid)
    String username = (String) map.get("username");
    String password = (String) map.get("password");
    String verificationCode = (String) map.get("verificationCode");
    String uuid = (String) map.get("uuid");
    //2、判断用户名或者密码是否为空
    if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
        //用户名或者密码为空
        //throw new BusinessException("用户名或者密码为空");
        Map map1 = new HashMap();
        map.put("message","用户名或者密码为空");
        return ResponseEntity.status(500).body(map1);
    }
    //3、判断验证码是否正确
    if (StringUtils.isEmpty(username)) {
        //验证码为空
        throw new BusinessException("验证码为空");
    }
    //从redis中获取验证码
    String key = Constants.CAP_CODE+uuid;
    String code = redisTemplate.opsForValue().get(key);
    if (StringUtils.isEmpty(code) || !code.equals(verificationCode)) {
        //验证码错误
        throw new BusinessException("验证码错误");
    }
    redisTemplate.delete(key);
    //4、根据用户名查询用户
    QueryWrapper<Admin> qw = new QueryWrapper<>();
    qw.eq("username", username);
    Admin admin = adminMapper.selectOne(qw);
    //5、判断用户是否存在,密码是否一致
    password = SecureUtil.md5(password); //md5加密
    if(admin == null || !admin.getPassword().equals(password)) {
        //用户名错误或者密码不一致
        throw new BusinessException("用户名或者密码");
    }
    //6、通过JWT生成token
    Map<String, Object> claims = new HashMap<String, Object>();
    claims.put("username", username);
    claims.put("id", admin.getId());
    String token = JwtUtils.getToken(claims);
    //8、构造返回值
    Map res = new HashMap();
    res.put("token", token);
    return ResponseEntity.ok(res);
}
2.4.3 获取用户资料
AdminVO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdminVo {
    /**
     * id
     */
    private String id;
    /**
     * 用户名
     */
    private String username;

    /**
     * 头像
     */
    private String avatar;

    public static AdminVo init(Admin admin) {
        AdminVo vo = new AdminVo();
        vo.setAvatar(admin.getAvatar());
        vo.setUsername(admin.getUsername());
        vo.setId(admin.getId().toString());
        return vo;
    }

}
SystemController
/**
 * 获取用户的信息
 */
@PostMapping("/profile")
public ResponseEntity profile() {
    AdminVo vo = adminService.profile();
    return ResponseEntity.ok(vo);
}
SystemService
//获取当前用户的用户资料
public AdminVo profile() {
    Long id = AdminHolder.getId();
    Admin admin = adminMapper.selectById(id);
    return AdminVo.init(admin);
}

3、用户管理

3.1 需求分析

用户管理:对探花交友客户端注册用户的管理(查询业务数据),使用Dubbo的形式调用tanhua-dubbo-service获取响应的数据结果

3.2 查询用户列表

ManageController
@GetMapping("/users")
public ResponseEntity users(@RequestParam(defaultValue = "1") Integer page,
                            @RequestParam(defaultValue = "10") Integer pagesize) {
   PageResult result = managerService.findAllUsers(page,pagesize);
   return ResponseEntity.ok(result);
}
ManagerService
public ResponseEntity findAllUsers(Integer page, Integer pagesize) {
    //1、调用API分页查询数据列表   Ipage<UserInfo>
    IPage<UserInfo> iPage = userInfoApi.findAll(page,pagesize);
    //2、需要将Ipage转化为PageResult
    PageResult result = new PageResult(page, pagesize, iPage.getTotal(), iPage.getRecords());
    //3、构造返回值
    return ResponseEntity.ok(result);
}
UserInfoAPI
@Override
public IPage<UserInfo> findAll(Integer page, Integer pagesize) {
    return userInfoMapper.selectPage(new Page<UserInfo>(page,pagesize),null);
}

3.3 查看用户详情

ManageController
/**
 * 根据id查询用户详情
 */
@GetMapping("/users/{userId}")
public ResponseEntity findById(@PathVariable("userId") Long userId) {
    return managerService.findById(userId);
}
ManagerService
//根据id查询用户详情
public ResponseEntity findById(Long userId) {
    UserInfo info = userInfoApi.findById(userId);
    return ResponseEntity.ok(info);
}

3.4 查看视频列表

ManageController
/**
 * 查询指定用户发布的所有视频列表
 */
@GetMapping("/videos")
public ResponseEntity videos(@RequestParam(defaultValue = "1") Integer page,
                            @RequestParam(defaultValue = "10") Integer pagesize,
                            Long uid ) {
    PageResult result = managerService.findAllVideos(page,pagesize,uid);
    return ResponseEntity.ok(result)
}
ManagerService
//根据用户id分页查询此用户发布的所有视频列表
public PageResult findAllVideos(Integer page, Integer pagesize, Long userId) {
    //1、调用API查询视频列表(PageResult<video>)
    List<Video> items = videoApi.findByUserId(page,pagesize,userId);
    //2、获取到分页对象中的List List<Video>
    UserInfo info = userInfoApi.findById(userId);
    //3、一个Video转化成一个VideoVo
    List<VideoVo> list = new ArrayList<>();
    for (Video item : items) {
        VideoVo vo = VideoVo.init(info, item);
        list.add(vo);
    }
    //4、构造返回
    return ResponseEntity.ok(new PageResult(page,pagesize,0l,list));
}

3.5 查看动态列表

ManageController
@GetMapping("/messages")
public ResponseEntity messages(@RequestParam(defaultValue = "1") Integer page,
                             @RequestParam(defaultValue = "10") Integer pagesize,
                             Long uid,Integer state ) {
     PageResult result = managerService.findAllMovements(page,pagesize,uid,state);
     return ResponseEntity.ok(result)
}
ManagerService
//查询指定用户发布的所有动态
public ResponseEntity findAllMovements(Integer page, Integer pagesize, Long userId, Integer state) {
    //1、调用API查询 :(PageResult<Publish>)
    PageResult result = movementApi.findByUserId(userId,page,pagesize);
    //2、一个Publsh构造一个Movements
    List<Movement> items = ( List<Movement>)result.getItems();
    List<MovementsVo> list = new ArrayList<>();
    for (Movement item : items) {
        UserInfo userInfo = userInfoApi.findById(item.getUserId());
        MovementsVo vo = MovementsVo.init(userInfo, item);
        list.add(vo);
    }
    //3、构造返回值
    result.setItems(list);
    return ResponseEntity.ok(result);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值