京淘项目流程

1 构建后台项目

1.1 创建项目

设置字体缩放

在这里插入图片描述

自动提示设置

在这里插入图片描述

设置参数自动提示

在这里插入图片描述

设定字符集,utf-8

在这里插入图片描述

设置自动编译

在这里插入图片描述

设置Maven仓库

在这里插入图片描述

1.2 编辑pom.xml文件

添加依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/>
    </parent>

    <properties>
        <!--指定JDK版本-->
        <java.version>1.8</java.version>
        <!--跳过测试类打包-->
        <skipTests>true</skipTests>
    </properties>

    <!--按需导入
        历史说明: 2010 原来SSM 需要手动的编辑大量的的配置文件
        思想: SpringBoot使用体现了"开箱即用"的思想
    -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <!--Springboot的启动器 在内部已经将整合的配置写好,实现拿来就用-->
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--支持热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <!--引入插件lombok 自动的set/get/构造方法插件  -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--引入数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--springBoot数据库连接  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!--spring整合mybatis-plus 删除mybatis的包 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>

    </dependencies>

    <!--build标签
        springboot项目在打包部署发布时,需要依赖maven工具API
        如果不添加该插件,则直接影响项目发布
    -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.3</version>
            </plugin>
        </plugins>
    </build>

1.3 导入src文件

1.4 修改YML文件

server:
  port: 8091
  servlet:
    context-path: /

#配置数据源
spring:
  datasource:
    #如果使用高版本驱动 则添加cj
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: root


#mybatis-plush配置
mybatis-plus:
  type-aliases-package: com.jt.pojo
  mapper-locations: classpath:/mappers/*.xml
  configuration:
    map-underscore-to-camel-case: true

logging:
  level:
    com.jt.mapper: debug

1.5 编辑启动项

在这里插入图片描述

2 前端项目搭建

2.2 操作步骤

2.2.2 项目解压之后复制到IDEA维护的目录中

2.2.3 导入项目

导入vue ui中

2.2.4 运行前端项目

vue ui启动
命令启动npm run(项目目录)

3 代码调试

前端代码调试

4 用户登录业务实现

4.1 项目划分

前端项目网址: http://localhost:8080/
后端项目网址: http://localhost:8091/
用户操作项目请求的流程: 用户----前端服务器------后端服务器

4.2 用户模块分析

4.2.1 表设计分析

注意事项:
1.密码 加密处理
2.created/updated 每张表中都有该字段(数据创建、更新时间)

4.2.2 POJO分析

注意:Linux严格区分大小写
使用@TableName、@TableId、@TableField匹配数据库
其中@TableField(exist=false)//表示该属性不在数据库中,可以定义children等

4.3 用户登录业务实现

4.3.2 接口文档说明

请求路径
请求方式
请求参数
响应数据

其中响应数据封装SysResult类,序列化
status(200、201)
msg(服务器返回的提示信息)
data(服务器返回的业务数据)
失败、成功方法(返回本类对象,需要重载)

4.3.3 编辑SysResult对象

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class SysResult implements Serializable {
    private Integer status; //200业务成功 201业务失败
    private String msg;     //服务器提示信息
    private Object data;    //封装后台返回值

    public static SysResult fail(){
        return new SysResult(201,"业务执行失败",null);
    }
	 public static SysResult success(){
        return new SysResult(200,"业务执行成功!", null);
    }

    //服务器返回值业务数据
    public static SysResult success(Object data){
        return new SysResult(200,"业务执行成功!", data);
    }

    public static SysResult success(String msg,Object data){

        return new SysResult(200,msg,data);
    }
}

4.3.4 页面JS分析

  1. 使用路由机制
  2. axios 全局定义
  3. axios的使用

4.3.5 MD5介绍

密码散列函数,128位(16字节)

4.3.6 编辑UserController

注入UserService

4.3.7 编辑UserService

编写login方法。
MD5加密

//1.获取明文
        String password = user.getPassword();
        //2.加密处理
        String md5 = DigestUtils.md5DigestAsHex(password.getBytes());
        user.setPassword(md5);

5 用户登录操作

跨域在这里插入图片描述

5.1 关于token的说明

5.2 生成UUID

 //6.使用UUID动态生成TOKEN,根据当前时间毫秒数+随机数利用hash算法生成
        //  几乎可以保证不重复.
        String token = UUID.randomUUID().toString()
                .replace("-","");

5.3 Session和Cookie

5.3.1 Session介绍

5.3.2 Cookie机制

5.3.3 session和cookie区别

5.4 前端保存用户信息

5.5 实现系统首页跳转

6 权限校验-路由导航守卫

6.1 业务需求

前端页面跳转是通过路由进行控制. 规定: 如果用户没有登录,则只允许访问登录页面.只有登录之后才能访问其它页面.
难点: 如何实现用户请求的拦截.
拦截器作用: 拦截用户的请求.
结果1: 请求放行
结果2: 请求拦截,一般配合重定向使用!!

6.2 路由导航守卫实现

6.2.1 参数说明

6.2.2 配置前端路由导航守卫

const router = new VueRouter({
  routes
})

/**
 * 定义路由导航守卫
 * 参数1. to    路由跳转的网址
 * 参数2. from  路由从哪里来
 * 参数3. next  是一个函数,表示放行或重定向
 *              next() 放行
 *              next("/login") 重定向
 * 业务实现:
 *    核心逻辑: 检查是否有token.
 *        如果访问login页面 直接放行.
 *        有token 表示已经登录,放行请求
 *        没有token 表示用户没有登录,重定向到登录页面
 */
router.beforeEach((to,from,next) => {
   if(to.path === "/login"){
     return next()
   }
    //说明用户访问的页面不是login 请求需要校验
    //获取token数据.
    let token = window.sessionStorage.getItem("token")
    //if(token !==null && token.length>0)
    //下列if 解释为: 如果token不为null
    if(token){
      return next()
    }

    next("/login")
})

7 左侧菜单列表实现

7.1 左侧菜单页面分析

7.2 编辑权限层级代码

