目录

  • 内容介绍
  • 1.店铺入驻业务分析
    • 1.1.为什么需要店铺入驻
    • 1.2.实现分析
  • 2.后台设计
    • 2.1.表设计
    • 2.2.Domain&Query
    • 2.3.mappe接口、ShopMapper.xml
    • 2.4.service(略)
    • 2.5.Controller
  • 3.入驻资料提交
    • 3.1. 跳转到入驻界面
    • 3.2.页面准备
    • 3.3.页面js
    • 3.4.后台实现
  • 4.图片上传-fastdfs
    • 4.1.为什么需要分布式文件系统
    • 4.2.分布式文件系统选型
    • 4.3.Fastdfs环境搭建
      • 4.3.1.Fastdfs是什么
      • 4.3.2.最简单的FastDFS架构
      • 4.3.3. Fastdfs搭建-一个运维工程师
    • 4.4.代码操作
      • 4.4.1.fastdfs-入门
      • 4.4.2.工具类封装
    • 4.5.项目实战
      • 4.5.1.页面准备
      • 4.5.2.后台代码
    • 4.6.小结
  • 5.审核邮件通知
    • 5.1.列表(高级查询+分页省略)
    • 5.2.邮件发送
      • 5.2.1.准备工作
      • 5.2.2.导入jar包
      • 5.2.3.发送人的信息配置
    • 5.3.发送简单邮件
    • 5.4.发送复杂邮件
    • 5.5.邮件激活登录账号(扩展)
  • 6.课程总结
    • 6.1.重点
    • 6.2.难点
    • 6.3.如何掌握
    • 6.4.排错技巧(技巧)
  • 7.常见问题
  • 8.课后练习
  • 9.面试题
  • 10.扩展知识或课外阅读推荐(可选)
    • 10.1.扩展知识
    • 10.2.课外阅读

店铺入驻

内容介绍

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.mappe接口、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 {@Autowiredprivate 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转换结果*/@PutMappingpublic 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*/@GetMappingpublic List<Shop> list(){return shopService.getAll();}/*** 分页查询数据** @param query 查询对象* @return PageList 分页对象*/@PostMappingpublic 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-uploadclass="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-dialogtitle="选择地址":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) { //500e.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 保存管理员信息并放回自增idemployeeMapper.save(admin);// 2.2 给shop添加admin.id,再保存Shop----名称可以重复但是店铺地址在方圆几公里不能重复入驻,由审核人员把控。shopMapper.save(shop);  //上一步保存完毕shop.admin里面就有id//让管理员关联店铺idadmin.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 SETusername=#{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 的引用,值为 nullStorageServer 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 {@PostMappingpublic AjaxResult upload(@RequestPart(required = true,value = "file") MultipartFile file){try {System.out.println(file.getOriginalFilename() + ":" + file.getSize());String originalFilename = file.getOriginalFilename();// xxx.jpgString 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* 返回值:成功与否,还要返回地址**/@DeleteMappingpublic AjaxResult del(@RequestParam(required = true,value = "path") String path){String pathTmp = path.substring(1); // goup1/xxxxx/yyyyString groupName =  pathTmp.substring(0, pathTmp.indexOf("/")); //goup1String remotePath = pathTmp.substring(pathTmp.indexOf("/")+1);// /xxxxx/yyyySystem.out.println(groupName);System.out.println(remotePath);FastDfsUtil.delete(groupName, remotePath);return  AjaxResult.me();}
}

4.6.小结

 为什么需要分布式文件系统为什么选择fastdfsFastdfs搭建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
# 设置用户名
spring.mail.username=64009120@qq.com
# 设置密码,该处的密码是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:host: smtp.qq.comusername: 64009120@qq.compassword: qzbxiwjfrweecacf

