'未开启', 2 => '进行中', 3 => '已结束' ]; // 拼团的商品规格key %u: activity_id, %u: sku protected $piecingKey = 'piecing:%u:%s'; // 秒杀限制信息key %u: activity_id protected $piecingLimitKey = 'piecing_limit:%u'; /** * 秒杀商品列表 * @param $param */ public function getList($param) { $seckillTimeModel = new PiecingActivity(); // 一键关闭过期的 $seckillTimeModel->updateByWhere([ 'status' => 3, // 已结束 'update_time' => now() ], [ ['end_time', '<', date('Y-m-d H:i:s')], ['status', '=', 1], ['is_open', '=', 1] ]); $limit = $param['limit']; $name = $param['name'] ?? ''; $where = []; if (!empty($name)) { $where[] = ['name', 'like', '%' . $name . '%']; } $activityStatus = $this->activityStatus; $list = $seckillTimeModel->getPaginateList($where, ['*'], ['id' => 'desc'], [], $limit); foreach ($list['data'] as $k => $item) { $list['data'][$k]['status_txt'] = $activityStatus[$item['status']]; } return dataReturn(0, 'success', $list); } /** * 检测参数 * @param $param * @return array */ protected function checkParam($param) { $validate = new PiecingActivityValidate(); if (!$validate->check($param)) { throw new ApiException($validate->getError()); } if ($param['total_buy_num'] < $param['once_buy_num']) { throw new ApiException('购买总是限制不得小于单次购买限制'); } // 单规格 if ($param['goods_rule'] == 1) { if (!isset($param['seckill_price']) || empty($param['seckill_price'])) { throw new ApiException('选择的规格,拼团价应大于等于0'); } if (!is_numeric($param['stock']) || $param['stock'] < 0) { throw new ApiException('选择的规格,限量应该大于等于0'); } if ($param['stock'] > $param['goods_stock']) { throw new ApiException('限量库存不得大于商品库存'); } } else { // 多规格 foreach ($param['final'] as $vo) { if (!is_numeric($vo['piecing_price']) || $vo['piecing_price'] < 0) { throw new ApiException('选择的规格,拼团价应大于等于0'); } if (!is_numeric($vo['stock']) || $vo['stock'] < 0) { throw new ApiException('选择的规格,限量应该大于等于0'); } if ($vo['stock'] > $vo['goods_stock']) { throw new ApiException('限量库存不得大于商品库存'); } } } return true; } public function addPiecingActivityGoods($param) { $this->checkParam($param); $timeMap = $param['activity_time']; Db::beginTransaction(); try { //添加活动 $activity = [ 'goods_id' => $param['goods_id'], 'goods_rule' => $param['goods_rule'], 'pic' => $param['pic'], 'name' => $param['name'], 'desc' => $param['desc'], 'pintuan_time' => $param['pintuan_time'], 'is_recommend' => 0, 'start_time' => $timeMap[0], 'end_time' => $timeMap[1], 'total_buy_num' => $param['total_buy_num'], 'once_buy_num' => $param['once_buy_num'], 'status' => ($param['is_open'] == 1) ? 2 : 1, 'pintuan_num' => $param['once_buy_num'], 'is_open' => $param['is_open'], 'create_time' => now(), 'update_time' => now() ]; $activityModel = new PiecingActivity(); $activity_id = $activityModel->insertOne($activity); //生成活动商品 $activityGoods = $piecingPriceArr = []; $stock = $piecingPrice = 0; $ttl = strtotime($timeMap[1]) - time() + 60 * 5; // 有效期 // 单规格商品 if ($param['goods_rule'] == 1) { $activityGoods[] = [ 'activity_id' =>$activity_id, 'goods_id' => $param['goods_id'], 'sku' => '', 'image' => $param['pic'], 'goods_price' => $param['goods_price'], 'piecing_price' => $param['piecing_price'], 'stock' => $param['stock'], 'create_time' => now(), 'update_time' => now() ]; $stock += $param['stock']; $piecingPriceArr = [$param['piecing_price']]; $key = sprintf($this->piecingKey, $activity_id, ''); Redis::del($key); Redis::set($key, $param['stock']); Redis::expire($key, $ttl); } else { // 多规格商品 $selectedRules = []; foreach ($param['final'] as $vo) { $sku = implode('※', $vo['sku']); $activityGoods[] = [ 'activity_id' => $activity_id, 'sku' => $sku, 'goods_id' => $vo['goods_id'], 'image' => $vo['image'], 'goods_price' => $vo['goods_price'], 'piecing_price' => $vo['piecing_price'], 'stock' => $vo['stock'], 'create_time' => now(), 'update_time' => now() ]; foreach ($vo['sku'] as $key => $v) { $selectedRules[$key][] = $v; } $stock += $vo['stock']; //获取当前商品规格的最小价格 $piecingPriceArr[] = $vo['piecing_price']; $key = sprintf($this->piecingKey, $activity_id, $sku); Redis::del($key); Redis::set($key, $param['stock']); Redis::expire($key, $ttl); } // 选中的规格 foreach ($param['rule'] as $key => $vo) { $param['rule'][$key]['item'] = array_values(array_intersect($vo['item'], $selectedRules[$key])); } $activityModel->updateById([ 'piecing_goods_rule' => json_encode($param['rule']) ], $activity_id); } // 设置单次购买数量限制 $limitKey = sprintf($this->piecingLimitKey, $activity_id, $param['goods_id']); Redis::del($limitKey); Redis::setex($limitKey, $ttl, json_encode([ 'once_buy_num' => $param['once_buy_num'], 'total_buy_num' => $param['total_buy_num'], ])); // 写入活动商品 $seckillActivityGoodsModel = new PiecingActivityGoods(); $seckillActivityGoodsModel->insertBatch($activityGoods); $piecingPrice = min($piecingPriceArr); $activityModel->updateById([ 'piecing_price' => $piecingPrice, ],$activity_id); Db::commit(); } catch (\Throwable $e) { Db::rollBack(); throw new ApiException($e->getMessage() . '|' . $e->getFile() . '|' . $e->getLine()); } return dataReturn(0, '添加成功'); } /** * 编辑拼团商品 * @param $param * @return array */ public function editSeckillGoods($param) { $this->checkParam($param); $timeMap = $param['activity_time']; Db::beginTransaction(); try { $ttl = strtotime($timeMap[1]) - time() + 60 * 5; // 有效期 $activityModel = new PiecingActivity(); $info = $activityModel->with('activityGoods')->where('id', $param['id'])->first(); // 限制进行中的活动不得更改,防止数据错乱 if ($info['start_time'] < now() && $info['end_time'] > now()) { throw new LogicException('该活动时间正在进行中,不可修改'); } $activity = [ 'goods_id' => $param['goods_id'], 'goods_rule' => $param['goods_rule'], 'pic' => $param['pic'], 'name' => $param['name'], 'desc' => $param['desc'], 'pintuan_time' => $param['pintuan_time'], 'is_recommend' => 0, 'start_time' => $timeMap[0], 'end_time' => $timeMap[1], 'total_buy_num' => $param['total_buy_num'], 'once_buy_num' => $param['once_buy_num'], 'status' => ($param['is_open'] == 1) ? 2 : 1, 'pintuan_num' => $param['once_buy_num'], 'is_open' => $param['is_open'], 'update_time' => now() ]; $activityModel->updateById($activity, $param['id']); $activityGoods = $piecingPriceArr = []; $stock = $piecingPrice = 0; // 单规格 if ($param['goods_rule'] == 1) { $activityGoods[] = [ 'activity_id' => $param['id'], 'goods_id' => $param['goods_id'], 'sku' => '', 'image' => $param['pic'], 'goods_price' => $param['goods_price'], 'piecing_price' => $param['piecing_price'], 'stock' => $param['stock'], 'create_time' => now(), 'update_time' => now() ]; $stock += $param['stock']; $piecingPriceArr = [$param['piecing_price']]; $key = sprintf($this->piecingKey, $info['id'], ''); Redis::del($key); Redis::set($key, $param['stock']); Redis::expire($key, $ttl); } else { // 多规格 // 移除旧的key foreach ($info['activityGoods'] as $vo) { $key = sprintf($this->piecingKey, $param['id'], $vo['sku']); Redis::del($key); } // 多规格商品 $selectedRules = []; foreach ($param['final'] as $vo) { $sku = implode('※', $vo['sku']); $activityGoods[] = [ 'activity_id' => $param['id'], 'sku' => $sku, 'goods_id' => $vo['goods_id'], 'image' => $vo['image'], 'goods_price' => $vo['goods_price'], 'piecing_price' => $vo['piecing_price'], 'stock' => $vo['stock'], 'create_time' => now(), 'update_time' => now() ]; foreach ($vo['sku'] as $key => $v) { $selectedRules[$key][] = $v; } $stock += $vo['stock']; //获取当前商品规格的最小价格 $piecingPriceArr[] = $vo['piecing_price']; $key = sprintf($this->piecingKey, $info['id'], $sku); Redis::del($key); Redis::set($key, $param['stock']); Redis::expire($key, $ttl); } // 选中的规格 foreach ($param['rule'] as $key => $vo) { $param['rule'][$key]['item'] = array_values(array_intersect($vo['item'], $selectedRules[$key])); } $activityModel->updateById([ 'piecing_goods_rule' => json_encode($param['rule']) ], $info['id']); } // 设置单次购买数量限制 $limitKey = sprintf($this->piecingLimitKey, $info['id'], $param['goods_id']); Redis::del($limitKey); Redis::setex($limitKey, $ttl, json_encode([ 'once_buy_num' => $param['once_buy_num'], 'total_buy_num' => $param['total_buy_num'], ])); $seckillActivityGoodsModel = new PiecingActivityGoods(); // 删除旧的数据 $seckillActivityGoodsModel->delByWhere([ 'activity_id' => $param['id'] ]); // 写入活动商品 $seckillActivityGoodsModel->insertBatch($activityGoods); $piecingPrice = min($piecingPriceArr); $activityModel->updateById([ 'piecing_price' => $piecingPrice, ], $info['id']); Db::commit(); } catch (\Throwable $e) { Db::rollback(); throw new ApiException($e->getMessage() . '|' . $e->getFile() . '|' . $e->getLine()); } return dataReturn(0, '编辑成功'); } /** * 构建编辑参数 * @param $activityId * @return array */ public function buildEditParam($activityId) { $activityModel = new PiecingActivity(); $activityInfo = $activityModel->with(['activityGoods'])->where('id', $activityId)->first(); // 多规格商品 if ($activityInfo['goods_rule'] == 2) { $goodsRuleExtendModel = new GoodsRuleExtend(); $ruleList = $goodsRuleExtendModel->getAllList(['goods_id' => $activityInfo['goods_id']]); $sku2Stock = []; foreach ($ruleList as $vo) { $sku2Stock[$vo['sku']] = $vo['stock']; } foreach ($activityInfo['activityGoods'] as $key => $vo) { $activityInfo['activityGoods'][$key]['goods_stock'] = $sku2Stock[$vo['sku']] ?? 0; } } else { // 单规格商品 $goodsModel = new Goods(); $info = $goodsModel->getInfoByWhere(['id' => $activityInfo['goods_id']]); foreach ($activityInfo['activityGoods'] as $key => $vo) { $activityInfo['activityGoods'][$key]['goods_stock'] = $info['stock']; } } return dataReturn(0, 'success', [ 'jsonInfo' => $activityInfo['activityGoods'] ]); } /** * 删除秒杀数据 * @param $id */ public function delSeckill($id) { $activityModel = new PiecingActivity(); $info = $activityModel->with('activityGoods')->where('id', $id)->first(); // 限制进行中的活动不得更改,防止数据错乱 if ($info['start_time'] < now() && $info['end_time'] > now()) { throw new LogicException('该活动时间正在进行中,不可修改'); } foreach ($info['activityGoods'] as $vo) { $key = sprintf($this->piecingKey, $info['id'], $vo['sku']); Redis::del($key); } $limitKey = sprintf($this->piecingLimitKey, $info['id'], $info['goods_id']); Redis::del($limitKey); $activityModel->delById($id); $seckillActivityModel = new PiecingActivityGoods(); $seckillActivityModel->delByWhere([ 'activity_id' => $id ]); return dataReturn(0, '删除成功'); } }