当前位置: 首页 > news >正文

【商城实战(20)】商品管理功能深化实战

【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想深入钻研技术细节,还是探寻商城运营之道,本专栏都能提供从 0 到 1 的系统讲解,助力你打造独具竞争力的电商平台,开启电商实战之旅。

目录

  • 一、商品批量上架、下架功能实现
    • 1.1 前端交互设计
    • 1.2 后端接口开发(SpringBoot)
    • 1.3 数据库操作(Mybatis-plus)
    • 1.4 完整源码展示与分析
  • 二、商品库存管理页面开发
    • 2.1 库存管理页面布局设计
    • 2.2 库存预警功能实现
    • 2.3 库存盘点功能实现
    • 2.4 源码实现与解读
  • 三、完善商品信息编辑功能
    • 3.1 前端编辑页面优化
    • 3.2 后端编辑接口升级
    • 3.3 数据库关联更新(Mybatis-plus)
    • 3.4 完整代码示例与解析
  • 四、总结与展望


一、商品批量上架、下架功能实现

在电商系统中,商品的批量上架与下架操作是提升商品管理效率的关键环节。当商家需要快速调整一批商品的展示状态时,逐个操作不仅耗时费力,还容易出错。批量操作功能能够一次性处理多个商品,大大节省了时间和人力成本,使得商家能够更高效地应对市场变化和业务需求。接下来,我们将详细介绍如何使用 uniapp、Element Plus、SpringBoot 和 Mybatis-plus 实现这一功能。

1.1 前端交互设计

  • uniapp 实现:在 uniapp 中,使用checkbox组件实现商品选择,checkbox-group组件用于收集选中的商品。通过v-model指令绑定选中的商品列表,然后添加 “批量上架” 和 “批量下架” 按钮,并绑定对应的点击事件。示例代码如下:
<template>
  <view>
    <checkbox-group v-model="selectedIds">
      <view v-for="(item, index) in productList" :key="index">
        <checkbox :value="item.id">{{ item.productName }}</checkbox>
      </view>
    </checkbox-group>
    <button @click="batchOperate('上架')">批量上架</button>
    <button @click="batchOperate('下架')">批量下架</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      selectedIds: [],
      productList: []
    };
  },
  methods: {
    async batchOperate(status) {
      if (this.selectedIds.length === 0) {
        uni.showToast({ title: '请选择商品', icon: 'none' });
        return;
      }
      const res = await uni.request({
        url: 'http://your-backend-url/batchProduct',
        method: 'POST',
        data: {
          status,
          productIds: this.selectedIds
        }
      });
      if (res.data.success) {
        uni.showToast({ title: `批量${status}成功`, icon:'success' });
        // 刷新商品列表
        await this.getProductList();
      } else {
        uni.showToast({ title: `批量${status}失败`, icon: 'none' });
      }
    },
    async getProductList() {
      const res = await uni.request({
        url: 'http://your-backend-url/products',
        method: 'GET'
      });
      this.productList = res.data.data;
    }
  },
  onLoad() {
    this.getProductList();
  }
};
</script>
  • Element Plus 实现:Element Plus 中,使用el-checkbox和el-checkbox-group组件实现商品选择,同样通过v-model绑定选中的商品 ID 数组。按钮部分使用el-button组件,并绑定点击事件。示例代码如下:
<template>
  <el-container>
    <el-main>
      <el-checkbox-group v-model="selectedIds">
        <el-checkbox v-for="(item, index) in productList" :key="index" :label="item.id">{{ item.productName }}</el-checkbox>
      </el-checkbox-group>
      <el-button @click="batchOperate('上架')">批量上架</el-button>
      <el-button @click="batchOperate('下架')">批量下架</el-button>
    </el-main>
  </el-container>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';

const selectedIds = ref([]);
const productList = ref([]);

const batchOperate = async (status) => {
  if (selectedIds.value.length === 0) {
    alert('请选择商品');
    return;
  }
  try {
    const res = await axios.post('http://your-backend-url/batchProduct', {
      status,
      productIds: selectedIds.value
    });
    if (res.data.success) {
      alert(`批量${status}成功`);
      await getProductList();
    } else {
      alert(`批量${status}失败`);
    }
  } catch (error) {
    console.error('操作失败', error);
    alert(`批量${status}失败`);
  }
};

const getProductList = async () => {
  const res = await axios.get('http://your-backend-url/products');
  productList.value = res.data.data;
};

onMounted(() => {
  getProductList();
});
</script>

1.2 后端接口开发(SpringBoot)

在 SpringBoot 中,创建一个 Controller 来接收前端传递的批量操作请求。通过@RequestBody注解获取前端发送的参数,包括操作状态(上架或下架)和商品 ID 数组。然后调用 Service 层方法来处理业务逻辑。示例代码如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class ProductController {

    @Autowired
    private ProductService productService;

    @PostMapping("/batchProduct")
    public Result batchProduct(@RequestBody BatchProductDTO batchProductDTO) {
        try {
            productService.batchUpdateProductStatus(batchProductDTO.getStatus(), batchProductDTO.getProductIds());
            return Result.success("操作成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("操作失败");
        }
    }
}

class BatchProductDTO {
    private String status;
    private List<Long> productIds;

    // 省略getter和setter方法
}

class Result {
    private boolean success;
    private String message;

    public static Result success(String message) {
        Result result = new Result();
        result.success = true;
        result.message = message;
        return result;
    }

    public static Result error(String message) {
        Result result = new Result();
        result.success = false;
        result.message = message;
        return result;
    }

    // 省略getter和setter方法
}

1.3 数据库操作(Mybatis-plus)

Mybatis-plus 提供了强大的 CRUD 操作,无需手写 mapper 文件。在 Service 层中,通过LambdaUpdateWrapper来构建更新条件,实现批量更新商品的上下架状态。示例代码如下:

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

@Service
public class ProductService {

    @Resource
    private ProductMapper productMapper;

    @Transactional
    public void batchUpdateProductStatus(String status, List<Long> productIds) {
        LambdaUpdateWrapper<Product> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(Product::getId, productIds);
        updateWrapper.set(Product::getStatus, status);
        productMapper.update(null, updateWrapper);
    }
}

1.4 完整源码展示与分析

上述代码分别展示了 uniapp、Element Plus 前端以及 SpringBoot 后端和 Mybatis-plus 数据库操作实现商品批量上架、下架功能的关键部分。前端部分通过checkbox组件实现商品选择,点击按钮触发请求,将选中的商品 ID 和操作状态发送到后端。后端 Controller 接收请求后,调用 Service 层方法,Service 层利用 Mybatis-plus 的LambdaUpdateWrapper构建更新条件,实现对数据库中商品状态的批量更新。这样,通过前后端的协同工作,高效地实现了商品批量上架、下架功能,提升了商品管理的效率。

二、商品库存管理页面开发

商品库存管理是商城运营的核心环节之一,它直接关系到商品的供应与销售。准确的库存管理能够避免缺货现象,提升客户满意度,同时防止库存积压,减少资金占用。开发支持库存预警和库存盘点功能的页面,有助于商家及时掌握库存动态,做出合理的采购与销售决策,保障商城的稳定运营。

2.1 库存管理页面布局设计

  • uniapp 实现:使用view组件构建页面布局,u-table组件展示库存数据表格,每列展示商品 ID、名称、库存数量、预警阈值等信息。在表格列中添加操作按钮,如盘点按钮,绑定点击事件。示例代码如下:
<template>
  <view>
    <u-table :columns="columns" :data="stockList">
      <template #default="{ row }">
        <view :key="row.id" class="u-table-cell">
          {{ row.productId }}
        </view>
        <view :key="row.id + 'name'" class="u-table-cell">
          {{ row.productName }}
        </view>
        <view :key="row.id + 'quantity'" class="u-table-cell">
          {{ row.stockQuantity }}
        </view>
        <view :key="row.id + 'threshold'" class="u-table-cell">
          {{ row.threshold }}
        </view>
        <view :key="row.id + 'operation'" class="u-table-cell">
          <button @click="startStockTaking(row.id)">盘点</button>
        </view>
      </template>
    </u-table>
  </view>
</template>

<script>
export default {
  data() {
    return {
      columns: [
        { title: '商品ID' },
        { title: '商品名称' },
        { title: '库存数量' },
        { title: '预警阈值' },
        { title: '操作' }
      ],
      stockList: []
    };
  },
  methods: {
    async getStockList() {
      const res = await uni.request({
        url: 'http://your-backend-url/stockList',
        method: 'GET'
      });
      this.stockList = res.data.data;
    },
    async startStockTaking(productId) {
      // 发起盘点请求逻辑
    }
  },
  onLoad() {
    this.getStockList();
  }
};
</script>
  • Element Plus 实现:通过el-table组件展示库存数据,el-table-column定义每列内容。操作按钮使用el-button,并绑定点击事件。示例代码如下:
<template>
  <el-container>
    <el-main>
      <el-table :data="stockList" border>
        <el-table-column prop="productId" label="商品ID"></el-table-column>
        <el-table-column prop="productName" label="商品名称"></el-table-column>
        <el-table-column prop="stockQuantity" label="库存数量"></el-table-column>
        <el-table-column prop="threshold" label="预警阈值"></el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button @click="startStockTaking(scope.row.productId)">盘点</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-main>
  </el-container>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';

const stockList = ref([]);

const getStockList = async () => {
  const res = await axios.get('http://your-backend-url/stockList');
  stockList.value = res.data.data;
};

const startStockTaking = async (productId) => {
  // 发起盘点请求逻辑
};

onMounted(() => {
  getStockList();
});
</script>

2.2 库存预警功能实现

  • 后端实现:在 SpringBoot 的 Service 层中,通过LambdaQueryWrapper查询库存数量小于预警阈值的商品。当查询到符合条件的商品时,将预警信息封装成对象返回给前端。示例代码如下:
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class StockService {

    @Resource
    private StockMapper stockMapper;

    public List<Stock> getLowStockProducts() {
        LambdaQueryWrapper<Stock> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.lt(Stock::getStockQuantity, Stock::getThreshold);
        return stockMapper.selectList(queryWrapper);
    }
}
  • 前端实现:在 uniapp 或 Element Plus 前端页面,通过定时器定时调用后端接口获取预警信息。当接收到预警信息后,在页面上以红色字体或特殊图标标识预警商品。在 uniapp 中,可以在u-table组件的对应列中根据数据判断添加样式;在 Element Plus 中,在el-table-column的default插槽中根据数据判断添加样式。示例代码(以 Element Plus 为例):
<template>
  <el-container>
    <el-main>
      <el-table :data="stockList" border>
        <el-table-column prop="productId" label="商品ID"></el-table-column>
        <el-table-column prop="productName" label="商品名称">
          <template #default="scope">
            <span :class="scope.row.stockQuantity < scope.row.threshold? 'warning-text' : ''">
              {{ scope.row.productName }}
            </span>
          </template>
        </el-table-column>
        <el-table-column prop="stockQuantity" label="库存数量"></el-table-column>
        <el-table-column prop="threshold" label="预警阈值"></el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button @click="startStockTaking(scope.row.productId)">盘点</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-main>
  </el-container>
</template>

<script setup>
import { ref, onMounted, setInterval } from 'vue';
import axios from 'axios';

const stockList = ref([]);

const getStockList = async () => {
  const res = await axios.get('http://your-backend-url/stockList');
  stockList.value = res.data.data;
};

const startStockTaking = async (productId) => {
  // 发起盘点请求逻辑
};

onMounted(() => {
  getStockList();
  setInterval(getStockList, 60 * 1000); // 每分钟获取一次预警信息
});
</script>

<style scoped>
.warning-text {
  color: red;
}
</style>

2.3 库存盘点功能实现

  • 后端实现:在 SpringBoot 的 Controller 中接收前端传递的盘点数据,包括商品 ID 和实际盘点数量。调用 Service 层方法更新数据库中的库存数据。在 Service 层中,通过LambdaUpdateWrapper构建更新条件,实现库存数据的更新。示例代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StockController {

    @Autowired
    private StockService stockService;

    @PostMapping("/stockTaking")
    public Result stockTaking(@RequestBody StockTakingDTO stockTakingDTO) {
        try {
            stockService.updateStock(stockTakingDTO.getProductId(), stockTakingDTO.getActualQuantity());
            return Result.success("盘点成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("盘点失败");
        }
    }
}

class StockTakingDTO {
    private Long productId;
    private Integer actualQuantity;

    // 省略getter和setter方法
}

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service
public class StockService {

    @Resource
    private StockMapper stockMapper;

    @Transactional
    public void updateStock(Long productId, Integer actualQuantity) {
        LambdaUpdateWrapper<Stock> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Stock::getProductId, productId);
        updateWrapper.set(Stock::getStockQuantity, actualQuantity);
        stockMapper.update(null, updateWrapper);
    }
}
  • 前端实现:当用户点击盘点按钮时,弹出盘点弹窗。在弹窗中,用户输入实际盘点数量,点击确认按钮后,将盘点数据发送到后端。在 uniapp 中,使用uni.showModal弹出确认框,获取用户输入后发送请求;在 Element Plus 中,使用el-dialog组件作为弹窗,通过表单获取用户输入,点击按钮时发送请求。示例代码(以 Element Plus 为例):
<template>
  <el-container>
    <el-main>
      <el-table :data="stockList" border>
        <el-table-column prop="productId" label="商品ID"></el-table-column>
        <el-table-column prop="productName" label="商品名称"></el-table-column>
        <el-table-column prop="stockQuantity" label="库存数量"></el-table-column>
        <el-table-column prop="threshold" label="预警阈值"></el-table-column>
        <el-table-column label="操作">
          <template #default="scope">
            <el-button @click="showStockTakingDialog(scope.row.productId)">盘点</el-button>
          </template>
        </el-table-column>
      </el-table>

      <el-dialog title="库存盘点" :visible.sync="stockTakingDialogVisible" width="30%">
        <el-form :model="stockTakingForm" label-width="80px">
          <el-form-item label="实际数量">
            <el-input v-model="stockTakingForm.actualQuantity" type="number"></el-input>
          </el-form-item>
        </el-form>
        <template #footer>
          <el-button @click="stockTakingDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="confirmStockTaking">确认</el-button>
        </template>
      </el-dialog>
    </el-main>
  </el-container>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';

const stockList = ref([]);
const stockTakingDialogVisible = ref(false);
const stockTakingForm = ref({
  productId: null,
  actualQuantity: null
});

const getStockList = async () => {
  const res = await axios.get('http://your-backend-url/stockList');
  stockList.value = res.data.data;
};

const showStockTakingDialog = (productId) => {
  stockTakingForm.value.productId = productId;
  stockTakingDialogVisible.value = true;
};

const confirmStockTaking = async () => {
  try {
    const res = await axios.post('http://your-backend-url/stockTaking', stockTakingForm.value);
    if (res.data.success) {
      stockTakingDialogVisible.value = false;
      await getStockList();
    } else {
      alert('盘点失败');
    }
  } catch (error) {
    console.error('盘点失败', error);
    alert('盘点失败');
  }
};

onMounted(() => {
  getStockList();
});
</script>

2.4 源码实现与解读

上述代码展示了库存管理页面前后端实现的关键部分。前端通过表格展示库存数据,实现了库存预警标识和库存盘点操作的交互。后端通过 SpringBoot 和 Mybatis-plus 实现了数据的查询、更新等业务逻辑。在整个实现过程中,前后端通过 HTTP 请求进行数据交互,确保了库存管理功能的正常运行。通过对这些源码的解读,可以清晰地了解库存管理功能的实现思路和技术细节,为进一步优化和扩展功能提供了基础。

三、完善商品信息编辑功能

在商城系统中,完善商品信息编辑功能是实现精细化商品管理的重要一环。随着业务的发展,商品的属性和关联信息可能会发生变化,例如商品的分类调整、品牌更换等。一个完善的商品信息编辑功能能够满足这些业务需求,确保商品数据的准确性和完整性,提升用户体验和商城的运营效率。接下来,我们将详细介绍如何使用 uniapp、Element Plus、SpringBoot 和 Mybatis-plus 来完善这一功能。

3.1 前端编辑页面优化

  • uniapp 实现:在 uniapp 中,使用u-form组件搭建商品信息编辑表单。对于商品分类和品牌选择,使用u-picker组件实现下拉选择功能。通过v-model指令绑定表单数据,实现数据的双向绑定。示例代码如下:
<template>
  <view>
    <u-form :model="productForm" ref="productFormRef">
      <u-form-item label="商品名称">
        <u-input v-model="productForm.productName"></u-input>
      </u-form-item>
      <u-form-item label="商品分类">
        <u-picker :columns="categoryOptions" @change="handleCategoryChange" v-model="productForm.categoryId"></u-picker>
      </u-form-item>
      <u-form-item label="商品品牌">
        <u-picker :columns="brandOptions" @change="handleBrandChange" v-model="productForm.brandId"></u-picker>
      </u-form-item>
      <!-- 其他商品信息字段 -->
      <button @click="submitEdit">提交编辑</button>
    </u-form>
  </view>
</template>

<script>
export default {
  data() {
    return {
      productForm: {
        productName: '',
        categoryId: '',
        brandId: ''
      },
      categoryOptions: [],
      brandOptions: []
    };
  },
  methods: {
    async getCategoryOptions() {
      const res = await uni.request({
        url: 'http://your-backend-url/categories',
        method: 'GET'
      });
      this.categoryOptions = res.data.data.map(item => ({ label: item.categoryName, value: item.id }));
    },
    async getBrandOptions() {
      const res = await uni.request({
        url: 'http://your-backend-url/brands',
        method: 'GET'
      });
      this.brandOptions = res.data.data.map(item => ({ label: item.brandName, value: item.id }));
    },
    handleCategoryChange(e) {
      this.productForm.categoryId = e.detail.value;
    },
    handleBrandChange(e) {
      this.productForm.brandId = e.detail.value;
    },
    async submitEdit() {
      await this.$refs.productFormRef.validate();
      const res = await uni.request({
        url: 'http://your-backend-url/products/' + this.productForm.id,
        method: 'PUT',
        data: this.productForm
      });
      if (res.data.success) {
        uni.showToast({ title: '编辑成功', icon:'success' });
        // 刷新商品列表或返回上一页
      } else {
        uni.showToast({ title: '编辑失败', icon: 'none' });
      }
    }
  },
  onLoad() {
    this.getCategoryOptions();
    this.getBrandOptions();
    // 从路由参数获取商品ID,查询商品信息并填充表单
    const productId = this.$route.query.productId;
    if (productId) {
      this.getProductDetail(productId);
    }
  },
  async getProductDetail(productId) {
    const res = await uni.request({
      url: 'http://your-backend-url/products/' + productId,
      method: 'GET'
    });
    this.productForm = res.data.data;
  }
};
</script>
  • Element Plus 实现:利用el-form组件创建商品信息编辑表单,el-select组件实现商品分类和品牌的下拉选择。同样通过v-model绑定数据,实现数据的实时更新。示例代码如下:
<template>
  <el-container>
    <el-main>
      <el-form :model="productForm" ref="productFormRef" label-width="120px">
        <el-form-item label="商品名称">
          <el-input v-model="productForm.productName"></el-input>
        </el-form-item>
        <el-form-item label="商品分类">
          <el-select v-model="productForm.categoryId" placeholder="请选择分类">
            <el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName" :value="item.id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="商品品牌">
          <el-select v-model="productForm.brandId" placeholder="请选择品牌">
            <el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id"></el-option>
          </el-select>
        </el-form-item>
        <!-- 其他商品信息字段 -->
        <el-form-item>
          <el-button type="primary" @click="submitEdit">提交编辑</el-button>
        </el-form-item>
      </el-form>
    </el-main>
  </el-container>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';

const productForm = ref({
  productName: '',
  categoryId: '',
  brandId: ''
});
const categoryOptions = ref([]);
const brandOptions = ref([]);

const getCategoryOptions = async () => {
  const res = await axios.get('http://your-backend-url/categories');
  categoryOptions.value = res.data.data;
};

const getBrandOptions = async () => {
  const res = await axios.get('http://your-backend-url/brands');
  brandOptions.value = res.data.data;
};

const submitEdit = async () => {
  await productFormRef.value.validate();
  try {
    const res = await axios.put('http://your-backend-url/products/' + productForm.value.id, productForm.value);
    if (res.data.success) {
      alert('编辑成功');
      // 刷新商品列表或返回上一页
    } else {
      alert('编辑失败');
    }
  } catch (error) {
    console.error('编辑失败', error);
    alert('编辑失败');
  }
};

onMounted(() => {
  getCategoryOptions();
  getBrandOptions();
  // 从路由参数获取商品ID,查询商品信息并填充表单
  const productId = new URLSearchParams(window.location.search).get('productId');
  if (productId) {
    getProductDetail(productId);
  }
});

const getProductDetail = async (productId) => {
  const res = await axios.get('http://your-backend-url/products/' + productId);
  productForm.value = res.data.data;
};
</script>

3.2 后端编辑接口升级

在 SpringBoot 后端,创建一个 Controller 来接收前端传递的商品编辑请求。通过@PathVariable获取商品 ID,@RequestBody获取编辑后的商品数据。在 Service 层中,根据商品 ID 查询原有商品信息,更新需要修改的字段,如商品分类和品牌关联信息,然后调用 Mybatis-plus 的更新方法将数据持久化到数据库。示例代码如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    @PutMapping("/{id}")
    public Result updateProduct(@PathVariable Long id, @RequestBody Product product) {
        try {
            product.setId(id);
            productService.updateProduct(product);
            return Result.success("编辑成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("编辑失败");
        }
    }
}

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service
public class ProductService {

    @Resource
    private ProductMapper productMapper;

    @Transactional
    public void updateProduct(Product product) {
        // 根据ID查询原有商品信息
        Product originalProduct = productMapper.selectById(product.getId());
        // 更新需要修改的字段
        originalProduct.setProductName(product.getProductName());
        originalProduct.setCategoryId(product.getCategoryId());
        originalProduct.setBrandId(product.getBrandId());
        // 其他字段更新

        // 更新数据库
        productMapper.updateById(originalProduct);
    }
}

3.3 数据库关联更新(Mybatis-plus)

Mybatis-plus 提供了便捷的更新操作,无需编写复杂的 SQL 语句。在上述 Service 层代码中,通过productMapper.updateById(originalProduct)方法即可实现商品信息的更新。如果商品与分类、品牌存在关联关系,并且在数据库设计中通过外键关联,当更新商品的分类 ID 和品牌 ID 时,数据库会自动维护关联关系的一致性。例如,假设商品表product中有category_id和brand_id字段分别关联分类表category和品牌表brand的主键:

<!-- 假设Mybatis-plus的配置文件,虽然不需要手写mapper文件,但这里展示配置结构 -->
<configuration>
    <settings>
        <!-- 开启驼峰命名映射 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

在 Java 代码中,当执行productMapper.updateById(originalProduct)时,Mybatis-plus 会根据originalProduct对象中的属性值生成对应的 SQL 语句,实现商品信息及关联字段的更新。例如,生成的 SQL 语句可能类似于:

UPDATE product 
SET product_name = #{productName}, category_id = #{categoryId}, brand_id = #{brandId} 
WHERE id = #{id}

这样就完成了商品信息及其关联的分类、品牌数据在数据库中的更新操作。

3.4 完整代码示例与解析

  • 前端完整代码(以 Element Plus 为例)
<template>
  <el-container>
    <el-main>
      <el-form :model="productForm" ref="productFormRef" label-width="120px">
        <el-form-item label="商品名称">
          <el-input v-model="productForm.productName"></el-input>
        </el-form-item>
        <el-form-item label="商品分类">
          <el-select v-model="productForm.categoryId" placeholder="请选择分类">
            <el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName" :value="item.id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="商品品牌">
          <el-select v-model="productForm.brandId" placeholder="请选择品牌">
            <el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="商品价格">
          <el-input-number v-model="productForm.price" :precision="2"></el-input-number>
        </el-form-item>
        <el-form-item label="商品库存">
          <el-input-number v-model="productForm.stock"></el-input-number>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="submitEdit">提交编辑</el-button>
        </el-form-item>
      </el-form>
    </el-main>
  </el-container>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';

const productForm = ref({
  id: null,
  productName: '',
  categoryId: '',
  brandId: '',
  price: 0,
  stock: 0
});
const categoryOptions = ref([]);
const brandOptions = ref([]);

const getCategoryOptions = async () => {
  const res = await axios.get('http://your-backend-url/categories');
  categoryOptions.value = res.data.data;
};

const getBrandOptions = async () => {
  const res = await axios.get('http://your-backend-url/brands');
  brandOptions.value = res.data.data;
};

const submitEdit = async () => {
  await productFormRef.value.validate();
  try {
    if (productForm.value.id) {
      const res = await axios.put('http://your-backend-url/products/' + productForm.value.id, productForm.value);
      if (res.data.success) {
        alert('编辑成功');
        // 刷新商品列表或返回上一页
      } else {
        alert('编辑失败');
      }
    } else {
      alert('商品ID为空,无法编辑');
    }
  } catch (error) {
    console.error('编辑失败', error);
    alert('编辑失败');
  }
};

onMounted(() => {
  getCategoryOptions();
  getBrandOptions();
  const productId = new URLSearchParams(window.location.search).get('productId');
  if (productId) {
    getProductDetail(productId);
  }
});

const getProductDetail = async (productId) => {
  const res = await axios.get('http://your-backend-url/products/' + productId);
  productForm.value = res.data.data;
};
</script>
  • 后端完整代码
// ProductController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    @PutMapping("/{id}")
    public Result updateProduct(@PathVariable Long id, @RequestBody Product product) {
        try {
            product.setId(id);
            productService.updateProduct(product);
            return Result.success("编辑成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("编辑失败");
        }
    }
}

// ProductService.java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service
public class ProductService {

    @Resource
    private ProductMapper productMapper;

    @Transactional
    public void updateProduct(Product product) {
        Product originalProduct = productMapper.selectById(product.getId());
        originalProduct.setProductName(product.getProductName());
        originalProduct.setCategoryId(product.getCategoryId());
        originalProduct.setBrandId(product.getBrandId());
        originalProduct.setPrice(product.getPrice());
        originalProduct.setStock(product.getStock());
        productMapper.updateById(originalProduct);
    }
}

// ProductMapper.java 由Mybatis-plus自动生成,继承BaseMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import com.example.demo.entity.Product;

@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}

