You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
12 KiB
289 lines
12 KiB
11 months ago
|
<?php
|
||
|
// +----------------------------------------------------------------------
|
||
|
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||
|
// +----------------------------------------------------------------------
|
||
|
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||
|
// +----------------------------------------------------------------------
|
||
|
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||
|
// +----------------------------------------------------------------------
|
||
|
// | Author: 萤火科技 <admin@yiovo.com>
|
||
|
// +----------------------------------------------------------------------
|
||
|
declare (strict_types=1);
|
||
|
|
||
|
namespace app\api\model\groupon;
|
||
|
|
||
|
use app\api\service\User as UserService;
|
||
|
use app\api\model\Goods as GoodsModel;
|
||
|
use app\api\model\groupon\Task as TaskModel;
|
||
|
use app\api\model\groupon\GoodsSku as GrouponGoodsSkuModel;
|
||
|
use app\common\model\groupon\Goods as GrouponGoodsModel;
|
||
|
use app\common\enum\groupon\ActiveType as ActiveTypeEnum;
|
||
|
use app\common\enum\groupon\GoodsStatus as GoodsStatusEnum;
|
||
|
use app\common\library\helper;
|
||
|
use cores\exception\BaseException;
|
||
|
|
||
|
/**
|
||
|
* 拼团商品模型
|
||
|
* Class Goods
|
||
|
* @package app\api\model\groupon
|
||
|
*/
|
||
|
class Goods extends GrouponGoodsModel
|
||
|
{
|
||
|
/**
|
||
|
* 隐藏字段
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $hidden = [
|
||
|
'is_mock_task',
|
||
|
'mock_min_people',
|
||
|
'initial_sales',
|
||
|
'actual_sales',
|
||
|
'sort',
|
||
|
'group_task_num',
|
||
|
'store_id',
|
||
|
'create_time',
|
||
|
'update_time',
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* 获取拼团活动列表(根据商品ID集)
|
||
|
* @param array $grouponGoodsIds
|
||
|
* @param array $param
|
||
|
* @return mixed|\think\Paginator
|
||
|
* @throws \think\Exception
|
||
|
* @throws \think\db\exception\DataNotFoundException
|
||
|
* @throws \think\db\exception\DbException
|
||
|
* @throws \think\db\exception\ModelNotFoundException
|
||
|
*/
|
||
|
public function getListByIds(array $grouponGoodsIds, array $param = [])
|
||
|
{
|
||
|
return $this->getList(array_merge($param, ['grouponGoodsIds' => $grouponGoodsIds]));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取拼团商品列表
|
||
|
* @param array $param
|
||
|
* @return mixed|\think\Paginator
|
||
|
* @throws \think\Exception
|
||
|
* @throws \think\db\exception\DataNotFoundException
|
||
|
* @throws \think\db\exception\DbException
|
||
|
* @throws \think\db\exception\ModelNotFoundException
|
||
|
*/
|
||
|
public function getList(array $param)
|
||
|
{
|
||
|
// 查询条件
|
||
|
$params = $this->setQueryDefaultValue($param, [
|
||
|
'grouponGoodsIds' => [], // 拼团商品ID集
|
||
|
'status' => 10, // 商品状态
|
||
|
'sortType' => 'all', // 排序类型
|
||
|
'sortPrice' => false, // 价格排序 高低
|
||
|
'listRows' => 15, // 每页数量
|
||
|
]);
|
||
|
// 查询模型
|
||
|
$query = $this->getNewQuery();
|
||
|
// 根据活动ID集
|
||
|
if (is_array($params['grouponGoodsIds']) && !empty($params['grouponGoodsIds'])) {
|
||
|
$query->orderRaw('field(groupon_goods_id, ' . implode(',', $params['grouponGoodsIds']) . ')');
|
||
|
$query->where('groupon_goods_id', 'in', $params['grouponGoodsIds']);
|
||
|
}
|
||
|
// 排序规则
|
||
|
if ($params['sortType'] === 'all') {
|
||
|
$query->order(['sort' => 'asc']);
|
||
|
} elseif ($params['sortType'] === 'sales') {
|
||
|
$query->order(['active_sales' => 'desc']);
|
||
|
} elseif ($params['sortType'] === 'price') {
|
||
|
$query->order(['groupon_price_min' => $params['sortPrice'] ? 'desc' : 'asc']);
|
||
|
}
|
||
|
// 拼团活动列表
|
||
|
$list = $query->field(['*', '(actual_sales + initial_sales) as active_sales'])
|
||
|
->where('start_time', '<=', time())
|
||
|
->where('end_time', '>=', time())
|
||
|
->where('status', '=', $params['status'])
|
||
|
->where('is_delete', '=', 0)
|
||
|
->order(['sort' => 'asc'])
|
||
|
->paginate($params['listRows']);
|
||
|
// 设置商品数据
|
||
|
if (!$list->isEmpty()) {
|
||
|
$list = $this->setGoodsListData($list, true, GoodsModel::getHidden(['goods_images']));
|
||
|
}
|
||
|
return $list;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取拼团商品详情 (包含主商品信息, 用于页面详情)
|
||
|
* @param int $grouponGoodsId 拼团商品ID
|
||
|
* @return GoodsModel
|
||
|
* @throws BaseException
|
||
|
* @throws \think\db\exception\DataNotFoundException
|
||
|
* @throws \think\db\exception\DbException
|
||
|
* @throws \think\db\exception\ModelNotFoundException
|
||
|
*/
|
||
|
public function getGoodsDetail(int $grouponGoodsId): GoodsModel
|
||
|
{
|
||
|
// 获取拼团商品详情
|
||
|
$grouponGoods = static::detail($grouponGoodsId, ['skuList']);
|
||
|
if (empty($grouponGoods) || $grouponGoods['is_delete'] || $grouponGoods['status'] == GoodsStatusEnum::FINISH) {
|
||
|
throwError('很抱歉,拼团商品不存在或已下架');
|
||
|
}
|
||
|
// 获取主商品详情
|
||
|
$goods = (new GoodsModel)->getDetails($grouponGoods['goods_id'], false);
|
||
|
// 整理拼团商品信息
|
||
|
$goods = $this->mergeMainGoods($grouponGoods, $goods);
|
||
|
// 商品sku信息
|
||
|
$goods['skuList'] = $this->getGrouponSku($grouponGoods['skuList'], $goods['skuList']);
|
||
|
// 当前商品的可加入的凑团列表
|
||
|
$goods['taskQuickJoinList'] = TaskModel::getQuickJoinList($grouponGoodsId, 2);
|
||
|
return $goods;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取拼团商品基本信息
|
||
|
* @param int $grouponGoodsId
|
||
|
* @return GoodsModel
|
||
|
* @throws BaseException
|
||
|
*/
|
||
|
public function getGoodsBasic(int $grouponGoodsId): GoodsModel
|
||
|
{
|
||
|
// 获取拼团商品详情
|
||
|
$grouponGoods = static::detail($grouponGoodsId);
|
||
|
if (empty($grouponGoods) || $grouponGoods['is_delete'] || $grouponGoods['status'] == GoodsStatusEnum::FINISH) {
|
||
|
throwError('很抱歉,拼团商品不存在或已下架');
|
||
|
}
|
||
|
// 获取主商品详情
|
||
|
$goods = (new GoodsModel)->getBasic($grouponGoods['goods_id'], false);
|
||
|
// 隐藏冗余的属性
|
||
|
$goods->hidden(GoodsModel::getHidden(['content', 'goods_images']));
|
||
|
// 整理拼团商品信息
|
||
|
return $this->mergeMainGoods($grouponGoods, $goods);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 合并拼团商品信息到主商品
|
||
|
* @param GrouponGoodsModel $grouponGoods 拼团商品详情
|
||
|
* @param GoodsModel $goods 主商品详情
|
||
|
* @return GoodsModel
|
||
|
*/
|
||
|
private function mergeMainGoods(GrouponGoodsModel $grouponGoods, GoodsModel $goods): GoodsModel
|
||
|
{
|
||
|
$diffPrice = helper::bcsub($goods['goods_price_min'], $grouponGoods['groupon_price_min']);
|
||
|
$goods['original_price'] = $goods['goods_price_min'];
|
||
|
$goods['groupon_goods_id'] = $grouponGoods['groupon_goods_id'];
|
||
|
$goods['active_type'] = $grouponGoods['active_type'];
|
||
|
$goods['start_time'] = $grouponGoods['start_time'];
|
||
|
$goods['end_time'] = $grouponGoods['end_time'];
|
||
|
$goods['groupon_price'] = $grouponGoods['groupon_price_min'];
|
||
|
$goods['is_alone_buy'] = $grouponGoods['is_alone_buy'];
|
||
|
$goods['active_status'] = $grouponGoods['active_status'];
|
||
|
$goods['active_sales'] = $grouponGoods['active_sales'];
|
||
|
$goods['is_restrict'] = $grouponGoods['is_restrict'];
|
||
|
$goods['restrict_single'] = $grouponGoods['restrict_single'];
|
||
|
$goods['restrict_total'] = $grouponGoods['restrict_total'];
|
||
|
$goods['steps_config'] = $grouponGoods['steps_config'];
|
||
|
$goods['diff_price'] = helper::number2(max($diffPrice, 0));
|
||
|
return $goods;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取拼团商品的sku信息
|
||
|
* @param $grouponSku
|
||
|
* @param $goodsSku
|
||
|
* @return mixed
|
||
|
*/
|
||
|
protected function getGrouponSku($grouponSku, $goodsSku)
|
||
|
{
|
||
|
$grouponSku = helper::arrayColumn2Key($grouponSku, 'goods_sku_id');
|
||
|
foreach ($goodsSku as &$item) {
|
||
|
$grouponSkuItem = clone $grouponSku[$item['goods_sku_id']];
|
||
|
$item['original_price'] = $item['goods_price'];
|
||
|
$item['groupon_price'] = $grouponSkuItem['groupon_price'];
|
||
|
$item['steps_price_config'] = $grouponSkuItem['steps_price_config'];
|
||
|
}
|
||
|
return $goodsSku;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取订单提交的商品列表
|
||
|
* @param int $grouponGoodsId 拼团商品ID
|
||
|
* @param string $goodsSkuId 商品skuID
|
||
|
* @param int $goodsNum 购买数量
|
||
|
* @param int $taskId 拼单ID (参团时传参)
|
||
|
* @param int $stepPeople 拼团人数 (仅阶梯团时传入)
|
||
|
* @return array
|
||
|
* @throws BaseException
|
||
|
*/
|
||
|
public function getCheckoutGoodsList(int $grouponGoodsId, string $goodsSkuId, int $goodsNum, int $taskId, int $stepPeople): array
|
||
|
{
|
||
|
if ($taskId > 0) {
|
||
|
// 获取当前拼单详情
|
||
|
$taskInfo = TaskModel::getBasic($taskId);
|
||
|
// 验证当前拼单是否允许加入新成员
|
||
|
$this->checkToJoinTask($taskInfo);
|
||
|
$stepPeople = $taskInfo['people'];
|
||
|
}
|
||
|
// 拼团商品详情
|
||
|
$grouponGoods = $this->getGoodsBasic($grouponGoodsId);
|
||
|
// 商品sku信息
|
||
|
$grouponGoods['skuInfo'] = GrouponGoodsSkuModel::getSkuInfo($grouponGoods['goods_id'], $grouponGoodsId, $goodsSkuId);
|
||
|
// 商品封面 (优先sku封面)
|
||
|
$grouponGoods['goods_image'] = $grouponGoods['skuInfo']['goods_image'] ?: $grouponGoods['goods_image'];
|
||
|
// 商品列表 (隐藏用不到的属性)
|
||
|
$goodsList = [$grouponGoods->hidden(GoodsModel::getHidden(['content', 'images', 'goods_images']))];
|
||
|
foreach ($goodsList as &$item) {
|
||
|
// 商品价格
|
||
|
$item['goods_price'] = $this->getSkuGrouponPrice($item, $stepPeople);
|
||
|
$item['line_price'] = $item['original_price'];
|
||
|
// 记录拼团商品ID
|
||
|
$item['goods_sku_id'] = $item['skuInfo']['goods_sku_id'];
|
||
|
$item['goods_source_id'] = $item['groupon_goods_id'];
|
||
|
// 商品购买数量
|
||
|
$item['total_num'] = $goodsNum;
|
||
|
// 商品购买总金额
|
||
|
$item['total_price'] = helper::bcmul($item['goods_price'], $goodsNum);
|
||
|
}
|
||
|
return $goodsList;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 获取实际的拼团商品SKU价格
|
||
|
* @param $grouponGoods
|
||
|
* @param int $stepPeople 拼团人数 (仅阶梯团时传入)
|
||
|
* @return mixed
|
||
|
* @throws BaseException
|
||
|
*/
|
||
|
private function getSkuGrouponPrice($grouponGoods, int $stepPeople)
|
||
|
{
|
||
|
if ($grouponGoods['active_type'] != ActiveTypeEnum::STEPS) {
|
||
|
return $grouponGoods['skuInfo']['groupon_price'];
|
||
|
}
|
||
|
$stepsIndex = array_search($stepPeople, $grouponGoods['steps_config']);
|
||
|
$stepsIndex === false && throwError('很抱歉,阶梯团人数不正确');
|
||
|
if (!array_key_exists($stepsIndex, $grouponGoods['skuInfo']['steps_price_config'])) {
|
||
|
throwError('很抱歉,未找到阶梯团价格');
|
||
|
}
|
||
|
return $grouponGoods['skuInfo']['steps_price_config'][$stepsIndex];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 验证当前拼单是否允许加入新成员
|
||
|
* @param $taskInfo
|
||
|
* @return void
|
||
|
* @throws BaseException
|
||
|
*/
|
||
|
private function checkToJoinTask($taskInfo): void
|
||
|
{
|
||
|
$model = new TaskModel;
|
||
|
$userId = UserService::getCurrentLoginUserId(true);
|
||
|
if (!empty($userId) && !$model->checkToJoin($taskInfo, $userId)) {
|
||
|
throwError($model->getError());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 累计拼团商品销量 (实际)
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public static function setIncSales(int $grouponGoodsId)
|
||
|
{
|
||
|
return (new static)->setInc($grouponGoodsId, 'actual_sales', 1);
|
||
|
}
|
||
|
}
|