通用商城项目(上)

通用型产品(电商)发布解决方案+落地实现(基于分布式微服务技术栈:

SpringBoot+Spring Cloud+Spring Cloud Alibaba Vue+ElementUl +MyBatis-Plus

+ MySQL Git Maven Linux Nginx Docker +前后端分离)

项目技术栈和前置技术

  • 项目技术栈

1.前端:Html、Css、Js、Vue、ElementUI、Axios、JQuery、Node.js等

2.框架:Spring Boot、Spring Cloud、MyBatis-Plus

3.数据库:MySQL

4.项目管理:Git、Maven

5.平台:Linux、Docker

6.Alibaba/Sentinel:流量控制、熔断降级、系统负载保护

7.Alibaba/Nacos:注册中心

8.GateWay:网关服务

9.Alibaba/云存储OOS

10.快速开发技术:代码生成器、Vue前端脚手架项目Vue-cli

11.工具:Virtual box(类似vmware)、Vagrant

12.常用技术:Postman、浏览器跨域技术

13.Nginx:反向代理、负载均衡、动静分离

14.SPU,SKU表设计方案

15.更多…


环境配置

登录Vagrant

需要在虚拟系统的文件夹,打开cmd

登录: vagrant ssh

如果报错

vagrant@127.0.0.1: Permission denied (publickey,gssapi-keyex,gssapi-with-mic)

解决办法如下:

set VAGRANT_PREFER_SYSTEM_BIN=0

这个命令的作用是告诉Vagrant使用自己的ssh客户端,而不是Windows ssh客户端。

或将其放入您的.bash_profile:(没有找到该文件,不知道该方法是否可行)

export VAGRANT_PREFER_SYSTEM_BIN=0

查看ip地址:

windows: ipconfig

linux: ifconfig

virtualBox: ip a

查看eth1的inet

查看当前镜像:sudo docker images


Docker的使用

启动Docker: sudo systemctl start docker

client端的指令:

docker pull 拉取镜像

docker run 创建并启动容器

sudo docker exec -it mysql /bin/bash 创建并运行,只需执行一次

//语法说明: mysql 这儿是自定义的容器名

mysql -u root -p 执行mysql

sudo docker stop/start/restart mysql(这儿是容器的名称,自定义的name)

//以后可以使用它来关闭、打开或重启

docker ps 查看启动的容器执行情况

docker images

容器自动启动

通过docker update-restarta=always容器名称命令可以设置Docker中的mysql、redis等自动启动

切换root用户

su root 切换到 root 用户

root 默认密码就是 vagrant

exit 退出;即可返回登陆时的用户


Mysql

正确安装后启动mysql(非第一次启动)

sudo docker restart mysql

需要使用重新启动命令,原因是超过8小时,mysql自动断开

sudo docker start mysql

本来就是启动的,使用这条命令不会刷新时间

解决超时8小时,mysql断开连接的问题(未解决)

主要问题是找不到文件,以及无法使用vim

linux的vagrant上运行mysql程序:sudo docker exec -it mysql /bin/bash

登录mysql: mysql -u root -p

#然后输入命令查看mysql的保存位置

root@9939dd1fabf2:/# whereis mysql

我的位置显示如下:

mysql: /usr/bin/mysql /usr/lib/mysql /etc/mysql /usr/share/mysql

#切换目录

cd /etc/mysql/mysql.conf.d

#修改文件

vim mysqld.cnf

#在 [mysqlid] 最后一行添加配置

sql_mode = NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

或者

wait_timeout=1814400


Gitee创建新项目


搭建产品发布系统 后端 实现

快速开发平台

欢迎使用renren-fast人人快速开发平台-Powered By https://www.renren.io/

Java项目快速开发脚手架

https://zhuanlan.zhihu.com/p/145987215


pom.xml的设置

<module>module名</module>

经测试发现,module名是按照生成的文件夹名称来确定的,如果文件夹有两层的话,两层的名称都要写上,比如renren-fast/renren-fast。当时把文件夹重复了。

而不跟<artifactID>,<name>这些关联。

将自己创建的pom.xml的加入到maven

自己创建的pom.xml没有生效,图标是这样的:

点开maven,找到“+”号添加,选择父工程的pom.xml,确定,刷新。生效后,变成:

接着发现,子项目原本显示异常的Java图标,也变成了C这样的正常显示:

创建后台基础数据库和表

创建数据库hspliving_manage,“基字符集”选择utf8mb4的编码,mb4就是most bytes4的意思,用来兼容四字节的unicode,可以支持常见的moji表情图标

运行renren-fast踩的坑

解决办法:MySQL连接报错No appropriate protocol

大概原因:jdk1.8的高版本对SSL协议做了限制

我的解决办法:在sql的url中添加了 useSSL=false

安装NodeJs

需要安装v10.16.3,我的版本太高了。导致后面报错:

<% if (process.env.NODE_ENV === 'production') { %> <% }else { %> <% } %>

卸载Node.js,然后重装该版本。删除了renren-fast-vue,全部重来,终于打开了页面。

NodeJs设置淘宝镜像

在安装node.js时,会安装npm,npm是随nodejs安装的一款包管理工具,类似Maven,给npm设置淘宝镜像。运行cmd:

npm config set registry http://registry.npm.taobao.org

配置npm

在cmd的对应目录下执行如下指令:

npm install chromedriver --chromedriver_cdnurl=http://cdn.npm.taobao.org/dist/chromedriver

D:\idea_java_projects\renren-fast-vue>npm install

D:\idea_java_projects\renren-fast-vue>npm run dev

上面这两条命令都需要在cmd运行。因为IDEA的命令行会报错“因为在此系统上禁止运行脚本”

可以在cmd执行npm,查看是否已经正常安装

提示:直接到目录下,通过地址栏进入cmd,可以直接进入到该目录,免于录入目录名称

注意事项和细节说明

1.注意要保证启动renren-fast后台模块,否则看不到验证码

2.保证renren-fast后台:io/renren,/config/CorsConfig-java,跨域设置是开启的

前端项目配置npm

前面是通过npm run dev语句来启动npm的,现在直接把它在IDEA里面运行

前端项目的IDEA,点开运行的下拉列表,找到Edit Configuration...,弹出对话框后,点击左上角的“+”号,Add New Configuration,找到npm。右侧脚本Script选择dev,其他正常情况是默认填好的,然后确定。点击运行。


创建表和数据

创建商品分类表(支持层级)

建立一个父级字段,类似指针的方式,指向父级,实现了子级和父级在一张表内存储

renren-generator项目介绍

一句话:renren-generator是人人开源项目的代码生成器,可在线生成entity、xml、dao、service、html、js、sql代码,减少70%以上的开发任务

终于有种解放了的感觉,原来码农还是脱离了“原始社会”的。

搭建renren-generator模块/项目步骤

地址:https://gitee.com/renrenio/renren-generator

配置生成后,放到后端的文件夹。会发现大量报错,接下来就是去完善缺少的一些类和依赖,将他们组成一个common公共Module模块(比如工具类,安全配置)。从renren-fast的common中抽取出自己需要的。

然后在steinliving-commodify中引入该模块,便可引用里面的类

测试

使用postman进行测试,地址按照controller的路径来。


前端

前端页面快速自动生成

---有这么好了吗?

使用renren-generator生成的前端页面,集成到项目中

将生成的renren\main\resources\src\views\modules\commodity目录下的category.vue和category-add-or-update.vue拷贝到renren-fast-vue\src\views\modules\commodity\目录下,没有目录就创建。


按层级返回json数据

在service层处理数据分类,会使用到java8的流式计算(stream API)+递归操作(有一定难度)

在entity添加用于递归的字段:

@TableField(exist = false)

private List<CategoryEntity>childrenCategories;

把多个对象转为List

List<Person> list = Arrays.asList(person1,person2,person3,person4,person5);

把list转为流对象stream,这样就可以调用流的方法,处理一些复杂的业务

使用filter进行过滤,保留断言为真的数据

使用collect进行收集,collect()传入Collector,将数据收集到集合

【疑惑:Collectors.toList( ) 和Arrays.asList( )如何使用?】

map操作:希望给过滤得到的person对象加入cat

注意事项:

filter 不会影响原始引用中的数据。只返回true/false

map 操作会影响原始引用中的数据。因为它return这个结果了

stream常用的一些方法:filter(对数据进行过滤)、map(对数据进行映射操作)、Iimit(限制数据条数)、count (求取数据量)、sorted(排序)、collect(收集集合)等

后端层级树形结构

  1. 最先是在service层来处理业务。

  1. 获取数据库的所有信息。如何获取呢?

ctrl+B查看父类ServiceImpl,可以看到继承的属性baseMapper,可以通过BaseMapper看到它的方法有哪些。现在使用的是baseMapper.selectList(null)来获取所有数据。

  1. 显示所获得的entities数据,便于调试

  1. 现在才是controller中设置一个uri连接,实现网页的链接

  1. 添加entity属性字段,并注解@TableField(exist = false),避免以前数据封装出错

  1. 然后在Service层完善数据层级树形结构

组装成层级树形结构[使用到java8的stream api+递归操作

步骤思路

2.1过滤filter,返回1级分类

2.2进行map映射操作,给每个分类设置对应的子分类(这个过程会使用到递归)

2.3进行排序sorted操作

2.4将处理好的数据收集collect/转换到集合

前端树形层级显示

使用elementUI。为什么不用elementPlus,如何技术选型

引用到新创建的category.vue,但是如何修改代码呢?

获取层级信息data,是通过生命周期的created()主动调用this.$http()的所在方法获取的。

踩坑:created()调用方法时,需要使用this.方法名()的形式来调用,否则找不到

{data}中的大括号,表示里面的data是解构,返回值中只保留data属性的值。


删除Delete

后端部分

  • 逻辑删除

除了可以通过配置application.yml

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

还可以通过设置注解的参数来配置

@TableLogic(value ="1",delval ="0")

value表示逻辑已删除,delval表示逻辑未删除。

如果已经在yaml配置了,只使用注解@TableLogic标注对应字段就可以了,不再写参数。

测试

使用postman,在Json格式中输入[651,652] 之类的数据来进行删除,发现底层运行的是UPDATE

  • 删除实现

在后端controller里面,已经实现。调用即可。

前端部分:完善remove( )方法

以下需求,都可以参考src/views/modules/sys/role.vue里面的代码

需求1:实现删除超链接的正常功能。

      this.$http({
        //注释掉的这种写法会出错
        // url: this.$http.adornUrl('http://localhost:9090/commodity/category/delete'),
        url: 'http://localhost:9090/commodity/category/delete',
        method: 'post',
        // 发出请求时,携带的参数值
        data: this.$http.adornData(ids, false)
      }).then(({data})=>{
        this.getCategories()
      })

需求2:点删除后,需要出现提示信息确认删除。

      this.$confirm(`确定对【${data.name}】进行 '删除' 操作?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(({data}) => {
        this.$http({
         //需求1的代码
        }).then(({data})=>{
         //需求3的代码
          this.getCategories()
        }).catch(()=>{
          //取消不进行任何操作
        })
      })

需求3:删除成功后,显示提示信息

          this.$message({
            message: '操作成功',
            type: 'success',
          })

需求4:上述方法会自动折叠显示的数。需要将删除的结点,保持展开。

参考elementUI的Tree树形控件:默认展开和默认选中

1.模板中引入“ :default-expanded-keys="expandedKey" ”

2.常量池引入变量 expandedKey:[]

3.方法中,对该变量进行赋值

把数组中的值换成需要展开的父级id即可

放在this.getCategories()的前面或者后面都可以

这儿选用node是因为,使用data的话没有数据。估计node是全局变量,data不是。

          //数组格式,没有[]中括号,会不显示
          this.expandedKey= [node.data.parentId]

新增Append

使用elementUI的Dialog对话框。添加的内容会使dev报错。

用<div></div>把template的内容包起来,就可以避免报错,再进一步修整。

遇到的问题:

  1. 超链接是各个append,是如何将对话框对应上的?

append是之前创建的超链接,在其方法里面调用visible=true,即可显示对话框

  1. 是哪一步的完善,让前端的分类正确显示了的

在设置了<div>对的前提下。将v-model="category.name"的变量名设置好,就正确显示前端页面了。

  1. category是如何在不同的方法中被调用的?

通过在数据池设置category的所有属性的默认值,这样在方法中进行修改值,然后提交的操作。

  1. data数据不全

在then方法里面data没有数据。是通过使用node这样的全局变量,或者category的值来处理的。

    addCategory(data) {//调用后端添加分类
      console.log("data",data)
        this.$http({
          url: 'http://localhost:9090/commodity/category/save',
          method: 'post',
          // 发出请求时,携带的参数值
          data: this.$http.adornData(this.category, false)
        }).then(({}) => {
          this.$message({
            message: '操作成功',
            type: 'success',
          })
          // console.log("node=",node)
          //刷新分类列表
          this.getCategories()
          //数组格式,没有[]中括号,会不显示
          this.expandedKey = [this.category.parentId]
          this.dialogVisible = false
        })
    },
    append(data) {//打开对话框
      // console.log(data)
      this.dialogVisible = true
      this.category.parentId = data.id
      // 乘以*1是为了避免当做String,做成了字符拼接的操作
      this.category.catLevel = data.catLevel*1 + 1
      console.log("this.category=>",this.category)
    },

修改Update

  1. 回显信息

打开update的对话框,设置category的属性值

url动态获取,改为用反引号``括起来。

注意这儿使用的${data.id}语句

因为是路径变量,不需要传参,所以adornData注销了

    update(data){//更新category的前端表单数据
      this.categoryType = "update"
      this.dialogVisible=true
      // console.log("data=>", data)
      this.category.id=data.id
      this.$http({
        url: `http://localhost:9090/commodity/category/info/${data.id}`,
        method: 'get',
        // 发出请求时,携带的参数值。此处不需要携带id
        // data: this.$http.adornData(this.data, false)
      }).then(({data})=>{
        //注意数据的结构,先输出查看后,再进行赋值
        // console.log("category=>",data)
        this.category.name=data.category.name
        this.category.icon=data.category.icon
        this.category.proUnit=data.category.proUnit
        this.category.parentId=data.category.parentId
        this.category.catLevel=data.category.catLevel
      })
    },

  1. 修改信息

