樂優商城(填坑)——秒殺商品新增
阿新 • • 發佈:2019-01-01
一、需求
後臺商品管理中,將商品新增到可秒殺商品列表
選中商品將其設定為可秒殺。
選擇具體的參與秒殺的商品規格,然後設定相關引數,點選儲存即可。
二、後端介面修改
原來的新增秒殺商品介面在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>
主頁面中呼叫: