Bootstrap

A069_店铺入驻_FastDfs_邮件


店铺入驻

内容介绍

1. 店铺入驻需求分析; (掌握)
2. 店铺管理; (掌握)
3. 图片上传fastdfs (掌握)
4. fastdfs分布式文件系统 (掌握)
5. 发送邮件 (掌握)

店铺管理和部门,员工一样,也是属于组织机构管理模块。

1.店铺入驻业务分析

1.1.为什么需要店铺入驻

平台运营模式回顾:
在这里插入图片描述

1.2.实现分析

店家自动上门
市场挖掘
店家可以自己入驻
市场人员可以帮他入驻
在这里插入图片描述
填写资料里面比较重要
店铺信息:地址(比较重要),logo,名称,营业执照。。。。
管理员: 用户名和密码,身份证,手机,邮箱。。。
支付模式所需资料:支付宝账号,微信账号,银行卡绑定等等

入驻时填写必要的一些信息,入驻后再填写敏感及其其他信息,提交审核,等平台审核通过才能使用。

店铺管理模块:店铺管理员只能看到自己店铺,并且完善信息,提交审核
如果平台管理员,进行审核。
在这里插入图片描述
入驻填写-填写其他资料-提交审核(前三步建议店家做)-审核(平台小妹)-正常使用

2.后台设计

表&domain/query/mapper/service/controller

2.1.表设计

店铺信息-管理员信息-实名认证信息(扩展)-支付宝支付信息-微信支付信息-银行卡信息

在做门店入驻时候,需要提交的数据门店相关的基本数据
在这里插入图片描述
对于门店用户,在注册以后需要管理和门店相关的业务。例如订单管理,业务报表,上架领养宠物等。所以需要添加一个对应登录后台管理系统的账户。
所以在用户提交资料时候,需要传入对应的管理店铺的信息,生成Employee表数据和店铺数据关联。管理字段admin_id。
在这里插入图片描述

2.2.Domain&Query
@Data
public class Shop extends BaseDomain {
    private String name;
    private String tel;
    private Date registerTime = new Date();
    private int state = 0;
    private String address;
    private String logo;
    private Employee admin;
}
2.3.mapper接口、ShopMapper.xml

接口略过

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cn.itsource.org.mapper.ShopMapper">
    <insert id="save" parameterType="Shop">
        insert into t_shop(
            name,
            tel,
            registerTime,
            state,
            address,
            logo,
            admin_id
        )
        values
        (
             #{   name},
             #{   tel},
             #{   registerTime},
             #{   state},
             #{   address},
             #{   logo},
             #{   admin.id}
        )
    </insert>

    <update id="update" parameterType="Shop">
        update t_shop set name=#{name},tel=#{tel},registerTime=#{registerTime},state=#{state},address=#{address},logo=#{logo},admin_id=#{admin.id} WHERE  id=#{id}

    </update>


    <delete id="remove" parameterType="long">
        delete from t_shop where id=#{id}
    </delete>
    <!--    //分页查询总条数-->
    <!--    Long queryCount(BaseQuery baseQuery);-->
    <select id="queryCount" parameterType="ShopQuery" resultType="long">
        select count(*) from t_shop t
        <where>
            <if test="keyword!=null and keyword!=''">
                AND t.name LIKE concat('%',#{keyword},'%')
            </if>
            <if test="state!=-1">
                AND t.state =#{state}
            </if>
        </where>
    </select>

    <!--    //查询当前页数-->
    <!--    List<T> queryData(BaseQuery baseQuery);-->
    <select id="queryData" parameterType="ShopQuery" resultType="Shop">
        select t.* from t_shop t
        <where>
            <if test="keyword!=null and keyword!=''">
                AND t.name LIKE concat('%',#{keyword},'%')
            </if>
            <if test="state!=-1">
                AND t.state =#{state}
            </if>
        </where>
        limit #{start},#{pageSize}
    </select>
</mapper>
2.4.service(略)
2.5.Controller
package cn.itsource.org.controller;

import cn.itsource.basic.util.AjaxResult;
import cn.itsource.basic.util.PageList;
import cn.itsource.org.domain.Shop;
import cn.itsource.org.query.ShopQuery;
import cn.itsource.org.service.IShopService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/shop")
public class ShopController {
    @Autowired
    private IShopService shopService;


    /**
     * 删除对象信息
     * @param id
     * @return
     */
    @DeleteMapping(value="/{id}")
    public AjaxResult delete(@PathVariable("id") Long id){
        try {
            shopService.del(id);
            return AjaxResult.me();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setMessage("删除对象失败!"+e.getMessage());
        }
    }

    /**
     * 修改对象
     * @return Ajaxresult转换结果
     */
    @PutMapping
    public AjaxResult addOrUpdate(@RequestBody Shop shop){
        try {
            if (shop.getId()==null){
                shopService.add(shop);
            }else{
                shopService.update(shop);
            }

            return AjaxResult.me();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setMessage("保存对象失败!"+e.getMessage());
        }
    }

    //获取用户
    @GetMapping("/{id}")
    public Shop get(@PathVariable("id")Long id)
    {
        return shopService.getById(id);
    }


    /**
     * 查看所有的员工信息
     * @return
     */
    @GetMapping
    public List<Shop> list(){

        return shopService.getAll();
    }

    /**
     * 分页查询数据
     *
     * @param query 查询对象
     * @return PageList 分页对象
     */
    @PostMapping
    public PageList<Shop> json(@RequestBody ShopQuery query)
    {
        return shopService.queryData(query);
    }

    /**
     * 添加对象
     * @param shop  传递的实体
     * @return Ajaxresult转换结果
     */
    @PostMapping("/settledIn")
    public AjaxResult settledIn(@RequestBody Shop shop){
        System.out.println(shop.getName());
        try {
            shopService.settledIn(shop);
            return AjaxResult.me();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setMessage("保存对象失败!"+e.getMessage());
        }
    }
}

3.入驻资料提交

对于店铺而言,要想使用后台,必须先登录。但是登录需要有账号,就需要先入住。。。。。

可以在登录页面放一个“店铺入驻”按钮,点击后跳转到“入驻”页面,填写相关信息后,进行入驻提交。如果入驻成功跳转到“登录页面”

3.1. 跳转到入驻界面

1)在登录页面搞个“店铺入驻”按钮 login.vue

<el-form-item style="width:100%;">
      <el-button type="primary" style="width:45%;" @click.native.prevent="handleSubmit2" :loading="logining">登录</el-button>
      <el-button type="primary" style="width:45%;" @click.native.prevent="handleSettledIn">店铺入驻</el-button>
</el-form-item>

2)路由中配置入驻组件 routes.js

let routes = [
    {
        path: '/shopRegister',
        component: ShopRegister,
        name: '', //不需显示name没有意义
        hidden: true //需要在菜单显示
    },

3)点击“店铺入驻”跳转到“入驻”界面的链接 login.vue

