人力资源后端项目_04-FastDFS分布式文件存储系统

一、租户入驻

1、前端

人力资源后端项目_04-FastDFS分布式文件存储系统_第1张图片

(1)租户入驻页面的跳转

  • 更改路由配置文件routes.js
  • 更改main.js修改前端的认证,放行/register

(2)表单

(3)套餐和租户类型的选择框

人力资源后端项目_04-FastDFS分布式文件存储系统_第2张图片

(4)表单验证

自定义验证规则

data() {
    var validateTel = (rule, value, callback) => {
        let reg = /^1[3,4,5,6,7,8,9][0-9]{9}$/;
        if (!reg.test(value)) {
            callback(new Error('请输入正确的手机号码'));
        } else {
            callback();
        }
    };
    var validateConfirmPassword = (rule, value, callback) => {
        if (value !== this.tenant.password) {
            callback(new Error('两次输入密码不一致!'));
        } else {
            callback();
        }
    };
    return {
        tenantRules:{
            companyName:[
                { required: true, message: '公司名称不能为空', trigger: 'blur' }
            ],
            companyNum:[
                { required: true, message: '公司电话不能为空', trigger: 'blur' },
                { validator: validateTel, trigger: 'blur' }
            ],
            username:[
                { required: true, message: '账号不能为空', trigger: 'blur' }
            ],
            password:[
                { required: true, message: '密码不能为空', trigger: 'blur' }
            ],
            confirmPassword:[
                { required: true, message: '请再次输入密码', trigger: 'blur' },
                { validator: validateConfirmPassword, trigger: 'blur' }

            ]
        }
    };
}

(5)发送请求

2、后端

(1)使用Vo封装请求参数

@Data
public class TenantVo {

    private String companyName;
    private String companyNum;
    private Long tenantType;
    private String address;
    private String logo;
    private String username;
    private String password;
    /**
     * 套餐
     */
    private Long meal;

}

(2)租户入驻的业务处理

 /**
     * 公司入驻
     * 思考:公司入驻的业务流程是什么样子的?要操作哪些表?先后顺序?
     *
     * t_tenant 租户表
     *
     * t_employee 存储租户的管理员账号信息
     *
     * t_tenant_meal 租户套餐表
     *
     * mybatis操作表不能级联操作,只能一张表一张表的操作
     *
     * @param tenantVo
     */
@Override
@Transient //同一个事物,要么全部成功,要么全部失败
public void register(TenantVo tenantVo) {

    //向租户表中插入数据  返回租户id
    Tenant tenant = new Tenant();
    BeanUtils.copyProperties(tenantVo,tenant);
    //初始化租户数据
    tenant.setRegisterTime(System.currentTimeMillis());
    tenant.setState(0);
    baseMapper.insert(tenant);//mybatisplus默认会返回新增数据的主键

    //向员工表中插入数据 - 租户管理员的用户和密码 - 返回员工id
    Employee employee = new Employee();
    BeanUtils.copyProperties(tenantVo,employee);
    //设置这个员工的租户id
    employee.setTenantId(tenant.getId());
    employeeMapper.insert(employee);


    //设置租户表中管理员的ID
    //为了防止修改的时候数据丢失,我们先查询出来,在更改
    tenant = baseMapper.selectById(tenant.getId());
    tenant.setAdminId(employee.getId());
    baseMapper.updateById(tenant);



    //最后修改租户-套餐中间表
    TenantMeal tenantMeal = new TenantMeal();
    tenantMeal.setTenantId(tenant.getId());
    tenantMeal.setMealId(tenantVo.getMeal());
    //计算一年之后
    long time = DateUtils.addYears(new Date(), 1).getTime();
    tenantMeal.setExpireDate(time);//失效日期
    tenantMealMapper.insert(tenantMeal);


}

(3)测试 - mybatisplus忽略字段

package io.niker.hrm.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

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

/**
 * 

* *

* * @author lidong * @since 2020-03-30 */
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("t_tenant") public class Tenant implements Serializable { private static final long serialVersionUID=1L; @TableId(value = "id", type = IdType.AUTO) private Long id; private Long tenantType; @TableField("companyName") private String companyName; @TableField("companyNum") private String companyNum; @TableField("registerTime") private Date registerTime; private Integer state; private String address; private String logo; private Long adminId; /** * 忽略的字段,生成增删改sql语句的时候,不生成这两个数据的sql * mybatis的多对一 */ @TableField(exist = false) private Employee admin; // 租户管理员 @TableField(exist = false) private TenantType type; // 机构类型 }

三、分布式文件系统-FastDFS

1、为什么要使用分布式文件系统

(1)不使用分布式文件系统

人力资源后端项目_04-FastDFS分布式文件存储系统_第3张图片

(2)使用分布式文件系统

人力资源后端项目_04-FastDFS分布式文件存储系统_第4张图片

(3)小结

集群环境中必然要使用到分布式文件系统!!!

2、方案选择

(1)分布式文件系统的原理

人力资源后端项目_04-FastDFS分布式文件存储系统_第5张图片

(2)方案选择

  • 别人搭建好的(花钱)

    阿里云、七牛云

  • 自己搭建

    hdfs、fastdfs【选择】

3、fastdfs的架构原理

人力资源后端项目_04-FastDFS分布式文件存储系统_第6张图片

Tracker:调度和负载均衡

Storager:存储数据的

(1)用户请求Tracker,Tracker调度Storage,将Storage的ip和端口返回给client

(2)clinet发送数据到这个Storage中

文件上传

人力资源后端项目_04-FastDFS分布式文件存储系统_第7张图片

文件下载

人力资源后端项目_04-FastDFS分布式文件存储系统_第8张图片

四、使用FastDFS

1、需求分析

人力资源后端项目_04-FastDFS分布式文件存储系统_第9张图片人力资源后端项目_04-FastDFS分布式文件存储系统_第10张图片

2、后端服务搭建

人力资源后端项目_04-FastDFS分布式文件存储系统_第11张图片

(1)hrm-dfs-service

pom.xml

<dependencies>

    <dependency>
        <groupId>io.nikergroupId>
        <artifactId>hrm-dfs-commonartifactId>
        <version>${project.version}version>
    dependency>

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>

    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>

    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-configartifactId>
    dependency>

    
    <dependency>
        <groupId>cn.bestwugroupId>
        <artifactId>fastdfs-client-javaartifactId>
        <version>1.27version>
    dependency>

    
    <dependency>
        <groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger2artifactId>
        <version>2.9.2version>
    dependency>
    
    <dependency>
        <groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger-uiartifactId>
        <version>2.9.2version>
    dependency>

dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
            <version>${spring-boot.version}version>
            <configuration>
                <mainClass>io.niker.hrm.DFSApplicationmainClass>
            configuration>
        plugin>
    plugins>
build>

bootstrap.yml

#配置中心
spring:
  cloud:
    config:
      discovery:
        enabled: true
        service-id: CONFIG-SERVER
      name: application-dfs
      profile: dev

#eureka客户端配置
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

配置中心 application-dfs-dev.yml

server:
  port: 8001

#配置中心
spring:
  application:
    name: DFS-SERVICE

#eureka客户端配置
eureka:
  instance:
    instance-id: dfs-service:8001
    prefer-ip-address: false

swagger的集成

package io.niker.hrm.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * swagger的配置
 *  swagger的目的:(1)通过html的方式展示项目中所有的接口(2)提供接口测试
 */
@Configuration
@EnableSwagger2
public class Swagger2 {
 
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                //对外暴露服务的包,以controller的方式暴露,所以就是controller的包.
                .apis(RequestHandlerSelectors.basePackage("io.niker.hrm.controller"))
                .paths(PathSelectors.any())
                .build();
    }


    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("文件服务api")
                .description("文件服务接口文档说明")
                .contact(new Contact("liodng", "", "[email protected]"))
                .version("1.0")
                .build();
    }

}

启动类

(2)网关

网关的配置文件添加路由

server:
  port: 1299

spring:
  application:
    name: ZUUL-SERVICE


#测试一波

eureka:
  instance:
    instance-id: zuul-service:1299 #服务实例的标识
    prefer-ip-address: true #以ip注册

zuul:
  ignored-services: "*"
  prefix: /services
#路由配置
  routes:
    system:
      path: /system/**
      serviceId: HRM-SYSTEM
    file:
      path: /file/**
      serviceId: DFS-SERVICE

网关的swagger配置

DocumentationConfig.java

package io.niker.hrm.util;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

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

@Component
@Primary
public class DocumentationConfig implements SwaggerResourcesProvider {
    @Override
    public List<SwaggerResource> get() {
        List resources = new ArrayList<>();
        resources.add(swaggerResource("资源管理", "/services/system/v2/api-docs", "2.0"));
        resources.add(swaggerResource("文件管理", "/services/file/v2/api-docs", "2.0"));
        resources.add(swaggerResource("课程管理", "/services/course/v2/api-docs", "2.0"));
        resources.add(swaggerResource("页面静态化管理", "/services/page/v2/api-docs", "2.0"));
        resources.add(swaggerResource("Es文档搜索管理", "/services/es/v2/api-docs", "2.0"));
        resources.add(swaggerResource("缓存管理", "/services/cache/v2/api-docs", "2.0"));
        resources.add(swaggerResource("用户管理", "/services/user/v2/api-docs", "2.0"));
        resources.add(swaggerResource("短信管理", "/services/sms/v2/api-docs", "2.0"));
        resources.add(swaggerResource("系统管理", "/services/system/v2/api-docs", "2.0"));
        return resources;
    }

    private SwaggerResource swaggerResource(String name, String location, String version) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion(version);
        return swaggerResource;
    }
}

3、上传与删除的接口

人力资源后端项目_04-FastDFS分布式文件存储系统_第12张图片

(1)添加工具类

package io.niker.hrm.util;

import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;

public class FastDfsApiOpr {
     
    public static String CONF_FILENAME  = FastDfsApiOpr.class.getClassLoader()
            .getResource("fast_client.conf").getFile();


    /**
     * 上传文件
     * @param file
     * @param extName
     * @return
     */
    public static  String upload(byte[] file,String extName) {

        try {
            ClientGlobal.init(CONF_FILENAME);

            TrackerClient tracker = new TrackerClient();
            TrackerServer trackerServer = tracker.getConnection();
            StorageServer storageServer = null;

            StorageClient storageClient = new StorageClient(trackerServer, storageServer);
            NameValuePair nvp [] = new NameValuePair[]{
                    new NameValuePair("age", "18"),
                    new NameValuePair("sex", "male")
            };
            String fileIds[] = storageClient.upload_file(file,extName,nvp);

            System.out.println(fileIds.length);
            System.out.println("组名:" + fileIds[0]);
            System.out.println("路径: " + fileIds[1]);
            return  "/"+fileIds[0]+"/"+fileIds[1];

        } catch (Exception e) {
            e.printStackTrace();
            return  null;
        }
    }
    /**
     * 上传文件
     * @param extName
     * @return
     */
    public static  String upload(String path,String extName) {
 
        try { 
            ClientGlobal.init(CONF_FILENAME);
 
            TrackerClient tracker = new TrackerClient(); 
            TrackerServer trackerServer = tracker.getConnection(); 
            StorageServer storageServer = null;
            StorageClient storageClient = new StorageClient(trackerServer, storageServer);
            String fileIds[] = storageClient.upload_file(path, extName,null);
             
            System.out.println(fileIds.length); 
            System.out.println("组名:" + fileIds[0]); 
            System.out.println("路径: " + fileIds[1]);
            return  "/"+fileIds[0]+"/"+fileIds[1];
 
        } catch (Exception e) {
            e.printStackTrace();
            return  null;
        }
    }

    /**
     * 下载文件
     * @param groupName
     * @param fileName
     * @return
     */
    public static byte[] download(String groupName,String fileName) {
        try {
 
            ClientGlobal.init(CONF_FILENAME);
 
            TrackerClient tracker = new TrackerClient(); 
            TrackerServer trackerServer = tracker.getConnection(); 
            StorageServer storageServer = null;
 
            StorageClient storageClient = new StorageClient(trackerServer, storageServer); 
            byte[] b = storageClient.download_file(groupName, fileName);
            return  b;
        } catch (Exception e) {
            e.printStackTrace();
            return  null;
        } 
    }
     
