电商项目:畅购商城4.0

1. 走进电商

1.1 电商行业分析

近年来,世界经济正向数字化转型,大力发展数字经济成为全球共识。党的十九大报告明确提出要建设“数字中国”“网络强国”,我国数字经济发展进入新阶段,市场规模位居全球第二,数字经济与实体经济深度融合,有力促进了供给侧结构性改革。电子商务是数字经济的重要组成部分,是数字经济最活跃、最集中的表现形式之一。
我国电子商务交易规模继续扩大,全国电子商务交易额达保持高速增长。国家统计局数据显示,2017年29.16万亿元,2018年31.64万亿元,2019年34.81万亿元,2020年交易额达38.21万亿元。
最近几年天猫双十一成交额

电商项目:畅购商城4.0_第1张图片

1.2 主要电商模式

  • B2B
    B2B ( Business to Business)是指进行电子商务交易的供需双方都是商家(或企业、公司),她(他)们使用了互联网的技术或各种商务网络平台,完成商务交易的过程。电子商务是现代 B2B marketing的一种具体主要的表现形式。
    案例:阿里巴巴、慧聪网

  • C2C
    C2C即 Customer(Consumer) to Customer(Consumer),意思就是消费者个人间的电子商务行为。比如一个消费者有一台电脑,通过网络进行交易,把它出售给另外一个消费者,此种交易类型就称为C2C电子商务。
    案例:淘宝、易趣、瓜子二手车

  • B2C
    B2C是Business-to-Customer的缩写,而其中文简称为“商对客”。“商对客”是电子商务的一种模式,也就是通常说的直接面向消费者销售产品和服务商业零售模式。这种形式的电子商务一般以网络零售业为主,主要借助于互联网开展在线销售活动。B2C即企业通过互联网为消费者提供一个新型的购物环境——网上商店,消费者通过网络在网上购物、网上支付等消费行为。
    案例:唯品会、乐蜂网

  • C2B
    C2B(Consumer to Business,即消费者到企业),是互联网经济时代新的商业模式。这一模式改变了原有生产者(企业和机构)和消费者的关系,是一种消费者贡献价值(Create Value), 企业和机构消费价值(Consume Value)。

C2B模式和我们熟知的供需模式(DSM, Demand SupplyModel)恰恰相反,真正的C2B 应该先有消费者需求产生而后有企业生产,即先有消费者提出需求,后有生产企业按需求组织生产。通常情况为消费者根据自身需求定制产品和价格,或主动参与产品设计、生产和定价,产品、价格等彰显消费者的个性化需求,生产企业进行定制化生产。
案例:海尔商城、 尚品宅配

  • O2O
    O2O即Online To Offline(在线离线/线上到线下),是指将线下的商务机会与互联网结合,让互联网成为线下交易的平台,这个概念最早来源于美国。O2O的概念非常广泛,既可涉及到线上,又可涉及到线下,可以通称为O2O。主流商业管理课程均对O2O这种新型的商业模式有所介绍及关注。
    案例:美团、饿了吗

  • B2B2C
    B2B2C是一种电子商务类型的网络购物商业模式,B是BUSINESS的简称,C是CUSTOMER的简称,第一个B指的是商品或服务的供应商,第二个B指的是从事电子商务的企业,C则是表示消费者。

案例:京东商城、天猫商城

  • 注:《畅购电商系统开发》采用B2C模式

2. 畅购前台-需求分析与系统设计

  • 电商项目分为:前台和后台
  • 前台:提供用户使用
  • 后台:提供商家进行管理

2.2 系统设计

电商项目:畅购商城4.0_第2张图片

2.2.1 技术架构

电商项目:畅购商城4.0_第3张图片

3. 架构搭建

3.1 后端环境

3.2.1 父工程:changgou4_parent_ali

  • 修改pom.xml文件,确定spring boot、spring cloud、spring cloud Alibaba 等版本


  4.0.0

  com.czxy.changgou
  changgou4-parent-ali
  pom
  1.0-SNAPSHOT
  
      changgou4_common
      changgou4_common_auth
      changgou4_common_db
      changgou4_gateway
      changgou4_pojo
      changgou4_service_web
  

  
  
      org.springframework.boot
      spring-boot-starter-parent
      2.2.5.RELEASE
  

  
  
      UTF-8
      1.8
      Hoxton.SR3
      1.1.0
      2.2.1.RELEASE
      5.1.32
      3.4.0
      1.1.9
      0.9.0
      2.9.7
      2.7.0
      1.9.3
      3.3.1
      1.0.0
      1.0-SNAPSHOT
      1.0-SNAPSHOT
      1.0-SNAPSHOT
      1.0-SNAPSHOT

  

  
  
      
          
          
              org.springframework.cloud
              spring-cloud-dependencies
              ${spring-cloud-release.version}
              pom
              import
          
          
          
              com.alibaba.nacos
              nacos-client
              ${nacos.version}
          

          
          
              com.alibaba.cloud
              spring-cloud-starter-alibaba-nacos-discovery
              ${alibaba.cloud.version}
          

          
          
              com.alibaba.cloud
              spring-cloud-starter-alibaba-nacos-config
              ${alibaba.cloud.version}
          

          
          
              com.alibaba.cloud
              spring-cloud-starter-alibaba-sentinel
              ${alibaba.cloud.version}
          

          
          
              com.baomidou
              mybatis-plus-boot-starter
              ${mybatis.plus.version}
          
          
              com.baomidou
              mybatis-plus-annotation
              ${mybatis.plus.version}
          

          
          
              mysql
              mysql-connector-java
              ${mysql.version}
          

          
          
              com.alibaba
              druid-spring-boot-starter
              ${druid.starter.version}
          

          
          
              io.springfox
              springfox-swagger2
              ${swagger.version}
          
          
              io.springfox
              springfox-swagger-ui
              ${swagger.version}
          

          
          
          
              commons-beanutils
              commons-beanutils
              ${beanutils.version}
          

          
          
              io.jsonwebtoken
              jjwt
              ${jwt.jjwt.version}
          

          
          
              joda-time
              joda-time
              ${jwt.joda.version}
          

          
          
              com.aliyuncs
              aliyun-java-sdk-core
              ${aliyun.sdk.core.version}
          
          
              com.aliyuncs.dysmsapi
              aliyun-java-sdk-dysmsapi
              ${aliyun.sdk.dysmsapi.version}
          

          
          
              com.czxy.changgou
              changgou4_common
              ${changgou4.common.version}
          
          
              com.czxy.changgou
              changgou4_common_auth
              ${changgou4.common.auth.version}
          
          
              com.czxy.changgou
              changgou4_common_db
              ${changgou4.common.db.version}
          
          
              com.czxy.changgou
              changgou4_pojo
              ${changgou4.pojo.version}
          


      

  


3.2.2 公共项目(基础):changgou4-common

电商项目:畅购商城4.0_第4张图片

  • 通用工具项目

  • 基于spring cloud开发基本依赖

  • web开发常见的工具类

  • 步骤一:修改pom.xml文件,添加依赖



    
        changgou4-parent-ali
        com.czxy.changgou
        1.0-SNAPSHOT
    
    4.0.0

    changgou4-common

    
        
        
            com.aliyuncs
            aliyun-java-sdk-core
        
        
            com.aliyuncs.dysmsapi
            aliyun-java-sdk-dysmsapi
        
        
        
            org.projectlombok
            lombok
        
    



  • 步骤二:拷贝工具类
    电商项目:畅购商城4.0_第5张图片

3.2.3 公共项目(认证):changgou4-common-auth

  • 认证通用工具项目

  • 步骤一:修改pom.xml文件



    
        changgou4-parent-ali
        com.czxy.changgou
        1.0-SNAPSHOT
    
    4.0.0

    changgou4-common-auth

    
        
        
            com.czxy.changgou
            changgou4_common
        
        
        
            commons-beanutils
            commons-beanutils
        

        
        
            io.jsonwebtoken
            jjwt
        

        
        
            joda-time
            joda-time
        
    



  • 步骤二:拷贝工具类
    电商项目:畅购商城4.0_第6张图片

3.2.4 公共项目(数据库):changgou4-common-db

  • 数据库通用工具项目

  • 步骤一:修改pom.xml文件



    
        changgou4-parent-ali
        com.czxy.changgou
        1.0-SNAPSHOT
    
    4.0.0

    changgou4-common-db

    
        
        
            com.czxy.changgou
            changgou4-common
        
        
        
            com.baomidou
            mybatis-plus-boot-starter
        
        
        
            mysql
            mysql-connector-java
        
        
        
            com.alibaba
            druid-spring-boot-starter
        
    


  • 步骤二:配置类
  • 要求所有的服务项目包名必须是“com.czxy.changgou4”,否则配置无法扫描,就需要每个项目单独拷贝。

电商项目:畅购商城4.0_第7张图片
3.2.5 POJO项目:changgou4-pojo
 统一管理所有的JavaBean

 修改pom.xml文件



org.projectlombok
lombok



org.springframework.boot
spring-boot-starter-json



com.baomidou
mybatis-plus-annotation

