Commit ad82b780 authored by Junling Bu's avatar Junling Bu
Browse files

feat[litemall-admin, litemall-admin-api]:商品上架页面。

在这一个页面同时添加商品相关的四个表,包括商品基本信息、商品规格、商品货品、商品参数。
parent 897980ed
package org.linlinjava.litemall.admin.dao;
import org.linlinjava.litemall.db.domain.LitemallGoods;
import org.linlinjava.litemall.db.domain.LitemallGoodsAttribute;
import org.linlinjava.litemall.db.domain.LitemallGoodsSpecification;
import org.linlinjava.litemall.db.domain.LitemallProduct;
public class GoodsAllinone {
LitemallGoods goods;
LitemallGoodsSpecification[] specifications;
LitemallGoodsAttribute[] attributes;
// 这里采用 Product 再转换到 LitemallProduct
Product[] products;
public LitemallGoods getGoods() {
return goods;
}
public Product[] getProducts() {
return products;
}
public void setProducts(Product[] products) {
this.products = products;
}
public void setGoods(LitemallGoods goods) {
this.goods = goods;
}
public LitemallGoodsSpecification[] getSpecifications() {
return specifications;
}
public void setSpecifications(LitemallGoodsSpecification[] specifications) {
this.specifications = specifications;
}
public LitemallGoodsAttribute[] getAttributes() {
return attributes;
}
public void setAttributes(LitemallGoodsAttribute[] attributes) {
this.attributes = attributes;
}
}
package org.linlinjava.litemall.admin.dao;
import java.math.BigDecimal;
public class Product {
String[] specifications;
BigDecimal price;
Integer number;
String url;
public String[] getSpecifications() {
return specifications;
}
public void setSpecifications(String[] specifications) {
this.specifications = specifications;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
package org.linlinjava.litemall.admin.util;
import java.util.List;
public class CatVo {
private Integer value = null;
private String label = null;
private List children = null;
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public List getChildren() {
return children;
}
public void setChildren(List children) {
this.children = children;
}
}
......@@ -3,6 +3,7 @@ package org.linlinjava.litemall.admin.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.admin.annotation.LoginAdmin;
import org.linlinjava.litemall.admin.util.CatVo;
import org.linlinjava.litemall.db.domain.LitemallCategory;
import org.linlinjava.litemall.db.service.LitemallCategoryService;
import org.linlinjava.litemall.core.util.ResponseUtil;
......@@ -10,6 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -98,4 +100,35 @@ public class AdminCategoryController {
return ResponseUtil.ok(data);
}
@GetMapping("/list2")
public Object list2(@LoginAdmin Integer adminId) {
if (adminId == null) {
return ResponseUtil.unlogin();
}
List<LitemallCategory> l1CatList = categoryService.queryL1();
List<CatVo> list = new ArrayList<>(l1CatList.size());
for(LitemallCategory l1 : l1CatList){
CatVo l1CatVo = new CatVo();
l1CatVo.setValue(l1.getId());
l1CatVo.setLabel(l1.getName());
List<LitemallCategory> l2CatList = categoryService.queryByPid(l1.getId());
List<CatVo> children = new ArrayList<>(l2CatList.size());
for(LitemallCategory l2 : l2CatList) {
CatVo l2CatVo = new CatVo();
l2CatVo.setValue(l2.getId());
l2CatVo.setLabel(l2.getName());
children.add(l2CatVo);
}
l1CatVo.setChildren(children);
list.add(l1CatVo);
}
return ResponseUtil.ok(list);
}
}
package org.linlinjava.litemall.admin.web;
import io.swagger.models.auth.In;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.admin.annotation.LoginAdmin;
import org.linlinjava.litemall.admin.dao.GoodsAllinone;
import org.linlinjava.litemall.admin.dao.Product;
import org.linlinjava.litemall.db.domain.LitemallGoods;
import org.linlinjava.litemall.db.domain.LitemallGoodsAttribute;
import org.linlinjava.litemall.db.domain.LitemallGoodsSpecification;
import org.linlinjava.litemall.db.domain.LitemallProduct;
import org.linlinjava.litemall.db.service.LitemallGoodsAttributeService;
import org.linlinjava.litemall.db.service.LitemallGoodsService;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.db.service.LitemallGoodsSpecificationService;
import org.linlinjava.litemall.db.service.LitemallProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -18,8 +34,17 @@ import java.util.Map;
public class AdminGoodsController {
private final Log logger = LogFactory.getLog(AdminGoodsController.class);
@Autowired
private PlatformTransactionManager txManager;
@Autowired
private LitemallGoodsService goodsService;
@Autowired
private LitemallGoodsSpecificationService specificationService;
@Autowired
private LitemallGoodsAttributeService attributeService;
@Autowired
private LitemallProductService productService;
@GetMapping("/list")
public Object list(@LoginAdmin Integer adminId,
......@@ -81,4 +106,73 @@ public class AdminGoodsController {
return ResponseUtil.ok();
}
@PostMapping("/publish")
public Object publish(@LoginAdmin Integer adminId, @RequestBody GoodsAllinone goodsAllinone){
if(adminId == null){
return ResponseUtil.unlogin();
}
LitemallGoods goods = goodsAllinone.getGoods();
LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes();
LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications();
Product[] products = goodsAllinone.getProducts();
String name = goods.getName();
if(goodsService.checkExistByName(name)){
return ResponseUtil.fail(403, "商品名已经存在");
}
// 开启事务管理
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// 商品基本信息表litemall_goods
goods.setAddTime(LocalDateTime.now());
goodsService.add(goods);
// 商品规格表litemall_goods_specification
Map<String, Integer> specIds = new HashMap<>();
for(LitemallGoodsSpecification specification : specifications){
specification.setGoodsId(goods.getId());
specification.setAddTime(LocalDateTime.now());
specificationService.add(specification);
specIds.put(specification.getValue(), specification.getId());
}
// 商品参数表litemall_goods_attribute
for(LitemallGoodsAttribute attribute : attributes){
attribute.setGoodsId(goods.getId());
attribute.setAddTime(LocalDateTime.now());
attributeService.add(attribute);
}
// 商品货品表litemall_product
for(Product product : products){
LitemallProduct litemallProduct = new LitemallProduct();
litemallProduct.setRetailPrice(product.getPrice());
litemallProduct.setGoodsNumber(product.getNumber());
litemallProduct.setUrl(product.getUrl());
litemallProduct.setGoodsId(goods.getId());
litemallProduct.setAddTime(LocalDateTime.now());
String[] values = product.getSpecifications();
Integer[] ids = new Integer[values.length];
for(int i = 0; i < values.length; i++){
ids[i] = specIds.get(values[i]);
}
Arrays.sort(ids);
litemallProduct.setGoodsSpecificationIds(ids);
productService.add(litemallProduct);
}
} catch (Exception ex) {
txManager.rollback(status);
logger.error("系统内部错误", ex);
}
txManager.commit(status);
return ResponseUtil.ok();
}
}
......@@ -46,3 +46,11 @@ export function deleteCategory(data) {
data
})
}
export function listCategory2(query) {
return request({
url: '/category/list2',
method: 'get',
params: query
})
}
......@@ -39,3 +39,11 @@ export function deleteGoods(data) {
data
})
}
export function publishGoods(data) {
return request({
url: '/goods/publish',
method: 'post',
data
})
}
......@@ -64,3 +64,6 @@ export function deleteStorage(data) {
export function getUploadApi(data) {
return process.env.OS_API + '/storage/create'
}
const uploadPath = process.env.OS_API + '/storage/create'
export { uploadPath }
......@@ -98,6 +98,7 @@ export const asyncRouterMap = [
icon: 'chart'
},
children: [
{ path: 'publish', component: _import('goods/publish'), name: 'publish', meta: { title: '商品上架', noCache: true }},
{ path: 'goods', component: _import('goods/goods'), name: 'goods', meta: { title: '商品管理', noCache: true }},
{ path: 'attribute', component: _import('goods/attribute'), name: 'attribute', meta: { title: '商品参数', noCache: true }},
{ path: 'specification', component: _import('goods/specification'), name: 'specification', meta: { title: '商品规格', noCache: true }},
......
......@@ -50,7 +50,8 @@ service.interceptors.response.use(
})
return Promise.reject('error')
} else if (res.errno !== 0) {
return Promise.reject('error')
// 非5xx的错误属于业务错误,留给具体页面处理
return Promise.reject(response)
} else {
return response
}
......
......@@ -181,8 +181,7 @@
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button v-if="dialogStatus=='create'" type="primary" @click="createData">确定</el-button>
<el-button v-else type="primary" @click="updateData">确定</el-button>
<el-button type="primary" @click="updateData">确定</el-button>
</div>
</el-dialog>
......@@ -208,7 +207,7 @@
</style>
<script>
import { listGoods, createGoods, updateGoods, deleteGoods } from '@/api/goods'
import { listGoods, updateGoods, deleteGoods } from '@/api/goods'
import { createStorage, getUploadApi } from '@/api/storage'
import waves from '@/directive/waves' // 水波纹指令
import BackToTop from '@/components/BackToTop'
......@@ -359,28 +358,7 @@ export default {
}
},
handleCreate() {
this.resetForm()
this.dialogStatus = 'create'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
createData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
createGoods(this.dataForm).then(response => {
this.list.unshift(response.data.data)
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
})
}
})
this.$router.push({ path: '/goods/publish' })
},
handleUpdate(row) {
this.dataForm = Object.assign({}, row)
......
......@@ -6,7 +6,6 @@
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID" v-model="listQuery.goodsId">
</el-input>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-download" @click="handleDownload" :loading="downloadLoading">导出</el-button>
</div>
......@@ -42,7 +41,6 @@
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
......@@ -54,20 +52,8 @@
</el-pagination>
</div>
<el-dialog title="添加货品" :visible.sync="createDialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="dataForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="商品ID" prop="goodsId">
<el-input v-model="dataForm.goodsId"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="createDialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="createData">确定</el-button>
</div>
</el-dialog>
<!-- 修改对话框 -->
<el-dialog title="修改货品" :visible.sync="editDialogFormVisible">
<el-dialog title="编辑商品货品" :visible.sync="editDialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="dataForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="商品ID" prop="goodsId">
<el-input v-model="dataForm.goodsId" :disabled="true"></el-input>
......@@ -112,7 +98,7 @@
</style>
<script>
import { listProduct, createProduct, updateProduct, deleteProduct } from '@/api/product'
import { listProduct, updateProduct } from '@/api/product'
import { createStorage } from '@/api/storage'
import waves from '@/directive/waves' // 水波纹指令
......@@ -133,7 +119,6 @@ export default {
goodsId: undefined,
sort: '+id'
},
createDialogFormVisible: false,
editDialogFormVisible: false,
dataForm: {
id: undefined,
......@@ -197,29 +182,6 @@ export default {
this.$message.error('上传失败,请重新上传')
})
},
handleCreate() {
this.resetForm()
this.createDialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
createData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
createProduct(this.dataForm).then(response => {
this.getList()
this.createDialogFormVisible = false
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleUpdate(row) {
this.dataForm = Object.assign({}, row)
this.editDialogFormVisible = true
......@@ -249,18 +211,6 @@ export default {
}
})
},
handleDelete(row) {
deleteProduct(row).then(response => {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
......
This diff is collapsed.
......@@ -6,7 +6,6 @@
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID" v-model="listQuery.goodsId">
</el-input>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button class="filter-item" type="primary" @click="handleCreate" icon="el-icon-edit">添加</el-button>
<el-button class="filter-item" type="primary" :loading="downloadLoading" v-waves icon="el-icon-download" @click="handleDownload">导出</el-button>
</div>
......@@ -30,7 +29,6 @@
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
......@@ -42,8 +40,8 @@
</el-pagination>
</div>
<!-- 添加或修改对话框 -->
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<!-- 修改对话框 -->
<el-dialog title="编辑商品规格" :visible.sync="dialogFormVisible">
<el-form :rules="rules" ref="dataForm" :model="dataForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-form-item label="商品ID" prop="goodsId">
<el-input v-model="dataForm.goodsId"></el-input>
......@@ -60,8 +58,7 @@
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button v-if="dialogStatus=='create'" type="primary" @click="createData">确定</el-button>
<el-button v-else type="primary" @click="updateData">确定</el-button>
<el-button type="primary" @click="updateData">确定</el-button>
</div>
</el-dialog>
......@@ -69,7 +66,7 @@
</template>
<script>
import { listGoodsSpecification, createGoodsSpecification, updateGoodsSpecification, deleteGoodsSpecification } from '@/api/goods-specification'
import { listGoodsSpecification, updateGoodsSpecification } from '@/api/goods-specification'
import waves from '@/directive/waves' // 水波纹指令
export default {
......@@ -97,10 +94,6 @@ export default {
},
dialogFormVisible: false,
dialogStatus: '',
textMap: {
update: '编辑',
create: '创建'
},
rules: {
goodsId: [{ required: true, message: '商品ID不能为空', trigger: 'blur' }],
specification: [{ required: true, message: '商品规格名称不能为空', trigger: 'blur' }],
......@@ -146,30 +139,6 @@ export default {
picUrl: undefined
}
},
handleCreate() {
this.resetForm()
this.dialogStatus = 'create'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
createData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
createGoodsSpecification(this.dataForm).then(response => {
this.list.unshift(response.data.data)
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleUpdate(row) {
this.dataForm = Object.assign({}, row)
this.dialogStatus = 'update'
......@@ -200,18 +169,6 @@ export default {
}
})
},
handleDelete(row) {
deleteGoodsSpecification(row).then(response => {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
......
......@@ -207,4 +207,10 @@ public class LitemallGoodsService {
}
return cats;
}
public boolean checkExistByName(String name) {
LitemallGoodsExample example = new LitemallGoodsExample();
example.or().andNameEqualTo(name).andDeletedEqualTo(false);
return goodsMapper.countByExample(example) != 0;
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment