RuoYi-Vue企业级实战:电商后台管理系统

【免费下载链接】RuoYi-Vue 🎉 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统,同时提供了 Vue3 的版本 【免费下载链接】RuoYi-Vue 项目地址: https://gitcode.com/yangzongzhuan/RuoYi-Vue

前言:为什么选择RuoYi-Vue构建电商后台?

在数字化转型浪潮中,电商企业面临着后台管理系统开发效率低、权限控制复杂、系统稳定性要求高等痛点。传统开发模式往往需要从零开始搭建权限体系、日志监控、代码生成等基础功能,耗费大量开发资源。

RuoYi-Vue作为基于SpringBoot + Vue的前后端分离权限管理系统,提供了完整的解决方案。通过本文,您将掌握:

  • ✅ 如何基于RuoYi-Vue快速搭建电商后台管理系统
  • ✅ 电商核心业务模块的设计与实现
  • ✅ 权限控制与数据安全的实战配置
  • ✅ 代码生成器在电商开发中的高效应用
  • ✅ 系统监控与性能优化的最佳实践

一、技术架构解析

1.1 整体架构设计

RuoYi-Vue采用经典的前后端分离架构,电商后台系统架构如下:

mermaid

1.2 技术栈明细

层级 技术组件 版本 作用
前端 Vue 2.x 2.6.12 响应式UI框架
前端 Element UI 2.15.14 UI组件库
前端 Axios 0.28.1 HTTP客户端
后端 Spring Boot 2.5.15 微服务框架
后端 Spring Security 5.7.12 安全认证
后端 JWT 0.9.1 Token认证
数据库 MySQL 5.7+ 关系型数据库
缓存 Redis 5.0+ 缓存数据库
监控 Druid 1.2.23 数据源监控

二、电商核心业务模块设计

2.1 数据库表结构设计

电商系统需要扩展以下核心表结构:

-- 商品分类表
CREATE TABLE `product_category` (
  `category_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
  `parent_id` bigint(20) DEFAULT '0' COMMENT '父分类ID',
  `category_name` varchar(50) NOT NULL COMMENT '分类名称',
  `level` int(1) DEFAULT '1' COMMENT '分类层级',
  `sort` int(4) DEFAULT '0' COMMENT '排序',
  `status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
  `icon` varchar(255) DEFAULT '' COMMENT '分类图标',
  `keywords` varchar(255) DEFAULT '' COMMENT '关键词',
  `description` varchar(500) DEFAULT '' COMMENT '描述',
  PRIMARY KEY (`category_id`)
) ENGINE=InnoDB COMMENT='商品分类表';

-- 商品信息表
CREATE TABLE `product_info` (
  `product_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `product_name` varchar(100) NOT NULL COMMENT '商品名称',
  `category_id` bigint(20) NOT NULL COMMENT '分类ID',
  `brand_id` bigint(20) DEFAULT NULL COMMENT '品牌ID',
  `main_image` varchar(255) DEFAULT '' COMMENT '主图',
  `sub_images` text COMMENT '副图',
  `price` decimal(10,2) NOT NULL COMMENT '价格',
  `market_price` decimal(10,2) DEFAULT NULL COMMENT '市场价',
  `stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存',
  `weight` decimal(10,2) DEFAULT NULL COMMENT '重量',
  `status` char(1) DEFAULT '0' COMMENT '状态(0上架 1下架)',
  `detail` text COMMENT '商品详情',
  `sale_count` int(11) DEFAULT '0' COMMENT '销量',
  `view_count` int(11) DEFAULT '0' COMMENT '浏览量',
  PRIMARY KEY (`product_id`)
) ENGINE=InnoDB COMMENT='商品信息表';