3.2.6 网关:changgou4-gateway

  • 修改pom.xml文档


    
        changgou4-parent-ali
        com.czxy.changgou
        1.0-SNAPSHOT
    
    4.0.0

    changgou4-gateway

    
        
        
            com.czxy.changgou
            changgou4-common-auth
        
        
            com.czxy.changgou
            changgou4-pojo
        
        
        
            org.springframework.cloud
            spring-cloud-starter-gateway
        

        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
    




  • 创建application.yml文档
#端口号
server:
  port: 10010
spring:
  application:
    name: changgou4-gateway
  servlet:
    multipart:
      max-file-size: 2MB    #上传文件的大小
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848   #nacos服务地址
    gateway:
      discovery:
        locator:
          enabled: true               #开启服务注册和发现的功能,自动创建router以服务名开头的请求路径转发到对应的服务
          lowerCaseServiceId: true    #将请求路径上的服务名配置为小写
  • 拷贝跨域配置类 GlobalCorsConfig

电商项目:畅购商城4.0_第8张图片

package com.czxy.changgou4.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

 
@Configuration
public class GlobalCorsConfig {

    @Bean
    public WebFilter corsFilter2() {
        return (ServerWebExchange ctx, WebFilterChain chain) -> {
            ServerHttpRequest request = ctx.getRequest();
            if (CorsUtils.isCorsRequest(request)) {
                HttpHeaders requestHeaders = request.getHeaders();
                ServerHttpResponse response = ctx.getResponse();
                HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
                HttpHeaders headers = response.getHeaders();
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
                headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
                        requestHeaders.getAccessControlRequestHeaders());
                if (requestMethod != null) {
                    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
                }
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
                headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
                if (request.getMethod() == HttpMethod.OPTIONS) {
                    response.setStatusCode(HttpStatus.OK);
                    return Mono.empty();
                }
            }
            return chain.filter(ctx);
        };
    }

}
  • 创建启动类
  • 电商项目:畅购商城4.0_第9张图片

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

3.3 前端环境

3.3.1 构建项目:changgou4-fore

  • 步骤一:使用脚手架构建项目
    npx create-nuxt-app changgou4-fore

  • 步骤二:npm打包方式、axios第三方模块、SSR渲染模式

3.3.2 整合axios

  • 步骤一:创建~/plugins/apiclient.js文件
  • 步骤二:编写nuxt整合模板,用于统一管理ajax请求路径
const request = {
  test : ()=> {
    return axios.get('/test')
  }
}

var axios = null
export default ({ $axios, redirect }, inject) => {

  //赋值
  axios = $axios

  //4) 将自定义函数交于nuxt
  // 使用方式1:在vue中,this.$request.xxx()
  // 使用方式2:在nuxt的asyncData中,content.app.$request.xxx()
  inject('request', request)
}
  • 步骤三:配置apiclient.js插件,修改nuxt.conf.js配置文件完成操作
    电商项目:畅购商城4.0_第10张图片
  plugins: [
    { src: '~plugins/apiclient.js'}
  ],
  • 步骤四:修改nuxt.conf.js配置文件,配置axios通用设置
    电商项目:畅购商城4.0_第11张图片
  axios: {
    baseURL: 'http://localhost:10010'
  },
  • 步骤五:测试 apiclient.js是否配置成功,访问test时,出现404
  async mounted() {
    let { data } = await this.$request.test()
    console.info(data)
  },

3.3.3 拷贝静态资源

  • 将所有静态资源拷贝到static目录中
    电商项目:畅购商城4.0_第12张图片
  • 通过浏览器访问静态页面
    电商项目:畅购商城4.0_第13张图片

3.3.4 修改Nuxt项目默认项

  • 1 修改默认布局,删除已有样式

电商项目:畅购商城4.0_第14张图片

  • 2 删除pages目录下的所有内容
    电商项目:畅购商城4.0_第15张图片

3.3.5 配置公共js和css

  • 修改默认布局,添加公共js和css
    电商项目:畅购商城4.0_第16张图片






4. 用户模块(8081)

4.1 搭建环境

4.1.1 后端web服务:changgou4-service-web

  • 修改pom.xml文档


    
        changgou4-parent-ali
        com.czxy.changgou
        1.0-SNAPSHOT
    
    4.0.0

    changgou4_service_web

    
        
        
            com.czxy.changgou
            changgou4_common_db
        
        
            com.czxy.changgou
            changgou4_pojo
        
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            com.alibaba.nacos
            nacos-client
        

        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            redis.clients
            jedis
        
        
        
            io.springfox
            springfox-swagger2
        
        
            io.springfox
            springfox-swagger-ui
        
    


  • 创建application.yml文档
    电商项目:畅购商城4.0_第17张图片
#端口号
server:
  port: 8081