7.2.1表设计分析

说明:
name: 权限名称
parent_id: 父级权限 实现了父子级关系.
path: 一级菜单没有路径的, 二级/三级才有路径.

7.2.2 编辑POJO

说明:
1.当前对象中children属性不属于字段,所以使用@TableField(exist = false)进行标识.
2.权限列表中有父子级关系,所以通过children封装子级

7.2.3 搭建Rights层级代码

RightsController->RightsService->RightsServiceImpl->RightsMapper extends BaseMapper< Rights>
RightsController->RightsService->RightsServiceImpl->@Mapper RightsMapper +application.yml(指定xml位置)+Mapper.xml(sql语句)

7.2.4 业务接口说明

7.2.5 编辑RightsController

7.2.6 编辑RightsService

二级分类查询

7.2.7 页面展现效果

7.3 debug说明

8 用户模块实现

8.1 实现用户页面跳转

8.1.1 Home组件说明

业务说明: URL:/user 要求跳转组件 User.vue组件,组件的跳转应该在Home组件内部跳转.
Home组件说明: 在main的区域中,设定 作用 Home的子级标签将来组件在该区域展现.

8.1.2 定义子级组件

说明: 通过路由实现父子组件嵌套.

8.1.3 页面效果展现

9 ElementUI学习

10 ElementUI说明

10.1 表格数据

10.1.1 编辑UI表格

10.1.2 表格数据展现

10.2 分页插件说明

10.2.1 分页页面JS

11 用户业务实现

11.1 用户列表实现

11.1.1 页面JS分析

11.1.2 业务接口文档

在这里插入图片描述

11.1.3 编辑PageResult对象

@Data
@Accessors(chain = true)
public class PageResult implements Serializable {
    private String query;
    private Integer pageNum;
    private Integer pageSize;
    private Long total;
    private Object rows;
}

11.1.4 编辑UserController

传入3个参数,返回5个参数

11.1.5 编辑UserServiceImpl

分页查询

/**
     * 分页Sql:
     *   语法: select * from user limit 起始位置,每页条数
     *   规则: 数组 含头不含尾
     *   查询第一页:
     *         select * from user limit 0,10
     *   查询第二页:
     *         select * from user limit 10,10
     *   查询第三页:
     *         select * from user limit 20,10
     *   查询第N页:
     *         select * from user limit (页数-1)条数,条数
     * @param pageResult
     *   方式1:  手写Sql
     *   方式2:  MP的方式实现
     * @return
     */
    @Override
    public PageResult getUserList(PageResult pageResult) {
        //1.获取总记录数  Integer--long 自动转化
        long total = userMapper.selectCount(null);
        //2.获取分页结果
        int size = pageResult.getPageSize();
        int start = (pageResult.getPageNum() - 1) * size;
        List<User> userList = userMapper.findListByPage(start,size);
        pageResult.setTotal(total)
                  .setRows(userList);
        return pageResult;
    }

11.1.6 编辑UserMapper

说明: 利用注解实现Sql查询, 映射文件和注解标签二选一 不能同时使用
在这里插入图片描述

11.1.7 页面效果展现

11.2 MP实现用户分页

11.2.1 UserController方法

传入3个参数,返回5个参数

11.2.2 编辑UserService方法

 /*
    * 业务说明: 利用MP方式查询数据库.
    * 步骤梳理:
    *       1.构建MP的分页对象
    *       2.根据分页对象查询数据.
    *       3.从分页对象中获取数据
    *       4.封装PageResult对象
    *       5.编辑配置类 封装分页拦截器
    * */
    @Override
    public PageResult getUserList(PageResult pageResult) {
        //1.定义分页对象
        IPage<User> page = new Page<>(pageResult.getPageNum(),
                                      pageResult.getPageSize());
        //2.定义条件构造器 指定动态查询Sql
        boolean flag = StringUtils.hasLength(pageResult.getQuery());
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(flag, "username",pageResult.getQuery());

        //3.进行分页查询
        page = userMapper.selectPage(page,queryWrapper);
        //4.从封装后的分页对象中获取数据
        pageResult.setTotal(page.getTotal())
                  .setRows(page.getRecords());
        return pageResult;
    }

11.2.3 编辑配置类

@Configuration
public class MybatisPlusConfig {

    // MybatisPlus在执行分页操作时,会被该拦截器拦截
    // 拦截器的作用 动态拼接where条件!!!
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));
        return interceptor;
    }
}

11.3 用户状态修改

11.3.1 业务说明

说明: 通过开关 可以控制数据类型的 true/false
参数说明: 1.传递userId主键
2.传递当前状态信息 true/false

11.3.2 页面JS

作用域插槽
作用: 可以在表格中的任意单元格,获取当前行的元素信息.
关键语法: scope.row 获取行元素.

11.3.3 业务接口文档

11.3.4 编辑UserController

11.3.5 编辑UserService

11.4 用户新增操作

11.4.1 对话框

说明: 通过属性dialogVisible 控制对话框是否可见

11.4.2 页面JS分析

11.4.3 用户新增业务接口

11.4.4 编辑UserController

11.4.5 编辑UserServiceImpl

密码MD5加密

 /**
     * 说明:
     *      1.用户入库操作需要手动补齐的数据有
     *        创建时间/修改时间 保证一致.
     *        status=true 手动填充.
     *      2.密码加密处理  新增和登录加密算法必须一致
     * @param user
     */
    @Override
    public void addUser(User user) {
        String password = user.getPassword();
        //加密处理
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        user.setPassword(password)
            .setStatus(true)
            .setCreated(new Date())
            .setUpdated(user.getCreated());
        userMapper.insert(user);
    }

12 用户业务实现

12.1 用户修改数据回显

12.1.1 业务需求分析

当用户点击修改按钮时应该出现弹出框.其中展现用户的数据信息.
展现方式:

  1. 获取用户ID,动态查询后台数据库信息,之后实现数据回显. 最新数据!!!
  2. 可以利用作用域插槽,获取当前行的对象,之后实现数据回显. 页面数据!!!