5.3.发送简单邮件

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class EmailTest {@Autowiredprivate JavaMailSender javaMailSender;@Testpublic void  send(){SimpleMailMessage mailMessage = new SimpleMailMessage();//设置发送人mailMessage.setFrom("64009120@qq.com");//邮件主题mailMessage.setSubject("新型冠状病毒防护指南");//邮件内容mailMessage.setText("好好在家待着.....");//收件人mailMessage.setTo("64009120@qq.com");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("64009120@qq.com");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("2518743821@qq.com");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.课外阅读

A069_店铺入驻_FastDfs_邮件相关推荐

  1. 商城店铺入驻系统_商家入驻开设店铺功能逻辑与流程设计

    店铺入驻与开设:商城系统根据运营的需要可以入驻各行各业的商家,商家入驻数量无限制.商家入驻之前平台要设计好店铺分类,特别是o2o频道店铺分类很重要.商家入驻需要三个方面的信息,公司信息和资质,店铺相关 ...

  2. 拼多多店铺入驻操作流程、打造店铺爆款方法

    刚入驻拼多多对操作流程及活动报名不清楚: 自已价钱有优势,打造爆款却有难度,眼睁睁的看着人家的款卖得超好,自已同样的款却卖不动: 作为生产型工厂,之前按订单做货,因而产品单一,店内产品丰富性不强,产品 ...

  3. 跨境电商各平台的入驻条件都有哪些?

    跨境电商各平台的入驻条件都有哪些? 近几年来,国内电商发展开始趋于平缓,而纵观跨境电商,却依然处于一路突飞猛进的趋势,由此,国内不少企业商家纷纷开始转变战略入局跨境电商,以此来获取更深层次的发展空间和 ...

  4. 多商户商城源码_多商户小程序开发搭建?开达应用多商户入驻商城小程序制作教程...

    多商户小程序如何搭建?多商户入驻商城小程序快读搭建教程 多商户设置 前言:多商户是配合商城主应用才能使用 基础设置 支持选择提现方式,多商户的用户在申请提现时使用 客服图标(开启) 商户距离排序(开启 ...

  5. DTS-SHOP微信小程序多店铺商城平台java源码

    介绍: DTS-SHOP微信小程序多店铺商城平台源码,基于 微信小程序 + springboot + vue 技术构建 ,支持单店铺,多店铺入驻的商城平台.项目包含 微信小程序,管理后台. 基于jav ...

  6. 小红书电商入驻全流程指南

    小红书电商入驻全流程指南#小红书 五类店铺入驻要求可售卖类目你知道吗?#运营#干货 你知道小红书的 5 类店铺的入驻要求以及可售卖类目吗?hello,大家好,我是专注搞流量的百收编辑狂潮老师.小红书目 ...

  7. 抖音商家入驻需要什么条件

    抖音目前正是电商风向口,对于准备转型电商线下商家而言,借助都应短视频的流量,能够更快的加速品牌孵化.布局抖音,入驻抖音小店还只是商家的第一步. Tiktok 抖音 商家入驻满足任一条件即可 1.商家资 ...

  8. 【Lilishop商城】No3-3.模块详细设计,会员信息(会员基本信息)、店铺设置(店铺信息、配送模板)的详细设计

      仅涉及后端,全部目录看顶部专栏,代码.文档.接口路径在: [Lilishop商城]记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇会结合业务介绍重点设计逻辑,其中重点包括 ...

  9. 拼多多企业入驻条件是什么?和个人入驻有啥区别?

    拼多多企业入驻条件是什么呢?和个人入住有什么区别?首先要知道拼多多企业入驻和个人入驻流程是差不多的,并且都需要缴纳一定的拼多多入驻保证金,但是具体入驻过程中所需要的资质却有所不同,拼多多个人店铺入驻要 ...

最新文章

  1. mysql种default约束的语句_sql语句大全之SQL DEFAULT 约束
  2. python基础代码事例-python基础示例
  3. [HNOI2008]玩具装箱toy
  4. 数据可视化高级部分:如何使用轨迹地图对路径数据进行分析
  5. vue 实现 js css html分离
  6. python获取最近N天工作日列表、节假日列表
  7. CV中的色彩空间大全
  8. c语言判断是否是回文字符串
  9. windows动态库和静态库VS导入
  10. 【转】linux图形界面编程基本知识
  11. 网络第八和九章要点总结
  12. Dapper使用在WCF上总是说Service找不到
  13. 使用JSP 编写九九乘法表
  14. sql日期中文大写显示
  15. 王垠:怎样尊重一个程序员
  16. Solr Facet 统计查询
  17. [Unity] 自定义日志系统 解决Unity Log的痛点
  18. chatGPT能生成图片吗-chatGPT如何用
  19. 启发式算法之蚁群算法
  20. 用php和mysql开发招聘网站

热门文章

  1. 买不起房的去俄罗斯?
  2. python __new__方法有什么用_学python日常工作有什么用?
  3. 新手如何入门短视频-今抖云创
  4. 机器视觉python推荐书籍_智能硬件与机器视觉:基于树莓派、Python和OpenCV
  5. CCD 芯片与 CMOS 芯片的主要参数有哪些?
  6. 单片机第一个程序----点亮LED灯
  7. 解决uniapp在H5端使用不能扫码问题
  8. adams2015怎么把工具栏打开_剪映怎么玩的更好?教你0基础也能秒会的剪映三大进阶级功能!...
  9. 使用打印机打印小册子是的注意事项
  10. 判断当前页面是否在微信浏览器中打开(四类方法)