spring:
  application:
    name: web-service          #服务名
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/changgou_db?useUnicode=true&characterEncoding=utf8
    username: root
    password: 1234
    druid:    #druid 连接池配置
      initial-size: 1       #初始化连接池大小
      min-idle: 1           #最小连接数
      max-active: 20        #最大连接数
      test-on-borrow: true  #获取连接时候验证,会影响性能
  redis:
    database:   0
    host: 127.0.0.1
    port: 6379
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848   #nacos服务地址
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
  • 创建启动类
    电商项目:畅购商城4.0_第18张图片
package com.czxy.changgou4;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

4.1.2 后端创建JavaBean:User

  • 在changgou4-pojo项目中添加User对象
    电商项目:畅购商城4.0_第19张图片
package com.czxy.changgou4.pojo;

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.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.beans.Transient;
import java.util.Date;

/**  与数据库对应JavaBean
 * Created by liangtong.
 */
@TableName("tb_user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    /*
        CREATE TABLE `tb_user` (
           `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
           `created_at` timestamp NULL DEFAULT NULL,
           `updated_at` timestamp NULL DEFAULT NULL,
           `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Email',
           `mobile` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '手机号码',
           `username` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '昵称',
           `password` char(60) COLLATE utf8_unicode_ci NOT NULL COMMENT '密码',
           `face` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '头像',
           `expriece` int(10) unsigned DEFAULT '0' COMMENT '经验值',
           PRIMARY KEY (`id`),
           UNIQUE KEY `users_mobile_unique` (`mobile`),
           UNIQUE KEY `users_name_unique` (`name`),
           UNIQUE KEY `users_email_unique` (`email`)
         ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
     */
    @TableId(value="id",type = IdType.AUTO)
    private Long id;

    @TableField(value="username")
    private String username;

    @TableField(value="password")
    private String password;

    @TableField(value="face")
    private String face;

    @TableField(value="expriece")
    private Integer expriece;

    @TableField(value="email")
    private String email;

    @TableField(value="mobile")
    private String mobile;

    @TableField(value="created_at")
    private Date createdAt;

    @TableField(value="updated_at")
    private Date updatedAt;

    @TableField(exist = false)
    private String code;
    @TableField(exist = false)
    private String password_confirm;

}

4.1.3 前端页面:创建公共组件

  • 1 删除components目录下所有内容,并创建3个新组件

电商项目:畅购商城4.0_第20张图片

  • 2)创建 TopNav.vue组件,用于配置“顶部导航”





  • 3 创建 HeaderLogo.vue组件,用于配置“页面头部,仅有LOGO”
    电商项目:畅购商城4.0_第21张图片





  • 4)创建 Footer.vue组件,用于配置“底部版权”
    电商项目:畅购商城4.0_第22张图片





4.2 用户注册:用户名占用

电商项目:畅购商城4.0_第23张图片

4.2.1 接口

http://localhost:10010/web-service/user/checkusername

4.2.2 后端

  • 创建三层需要的接口或类
    电商项目:畅购商城4.0_第24张图片
  • 步骤一:创建UserMapper,编写findByUsername()完成“通过用户名查询用户”
package com.czxy.changgou4.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.changgou4.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

 
@org.apache.ibatis.annotations.Mapper
public interface UserMapper extends BaseMapper<User> {
    /**
     * 通过用户名查询
     * @param username
     * @return
     */
    @Select("select * from tb_user where username = #{username}")
    User findByUsername(@Param("username") String username);
}
  • 步骤二:创建UserService接口,查询功能
package com.czxy.changgou4.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.czxy.changgou4.pojo.User;

public interface UserService extends IService<User> {
    /**
     * 通过用户名查询
     * @param username
     * @return
     */
    public User findByUsername(String username);
}
  • 步骤三:创建UserServiceImpl实现类,查询功能
package com.czxy.changgou4.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.changgou4.mapper.UserMapper;
import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author zzllforever
 *  
 */
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Override
    public User findByUsername(String username) {
        return baseMapper.findByUsername(username);
    }
}
  • 步骤四:创建UserController,完成用户名检查
package com.czxy.changgou4.controller;

import com.czxy.changgou4.pojo.User;
import com.czxy.changgou4.service.UserService;
import com.czxy.changgou4.vo.BaseResult;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;