12.1.2 页面JS分析

12.1.3 业务接口文档

12.1.4 编辑UserController

12.1.5 编辑UserService

12.1.6 页面效果展现

12.2 用户修改实现

12.2.1 页面JS分析

12.2.2 业务接口文档

12.2.3 编辑UserController

12.2.4 编辑UserService

12.3 用户删除业务实现

12.3.1 页面JS分析

12.3.2 业务接口文档

12.3.3 编辑UserController

12.3.4 编辑UserService

13 全局异常处理机制

13.1 业务需求说明

说明: 如果后台服务器发生运行时异常,应该第一时间通知用户.否则用户没有任何提示.影响用户体验.

分析:
1.页面没有提示的原因是服务器没有返回201的报错数据.
2.后端服务器报错之后,没有异常处理机制

13.2 全局异常处理用法

13.2.1 实现原理AOP

AOP说明:
名称: “面向切面编程”
作用: 在不影响源码的条件下,对方法进行扩展,降低了业务的耦合性.
通知:
1.前置通知: before
2.后置通知: afterReturning
3.异常通知: afterThrowing
4.最终通知: after
上述的四大通知,不能改变程序的运行的状态.
5.环绕通知: around
环绕通知是功能最为强大的通知方法,可以控制程序的流转过程.

13.2.2 全局异常处理实现

/**
 * 注解的作用:
 *   1.该注解只拦截Controller层抛出的异常信息!
 *      controller ---Service-----Mapper 异常向上抛出.
 *   2.需要配合指定异常的类型.
 */
@RestControllerAdvice
public class SystemAOP {

    //当前Controller层,只拦截运行时异常.
    //@ExceptionHandler({RuntimeException.class, SQLException.class})
    @ExceptionHandler({RuntimeException.class})
    public SysResult exception(Exception e){
        //控制台打印异常.
        e.printStackTrace();
        return SysResult.fail();
    }
}

14 Spring中的事务机制

14.1 事务

如果后台服务器发生问题,则数据信息应该回滚,而不是提交操作.

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。

事务作用: 可以保证数据的 /持久性(永久性)/一致性/隔离性/原子性

14.2 控制事务

说明: 在业务层 执行数据库更新操作时,添加注解,控制数据库事务.
在这里插入图片描述

15 实现数据自动填充功能

15.1 业务说明

在完成数据库入库操作时,其中创建时间和修改时间 都是用户必须添加的参数.但是该参数完全由用户填写,显得不智能. 公共的内容应该由框架完成.

15.2 自动填充实现

15.2.1 编辑POJO

说明: 在入库操作时,自动填充 created/updated时间.
在修改操作时,自动填充 updated时间.
在这里插入图片描述

15.2.2 配置自动填充功能

在这里插入图片描述

@Component //将该对象交给Spring容器管理
public class MyMetaObjectHandler implements MetaObjectHandler {

    //完成新增入库操作应该如何填充 created/updated
    //MetaObject代表自动填充的默认配置.
    @Override
    public void insertFill(MetaObject metaObject) {
        Date date = new Date();
        this.setFieldValByName("created",date,metaObject);
        this.setFieldValByName("updated",date,metaObject);
    }

    //完成修改操作应该如何填充 updated
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updated",new Date(),metaObject);
    }
}

16 商品分类实现

16.1 实现商品分类页面跳转

16.1.1 页面分析

需求: 当用户跳转/itemCat时,应该展现ItemCat.vue组件.

16.2 ItemCat设计说明

16.2.1 表设计说明

说明: 商品分类结构的字段信息. 通过parent_id 控制父子级关系.

16.2.2 常见业务说明

说明: 每个层级下,都会有自己的单独的子级. 1级下边有2级 2级下边有3级.
一般层级结构,不要超过3级.

16.2.3 定义POJO属性

在这里插入图片描述

16.3 层级代码结构展现

16.4 商品分类数据查询结构

16.5 商品分类页面分析

16.6 商品分类业务接口文档

16.7 编辑ItemCatController

16.8 编辑ItemCatService

16.9 页面效果展现

17 商品分类业务实现

17.1 问题说明

原始代码结构.出现2层循环结构. 如果外层循环10个,每个内层循环也是10个.完成这项业务需要查询100次数据库.
矛盾点: 多次查询数据库!
优化的策略: 数据库只查询一次,就可以获取商品分类三级嵌套结构

程序设计:
1.数据结构 Map<父级ID,子级列表> 列表信息中不包含嵌套关系
例如: Map<0,一级列表信息> 一级列表不包含二级/三级
Map<一级ID,二级列表信息> 只有2级列表 不包含3级
Map<二级ID,三级列表信息> 只有3级列表信息.
2.根据数据结构动态根据level查询子级.

17.2 列表业务实现

17.2.1 业务封装原理

在这里插入图片描述

17.2.2 封装Map集合