// Product.java 商品实体类
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

@Data
public class Product {
    @TableId
    private Long id;
    private String productName;
    private Long categoryId;
    private Long brandId;
    private Double price;
    private Integer stock;
}

// Result.java 返回结果类
class Result {
    private boolean success;
    private String message;

    public static Result success(String message) {
        Result result = new Result();
        result.success = true;
        result.message = message;
        return result;
    }

    public static Result error(String message) {
        Result result = new Result();
        result.success = false;
        result.message = message;
        return result;
    }

    // 省略getter和setter方法
}
  • 代码解析
    • 前端部分通过el-form和el-select等组件构建了商品信息编辑表单,实现了用户与页面的交互。getCategoryOptions和getBrandOptions方法从后端获取商品分类和品牌数据,填充下拉选择框。submitEdit方法在用户点击提交按钮时,验证表单数据并将编辑后的商品信息发送到后端。
    • 后端ProductController接收前端的编辑请求,调用ProductService的updateProduct方法处理业务逻辑。ProductService中,先根据商品 ID 查询原有商品信息,然后更新需要修改的字段,最后通过ProductMapper的updateById方法将更新后的数据保存到数据库。
    • Mybatis-plus 的BaseMapper为ProductMapper提供了基本的 CRUD 操作,使得数据库操作更加便捷高效。通过上述前后端代码的协同工作,实现了完善的商品信息编辑功能,满足了商城对商品精细化管理的需求。

