乐优商城(填坑)——秒杀商品添加
一、需求后台商品管理中,将商品添加到可秒杀商品列表选中商品将其设置为可秒杀。选择具体的参与秒杀的商品规格,然后设置相关参数,点击保存即可。二、后端接口修改原来的添加秒杀商品接口在leyou-secskill微服务中,现在将其移动到leyou-item中,代码如下:2.1 Controller/*** 添加秒杀商品* @param sec...
·
一、需求
后台商品管理中,将商品添加到可秒杀商品列表
选中商品将其设置为可秒杀。
选择具体的参与秒杀的商品规格,然后设置相关参数,点击保存即可。
二、后端接口修改
原来的添加秒杀商品接口在leyou-secskill微服务中,现在将其移动到leyou-item中,代码如下:
2.1 Controller
/**
* 添加秒杀商品
* @param seckillParameters
* @return
* @throws ParseException
*/
@PostMapping("/seckill/add")
public ResponseEntity<Boolean> addSeckillGoods(@RequestBody List<SeckillParameter> seckillParameters) throws ParseException {
if (seckillParameters != null && seckillParameters.size() > 0){
for (SeckillParameter seckillParameter : seckillParameters){
this.goodsService.addSeckillGoods(seckillParameter);
}
}else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
return ResponseEntity.ok().build();
}
2.2 Service
/**
* 添加秒杀商品
* @param seckillParameter
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void addSeckillGoods(SeckillParameter seckillParameter) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm" );
//1.根据spu_id查询商品
Long id = seckillParameter.getId();
Sku sku = this.querySkuById(id);
//2.插入到秒杀商品表中
SeckillGoods seckillGoods = new SeckillGoods();
seckillGoods.setEnable(true);
seckillGoods.setStartTime(sdf.parse(seckillParameter.getStartTime().trim()));
seckillGoods.setEndTime(sdf.parse(seckillParameter.getEndTime().trim()));
seckillGoods.setImage(sku.getImages());
seckillGoods.setSkuId(sku.getId());
seckillGoods.setStock(seckillParameter.getCount());
seckillGoods.setTitle(sku.getTitle());
seckillGoods.setSeckillPrice(sku.getPrice()*seckillParameter.getDiscount());
this.seckillMapper.insert(seckillGoods);
//3.更新对应的库存信息,tb_stock
Stock stock = stockMapper.selectByPrimaryKey(sku.getId());
System.out.println(stock);
if (stock != null) {
stock.setSeckillStock(stock.getSeckillStock() != null ? stock.getSeckillStock() + seckillParameter.getCount() : seckillParameter.getCount());
stock.setSeckillTotal(stock.getSeckillTotal() != null ? stock.getSeckillTotal() + seckillParameter.getCount() : seckillParameter.getCount());
stock.setStock(stock.getStock() - seckillParameter.getCount());
this.stockMapper.updateByPrimaryKeySelective(stock);
}else {
LOGGER.info("更新库存失败!");
}
//4.更新redis中的秒杀库存
updateSeckillStock();
}
/**
* 更新秒杀商品数量
* @throws Exception
*/
public void updateSeckillStock(){
//1.查询可以秒杀的商品
List<SeckillGoods> seckillGoods = this.querySeckillGoods();
if (seckillGoods == null || seckillGoods.size() == 0){
return;
}
BoundHashOperations<String,Object,Object> hashOperations = this.stringRedisTemplate.boundHashOps(KEY_PREFIX);
if (hashOperations.hasKey(KEY_PREFIX)){
hashOperations.delete(KEY_PREFIX);
}
seckillGoods.forEach(goods -> hashOperations.put(goods.getSkuId().toString(),goods.getStock().toString()));
}
service的具体实现和前边没有什么大的改变,就是多了一步更新redis中秒杀库存的操作,原来是放在leyou-secskill中Controller初始化时进行的。上面代码中所包含的辅助类直接copy到leyou-item中就可以了。
三、前端页面
新增一个对话框,用来显示可秒杀的商品:
点击保存的时候将数据进行封装,然后发到后台即可。
dataTable中新增秒杀按钮:
点击事件:(主要作用就是数据传递)
对话框代码:
<template>
<v-form ref="SeckillForm">
<v-container grid-list-md>
<v-layout column wrap>
<v-flex xs12 lg6>
<v-layout row>
<v-text-field
readonly
label="商品编号"
prepend-icon="label"
style="width: 50px"
v-model="goods_message.goodsId"
></v-text-field>
<v-text-field
readonly
label="商品名称"
prepend-icon="label"
style="width: 100px"
v-model="goods_message.goodsTitle"
></v-text-field>
<v-text-field
readonly
label="商品分类"
prepend-icon="label"
style="width: 100px"
v-model="goods_message.goodsCname"
></v-text-field>
<v-text-field
readonly
label="品牌"
prepend-icon="label"
style="width: 100px"
v-model="goods_message.goodsBname"
></v-text-field>
</v-layout>
<v-layout row>
<v-layout row>
<v-menu
ref="menu1"
:close-on-content-click="false"
v-model="menu1"
:nudge-right="40"
lazy
transition="scale-transition"
offset-y
full-width
max-width="290px"
min-width="290px"
>
<v-text-field
slot="activator"
v-model="dateFormatted"
label="开始日期"
prepend-icon="event"
persistent-hint
readonly
@blur="date = parseDate(dateFormatted)"
></v-text-field>
<v-date-picker locale="zh-cn" v-model="date" no-title @input="menu1 = false"></v-date-picker>
</v-menu>
<v-dialog
ref="dialog1"
v-model="modal1"
:return-value.sync="time1"
persistent
lazy
full-width
width="290px"
>
<v-text-field
slot="activator"
v-model="time1"
label="开始时间"
prepend-icon="access_time"
readonly
></v-text-field>
<v-time-picker
v-if="modal1"
v-model="time1"
full-width
>
<v-spacer></v-spacer>
<v-btn flat color="primary" @click="modal1 = false">Cancel</v-btn>
<v-btn flat color="primary" @click="$refs.dialog1.save(time1)">OK</v-btn>
</v-time-picker>
</v-dialog>
</v-layout>
<v-layout row>
<v-menu
:close-on-content-click="false"
v-model="menu2"
:nudge-right="40"
lazy
transition="scale-transition"
offset-y
full-width
max-width="390px"
min-width="390px"
>
<v-text-field
slot="activator"
v-model="dateFormatted2"
label="结束日期"
prepend-icon="event"
persistent-hint
readonly
@blur="date2 = parseDate(dateFormatted2)"
></v-text-field>
<v-date-picker locale="zh-cn" v-model="date2" no-title @input="menu2 = false"></v-date-picker>
</v-menu>
<v-dialog
ref="dialog2"
v-model="modal2"
:return-value.sync="time2"
persistent
lazy
full-width
width="290px"
>
<v-text-field
slot="activator"
v-model="time2"
label="结束时间"
prepend-icon="access_time"
readonly
></v-text-field>
<v-time-picker
v-if="modal2"
v-model="time2"
full-width
>
<v-spacer></v-spacer>
<v-btn flat color="primary" @click="modal2 = false">Cancel</v-btn>
<v-btn flat color="primary" @click="$refs.dialog2.save(time2)">OK</v-btn>
</v-time-picker>
</v-dialog>
</v-layout>
</v-layout>
</v-flex>
<v-flex xs12 lg6>
<v-data-table
:headers="headers"
:items="sku"
class="elevation-1"
hide-actions
>
<template v-if="props.item.enable === false" slot="items" slot-scope="props">
<td class="text-xs-center" style="width: 150px">{{ props.item.spec }}</td>
<td class="text-xs-center" >{{ props.item.price }}</td>
<td class="text-xs-center">{{ props.item.stock }}</td>
<td class="text-xs-center">
<v-select
:items="items"
item-text="dis"
item-value="value"
v-model="props.item.discount"
></v-select></td>
<td class="text-xs-center">
<v-text-field
label="数量"
v-model="props.item.seckill_count"
></v-text-field>
</td>
<td class="text-xs-center">
<v-checkbox color="primary" v-model="props.item.enable"/>
</td>
</template>
</v-data-table>
</v-flex>
</v-layout>
</v-container>
<v-layout class="pt-3" >
<v-spacer></v-spacer>
<v-btn color="blue darken-1" flat @click="clear">清空</v-btn>
<v-btn color="blue darken-1" flat @click="submit">保存</v-btn>
</v-layout>
</v-form>
</template>
<script>
export default {
name: "MySeckillForm",
data: vm => ({
headers: [
{ text: '规格属性', value: 'spec', align:"center", sortable:false},
{ text: '价格', value: 'price' , align:"center", sortable:false},
{ text: '现有库存', value: 'stock' , align:"center", sortable:false},
{ text: '折扣', align:"center", sortable:false},
{ text: '秒杀数量' , align:"center", sortable:false},
{ text: '是否秒杀' , align:"center", sortable:false}
],
goods_count:'',
date: new Date().toISOString().substr(0, 10),
date2: new Date().toISOString().substr(0, 10),
dateFormatted: vm.formatDate(new Date().toISOString().substr(0, 10)),
dateFormatted2: vm.formatDate(new Date().toISOString().substr(0, 10)),
menu1: false,
menu2: false,
time1: null,
modal1: false,
time2: null,
modal2: false,
items: [
{dis:'一折',value:0.1},
{dis:'二折',value:0.2},
{dis:'三折',value:0.3},
{dis:'四折',value:0.4},
{dis:'五折',value:0.5},
{dis:'六折',value:0.6},
{dis:'七折',value:0.7},
{dis:'八折',value:0.8},
],
goods_message:{}, //秒杀商品信息
skus:[],
sku_temp:[],
}),
props:{
seckill_goods_message:{type:Object},
},
computed:{
sku:{
// getter
get: function () {
let temp = [];
this.skus.forEach(sku => {
const ownSpec = JSON.parse(sku.ownSpec);
let str = "";
for (let key in ownSpec){
str += (" " + ownSpec[key])
}
temp.push({
id:sku.id,
spec:str,
price:this.$format(sku.price),
stock:sku.stock,
enable:false,
})
});
return temp;
},
// setter
set: function (newValue) {
}
}
},
watch: {
seckill_goods_message:{
deep:true,
handler(val){
console.log(val);
if(val){
this.goods_message = Object.deepCopy(val);
this.loadData(this.goods_message.goodsId);
}else{
this.clear();
}
}
},
date (val) {
this.dateFormatted = this.formatDate(this.date)
},
date2 (val) {
this.dateFormatted2 = this.formatDate(this.date2)
if (this.date2 < this.date){
this.$message.confirm("结束日期必须大于开始日期!").then(() => {
this.date2 = null;
}).catch(() => {
this.date2 = null;
});
}
},
},
created(){
let temp = [];
this.skus.forEach(sku => {
const ownSpec = JSON.parse(sku.ownSpec);
let str = "";
for (let key in ownSpec){
str += (" " + ownSpec[key])
}
temp.push({
id:sku.id,
spec:str,
price:this.$format(sku.price),
stock:sku.stock,
enable:false,
})
});
this.sku_temp = temp;
this.clear();
},
methods: {
loadData(id){
//查询spu下的所有sku
this.$http.get("/item/goods/sku/list/" + id).then((resp) => {
this.skus = resp.data;
}).catch();
},
formatDate (date) {
if (!date) return null;
const [year, month, day] = date.split('-');
return `${year}年${month}月${day}日`
},
parseDate (date) {
if (!date) return null;
const [month, day, year] = date.split('/');
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`
},
clear(){
this.goods_count = null;
this.date = null;
this.date2 = null;
this.time1 = null;
this.time2 = null;
this.discount = null;
},
submit(){
const startTime = this.date +" " + this.time1;
const endTime = this.date2 +" " + this.time2;
let result = [];
this.sku.forEach(temp => {
if (temp.enable){
result.push({
startTime:startTime,
endTime:endTime,
id:temp.id,
count:parseInt(temp.seckill_count),
discount:temp.discount
});
}
});
this.verify().then(() => {
this.$http({
url:"/item/goods/seckill/add",
method:"post",
headers : {
'Content-Type' : 'application/json;charset=utf-8'
},
dataType:"json",
data:JSON.stringify(result)
}).then(() =>{
//添加成功
setTimeout(() =>{
this.$emit('seckill_close');
this.$message.success("保存成功!");
this.clear();
},2000);
}).catch(() => {
this.$message.error("添加失败!");
});
}).catch(() => {
this.$router.push("/login");
});
}
}
}
</script>
<style scoped>
</style>
主页面中调用:
更多推荐
已为社区贡献9条内容
所有评论(0)