17.2.3 代码优化

 /**
     * 1.数据结构:  Map<K,V>  key=parentId   value="List<ItemCat>"
     * 2.封装Map的数据类型
     * 3.如果level=1 只获取一级.
     * 4.如果level=2 获取一级,一级嵌套二级
     * 5.如果level=3 获取一级,一级嵌套二级,二级嵌套三级.
     * @param level
     * @return
     */
    @Override
    public List<ItemCat> findItemCatList(Integer level) {
        long startTime = System.currentTimeMillis();
        //1.封装Map集合
        Map<Integer,List<ItemCat>> map = getMap();

        //2.判断level的值
        if(level == 1){
            return map.get(0);
        }

        if(level == 2){

            return getTwoList(map);
        }

        //如果level不是1-2级则一定是三级
        List<ItemCat> list = getThreeList(map);
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:"+(endTime - startTime)+"毫秒");
        return list;
    }

    private List<ItemCat> getThreeList(Map<Integer, List<ItemCat>> map) {
        //获取一级和二级
        List<ItemCat> oneList = getTwoList(map);
        //封装三级,遍历二级菜单,之后封装.
        for(ItemCat oneItemCat : oneList){
            //获取二级集合
            List<ItemCat> twoList = oneItemCat.getChildren();
            if(twoList == null || twoList.size() == 0){
                System.out.println("执行跳过循环操作");
                //由于业务数据不合理,跳过本次循环,执行下一次
                continue;
            }
            for (ItemCat twoItemCat : twoList){
                //查询三级列表,需要parentId=二级Id
                int parentId = twoItemCat.getId();
                List<ItemCat> threeList = map.get(parentId);
                twoItemCat.setChildren(threeList);
            }
        }
        return oneList;
    }

    private List<ItemCat> getTwoList(Map<Integer, List<ItemCat>> map) {
        //1.先获取一级列表
        List<ItemCat> oneList = map.get(0);
        //2.根据一级查询二级
        for(ItemCat oneItemCat :oneList){
            //查询二级,所以parentId是一级的Id
            int parentId = oneItemCat.getId();
            List<ItemCat> twoList = map.get(parentId);
            //封装数据
            oneItemCat.setChildren(twoList);
        }
        return oneList;
    }

    /**
     * 1.查询所有的商品分类列表.   查询一次数据库.
     * 2.循环遍历所有的数据,按照parentId,List<ItemCat>方式封装数据.
     * @return
     */
    private Map<Integer, List<ItemCat>> getMap() {
        Map<Integer,List<ItemCat>> map = new HashMap<>();
        List<ItemCat> list = itemCatMapper.selectList(null);
        for(ItemCat itemCat : list){
            //获取parentId
            int parentId = itemCat.getParentId();
            if(map.containsKey(parentId)){
                //key存在
                map.get(parentId).add(itemCat);
            }else{
                //key不存在
                List<ItemCat> childrenList = new ArrayList<>();
                childrenList.add(itemCat);
                //将第一个元素封装到map中
                map.put(parentId,childrenList);
            }
        }
        return map;
    }


17.3 商品分类状态修改

17.3.1 业务分析

17.3.2 页面JS分析

17.3.3 业务接口实现

17.3.4 编辑ItemCatController

17.3.5 编辑ItemCatService

17.4 商品分类新增

17.4.1 业务说明

说明: 商品分类实现中,需要添加一级/二级/三级分类信息. 但是父级下拉框中勾选1-2级菜单. 因为三级菜单不能当作父级. 当用户编辑完成之后,点击确定实现商品分类入库.

17.4.2 页面JS分析

17.4.3 业务接口文档

17.4.4 编辑ItemCatController

17.4.5 编辑ItemCatService

17.5 商品分类修改操作

17.5.1 修改按钮JS分析

17.5.2 修改-确定按钮实现

17.5.3 业务接口文档

17.5.4 编辑ItemCatController

17.5.5 编辑ItemCatService

17.6 商品分类删除操作

17.6.1 业务说明

规则:
1.如果删除的商品分类是三级,则可以直接删除.
2.如果删除的商品分类是二级,则先删除三级,在删除二级.
3.如果删除的商品分类是一级,则先删除三级/二级/一级
注意事务的控制.

17.6.2 页面JS分析

17.6.3 业务接口实现

17.6.4 编辑ItemCatController

17.6.5 编辑ItemCatService

/**
     * 需求: 删除商品分类信息
     * 条件: 如果有子级,应该先删除子级.
     * Sql:
     *  DELETE FROM item_cat WHERE (parent_id IN (?,?) OR parent_id = ? OR id = ?)
     * @param itemCat
     */
    @Override
    @Transactional
    public void deleteItemCats(ItemCat itemCat) {
        int level = itemCat.getLevel();
        if(level == 3){
            //表示需要删除的数据是三级菜单,可以直接删除
            itemCatMapper.deleteById(itemCat.getId());
        }

        if(level == 2){
            QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("parent_id",itemCat.getId())
                        .or()
                        .eq("id",itemCat.getId());
            itemCatMapper.delete(queryWrapper);
        }

        if(level == 1){
            //1.必须获取二级ID
            QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("parent_id",itemCat.getId());
            //2获取结果的第一列字段(主键)  二级Id
            List twoIdsList = itemCatMapper.selectObjs(queryWrapper);

            //3.删除三级的数据
            queryWrapper.clear();
            //删除parent_id中包含二级Id的数据,实则删除的是三级数据
            queryWrapper.in(twoIdsList.size()>0,"parent_id",twoIdsList)
                        .or()
                        //删除parent_id 等于一级ID的,实则删除的是二级数据
                        .eq("parent_id",itemCat.getId())
                        .or()
                        //删除id=一级Id  则删除一级数据.
                        .eq("id",itemCat.getId() );
            itemCatMapper.delete(queryWrapper);
        }
    }

18 商品模块实现

18.1 业务分析

18.1.1 表设计

18.1.2 编辑POJO对象

18.1.3 编辑Item的层级代码

18.1.4 实现前台页面跳转

18.2 商品列表展现

18.2.1 页面JS分析

18.2.2 业务接口说明

18.2.3 编辑ItemCatController

18.2.4 编辑ItemCatService

18.2.5 页面效果展现

18.3 VUE过滤器用法

18.3.1 需求说明

说明: 需要将价格信息缩小100倍. 保留2位小数.

18.3.2 过滤器使用

18.3.3 过滤器定义

说明: 由于过滤器是全局变量,写在main.js中

18.4 添加商品

18.4.1 商品新增页面跳转

当用户点击添加商品时,应该跳转到商品添加页面

18.4.2 商品新增分析

18.4.3 业务接口说明

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

18.4.4 封装ItemVO对象

/**
 * @author 刘昱江
 * 时间 2021/4/16
 */
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ItemVO {  //该对象封装商品所有的参数信息
    private Item item;
    private ItemDesc itemDesc;
    private ItemParam itemParam;
}

18.5 商品状态修改

18.5.1 业务分析

18.5.2 业务接口

18.5.3 编辑ItemController

18.5.4 编辑ItemService

18.6 商品详情说明

18.6.1 业务说明