//    @Test
//    public void testGetFileInfo(){
//        try {
//            ClientGlobal.init(conf_filename);
//
//            TrackerClient tracker = new TrackerClient();
//            TrackerServer trackerServer = tracker.getConnection();
//            StorageServer storageServer = null;
//
//            StorageClient storageClient = new StorageClient(trackerServer, storageServer);
//            FileInfo fi = storageClient.get_file_info("group1", "M00/00/00/wKgRcFV_08OAK_KCAAAA5fm_sy874.conf");
//            System.out.println(fi.getSourceIpAddr());
//            System.out.println(fi.getFileSize());
//            System.out.println(fi.getCreateTimestamp());
//            System.out.println(fi.getCrc32());
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
     
//    @Test
//    public void testGetFileMate(){
//        try {
//            ClientGlobal.init(conf_filename);
//
//            TrackerClient tracker = new TrackerClient();
//            TrackerServer trackerServer = tracker.getConnection();
//            StorageServer storageServer = null;
//
//            StorageClient storageClient = new StorageClient(trackerServer,
//                    storageServer);
//            NameValuePair nvps [] = storageClient.get_metadata("group1", "M00/00/00/wKgRcFV_08OAK_KCAAAA5fm_sy874.conf");
//            for(NameValuePair nvp : nvps){
//                System.out.println(nvp.getName() + ":" + nvp.getValue());
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

    /**
     * 删除文件
     * @param groupName
     * @param fileName
     */
    public static void delete(String groupName,String fileName){
        try { 
            ClientGlobal.init(CONF_FILENAME);
 
            TrackerClient tracker = new TrackerClient(); 
            TrackerServer trackerServer = tracker.getConnection(); 
            StorageServer storageServer = null;
 
            StorageClient storageClient = new StorageClient(trackerServer, 
                    storageServer); 
            int i = storageClient.delete_file(groupName,fileName);
            System.out.println( i==0 ? "删除成功" : "删除失败:"+i);
        } catch (Exception e) {
            e.printStackTrace();
            throw  new RuntimeException("删除异常,"+e.getMessage());
        } 
    }
}

(2)添加fastdfs的配置文件

fast_client.conf

tracker_server=FastDFS分布式文件系统的 ip地址:端口号

(3)接口

package io.niker.hrm.controller;

import io.niker.basic.util.AjaxResult;
import io.niker.hrm.util.FastDfsApiOpr;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
public class FileController {

    /**
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public AjaxResult upload(MultipartFile file){

        try {
            byte[] bytes = file.getBytes();//上传的文件内容

            //美女.png    extName="png"
            //文件名
            String filename = file.getOriginalFilename();
            //获取扩展名
            int index = filename.lastIndexOf(".");
            String extName = filename.substring(index+1);
            String fileId = FastDfsApiOpr.upload(bytes, extName);
            //要将fastdfs返回的file标识响应给前端
            return AjaxResult.me().setSuccess(true).setMessage("上传成功!").setResultObj(fileId);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setSuccess(false).setMessage("上传失败!");
        }

    }

    /**
     * 文件删除
     * 参数: /group1/M00/00/00/rBEAC16C9IKAQ6E3AAI8-cLFXhc547.png
     */
    @GetMapping("/delete")
    public AjaxResult delete(String fileId){

        try {
            String groupName = null;//    group1
            String fileName = null;//     M00/00/00/rBEAC16C9IKAQ6E3AAI8-cLFXhc547.png

            fileId = fileId.substring(1);// fileId = group1/M00/00/00/rBEAC16C9IKAQ6E3AAI8-cLFXhc547.png
            int index = fileId.indexOf("/");
            groupName = fileId.substring(0,index);
            fileName = fileId.substring(index+1);

            FastDfsApiOpr.delete(groupName,fileName);
            return AjaxResult.me().setSuccess(true).setMessage("删除成功!");
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setSuccess(false).setMessage("删除失败!"+e.getMessage());
        }
    }

}

你可能感兴趣的:(人力资源后端项目_04-FastDFS分布式文件存储系统)