-- 订单表
CREATE TABLE `order_info` (
  `order_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_sn` varchar(64) NOT NULL COMMENT '订单编号',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `total_amount` decimal(10,2) NOT NULL COMMENT '订单总额',
  `pay_amount` decimal(10,2) NOT NULL COMMENT '应付总额',
  `freight_amount` decimal(10,2) DEFAULT '0.00' COMMENT '运费金额',
  `pay_type` int(1) DEFAULT NULL COMMENT '支付方式',
  `status` int(1) DEFAULT '0' COMMENT '状态',
  `receiver_name` varchar(100) NOT NULL COMMENT '收货人姓名',
  `receiver_phone` varchar(32) NOT NULL COMMENT '收货人电话',
  `receiver_address` varchar(200) NOT NULL COMMENT '收货地址',
  `note` varchar(500) DEFAULT '' COMMENT '订单备注',
  `confirm_status` int(1) DEFAULT NULL COMMENT '确认状态',
  `payment_time` datetime DEFAULT NULL COMMENT '支付时间',
  `delivery_time` datetime DEFAULT NULL COMMENT '发货时间',
  `receive_time` datetime DEFAULT NULL COMMENT '收货时间',
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB COMMENT='订单表';

2.2 前后端接口设计

基于RuoYi的RESTful API规范,电商接口设计示例:

// ProductController.java
@RestController
@RequestMapping("/product")
public class ProductController extends BaseController {
    
    @Autowired
    private IProductService productService;
    
    /**
     * 获取商品列表
     */
    @PreAuthorize("@ss.hasPermi('product:list')")
    @GetMapping("/list")
    public TableDataInfo list(Product product) {
        startPage();
        List<Product> list = productService.selectProductList(product);
        return getDataTable(list);
    }
    
    /**
     * 新增商品
     */
    @PreAuthorize("@ss.hasPermi('product:add')")
    @Log(title = "商品管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody Product product) {
        return toAjax(productService.insertProduct(product));
    }
    
    /**
     * 修改商品
     */
    @PreAuthorize("@ss.hasPermi('product:edit')")
    @Log(title = "商品管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody Product product) {
        return toAjax(productService.updateProduct(product));
    }
}

前端API调用示例:

// product.js
import request from '@/utils/request'

// 查询商品列表
export function listProduct(query) {
  return request({
    url: '/product/list',
    method: 'get',
    params: query
  })
}

// 新增商品
export function addProduct(data) {
  return request({
    url: '/product',
    method: 'post',
    data: data
  })
}

// 修改商品
export function updateProduct(data) {
  return request({
    url: '/product',
    method: 'put',
    data: data
  })
}

三、权限控制实战配置

3.1 菜单权限配置

在RuoYi系统中配置电商管理菜单:

-- 添加电商管理菜单
INSERT INTO sys_menu VALUES
('2000', '电商管理', '0', '5', 'ecommerce', null, '', '', 1, 0, 'M', '0', '0', '', 'shopping', 'admin', sysdate(), '', null, '电商管理目录'),

('2100', '商品管理', '2000', '1', 'product', 'ecommerce/product/index', '', '', 1, 0, 'C', '0', '0', 'ecommerce:product:list', 'goods', 'admin', sysdate(), '', null, '商品管理菜单'),
('2101', '商品查询', '2100', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'ecommerce:product:query', '#', 'admin', sysdate(), '', null, ''),
('2102', '商品新增', '2100', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'ecommerce:product:add', '#', 'admin', sysdate(), '', null, ''),
('2103', '商品修改', '2100', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'ecommerce:product:edit', '#', 'admin', sysdate(), '', null, ''),
('2104', '商品删除', '2100', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'ecommerce:product:remove', '#', 'admin', sysdate(), '', null, '');

3.2 数据权限配置

电商系统中的数据权限控制:

// 数据权限注解使用
@DataScope(deptAlias = "d", userAlias = "u")
public List<Product> selectProductList(Product product) {
    return productMapper.selectProductList(product);
}

// 自定义权限校验
@Service("ps")
public class PermissionService {
    /**
     * 验证商品操作权限
     */
    public boolean hasProductPermission(Long productId) {
        // 获取当前用户
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long userId = loginUser.getUserId();
        
        // 查询用户是否有该商品的操作权限
        // 这里可以根据业务需求实现具体的权限逻辑
        return true;
    }
}

// 在Controller中使用
@PreAuthorize("@ps.hasProductPermission(#productId)")
@DeleteMapping("/{productId}")
public AjaxResult remove(@PathVariable Long productId) {
    return toAjax(productService.deleteProductById(productId));
}

四、代码生成器高效应用

4.1 生成电商业务代码

利用RuoYi代码生成器快速创建电商模块:

  1. 配置生成器参数
# generator.yml
gen:
  author: ecommerce
  packageName: com.ruoyi.ecommerce
  autoRemovePre: false
  tablePrefix: ec_
  1. 生成步骤
# 1. 在系统工具 -> 代码生成中导入表
# 2. 选择需要生成的表(如ec_product, ec_order等)
# 3. 编辑生成配置,设置模块名称为ecommerce
# 4. 生成代码并下载
# 5. 将生成的代码放入对应模块
  1. 生成的文件结构
src/
├── main/
│   ├── java/
│   │   └── com/
│   │       └── ruoyi/
│   │           └── ecommerce/
│   │               ├── domain/Product.java
│   │               ├── mapper/ProductMapper.java
│   │               ├── service/IProductService.java
│   │               ├── service/impl/ProductServiceImpl.java
│   │               └── controller/ProductController.java
│   └── resources/
│       └── mapper/
│           └── ecommerce/
│               └── ProductMapper.xml

4.2 自定义模板优化

根据电商业务需求自定义生成模板:

## 在vm/java/domain.java.vm中添加电商特有字段
#if($table.subTables.size() > 0)
#foreach($subTable in $table.subTables)
    /** $subTable.tableComment */
    private List<${subTable.className}> ${subTable.classname}List;
#end
#end

## 电商特有字段
/** 商品销量 */
private Integer saleCount;

/** 商品浏览量 */
private Integer viewCount;

/** 商品状态(0上架 1下架) */
private String status;

五、前端组件开发实战

5.1 商品管理组件

<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="100px">
      <el-form-item label="商品名称" prop="productName">
        <el-input
          v-model="queryParams.productName"
          placeholder="请输入商品名称"
          clearable
          style="width: 200px"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="商品状态" prop="status">
        <el-select v-model="queryParams.status" placeholder="商品状态" clearable style="width: 200px">
          <el-option label="上架" value="0" />
          <el-option label="下架" value="1" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          icon="el-icon-plus"
          @click="handleAdd"
          v-hasPermi="['ecommerce:product:add']"
        >新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          icon="el-icon-edit"
          :disabled="single"
          @click="handleUpdate"
          v-hasPermi="['ecommerce:product:edit']"
        >修改</el-button>
      </el-col>
    </el-row>

    <el-table v-loading="loading" :data="productList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="商品ID" align="center" prop="productId" />
      <el-table-column label="商品名称" align="center" prop="productName" />
      <el-table-column label="价格" align="center" prop="price">
        <template slot-scope="scope">
          <span>¥{{ scope.row.price }}</span>
        </template>
      </el-table-column>
      <el-table-column label="库存" align="center" prop="stock" />
      <el-table-column label="销量" align="center" prop="saleCount" />
      <el-table-column label="状态" align="center" prop="status">
        <template slot-scope="scope">
          <el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">
            {{ scope.row.status === '0' ? '上架' : '下架' }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['ecommerce:product:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['ecommerce:product:remove']"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 添加或修改商品对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
        <el-row>
          <el-col :span="12">
            <el-form-item label="商品名称" prop="productName">
              <el-input v-model="form.productName" placeholder="请输入商品名称" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="商品分类" prop="categoryId">
              <el-tree-select
                v-model="form.categoryId"
                :data="categoryOptions"
                :props="{ value: 'categoryId', label: 'categoryName', children: 'children' }"
                placeholder="请选择商品分类"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="商品价格" prop="price">
          <el-input-number v-model="form.price" :min="0" :precision="2" />
        </el-form-item>
        <el-form-item label="商品库存" prop="stock">
          <el-input-number v-model="form.stock" :min="0" />
        </el-form-item>
        <el-form-item label="商品状态">
          <el-radio-group v-model="form.status">
            <el-radio label="0">上架</el-radio>
            <el-radio label="1">下架</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="商品主图">
          <image-upload v-model="form.mainImage"/>
        </el-form-item>
        <el-form-item label="商品详情">
          <editor v-model="form.detail" :min-height="192"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { listProduct, getProduct, addProduct, updateProduct, delProduct } from "@/api/ecommerce/product";
import { listCategory } from "@/api/ecommerce/category";

export default {
  name: "Product",
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 商品表格数据
      productList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 分类选项
      categoryOptions: [],
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        productName: undefined,
        status: undefined
      },
      // 表单参数
      form: {},
      // 表单校验
      rules: {
        productName: [
          { required: true, message: "商品名称不能为空", trigger: "blur" }
        ],
        categoryId: [
          { required: true, message: "商品分类不能为空", trigger: "blur" }
        ],
        price: [
          { required: true, message: "商品价格不能为空", trigger: "blur" }
        ]
      }
    };
  },
  created() {
    this.getList();
    this.getCategoryTree();
  },
  methods: {
    /** 查询商品列表 */
    getList() {
      this.loading = true;
      listProduct(this.queryParams).then(response => {
        this.productList = response.rows;
        this.total = response.total;
        this.loading = false;
      });
    },
    /** 查询分类下拉树结构 */
    getCategoryTree() {
      listCategory().then(response => {
        this.categoryOptions = this.handleTree(response.data, "categoryId", "parentId");
      });
    },
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        productId: undefined,
        productName: undefined,
        categoryId: undefined,
        price: 0,
        stock: 0,
        status: "0",
        mainImage: undefined,
        detail: undefined
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.productId);
      this.single = selection.length != 1;
      this.multiple = !selection.length;
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加商品";
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      const productId = row.productId || this.ids;
      getProduct(productId).then(response => {
        this.form = response.data;
        this.open = true;
        this.title = "修改商品";
      });
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          if (this.form.productId != null) {
            updateProduct(this.form).then(response => {
              this.$modal.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            });
          } else {
            addProduct(this.form).then(response => {
              this.$modal.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            });
          }
        }
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const productIds = row.productId || this.ids;
      this.$modal.confirm('是否确认删除商品编号为"' + productIds + '"的数据项?').then(function() {
        return delProduct(productIds);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("删除成功");
      }).catch(() => {});
    }
  }
};
</script>

六、系统监控与性能优化

6.1 电商系统监控配置

# application.yml 监控配置
spring:
  datasource:
    druid:
      stat-view-servlet:
        enabled: true
        login-username: admin
        login-password: 123456
      web-stat-filter:
        enabled: true
      filter:
        stat:
          enabled: true
          log-slow-sql: true
          slow-sql-millis: 1000

# 自定义监控端点
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always

6.2 性能优化策略

  1. 数据库优化
-- 创建商品表索引
CREATE INDEX idx_product_category ON product_info(category_id);
CREATE INDEX idx_product_status ON product_info(status);
CREATE INDEX idx_product_name ON product_info(product_name);

-- 创建订单表索引
CREATE INDEX idx_order_user ON order_info(user_id);
CREATE INDEX idx_order_status ON order_info(status);
CREATE INDEX idx_order_time ON order_info(create_time);
  1. 缓存策略
// 商品信息缓存配置
@Cacheable(value = "product", key = "#productId")
public Product getProductById(Long productId) {
    return productMapper.selectProductById(productId);
}

// 分类信息缓存
@Cacheable(value = "category", key = "#categoryId")
public ProductCategory getCategoryById(Long categoryId) {
    return categoryMapper.selectCategoryById(categoryId);
}

// 热门商品缓存
@Cacheable(value = "hotProducts", key = "'list'")
public List<Product> getHotProducts() {
    return productMapper.selectHotProducts();
}
  1. 接口性能监控
// 使用AOP监控接口性能
@Aspect
@Component
@Slf4j
public class PerformanceAspect {
    
    @Around("execution(* com.ruoyi.ecommerce..*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        
        String methodName = joinPoint.getSignature().getName();
        log.info("方法 {} 执行耗时: {}ms", methodName, (endTime - startTime));
        
        // 记录慢查询
        if (endTime - startTime > 1000) {
            log.warn("慢接口警告: {} 耗时 {}ms", methodName, (endTime - startTime));
        }
        
        return result;
    }
}

七、部署与运维指南

7.1 生产环境部署

# 后端部署脚本
#!/bin/bash
# deploy.sh

# 停止现有服务
echo "停止现有服务..."
pkill -f ruoyi-admin

# 备份旧版本
echo "备份旧版本..."
tar -czf /backup/ruoyi-$(date +%Y%m%d%H%M%S).tar.gz /app/ruoyi/

# 部署新版本
echo "部署新版本..."
unzip -o ruoyi-admin-3.9.0.jar -d /app/ruoyi/

# 启动服务
echo "启动服务..."
nohup java -jar /app/ruoyi/ruoyi-admin-3.9.0.jar \
--spring.profiles.active=prod \
--server.port=8080 \
> /app/ruoyi/ruoyi.log 2>&1 &

echo "部署完成!"

7.2 前端部署配置

// vue.config.js 生产环境配置
module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? '/ecommerce/' : '/',
  outputDir: 'dist',
  assetsDir: 'static',
  lintOnSave: process.env.NODE_ENV === 'development',
  productionSourceMap: false,
  
  configureWebpack: {
    externals: process.env.NODE_ENV === 'production' ? {
      'vue': 'Vue',
      'element-ui': 'ELEMENT'
    } : {}
  },
  
  chainWebpack: config => {
    config.plugin('html').tap(args => {
      args[0].title = '电商后台管理系统';
      return args;
    });
    
    // 配置CDN
    if (process.env.NODE_ENV === 'production') {
      config.plugin('html').tap(args => {
        args[0].cdn = {
          js: [
            'https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js',
            'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.14/index.js'
          ],
          css: [
            'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.14/theme-chalk/index.css'
          ]
        };
        return args;
      });
    }
  }
};

【免费下载链接】RuoYi-Vue 🎉 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统,同时提供了 Vue3 的版本 【免费下载链接】RuoYi-Vue 项目地址: https://gitcode.com/yangzongzhuan/RuoYi-Vue

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