说明: Item中存储的是商品的基本信息,通常用于检索,或者数据展现. itemDesc一般存储的都是商品的详情信息. 为了用户的检索效率更好,所以一般先查询item数据,(itemDesc一般采用大字段的形式,存储html代码的片段).

要求: 一个商品对应一个详情信息, 所以 item和itemDesc一对一
一个商品对应一个详情
一个详情对应一个商品
隐藏属性: item.id = itemDesc.id

补充知识: 数据库中常见对应关系
一对一: 老婆和老公(通常)
一对多: 老公和情人
多对多: 老师和学生
一个老师对应多个学生
一个学生对应多个老师

18.6.2 表设计

18.6.3 编辑ItemDesc

在这里插入图片描述

18.6.4 重构ItemServiceImpl

//实现商品入库操作
@Override
@Transactional
public void saveItem(ItemVO itemVO) {
//item 主键自增 数据库入库之后,才会有主键!!!
Item item = itemVO.getItem();
item.setStatus(true);
itemMapper.insert(item);
//问题: 入库之后才有ID,现阶段item.id=null
//mybatis实现业务功能,自动回显主键!!!
//MP自动的将主键的回显功能实现!!!

    //规则: itemId与ItemDescId是一样的.
    ItemDesc itemDesc = itemVO.getItemDesc();
    itemDesc.setId(item.getId());
    itemDescMapper.insert(itemDesc);
}

18.7 富文本编辑器

19 关于数据库主外键说明

说明:

  1. 数据库提供了主外键的约束关系, 操作数据时必须严格的遵守.但是如果添加了主键/外键,则必然会影响执行的效率. 所以一般主键和外键的约束由程序员通过代码进行控制.
  2. 一般项目中 很少直接定义主键/外键.

20 ElementUI中文件上传

20.1 UI入门案例

21 文件上传入门案例

21.1 UI页面JS分析

21.2 业务接口说明

在这里插入图片描述

21.3 编辑ImageVO

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO implements Serializable {

    private String virtualPath; //虚拟地址,不包含磁盘地址
    private String urlPath;     //URL地址信息.
    private String fileName;    //文件名称
}

21.4 入门案例实现

22 图片上传业务实现

22.1 文件上传的注入事项

控制文件上传的类型(后台为主)
控制恶意程序的上传 通过宽度和高度
为了提高检索的速度,应该分目录存储.
动态生成文件名称 UUID,防止文件重名.
实现文件上传 注意路径.

22.2 正则表达式

22.2.1 正则说明

22.2.2 语法介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

22.3 编辑FileController

@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {

    @Autowired
    private FileService fileService;

    @PostMapping("/upload")
    public SysResult upload(MultipartFile file) throws IOException {

        ImageVO imageVO = fileService.upload(file);
        if(imageVO == null){//说明业务执行有误
            return SysResult.fail();
        }
        return SysResult.success(imageVO);
    }


    /**
     * 业务: 实现文件上传
     * url: /file/upload
     * 请求类型: POST
     * 参数: file
     * 返回值: SysResult(imageVO)
     * 高级API:MultipartFile 自动维护了缓存流/自动开关
     *
     * 文件上传步骤:
     *      1.获取文件名称.
     *      2.准备上传文件的目录
     *      3.封装文件全路径  目录/文件名称
     *      4.实现文件上传
     */
    /*@PostMapping("/upload")
    public SysResult upload(MultipartFile file) throws IOException {
        //1.获取文件名称  a.jpg
        String fileName = file.getOriginalFilename();
        //2.准备文件目录
        String fileDir = "G:/images/";
        //2.1 判断目录是否存在
        File dir = new File(fileDir);
        if(!dir.exists()){
            //如果目录不存在,则创建多级目录
            dir.mkdirs();
        }
        //3.准备文件全路径
        String localPath = fileDir + fileName;
        //4.实现文件输出
        file.transferTo(new File(localPath));
        System.out.println("文件上传成功!!!!");
        return SysResult.success();
    }*/


}

22.4 编辑FileService

package com.jt.service;

import com.jt.vo.ImageVO;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;

@Service
public class FileServiceImpl implements FileService{

    private String localDir = "G:/images";

    /**
     * 完成校验:
     *  1.校验是否为图片
     *  2.木马.exe.jpg 判断是否满足图片固有属性 高度/宽度
     *  3.为了提高查询效率,要求分目录存储.
     *      3.1 按照后缀名分配  jpg,png,gif 效率提升不能满足要求
     *      3.2 按照日期分   yyyy/MM/dd/HH   可以
     *      3.3 商品分类     出现分布不均现象.
     *      3.4 根据名称hash 之后截串
     *          demo: hash(a)=qw|er|as|dg/a.jpg
     *          弊端: hash码可能出现分布不均的现象.
     *  4.防止文件重名  使用uuid代替名称
     * @param file
     * @return
     */
    @Override
    public ImageVO upload(MultipartFile file) {
        //1.获取图片名称    demo: abc.jpg  abc.JPG
        String fileName = file.getOriginalFilename();
        //bug说明: 由于windows系统不区分大小写,所以将字母全部转化为小写
        fileName = fileName.toLowerCase();
        //利用正则判断是否为图片.
        if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
            //如果不是图片,则返回null
            return null;
        }