methods: {
      handleSettledIn() {
            //要跳转到路由的路径
            this.$router.push({ path: '/shopRegister' });
      },
<template>
  <div>
    <!--:model="shop" 数据双向绑定-->
    <!--ref="shopForm" id="shopForm",给form去一个名字-->
    <!--:rules="formRules" 校验规则-->
    <el-form :model="shop" ref="shopForm" :rules="formRules" label-position="left" label-width="100px" class="demo-ruleForm login-container">
      <h3 class="title">店铺入驻</h3>
      <el-form-item prop="name"label="名称">
        <el-input type="text" v-model="shop.name" auto-complete="off" placeholder="请输入店铺名称!"></el-input>
      </el-form-item>
      <el-form-item prop="tel" label="座机">
        <el-input type="text" v-model="shop.tel" auto-complete="off" placeholder="请输入座机!"></el-input>
      </el-form-item>
      <el-form-item prop="address" label="店铺地址">
        <el-input type="text" style="width: 350px" v-model="shop.address" auto-complete="off" placeholder="请输入地址!"></el-input>
<!--        <el-button size="small" type="primary" @click="selectAdrress">选择</el-button>-->
      </el-form-item>
      <el-form-item prop="logo" label="店铺Logo">
      <el-upload
              class="upload-demo"
              action="http://localhost:1030/services/common/fastDfs/"
              :on-preview="handlePreview"
              :on-remove="handleRemove"
              :on-success="handleSuccess"
              :file-list="fileList"
              list-type="picture">
        <el-button size="small" type="primary">点击上传</el-button>
        <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
      </el-upload>
      </el-form-item>
        <h3>管理员信息设置</h3>
      <el-form-item prop="username" label="账号">
        <el-input type="text" v-model="shop.username" auto-complete="off" placeholder="请输入账号!"></el-input>
      </el-form-item>
      <el-form-item prop="tel" label="手机号码">
        <el-input type="text" v-model="shop.phone" auto-complete="off" placeholder="请输入电话!"></el-input>
      </el-form-item>
      <el-form-item prop="email" label="电子邮件">
        <el-input type="text" v-model="shop.email" auto-complete="off" placeholder="请输入邮件!"></el-input>
      </el-form-item>
      <el-form-item prop="password" label="密码">
        <el-input type="password" v-model="shop.password" auto-complete="off" placeholder="请输入密码!"></el-input>
      </el-form-item>
      <el-form-item prop="comfirmPassword" label="确认密码">
        <el-input type="password" v-model="shop.comfirmPassword" auto-complete="off" placeholder="请输入确认密码!"></el-input>
      </el-form-item>
      <el-form-item style="width:100%;">
        <el-button type="primary" style="width:100%;" @click.native.prevent="settledIn" >入驻</el-button>
      </el-form-item>
    </el-form>
    <el-dialog
            title="选择地址"
            :visible.sync="dialogVisable"
            width="30%">
      <baidu-map :center="{lng: 116.403765, lat: 39.914850}" :zoom="11">
        <bm-view class="map"></bm-view>
        <bm-control :offset="{width: '10px', height: '10px'}">
          <!--:sugStyle="{zIndex: 2100} 让搜索提示上浮-->
          <bm-auto-complete v-model="keyword" :sugStyle="{zIndex: 2100}">
            <div style="margin-bottom:10px">
              <input id="searchInput" type="text" placeholder="请输入关键字" class="searchinput"/>
              <el-button type="success" @click="selectAdrressConfirm">确定</el-button>
            </div>
          </bm-auto-complete>
        </bm-control>
        <bm-local-search :keyword="keyword" :auto-viewport="true" ></bm-local-search>
      </baidu-map>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisable=false">取 消</el-button>
        <el-button type="primary" @click="dialogVisable=false">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<style lang="scss" scoped>
  .login-container {
    -webkit-border-radius: 5px;
    border-radius: 5px;
    -moz-border-radius: 5px;
    background-clip: padding-box;
    margin: 180px auto;
    width: 500px;
    padding: 35px 35px 15px 35px;
    background: #fff;
    border: 1px solid #eaeaea;
    box-shadow: 0 0 25px #cac6c6;
    .title {
      margin: 0px auto 40px auto;
      text-align: center;
      color: #505458;
    }
    .remember {
      margin: 0px 0px 35px 0px;
    }
  }

  .map {
    width: 100%;
    height: 300px;
  }
  .searchinput{
    width: 300px;
    box-sizing: border-box;
    padding: 9px;
    border: 1px solid #dddee1;
    line-height: 20px;
    font-size: 16px;
    height: 38px;
    color: #333;
    position: relative;
    border-radius: 4px;
  }
</style>
3.2.页面准备

在这里插入图片描述

3.3.页面js
<script>
    export default {
        data() {
            var validatePass2 = (rule, value, callback) => {
                console.log(value); //确认密码
                if (value === '') {
                    callback(new Error('请再次输入密码'))
                } else if (value !== this.shop.password) {
                    callback(new Error('两次输入密码不一致!'))
                } else {
                    callback()
                }
            }
            return {
                keyword:'',
                dialogVisable:false,
                fileList: [],
                //shop:shop 为了做数据表单校验不要嵌套对象
                shop: {
                    name: '',
                    tel: '',
                    address: '',
                    logo:'',
                    username:'',
                    tel:'',
                    phone:'',
                    email:'',
                    password:'',
                    comfirmPassword:''
                },
                formRules: {
                    name: [
                        { required: true, message: '请输入店铺名称!', trigger: 'blur' }
                    ],
                    tel: [
                        { required: true, message: '请输入店铺座机!', trigger: 'blur' }
                    ],
                    address: [
                        { required: true, message: '请输入店铺地址!', trigger: 'blur' }
                    ],
                    // logo: [
                    //     { required: true, message: '请输入店铺logo!', trigger: 'blur' }
                    // ],
                    username: [
                        { required: true, message: '请输入账号!', trigger: 'blur' }
                    ],
                    tel: [
                        { required: true, message: '请输入座机!', trigger: 'blur' }
                    ],
                    phone: [
                        { required: true, message: '请输入手机号!', trigger: 'blur' }
                    ],
                    // email: [
                    //     { type: 'email',required: true, message: '请输入邮箱!', trigger: 'blur' }
                    // ],
                    password: [
                        { required: true, message: '请输入密码!', trigger: 'blur' }
                    ],
                    comfirmPassword: [
                        {required: true,validator: validatePass2, trigger: 'blur' } //自定义校验规则
                    ]
                }
            };
        },
        methods: {
            selectAdrressConfirm(){
              //获取值搜索框值,设置给地址
                var searchInputV=document.getElementById("searchInput").value;
                this.shop.address = searchInputV;
                //关闭对话框
                this.dialogVisable = false;
            },
            selectAdrress(){
                this.dialogVisable = true;
            },
            handleSuccess(response, file, fileList){
                console.log("===========")
                console.log(response);
                console.log(file);
                console.log(fileList);
                this.shop.logo = response.resultObj;
            },
            handleRemove(file, fileList) {
              var filePath =file.response.resultObj;
              this.$http.delete("/common/fastDfs/?path="+filePath)
                      .then(res=>{
                        if(res.data.success){
                          this.$message({
                            message: '删除成功!',
                            type: 'success'
                          });
                        }else{
                          this.$message({
                            message: '删除失败!',
                            type: 'error'
                          });
                        }
                      })
            },


            handlePreview(file) {
                console.log(file);
            },
            settledIn(){
                this.$refs.shopForm.validate((valid) => {
                    //校验表单成功后才做一下操作
                    if (valid) {
                        this.$confirm('确认入驻吗?', '提示', {}).then(() => {
                            //拷贝后面对象的值到新对象,防止后面代码改动引起模型变化
                            let para = Object.assign({}, this.shop); //shop 本身这个参数里面就有店铺和管理员信息
                            // 为了后台好接收,封装一个对象放到里面
                            let admin = {
                              username: para.username,
                              phone: para.phone,
                              email: para.email,
                              password:para.password,
                              comfirmPassword:para.comfirmPassword
                            }
                            para.admin = admin;
                            //判断是否有id有就是修改,否则就是添加
                            this.$http.post("/shop/settlement",para).then((res) => {
                                if(res.data.success){
                                    this.$message({
                                        message: '操作成功!',
                                        type: 'success'
                                    });
                                    //重置表单
                                    this.$refs['shopForm'].resetFields();
                                    //跳转登录页面
                                    this.$router.push({ path: '/login' });
                                }
                                else{
                                    this.$message({
                                        message: res.data.message,
                                        type: 'error'
                                    });
                                }

                            });
                        });
                    }
                })
            }
        }
    }

</script>
3.4.后台实现

ShopController

//入驻请求
@PostMapping("/settlement")
public AjaxResult settledIn(@RequestBody Shop shop){
    try {
         shopService.settledIn(shop);
        return AjaxResult.me();
    } catch (BusinessException e) //业务异常捕获
    {
        return AjaxResult.me().setMessage("入驻失败!"+e.getMessage());
    }
    catch (Exception e) { //500
        e.printStackTrace();
        return AjaxResult.me().setMessage("系统异常!"+e.getMessage());
    }
}
Service
@Override
public void settledIn(Shop shop) {
    //对用户进行校验
    // 1 判断是否存在,提示“已经入驻,请直接登陆!如果忘记了请找回密码!”
    Employee admin = shop.getAdmin();
    Employee employee = employeeMapper.loadByUser(admin);
    if (employee!=null)
         //不仅要错误,还要提示信息
        throw new BusinessException("已经入驻,请直接登陆!如果忘记了请找回密码!");
    // 2 如果不存在,走入驻流程
    // 2.1 保存管理员信息并放回自增id
    employeeMapper.save(admin);
    // 2.2 给shop添加admin.id,再保存Shop----名称可以重复但是店铺地址在方圆几公里不能重复入驻,由审核人员把控。
    shopMapper.save(shop);  //上一步保存完毕shop.admin里面就有id

    //让管理员关联店铺id
    admin.setShop(shop);
    employeeMapper.update(admin);
}

Mapper.xml

EmployeeMapper.xml

<!--Employee loadByUser(Employee admin); username phone email-->
<select id="loadByUser" resultType="Employee" parameterType="Employee">
    select * from t_employee where username = #{username} or phone = #{phone} or email = #{email}
</select>

<!--//保存对象-->
<!--void save(Employee employee);-->
<insert id="save" parameterType="Employee"
useGeneratedKeys="true"   keyColumn="id"  keyProperty="id" >
        insert into t_employee (username,email,phone,password,age,state,department_id)
        values (#{username},#{email},#{phone},#{password},#{age},#{state},#{department.id})
</insert>

<!--//更新一个对象-->
<!--void update(Employee employee);-->
<update id="update" parameterType="Employee">
   UPDATE t_employee SET
    username=#{username},
    email=#{email},
    phone=#{phone},
    salt=#{salt},
    password=#{password},
    age=#{age},
    state=#{state},
    department_id=#{department.id},
    logininfo_id=#{logininfo_id},
    shop_id=#{shop.id}
    WHERE id=#{id}
</update>

ShopMapper.xml

<insert id="save" parameterType="Shop">
    insert into t_shop(
    name,
    tel,
    registerTime,
    state,
    address,
    logo,
    admin_id
    )
    values
    (
    #{   name},
    #{   tel},
    #{   registerTime},
    #{   state},
    #{   address},
    #{   logo},
    #{   admin.id}
    )
</insert>

4.图片上传-fastdfs

4.1.为什么需要分布式文件系统

在这里插入图片描述

4.2.分布式文件系统选型
   方案1:租用服务
          七牛云(20G),阿里云对象存储,腾讯云对象存储等等。。。。。

          优点:自己不用维护,短期投入比较小
          缺点:数据量大和长期使用都不划算
       
   方案2:自己搭建  
		需要硬件服务器 + 选择一个分布式的文件系统软件 ( fastdfs hdfs等)
        安装方式:
		Linux--比较麻烦
		Docker-容器安装,比较简单(一条命令)

我们现在先使用云服务器,等到后面同学们学了docker后,再来安装。并且以后在企业中,会有专门的运维来安装,我们不用考虑
4.3.Fastdfs环境搭建
4.3.1.Fastdfs是什么

FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。
Tracker server 作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到 Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。
Storage server 作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上,Storageserver 没有实现自己的文件系统而是利用操作系统 的文件系统来管理文件。可以将storage称为存储服务器。
在这里插入图片描述
服务端两个角色:
Tracker:管理集群,tracker 也可以实现集群。每个 tracker 节点地位平等。收集 Storage 集群的状态。
Storage:实际保存文件 Storage 分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。

参考:
官方网站:https://github.com/happyfish100/
配置文档:https://github.com/happyfish100/fastdfs/wiki/

参考资料:https://www.oschina.net/question/tag/fastdfs
Java客户端:https://github.com/happyfish100/fastdfs-client-java

上传
在这里插入图片描述

客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。
在这里插入图片描述
 组名(卷):文件上传后所在的 storage 组名称,在文件上传成功后有 storage 服务器返回,需要客户端自行保存。
 虚拟磁盘路径:storage 配置的虚拟路径,与磁盘选项 store_path*对应。如果配置了
store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推。
 数据两级目录:storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据
文件。
 文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储
服务器 IP 地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。

下载
在这里插入图片描述

4.3.2.最简单的FastDFS架构

在这里插入图片描述

4.3.3. Fastdfs搭建-一个运维工程师

方案1: Linux慢慢搭建-非常复杂,耗用资源
方案2:docker搭建-非常简单
https://blog.csdn.net/tttzzztttzzz/article/details/86709318

后面学习了docker ,我们再来搭建fastdfs,非常简单的。现在直接用我的。
115.159.217.249/

 上传用22122端口
 访问用8888端口

4.4.代码操作
4.4.1.fastdfs-入门

需求:将本地图片上传至图片服务器,再控制台打印url
(1)创建Maven工程fastDFSdemo
pom.xml中引入

  	<!-- https://mvnrepository.com/artifact/cn.bestwu/fastdfs-client-java -->
<dependency>
    <groupId>cn.bestwu</groupId>
    <artifactId>fastdfs-client-java</artifactId>
    <version>1.27</version>
</dependency>

(2)添加配置文件fdfs_client.conf ,将其中的服务器地址设置为192.168.25.133

//......
tracker_server=115.159.217.249:22122
//......上传用端口

(3)创建java类,main方法代码如下:

         // 1、加载配置文件,配置文件中的内容就是 tracker 服务的地址。
		ClientGlobal.init("D:/maven_work/fastDFS-demo/src/fdfs_client.conf");
		// 2、创建一个 TrackerClient 对象。直接 new 一个。
		TrackerClient trackerClient = new TrackerClient();
		// 3、使用 TrackerClient 对象创建连接,获得一个 TrackerServer 对象。
		TrackerServer trackerServer = trackerClient.getConnection();
		// 4、创建一个 StorageServer 的引用,值为 null
		StorageServer storageServer = null;
		// 5、创建一个 StorageClient 对象,需要两个参数 TrackerServer 对象、StorageServer 的引用
		StorageClient storageClient = new StorageClient(trackerServer, storageServer);
		// 6、使用 StorageClient 对象上传图片。
		//扩展名不带“.”
		String[] strings = storageClient.upload_file("D:/pic/benchi.jpg", "jpg",
				null);
		// 7、返回数组。包含组名和图片的路径。
		for (String string : strings) {
			System.out.println(string);
		}

控制台输出如下结果:

group1
M00/00/00/wKgZhVkMP4KAZEy-AAA-tCf93Fo973.jpg

在浏览器输入:
http://115.159.217.249:8888/group1/M00/00/00/wKgZhVkMP4KAZEy-AAA-tCf93Fo973.jpg

4.4.2.工具类封装

见resource

4.5.项目实战
4.5.1.页面准备
handleSuccess(response, file, fileList){
    console.log("===========");
    console.log(response);
    console.log(file);
    console.log(fileList);
    this.shop.logo = response.resultObj;
},
handleRemove(file, fileList) {
    var filePath =file.response.resultObj;
    this.$http.delete("/dfs?path="+filePath)
        .then(res=>{
            if(res.data.success){
                this.$message({
                    message: '删除成功!',
                    type: 'success'
                });
            }else{
                this.$message({
                    message: '删除失败!',
                    type: 'error'
                });
           }
     })
},
4.5.2.后台代码
@RestController
@RequestMapping("/dfs")
public class FastDfsController {
    @PostMapping
    public AjaxResult upload(@RequestPart(required = true,value = "file") MultipartFile file){

        try {
            System.out.println(file.getOriginalFilename() + ":" + file.getSize());
            String originalFilename = file.getOriginalFilename();
            // xxx.jpg
            String extName = originalFilename.substring(originalFilename.lastIndexOf(".")+1);
            System.out.println(extName);
            String filePath =  FastDfsUtil.upload(file.getBytes(), extName);
            return AjaxResult.me().setResultObj(filePath); //把上传后的路径返回回去
        } catch (IOException e) {
            e.printStackTrace();
            return AjaxResult.me().setSuccess(false).setMessage("上传失败!"+e.getMessage());
        }
    }
    /**
     * 参数:完整路径 /goup1/xxxxx/yyyy
     * 返回值:成功与否,还要返回地址
     *
     */
    @DeleteMapping
    public AjaxResult del(@RequestParam(required = true,value = "path") String path){
        String pathTmp = path.substring(1); // goup1/xxxxx/yyyy
        String groupName =  pathTmp.substring(0, pathTmp.indexOf("/")); //goup1
        String remotePath = pathTmp.substring(pathTmp.indexOf("/")+1);// /xxxxx/yyyy
        System.out.println(groupName);
        System.out.println(remotePath);
        FastDfsUtil.delete(groupName, remotePath);
        return  AjaxResult.me();
    }
}
4.6.小结
 为什么需要分布式文件系统
 为什么选择fastdfs
 Fastdfs搭建
 Fastdfs入门
 Fastdfs项目实战

5.审核邮件通知

5.1.列表(高级查询+分页省略)

店铺管理员:
在这里插入图片描述
平台管理:
在这里插入图片描述
当后台的审核人员点击审核通过的时候,向用户注册时留下的邮箱发送邮件,告知用户审核通过。

5.2.邮件发送

在这里插入图片描述

5.2.1.准备工作

所有邮件的发送都需要有运营商的支持,例如qq邮箱,163等。要发送邮件需要先获取服务上提供的授权码。以qq邮箱为例:

email.qq.com
在这里插入图片描述
在这里插入图片描述
vvjyrwvyzgebbgdi

5.2.2.导入jar包
        <!--对邮件的支持jar-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
5.2.3.发送人的信息配置

在这里插入图片描述
发送邮件需要配置很多关于发送人的信息,Springboot对此已经通过MailSenderAutoConfiguration类进行了基本的配置。
在这里插入图片描述
这些基本属性又是通过MailProperties获取属性
在这里插入图片描述
我们的需要做的工作就是给上面的类配置我们自己的发件人相关信息。
添加配置文件:application.Properties 或者 直接yml里添加配置

# 设置邮箱主机(服务商)
spring.mail.host=smtp.qq.com
# 设置用户名
[email protected]
# 设置密码,该处的密码是QQ邮箱开启SMTP的授权码而非QQ密码
spring.mail.password=qzbxiwjfrweecacf
# 必须进行授权认证,它的目的就是阻止他人任意乱发邮件
spring.mail.properties.mail.smtp.auth=true
#SMTP加密方式:连接到一个TLS保护连接
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
mail:	# mail在spring层级之下
  host: smtp.qq.com
  username: [email protected]
  password: qzbxiwjfrweecacf	# 授权码
5.3.发送简单邮件
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class EmailTest {
    @Autowired
    private JavaMailSender javaMailSender;
    @Test
    public void  send(){
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        //设置发送人
        mailMessage.setFrom("[email protected]");
        //邮件主题
        mailMessage.setSubject("新型冠状病毒防护指南");
        //邮件内容
        mailMessage.setText("好好在家待着.....");
        //收件人
        mailMessage.setTo("[email protected]");

        javaMailSender.send(mailMessage);
    }
}
5.4.发送复杂邮件
@Test
public void test2() throws Exception{
    //创建复杂邮件对象
    MimeMessage mimeMessage = javaMailSender.createMimeMessage();
    //发送复杂邮件的工具类
    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true,"utf-8");
    helper.setFrom("[email protected]");
    helper.setSubject("新型冠状病毒防护指南");
    http://img30.360buyimg.com/popWaterMark/jfs/t1/67988/7/14094/232759/5db64acfE6ab2b09e/38b5cb3dc38b4b1f.jpg"
    helper.setText("<h1>新型冠状病毒防护守则</h1>"+
            "<img src='http://img30.360buyimg.com/popWaterMark/jfs/t1/67988/7/14094/232759/5db64acfE6ab2b09e/38b5cb3dc38b4b1f.jpg' />",true);
    //添加附件
    helper.addAttachment("罗宾.jpg",new File("C:\\Users\\hm\\Desktop\\work\\aa.jpg"));
    helper.addAttachment("压缩文件", new File("C:\\Users\\hm\\Desktop\\20191010\\2020-02-05-智能商贸-DAY4\\resources\\resources.zip"));
    //收件人
    helper.setTo("[email protected]");

    javaMailSender.send(mimeMessage);
}

5.5.邮件激活登录账号(扩展)

在这里插入图片描述

6.课程总结

6.1.重点

返回自增id
使用场景?在同一个service方法中,下一步的操作需要用到上一步添加保存后的id

     <!--//保存对象-->
    <!--void save(Employee employee);-->
    <insert id="save" parameterType="Employee"
             useGeneratedKeys="true"
            keyColumn="id"
            keyProperty="id"
    >
            insert into t_employee (username,email,phone,password,age,state,department_id)
            values (#{username},#{email},#{phone},#{password},#{age},#{state},#{department.id})

    </insert>
6.2.难点
6.3.如何掌握
6.4.排错技巧(技巧)

7.常见问题

8.课后练习

1.完成邮件激活店铺账号功能

9.面试题

10.扩展知识或课外阅读推荐(可选)

10.1.扩展知识
10.2.课外阅读
;