四、总结与展望

通过上述对商品批量上架、下架功能,商品库存管理页面,以及商品信息编辑功能的深化开发,我们显著提升了商城后台商品管理的效率和灵活性。商品批量操作功能极大地减少了商家调整商品展示状态的时间成本,使得商品能够更及时地响应市场需求;库存管理页面的开发实现了库存的可视化管理和预警机制,有效避免了缺货和库存积压问题;完善的商品信息编辑功能则确保了商品数据的准确性和完整性,为商城的精细化运营提供了有力支持。

展望未来,我们可以进一步优化和拓展商品管理功能。在技术层面,持续提升系统性能,引入更先进的缓存技术和数据库优化策略,以应对高并发场景下的商品管理需求;在功能拓展方面,增加商品数据分析功能,通过对商品销售数据、库存数据等的深入挖掘,为商家提供更具针对性的决策支持,如智能补货建议、商品推荐策略等;同时,随着移动端购物的普及,进一步优化 uniapp 前端的用户体验,实现更流畅的操作和更友好的界面设计。此外,考虑到多语言和多地区的业务拓展,商品管理系统应具备良好的国际化支持,以满足不同地区用户的需求。通过不断地优化和创新,我们将为商城的稳定发展和业务增长奠定坚实的基础。

相关文章:

  • 【理想解法学习笔记】
  • 计算机操作系统
  • docker企业级事例部署phpmyadmin和MySQL
  • win10电脑鼠标速度突然变的很慢?
  • 【Go语言圣经1.1】
  • Linux 常用测试网络带宽命令
  • 八股打卡(七)
  • 密码学 网络安全 科普 网络安全密码技术
  • 【vllm】Qwen2.5-VL-72B-AWQ 部署记录
  • Webshell原理与利用
  • 天津大学:《深度解读DeepSeek:部署、使用、安全》
  • 仅仅使用pytorch来手撕transformer架构(2):多头注意力MultiHeadAttention类的实现和向前传播
  • 侯捷 C++ 课程学习笔记:C++内存管理机制
  • Qt 初识
  • Unity Android出包
  • Mysql高频面试题
  • Gemini 2.0 Flash
  • AQS及派生类
  • AI日报 - 2025年3月11日
  • Spring Cloud 负载均衡器架构选型