        //2.检查文件是否为恶意程序.
        try {
            BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
            int width = bufferedImage.getWidth();
            int height = bufferedImage.getHeight();
            if(width == 0 || height == 0){
                //说明文件不是图片.
                return null;
            }

            //3.根据时间实现目录的创建 时间--yyyy/MM/dd
            String dateDir = new SimpleDateFormat("/yyyy/MM/dd/")
                            .format(new Date());
            // "G:/images/2021/11/11
            String localDirPath = localDir + dateDir;
            //创建目录
            File dirFile = new File(localDirPath);
            if(!dirFile.exists()){
                dirFile.mkdirs();
            }

            //4. 使用uuid替换文件名称 唯一:系统内部唯一
            String uuid = UUID.randomUUID().toString()
                    .replace("-","");
            //截取文件的后缀  aa.bb.cc.jpg
            int index = fileName.lastIndexOf(".");
            //获取类型  .jpg
            String fileType = fileName.substring(index);
            String newFileName = uuid + fileType;

            //5.实现文件上传操作  目录/文件名称
            String realFilePath = localDirPath + newFileName;
            file.transferTo(new File(realFilePath));
            System.out.println("文件上传成功!!!");


            /*
                 6.封装返回值
             *  封装虚拟路径 在各个系统之间可以灵活切换,只保存动态变化的目录
             *  path = 时间/uuid.type
             */
            String virtualPath = dateDir + newFileName;
            String url = "https://img14.360buyimg.com/n0/jfs/t1/157402/13/13529/158789/60517f36E2e8f9939/958bdb78df7c145f.jpg";
            return new ImageVO(virtualPath,url,newFileName);

        } catch (IOException e) {
            e.printStackTrace();
            return null;    //表示程序有问题
        }
    }
}


22.5 页面效果展现

23 关于商品新增文件模块说明

重点: 业务需求. 业务顺序

23.1 商品基本模块

  1. 用户点击添加商品按钮时,
    1.获取商品分类3级列表信息. 2.跳转到商品新增页面.
  2. 用户录入基本的商品信息.
  3. 用户录入价格时 需要在后期将数据扩大100倍.

23.2 商品上传

当用户点击上传图片时,根据 属性名称 file=“图片的字节信息”,实现数据的传递.
后端通过特定的接口MultipartFile(mvc专门接收字节信息,内部封装IO流)
上传文件时 首先使用正则表达式保证文件的类型正确
判断图片是否为恶意程序.由于图片有特定的属性width/height 获取文件的宽度和高度,如果有一项为0则上传的文件不是图片.
为了提高文件的查询效率,将文件进行分目录存储. 采用的策略以"时间为维度"进行的目录分隔.
为了防止文件重名,采用UUID的方式重新定义文件名称.
最终实现文件上传.返回特定的VO数据供前端展现.

23.3 商品详情

  1. 通常商品信息和详情信息是分开维护的.由于商品详情是大字段(文本域),查询的效率低.
  2. 商品表与详情表是一对一的对应关系. 设计时 item.id=itemDesc.id.
  3. 之后利用富文本编辑器(所见即所得),展现详情信息,其中存储的是html代码片段.
    最后将item/itemDesc封装之后提交入库.

24 实现图片功能

24.1 图片删除

24.1.1 业务需求说明

当用户点击删除图片时,应该发起请求同时删除服务器的数据.
重点: 如果需要删除数据,则应该传递虚拟路径信息

24.1.2 删除JS分析

24.1.3 业务接口说明

24.1.4 编辑FileController

24.1.5 编辑FileServiceImpl

24.2 图片网络路径说明

24.2.1 业务说明

如果用户需要查看服务器中的图片,则一般使用网络地址.
难点: 图片真实存在于磁盘地址中. 如何实现磁盘地址与网络地址的映射!
业务分析:
1.本地磁盘地址: ‪G:\images\2021\09\09\abc.jpg
2.网络访问地址: http://image.jt.com\2021\09\09\abc.jpg

24.2.2 封装图片网络地址

@Service
public class FileServiceImpl implements FileService{
    //封装路径的前缀
    private String localDir = "G:/images";
    private String preUrl = "http://image.jt.com";

    /**
     * 完成校验:
     *  1.校验是否为图片
     *  2.木马.exe.jpg 判断是否满足图片固有属性 高度/宽度
     *  3.为了提高查询效率,要求分目录存储.
     *      3.1 按照后缀名分配  jpg,png,gif 效率提升不能满足要求
     *      3.2 按照日期分   yyyy/MM/dd/HH   可以
     *      3.3 商品分类     出现分布不均现象.
     *      3.4 根据名称hash 之后截串
     *          demo: hash(a)=qw|er|as|dg/a.jpg
     *          弊端: hash码可能出现分布不均的现象.
     *  4.防止文件重名  使用uuid代替名称
     * @param file
     * @return
     */
    @Override
    public ImageVO upload(MultipartFile file) {
        //1.获取图片名称    demo: abc.jpg  abc.JPG
        String fileName = file.getOriginalFilename();
        //bug说明: 由于windows系统不区分大小写,所以将字母全部转化为小写
        fileName = fileName.toLowerCase();
        //利用正则判断是否为图片.
        if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
            //如果不是图片,则返回null
            return null;
        }

        //2.检查文件是否为恶意程序.
        try {
            BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
            int width = bufferedImage.getWidth();
            int height = bufferedImage.getHeight();
            if(width == 0 || height == 0){
                //说明文件不是图片.
                return null;
            }

            //3.根据时间实现目录的创建 时间--yyyy/MM/dd
            String dateDir = new SimpleDateFormat("/yyyy/MM/dd/")
                            .format(new Date());
            // "G:/images/2021/11/11
            String localDirPath = localDir + dateDir;
            //创建目录
            File dirFile = new File(localDirPath);
            if(!dirFile.exists()){
                dirFile.mkdirs();
            }

            //4. 使用uuid替换文件名称 唯一:系统内部唯一
            String uuid = UUID.randomUUID().toString()
                    .replace("-","");
            //截取文件的后缀  aa.bb.cc.jpg
            int index = fileName.lastIndexOf(".");
            //获取类型  .jpg
            String fileType = fileName.substring(index);
            String newFileName = uuid + fileType;

            //5.实现文件上传操作  目录/文件名称
            String realFilePath = localDirPath + newFileName;
            file.transferTo(new File(realFilePath));
            System.out.println("文件上传成功!!!");

            /*
             *  6.封装返回值
             *  封装虚拟路径 在各个系统之间可以灵活切换,只保存动态变化的目录
             *  path = 时间/uuid.type
             *  网络地址://http://image.jt.com/2021/11/11/a.jpg
             */
            String virtualPath = dateDir + newFileName;
            String url = preUrl + virtualPath;
            System.out.println("磁盘地址:"+realFilePath);
            System.out.println("网络地址:"+url);
            return new ImageVO(virtualPath,url,newFileName);

        } catch (IOException e) {
            e.printStackTrace();
            return null;    //表示程序有问题
        }
    }

    /**
     * 实现思路:
     *      1.根据虚拟地址,拼接磁盘地址
     *      2.判断文件是否存在
     *      3.实现文件删除
     * @param virtualPath
     */
    @Override
    public void deleteFile(String virtualPath) {
        //1.生成本地磁盘地址
        String path = localDir + virtualPath;
        System.out.println(path);
        File file = new File(path);
        if(file.exists()){
            file.delete();
        }
    }
}