/**
 * Created by  .
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @PostMapping("/checkusername")
    public BaseResult checkUsername(@RequestBody User user){
        //查询用户
        User findUser = userService.findByUsername( user.getUsername() );
        //判断
        if(findUser != null){
            return BaseResult.error("用户名已经存在");
        } else {
            return BaseResult.ok("用户名可用");
        }
    }


}

4.2.3 前端

  • 步骤一:创建Register.vue
    在这里插入图片描述
  • 步骤二:添加公共组件
    电商项目:畅购商城4.0_第25张图片
<template>
  <div>
    <TopNav></TopNav>
    <div style="clear:both;"></div>
    <HeaderLogo></HeaderLogo>
    <div style="clear:both;"></div>
    <!-- 正文 -->
    <div style="clear:both;"></div>
    <Footer></Footer>
  </div>
</template>

<script>
import TopNav from '../components/TopNav'
import HeaderLogo from '../components/HeaderLogo'
import Footer from '../components/Footer'
export default {
  head: {
    title: '用户注册'
  },
  components : {
    TopNav,
    HeaderLogo,
    Footer
  }
}
</script>

<style>

</style>

  • 步骤三:编写注册表单,并导入独有样式





  • 步骤四:修改api.js,编写检查用户名的ajax函数
const request = {
  test : ()=> {
    return axios.get('/test')
  },
  //检查用户名
  checkUsername : ( username )=> {
    return axios.post('/web-service/user/checkusername', { username })
  }
}

  • 步骤五:修改Register.vue页面,完成检查功能
  • 发送ajax进行用户名是否可用检查
  • 如果可用,显示对应信息,并使用success样式显示
  • 如果不可用,显示对应信息,并使用error样式提示





4.3 用户注册:手机号检查

4.3.1 接口

http://localhost:10010/web-service/user/checkmobile
4.3.2 后端

  • 步骤一:修改UserService,添加 findByMobile() 方法,进行电话号码的查询
/**
 * 通过手机号查询
 * @param mobile
 * @return
 */
User findByMobile(String mobile);

	步骤二:编写UserServiceImpl,实现findByMobile() 方法
@Override
public User findByMobile(String mobile) {
    // 拼凑条件
    QueryWrapper queryWrapper = new QueryWrapper();
    queryWrapper.eq("mobile", mobile);
    // 查询一个
    List<User> list = baseMapper.selectList(queryWrapper);
    if(list.size() == 1) {
        return list.get(0);
    }
    return null;
}

	步骤三:修改UserController,添加checkMobile() 方法
/**
 * 通过手机号查询
 * @param user
 * @return
 */
@PostMapping("/checkmobile")
public BaseResult checkMobile(@RequestBody User user){
    //查询用户
    User findUser = userService.findByMobile( user.getMobile() );
    //判断
    if(findUser != null){
        return BaseResult.error("电话号码已经注册");
    } else {
        return BaseResult.ok("电话号码可用");
    }
}

4.3.3 前端

  • 步骤一:修改api.js,添加 checkMobile() 函数