真正保存category的信息到数据库进行保存

    updateFromDB(){//真正到数据库更新数据
      this.$http({
        url: 'http://localhost:9090/commodity/category/update',
        method: 'post',
        // 发出请求时,携带的参数值
        data: this.$http.adornData(this.category, false)
      }).then(({}) => {
        this.$message({
          message: '操作成功',
          type: 'success',
        })
        // console.log("node=",node)
        //刷新分类列表
        this.getCategories()
        //数组格式,没有[]中括号,会不显示
        this.expandedKey = [this.category.parentId]
        this.dialogVisible = false
      })
    },

3. 设置变量categoryType

通过设置变量categoryType,在打开对话框时,确定是append操作还是update操作,来明确“确定”键的具体功能调用。

    addOrUpdate(){
      if(this.categoryType==="update"){
        this.updateFromDB()
      }
      if(this.categoryType==="add"){
        this.addCategory()
      }
    },

批量删除

核心步骤:获取当前选中的结点

在el-tree控件中,添加属性ref="categoryTree",标记需要从哪个el-tree控件获取信息

然后有两个方法可以获取结点:

this.$refs.tree.getCheckedNodes() 获取结点的所有信息,推荐使用

this.$refs.tree.getCheckedKeys() 只获取id值

通过下标遍历访问获取ids和names

使用names回显名称,确定后,使用ids调用后端进行逻辑删除

    batchDelete(){
      var ids=[]
      var categoryNames=[]
      //获得当前选中的条目
      var checkedNodes = this.$refs.categoryTree.getCheckedNodes();
      console.log("getCheckedNodes=>",checkedNodes)
      for (let i = 0; i < checkedNodes.length; i++) {
        ids.push(checkedNodes[i].id)
        categoryNames.push(checkedNodes[i].name)
      }
      // console.log("names=",categoryNames)
      this.$confirm(`确定对【${categoryNames}】进行 '批量删除' 操作?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(({data}) => {
        this.$http({
          //注释掉的这种写法会出错
          // url: this.$http.adornUrl('http://localhost:9090/commodity/category/delete'),
          url: 'http://localhost:9090/commodity/category/delete',
          method: 'post',
          // 发出请求时,携带的参数值
          data: this.$http.adornData(ids, false)
        }).then(({data}) => {
          this.$message({
            message: '批量删除操作成功',
            type: 'success',
          })
          // console.log("node=",node)
          //刷新分类列表
          this.getCategories()
        }).catch(() => {
          //取消不进行任何操作
        })
      })
    },

完成家居品牌的—增删改查

  1. 在DB,创建家居品牌表

  1. 使用renren-generator生成代码

启动RenrenApplicationGener服务

访问localhost:80

导航菜单点击renren-fast

右侧会显示DB里面存在的表。是因为在:renren-fast/src/main/resources/application-dev.yml配置了对应的DB信息。

选中新建的品牌表,点击“生成代码”。

将解压后的main文件夹,去覆盖steinliving-commodity的main文件夹即可(提醒:注意不要把steinliving-commodity项目原有的文件/controller/service/dao等覆盖了,正常不会出现覆盖提示)

调整修复报错

BrandConctroller:注销与@RequiresPermissions有关的所有注解和引用

再重启该服务,查看是否正常启动

使用postman完成测试

注意:update的测试参数,是用的json格式完成的。

    {
        "id":1,
        "name":"海信"
    }

新建家居品牌菜单

  1. 在人人开发平台,系统管理->菜单管理 里面新增“菜单”

  1. 将生成的renren\main\resources\src\views\modules\commodity目录下的brand.vue和brand-add-or-update.vue拷贝到renren-fast-vue\src\views\modules\commodity\目录下

  1. 修改renren-fast-vue\src\views\modules\commodity\brand.vue和brand-add-or-update.vue把所有url的值(因为还会使用到添加/删除/修改等方法),都改成指向http://ocalhost:9090服务器(说明:后面我们使用Gateway微服务可以完美解决前端项目指向多个微服务的问题/放心)

修改Vue文件,使得前端页面能够正常显示

前端文件位置:src/views/modules/commodity/brand.vue

修改this.$http里面的url属性值,涉及查询和删除两条。事实上,把查询的list的url修改正确后,前面页面便可以显示了

打开权限检测,会出现添加和批量删除按钮

修改isAuth方法,暂时放行,renren-fast-vue\src\utils\index.js

注释原来的return,改为 return true 即可。

查询功能

不起作用。可以手动修改来实现

src/views/modules/commodity/brand-add-or-update.vue

将其查询和更新的url修改一下。

新增功能

注意新增"isshow"字段是int类型,需要输入数字才能正常录入


swift切换控件--还是在elementUI里面

需要用到table的自定义组件,使用插槽机制

最后完成后,发现刷新swift组件状态显示不正常。原因是需要在

:active-value="1"

:inactive-value="0"

这两个属性前加上":"冒号,表示它绑定的是Number值(上面的已添加)。然后刷新即可正常显示

但是看说明呢,“:”冒号表示的意思,应该是v-bind绑定的别名,即简写。


注意事项

root.id==category.id 出现的bug。

因为他们都是Long包装类,当数值<-128或者>127的时候,==判断的引用类型并不是同一个,会出错,不能正常显示分类。使用.equals()方法来解决这个问题。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值