24.3 后台数据优化

24.3.1 业务说明

说明: 如果路径信息会发生变化,则最好的方式通过动态赋值的方式完成.
解决方案:

  1. 编辑properties文件
  2. 通过@Value注解动态赋值.

24.3.2 编辑image.properties文件

#通过配置文件 动态赋值
image.localDir=G:/images
image.preUrl=http://image.jt.com

24.3.3 动态为属性赋值

25 代理机制

25.1 业务说明

网络地址: http://image.jt.com/2021/09/09/8a3e2666cfbc4f1c9f0136339d42a562.jpg
磁盘地址: G:/images/2021/09/09/8a3e2666cfbc4f1c9f0136339d42a562.jpg
如果用户直接通过网络地址进行访问,是无法直接获取图片信息. 如果需要获取图片则应该实现 域名与磁盘地址的映射.

25.2 反向代理

反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定。反向代理服务器通常可用来作为Web加速,即使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率。

特点:
1.反向代理服务器位于用户和服务器之间.
2.用户访问反向代理服务器,就可以获取真实的资源.
3.反向代理机制 用户无需了解真实的服务器信息.
4.反向代理保护了服务器端信息,也称之为服务器端代理.

25.3 正向代理

正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。

特点:

  1. 正向代理服务器位于用户和服务器之间.
  2. 用户发起请求时,非常明确自己访问的服务器到底是谁
  3. 真实的服务器不清楚真实的用户是谁.保护了用户的信息.
    所以称之为客户端代理.

25.4 正向/反向代理说明

说明: 用户每一次请求都包含了正向代理和反向代理.
正向代理一般适用于网络的通信.
反向代理一般适用于服务器获取信息.
在这里插入图片描述

26 Nginx

26.1 Nginx介绍

26.2 Nginx特点

  1. nginx是非常优秀的反向代理服务器.
  2. 占用内存少 不到2M tomcat服务器占用多大内存 200M左右
  3. 并发(负载)能力强 3-5万次/秒
    tomcat服务器并发能力 250-500次/秒
    调优之后可以实现1000次/秒
  4. nginx可以当作负载均衡服务器使用.

26.3 Nginx下载

26.4 Nginx启动报错说明

26.5 Nginx进程项说明

说明: 在windows中nginx服务每次点击启动之后,都会生成2个进程项.
注意事项: 在windows中nginx只能启动一次.
关于启动2项说明:
进程项1: nginx主要进程信息.
进程项2: nginx的守护进程 主要的任务防止主进程意外关闭.
关闭nginx 应该先关闭守护(内存小的)再关闭主进程(内存大的).

26.6 Nginx命令

说明: nginx命令执行 需要nginx.exe所在的根目录中执行.
命令:

  1. 启动命令 start nginx
  2. 重启命令 nginx -s reload
  3. 关闭命令 nginx -s stop

26.7 反向代理入门案例

  1. nginx反向代理需要http协议支持.
  2. server 每个反向代理服务都是一个server.
  3. listen 关键字 默认监听80端口.
  4. server_name 根据指定的域名进行反向代理
  5. location 反向代理时拦截的策略 / 所有的请求
  6. root 代表反向代理的是一个目录
  7. index 默认访问的页面

http {
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
}
}

26.8 实现图片的反向代理

26.8.1 业务说明

磁盘地址:G:/images/2021/09/09/a.jpg
网络地址:http://image.jt.com/2021/09/09/a.jpg
问题: 如何通过网络地址访问磁盘的图片?

1.用户通过域名访问真实的服务器.
2.nginx根据自身的配置进行拦截,根据配置文件将域名http://image.jt.com转化为具体的磁盘地址 G:/
3.根据磁盘地址访问真实的服务器资源.
4/5. 服务器将数据交给nginx,之后nginx将数据返回给用户.至此实现了反向代理.

26.8.2 图片服务器代理配置

注意事项:
1.启动时没有报错信息
2.重启时才会有报错. 所有最好先执行启动,再执行重启

26.8.3 图片回显流程

在这里插入图片描述
业务说明: 操作系统为了测试方便,在计算中保留了hosts文件. 该文件的主要的作用就是实现域名与IP地址的映射.但是该映射,只对本机有效.

26.8.4 修改hosts文件

路径: C:\Windows\System32\drivers\etc

#  IP 与   域名映射
#	127.0.0.1       localhost
#	::1             localhost

#图片服务器配置
127.0.0.1       image.jt.com
#前端服务器配置
127.0.0.1       www.jt.com
#后端服务器配置
127.0.0.1       manage.jt.com

26.8.5 页面效果展现

27 京淘项目前端项目发布

27.1 关于前端说明

vue项目开发阶段采用脚手架的方式进行的开发,如果开发完成应该将项目打包编译.编译为浏览器可以识别的静态资源文件 (HTML/CSS/JS)
Nginx可以作为web服务器.并且默认端口号80.

27.2 前端项目发布

27.2.1 路径修改

说明: 前端向后端发起请求时,网址 http://localhost:8091/xxxx,实际开发中服务器都是通过域名的方式访问,所以需要将前端的网址统一改为域名.

修改main.js 修改ajax请求的前缀
修改AddItem.vue文件 修改文件上传的路径

27.2.2 前端项目打包

通过build方式,将前端项目编译打包.

27.2.3 前端项目发布

说明: 将前端生成的dist目录 复制到nginx根目录中 如图所示:

27.2.4 前端反向代理

需求: 用户通过域名http://www.jt.com 访问系统的首页index.html
配置信息:

#配置前端服务器 www.jt.com
	server {
		listen 80;
		server_name www.jt.com;
		location / {
			root   dist;
			index  index.html;
		}
	}

28 京淘项目后端发布

28.1 项目部署流程

在这里插入图片描述

28.2 部署2台tomcat服务器

28.2.1 去除热部署

说明: 现在需要准备2台tomcat服务器,需要执行main方法2次.如果有热部署,则修改代码之后重启会影响配置流程. 所有关闭热部署.

28.2.2 动态获取端口

说明: 由于nginx中有负载均衡的策略 所以需要动态获取端口,验证是否负载均衡.

28.2.3 代码调试

28.2.4 IDEA主启动项说明

28.3 Nginx实现tomcat集群部署

28.3.1 配置nginx服务器

#定义tomcat集群
	# 负载均衡策略: 1.轮询策略
	upstream tomcats {
		server 127.0.0.1:8091;
		server 127.0.0.1:8092;
	}
	
	#配置后台服务器 manage.jt.com  8091/8092
	server {
		listen 80;
		server_name manage.jt.com;

		location / {
			#代理的是一个请求路径
			proxy_pass http://tomcats;
		}
	}

28.3.2 测试效果

28.4 Nginx负载均衡策略

28.4.1 轮询策略

说明: tomcat服务器依次访问.

#定义tomcat集群
	# 负载均衡策略: 1.轮询策略
	upstream tomcats {
		server 127.0.0.1:8091;
		server 127.0.0.1:8092;
	}

28.4.2 权重策略

说明: 根据权重设定,分配网络请求到不同的服务器中. 值越大访问越多.

#定义tomcat集群
	# 负载均衡策略: 1.轮询策略  2.权重策略
	upstream tomcats {
		server 127.0.0.1:8091 weight=10;
		server 127.0.0.1:8092 weight=1;
	}

28.4.3 IPHASH策略

需求: 如果需要让用户与服务器绑定.则可以使用ip_hash策略
使用说明:
1.方便进行压力测试.
2.某些用户的数据保存到服务器的Session中时,需要绑定数据.
3.公司特殊业务场景可能用到iphash.

#定义tomcat集群
	# 负载均衡策略: 1.轮询策略  2.权重策略  3.iphash策略
	upstream tomcats {
		ip_hash;
		server 127.0.0.1:8091;
		server 127.0.0.1:8092;
	}

在这里插入图片描述

29 Linux

在这里插入图片描述

29.1 部署JDK

29.1.1 部署JDK流程

上传JDK安装包 /usr/local/src
解压安装包
修改Linux环境变量
JDK环境测试

29.1.2 上传JDK

29.1.3 解压命令

tar -xvf jdk-8u51-linux-x64.tar.gz
修改文件名称 mv jdk1.8.0_51 jdk1.8

29.1.4 编辑Linux环境变量

1.路径: /etc/profile
2.修改环境变量:

#设定jdk环境
export JAVA_HOME=/usr/local/src/jdk1.8
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib

29.1.5 检查JDK是否有效

java - version

30 京淘后台项目发布流程

在这里插入图片描述

30.1 安装Mariadb数据库

30.2 完成单台tomcat部署

30.2.1 创建图片存储目录

说明: 准备文件上传的根目录

30.2.2 修改图片上传配置信息

编辑路径时注意空格问题

#通过配置文件 动态赋值
#image.localDir=G:/images
image.localDir=/usr/local/src/images
image.preUrl=http://image.jt.com

30.2.3 maven项目打包

检查数据源用户名和密码正确之后,将项目打包.

30.2.4 上传JDK的安装包

30.2.5 运行服务器

命令: [root@localhost tomcats]# java -jar 8091.jar &

30.2.6 服务器调试

说明:通过IP:端口直接测试tomcat服务是否正常.

30.3 关闭服务器进程

30.3.1 查询java服务

命令: jps

30.3.2 杀死进程

1.kill PID号 常规关闭
2.kill -15 PID号 较为严格的关闭
3.kill -9 PID号 强制关闭

30.3.3 "前台"项目发布

说明: 准备3台tomcat服务器,并且修改其中的端口号 分别改为8091/8092/8093.并且打包,如图所示.

执行启动命令: java -jar 8091.jar & java -jar 8092.jar & java -jar 8093.jar &

注意事项:

  1. 前台项目发布一般用于代码的调试.如果报错 效果非常明显.
  2. 如果链接Linux的终端关闭,则内部的所有的tomcat服务器都将关闭.

30.4 "后台"项目发布

30.4.1 需求说明

在前台发布调试成功之后,需要将tomcat服务器进行后台发布.后台发布时,tomcat会一直运行在Linux系统的内部,不会随终端的开关受到影响.

30.4.2 命令

nohup java -jar 8091.jar => 8091.log &

检查启动项: 检查日志是否正常,检查tomcat服务器是否正常

30.5 脚本方式启动

30.5.1 创建脚本

命令: vim start.sh

#!/bin/sh
nohup java -jar 8091.jar => 8091.log &
nohup java -jar 8092.jar => 8092.log &
nohup java -jar 8093.jar => 8093.log &


30.5.2 运行脚本

30.6 日志查看命令

cat 输出文件所有的内容
more 输出文档所有的内容,分页输出,空格浏览下一屏,q退出
less 用法和more相同,只是通过PgUp、PgDo键来控制
tail 用于显示文件后几号,使用频繁
tail -10 nginx.conf 查看nginx.conf的最后10行
tail –f nginx.conf 动态查看日志,方便查看日志新增的信息
ctrl+c 结束查看

31 Linux 安装Nginx

31.1 补充知识 关闭任意服务

命令: ps -ef |grep 服务名称

31.2 关于端口号说明

如果需要启动防火墙,则应该开放如下端口:

  1. 80端口
  2. 3306端口
  3. 8091/8092/8093
  4. 22
    开放端口之后,记得重启防火墙
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值