const request = {
  test : ()=> {
    return axios.get('/test')
  },
  //检查用户名
  checkUsername : ( username )=> {
    return axios.post('/web-service/user/checkusername', { username })
  },
  //检查电话号码
  checkMobile : ( mobile )=> {
    return axios.post('/web-service/user/checkmobile', { mobile })
  }
}
  • 步骤二:修改Register.vue,添加 checkMobileFn() 进行手机号检查
  methods: {
    async checkUsernameFn() {
      //检查用户名
      let {data} = await this.$request.checkUsername( this.user.username )
      this.userMsg.usernameData = data
    },
    async checkMobileFn() {
      //检查电话
      let {data} = await this.$request.checkMobile( this.user.mobile )
      this.userMsg.mobileData = data
    }
  },
  • 步骤三:编写需要的2个变量
  data() {
    return {
      user : {  //表单封装数据
        username : "",
        mobile : ""
      },
      userMsg : { //错误提示数据
        usernameData : "",
        mobileData : ""
      }
    }
  },
  • 步骤四:处理页面
  • 请输入手机号码

    {{userMsg.mobileData.message}}

  • ~~~ * 完整版 ~~~vue

    4.4 用户注册:前置技术–Redis

    4.5 用户注册:前置技术–阿里大鱼

    以往写过短信注册:

    https://editor.csdn.net/md/?articleId=121686836

    详情就不过赘述

    4.6 用户注册:短信验证码

    4.6.1 分析

    电商项目:畅购商城4.0_第26张图片

    4.6.2 接口

    http://localhost:10010/web-service/sms
    {
    	"mobile":"13344445555",
    	"username": "jack"
    }
    

    4.6.3 后端

    • 创建 SmsController类,调用阿里大鱼工具类,发送短信。
      电商项目:畅购商城4.0_第27张图片
    package com.czxy.changgou4.controller;
    
    import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
    import com.czxy.changgou4.pojo.User;
    import com.czxy.changgou4.utils.SmsUtil;
    import com.czxy.changgou4.vo.BaseResult;
    import org.apache.commons.lang.RandomStringUtils;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by   .
     */
    @RestController
    @RequestMapping("/sms")
    public class SmsController {
    
        @Resource
        private StringRedisTemplate redisTemplate;
    
        @PostMapping
        public BaseResult sendSms(@RequestBody User user){
            long start = System.currentTimeMillis();
            try {
                //发送短信
                //1 生产验证码
                String code = RandomStringUtils.randomNumeric(4);
                System.out.println("验证码:" + code);
    
                //2 并存放到reids中 , key: "sms_register" + 手机号 , value:验证码 , 1小时
                redisTemplate.opsForValue().set( "sms_register" + user.getMobile() , code , 1 , TimeUnit.HOURS);
    
                /**/
                //3 发送短信
                SendSmsResponse smsResponse = SmsUtil.sendSms(user.getMobile(), user.getUsername() , code, "", "");
    
                //https://help.aliyun.com/document_detail/55284.html?spm=5176.doc55322.6.557.KvvIJx
                if("OK".equalsIgnoreCase(smsResponse.getCode())){
                    return BaseResult.ok("发送成功");
                } else {
                    return BaseResult.error(smsResponse.getMessage());
                }
    
                /*
                //模拟数据
                System.out.println("验证码:" + code);
                return BaseResult.ok("发送成功");
                */
            } catch (Exception e) {
                long end = System.currentTimeMillis();
                System.out.println( end - start);
                return BaseResult.error("发送失败" );
            }
    
    
        }
    }
    

    4.6.4 前端

    • 步骤一:修改apiclient.js,发送短信ajax操作
      //发短信
      sendSms : ( user )=> {
        return axios.post('/web-service/sms', user )
      }
    
    • 步骤二:修改Register.vue页面,给“发送验证码”绑定点击事件
    sendSmsFn
    <button  @click.prevent="sendSmsFn" >
                    发送验证码<span>5</span>
                  </button>
    

    电商项目:畅购商城4.0_第28张图片

    • 步骤三:修改Register.vue页面,编写sendSmsFn函数,建议采用 ajax…then()…catch 可以处理异常
    sendSmsFn () {
          this.$request.sendSms( this.user )
          .then(( response )=>{
            //发送短信的提示信息
            this.userMsg.smsData = response.data
          })
          .catch(( error )=>{
            //错误提示信息
            alert( error.message )
          })
        }
    
    • 步骤四:修改Register.vue页面,提供变量smsData
    userMsg : { //错误提示数据
            usernameData : "",
            mobileData : "",
            smsData : ""
          }
    

    电商项目:畅购商城4.0_第29张图片

    • 步骤五:修改Register.vue页面,显示 smsData提示信息
      电商项目:畅购商城4.0_第30张图片

    4.6.5 倒计时

    • 步骤一:提供3个变量,用于控制倒计时
      电商项目:畅购商城4.0_第31张图片

    btnDisabled : false, //倒计时控制变量
    seconds : 5, //默认倒计时秒数
    timer : null, //接收定时器,清除定时器

    • 步骤二:在标签上面控制倒计时的显示

    • 步骤三:发送短信后,开启倒计时控制
    sendSmsFn () {
          this.$request.sendSms( this.user )
          .then(( response )=>{
            //发送短信的提示信息
            this.userMsg.smsData = response.data
            //按钮不可用
            this.btnDisabled = true;
              //倒计时
              this.timer = setInterval( ()=>{
                if(this.seconds <= 1){
                  //结束
                  // 重置秒数
                  this.seconds = 5;
                  // 按钮可用
                  this.btnDisabled = false;
                  // 停止定时器
                  clearInterval(this.timer);
                } else {
                  this.seconds --;
                }
              } , 1000);
          })
          .catch(( error )=>{
            //错误提示信息
            alert( error.message )
          })
        }
    

    4.7 用户注册

    4.7.1 接口

    http://localhost:10010/web-service/user/register
    {
    “mobile”:“13612345677”,
    “password”:“1234”,
    “username”:“jack3”,
    “code”:“3919”
    }

    4.7.2 后端

    • 保存前需要再次进行服务端校验
    • 用户名是否注册
    • 手机号是否注册
    • 验证码是否失效
    • 验证码是否错误
    • 密码需要使用 BCrypt进行加密

    *步骤一:修改UserService接口,添加register方法

    /**
     * 用户注册
     * @param user
     * @return
     */
    public boolean register(User user) ;
    

    步骤二:完善UserServiceImpl实现类

    @Override
    public boolean register(User user) {
        //密码加密
        String newPassword = BCrypt.hashpw(user.getPassword());
        user.setPassword(newPassword);
    
        //处理数据
        user.setCreatedAt(new Date());
        user.setUpdatedAt(user.getCreatedAt());
    
        int insert = baseMapper.insert(user);
    
        return insert == 1;
    }
    
    • 步骤三:修改UserController,添加register方法
    /**
     * 用户注册
     * @param user
     * @return
     */
    @PostMapping("/register")
    public BaseResult  register(@RequestBody User user){
    
        //服务端校验
        User findUser = userService.findByUsername(user.getUsername());
        if(findUser != null) {
            return BaseResult.error("用户名已经存在");
        }
    
        findUser = userService.findByMobile(user.getMobile());
        if(findUser != null) {
            return BaseResult.error("电话号码已经存在");
        }
    
        //验证码
        String code = stringRedisTemplate.opsForValue().get("sms_register" + user.getMobile());
        //删除redis中的验证码
        stringRedisTemplate.delete("sms_register" + user.getMobile());
        if(code == null) {
            return BaseResult.error("验证码失效");
        }
        if(!code.equals(user.getCode())) {
            return BaseResult.error("验证码不正确");
        }
    
        //注册
        boolean register = userService.register(user);
    
        if(register) {
            return BaseResult.ok("注册成功");
        }
        return BaseResult.error("注册失败");
    }
    

    4.7.3 日期处理(可选)

    • 编写 DateMetaObjectHandler 用于处理“创建时间”和“修改日期”
    package com.czxy.changgou4.handler;
    
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    /**
     * @author zzllforever
     * @email 2752776684.com
     */
    @Component
    public class DateMetaObjectHandler implements MetaObjectHandler {
        @Override
        public void insertFill(MetaObject metaObject) {
            this.setFieldValByName("createdAt", new Date(), metaObject);
            this.setFieldValByName("updatedAt", new Date(), metaObject);
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            this.setFieldValByName("updatedAt", new Date(), metaObject);
        }
    }
    
    • 完善User JavaBean,设置填充方式
    @TableField(value="created_at",fill = FieldFill.INSERT)
    private Date createdAt;
    
    @TableField(value="updated_at",fill = FieldFill.INSERT_UPDATE)
    private Date updatedAt;
    

    4.7.4 前端

    • 步骤一:修改 api.js ,添加注册函数
      //注册
      register : ( user )=> {
        return axios.post('/web-service/user/register', user )
      }
    
    • 步骤二:处理表单,验证码input绑定数据,提交按钮绑定事件

            
    • {{userMsg.smsData.message}}

    • 我已阅读并同意《用户注册协议》
    • 步骤三:完善data区域的user数据

    电商项目:畅购商城4.0_第32张图片

    user : { //表单封装数据
    username : “”, //用户名
    mobile : “13699282444”, //手机号
    password : “”, //密码
    code : “” //验证码
    },

    • 步骤四:编写registerFn函数

    async registerFn() {
    let { data } = await this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …功 this.router.push(’/login’)
    } else {
    //失败–与发送验证码使用一个位置显示错误信息
    this.userMsg.smsData = data
    }
    }

    4.8 用户登录

    4.8.1 构建页面:Login.vue

    • 步骤一:创建Login.vue
      电商项目:畅购商城4.0_第33张图片
    • 步骤二:绘制通用模块
    <template>
      <div>
        <TopNav>TopNav>
        <div style="clear:both;">div>
        <HeaderLogo>HeaderLogo>
        <div style="clear:both;">div>
        
        
        <div style="clear:both;">div>
        <Footer>Footer>
      div>
    template>
    
    <script>
    import TopNav from '../components/TopNav'
    import HeaderLogo from '../components/HeaderLogo'
    import Footer from '../components/Footer'
    export default {
      head: {
        title: '用户登录',
        link: [
          {rel:'stylesheet',href:'style/login.css'}
        ],
        script: [
        ]
      },
      components : {
        TopNav,
        HeaderLogo,
        Footer
      },
    
    }
    script>
    
    <style>
    
    style>
    
    • 步骤三:绘制登录表单
    <template>
      <div>
        <TopNav>TopNav>
        <div style="clear:both;">div>
        <HeaderLogo>HeaderLogo>
        <div style="clear:both;">div>
        
        
        <div class="login w990 bc mt10">
          <div class="login_hd">
            <h2>用户登录h2>
            <b>b>
          div>
          <div class="login_bd">
            <div class="login_form fl">
              <form action="" method="post">
                <ul>
                  <li>
                    <label for="">用户名:label>
                    <input type="text" class="txt" name="username" />
                  li>
                  <li>
                    <label for="">密码:label>
                    <input type="password" class="txt" name="password" />
                    <a href="">忘记密码?a>
                  li>
                  <li class="checkcode">
                    <label for="">验证码:label>
                    <input type="text"  name="checkcode" />
                    <img src="images/checkcode1.jpg" alt="" />
                    <span>看不清?<a href="">换一张a>span>
                  li>
                  <li>
                    <label for=""> label>
                    <input type="checkbox" class="chb"  /> 保存登录信息
                  li>
                  <li>
                    <label for=""> label>
                    <input type="submit" value="" class="login_btn" />
                  li>
                ul>
              form>
    
              <div class="coagent mt15">
                <dl>
                  <dt>使用合作网站登录商城:dt>
                  <dd class="qq"><a href=""><span>span>QQa>dd>
                  <dd class="weibo"><a href=""><span>span>新浪微博a>dd>
                  <dd class="yi"><a href=""><span>span>网易a>dd>
                  <dd class="renren"><a href=""><span>span>人人a>dd>
                  <dd class="qihu"><a href=""><span>span>奇虎360a>dd>
                  <dd class=""><a href=""><span>span>百度a>dd>
                  <dd class="douban"><a href=""><span>span>豆瓣a>dd>
                dl>
              div>
            div>
    
            <div class="guide fl">
              <h3>还不是商城用户h3>
              <p>现在免费注册成为商城用户,便能立刻享受便宜又放心的购物乐趣,心动不如行动,赶紧加入吧!p>
    
              <a href="regist.html" class="reg_btn">免费注册 >>a>
            div>
    
          div>
        div>
        
        <div style="clear:both;">div>
        <Footer>Footer>
      div>
    template>
    
    <script>
    import TopNav from '../components/TopNav'
    import HeaderLogo from '../components/HeaderLogo'
    import Footer from '../components/Footer'
    export default {
      head: {
        title: '用户登录',
        link: [
          {rel:'stylesheet',href:'style/login.css'}
        ],
        script: [
        ]
      },
      components : {
        TopNav,
        HeaderLogo,
        Footer
      },
    
    }
    script>
    
    <style>
    
    style>
    

    4.8.2 分析

    电商项目:畅购商城4.0_第34张图片

    4.8.3 验证码:接口

    http://localhost:10010/web-service/verifycode?username=jack

    4.8.4 验证码:生成与显示

    • 步骤一:后端生产验证码,并将用户保存Redis
    • 存放redis中验证码key格式:“login” + 用户名
    package com.czxy.changgou4.controller;
    
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.annotation.Resource;
    import javax.imageio.ImageIO;
    import javax.servlet.http.HttpServletResponse;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.util.Random;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author zzllforever
     * @email 2752776684.com
     */
    @Controller
    @RequestMapping("/verifycode")
    public class VerifyCodeController {
    
        @Resource
        private StringRedisTemplate stringRedisTemplate;
    
        @GetMapping
        public void verifyCode(String username , HttpServletResponse response ) throws IOException {
    
            //字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
            String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
    
            int IMG_WIDTH = 72;
            int IMG_HEIGTH = 27;
            Random random = new Random();
            //创建图片
            BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGTH, BufferedImage.TYPE_INT_RGB);
            //画板
            Graphics g = image.getGraphics();
            //填充背景
            g.setColor(Color.WHITE);
            g.fillRect(1,1,IMG_WIDTH-2,IMG_HEIGTH-2);
    
            g.setFont(new Font("楷体", Font.BOLD,25));
    
            StringBuilder sb = new StringBuilder();
            //写字
            for(int i = 1 ; i <= 4 ; i ++){
                //随机颜色
                g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
                int len = random.nextInt(VERIFY_CODES.length());
                String str = VERIFY_CODES.substring(len,len+1);
                sb.append(str);
                g.drawString(str, IMG_WIDTH / 6 * i , 22 );
            }
    
            //将验证码存放到redis
            stringRedisTemplate.opsForValue().set( "login" + username , sb.toString() , 1 , TimeUnit.HOURS);
    
    
            // 生成随机干扰线
            for (int i = 0; i < 30; i++) {
                //随机颜色
                g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
                int x = random.nextInt(IMG_WIDTH - 1);
                int y = random.nextInt(IMG_HEIGTH - 1);
                int x1 = random.nextInt(12) + 1;
                int y1 = random.nextInt(6) + 1;
                g.drawLine(x, y, x - x1, y - y1);
            }
    
            //响应到浏览器
            ImageIO.write(image,"jpeg", response.getOutputStream());
    
        }
    }
    
    • 步骤二:点击“换一张”显示验证码
    • 默认不显示验证码
    • 点击“换一张”获得验证码

    电商项目:畅购商城4.0_第35张图片

    
    
    
    
    
    

    4.8.5 通过用户名查询:接口

    http://localhost:10010/web-service/user/findByUsername {
    “username”:“jack” }

    你可能感兴趣的:(电商项目畅购,vue.js,spring,boot,前端框架,mybatis)