// +---------------------------------------------------------------------- declare (strict_types=1); namespace app\common\model; use app\common\enum\goods\Status as GoodsStatusEnum; use app\common\enum\order\DeliveryType as DeliveryTypeEnum; use app\common\library\helper; use app\store\model\GoodsCategoryRel as GoodsCategoryRelModel; use cores\BaseModel; use think\db\BaseQuery; use think\db\exception\DbException; use think\model\Collection; use think\model\relation\BelongsTo; use think\model\relation\HasMany; use think\model\relation\HasOne; use think\Paginator; use app\store\model\GoodsImage as GoodsImageModel; use app\store\model\UploadFile as UploadFileModel; /** * 商品模型 * Class Goods * @package app\common\model */ class Goods extends BaseModel { // 定义表名 protected $name = 'goods'; // 定义主键 protected $pk = 'goods_id'; // 追加字段 protected $append = ['goods_sales']; /** * 关联模型:主图视频文件 * @return HasOne */ public function video(): HasOne { return $this->hasOne('UploadFile', 'file_id', 'video_id'); } /** * 关联模型:主图视频封面图片文件 * @return HasOne */ public function videoCover(): HasOne { return $this->hasOne('UploadFile', 'file_id', 'video_cover_id'); } /** * 计算显示销量 (初始销量 + 实际销量) * @param $value * @param $data * @return mixed */ public function getGoodsSalesAttr($value, $data) { return ($data['sales_initial'] ?? 0) + ($data['sales_actual'] ?? 0); } /** * 商品详情:HTML实体转换回普通字符 * @param $value * @return string */ public function getContentAttr($value): string { return $value ? htmlspecialchars_decode($value) : ""; } /** * 获取器:单独设置折扣的配置 * @param $json * @return mixed */ public function getAloneGradeEquityAttr($json) { return helper::jsonDecode($json); } /** * 获取器:商品配送方式 * 如果配送方式为空,默认返回所有配送方式(用于后台商品管理时默认选中) * @param $json * @return mixed */ public function getDeliveryTypeAttr($json) { $values = helper::jsonDecode($json); return $values ?: array_keys(DeliveryTypeEnum::data()); } /** * 修改器:单独设置折扣的配置 * @param $data * @return false|string */ public function setAloneGradeEquityAttr($data) { return helper::jsonEncode($data); } /** * 修改器:商品配送方式 * @param $data * @return false|string */ public function setDeliveryTypeAttr($data) { return helper::jsonEncode($data); } /** * 关联商品规格表 * @return HasMany */ public function skuList(): HasMany { return $this->hasMany('GoodsSku', 'spu_id')->order(['id' => 'asc']); } /** * 关联商品规格表 * @return HasMany */ public function skuList1(): HasMany { return $this->hasMany('GoodsSku', 'goods_id')->order(['id' => 'asc']); } /** * 关联商品规格关系表 * @return HasMany */ public function specRel(): HasMany { return $this->hasMany('GoodsSpecRel'); } /** * 关联商品图片表 * @return HasMany */ public function images(): HasMany { return $this->hasMany('GoodsImage')->order(['id']); } /** * 关联运费模板表 * @return BelongsTo */ public function delivery(): BelongsTo { return $this->BelongsTo('Delivery'); } /** * 关联订单评价表 * @return HasMany */ public function commentData(): HasMany { return $this->hasMany('Comment'); } /** * 获取商品列表 * @param array $param 查询条件 * @param int $listRows 分页数量 * @return mixed * @throws DbException */ public function getAdminListExport(array $param = [], int $listRows = 15) { // 筛选条件 $query = $this->getQueryFilter($param); // 设置显示的销量 goods_sales //$query->field(['(sales_initial + sales_actual) as goods_sales', '(line_price_max - goods_price_min) as discount']); // 排序条件 $sort = $this->setQuerySort($param); $order = request()->get()['order'] ?? ''; $sort = request()->get()['sort'] ?? ''; if ($order && $sort) { $sort = [ $sort => $order, ]; } else { $sort = [ $this->getPk() => 'desc', ]; } // $field = $this->getAliasFields($this->name, ['content']); // $field[] = 'selling_point'; // 执行查询 $list = $query ->alias($this->name) ->field(['goods.goods_id', 'goods.goods_name', 'cmmdty_model', 'remark', 'cost_price_min', 'stock_total', 'is_check', 'goods_price_min','goods_no','goods_source','delivery_time','delivery_id','status','link','link_other','channel','is_use_jd_stock','video_id']) ->where('is_delete', '=', 0) ->order($sort) ->paginate($listRows); foreach ($list as &$goods) { if (!$goods->link_other) { $goods->link_other = "0"; } } // 整理列表数据并返回 return $list; } /** * 获取商品列表 * @param array $param 查询条件 * @param int $listRows 分页数量 * @return mixed * @throws DbException */ public function getAdminList(array $param = [], int $listRows = 15) { // 筛选条件 $query = $this->getQueryFilter($param); // 设置显示的销量 goods_sales $query->field(['(sales_initial + sales_actual) as goods_sales', '(line_price_max - goods_price_min) as discount']); // 排序条件 $sort = $this->setQuerySort($param); $order = request()->get()['order'] ?? ''; $sort = request()->get()['sort'] ?? ''; if ($order && $sort) { $sort = [ $sort => $order, ]; } else { $sort = [ $this->getPk() => 'desc', ]; } $field = $this->getAliasFields($this->name, ['content']); $field[] = 'selling_point'; // 执行查询 $list = $query->with(['images.file']) ->alias($this->name) ->field($field) ->where('is_delete', '=', 0) ->order($sort) ->paginate($listRows); // 整理列表数据并返回 foreach ($list as &$goods) { $goods['dic'] = 'admin'; $goods['after_markup_price'] = round($goods['cost_price_min'] / (1 - ($goods['markup_rate'] / 100)), 0); $goods['import_time'] = date("Y-m-d H:i:s", $goods['import_time']); $goods = $this->setGoodsData($goods, null); } return $list; //return $this->setGoodsListData($list); } /** * 获取商品列表 * @param array $param 查询条件 * @param int $listRows 分页数量 * @return mixed * @throws DbException */ public function getList(array $param = [], int $listRows = 15) { // 筛选条件 $query = $this->getQueryFilter($param); // 设置显示的销量 goods_sales $query->field(['(sales_initial + sales_actual) as goods_sales', '(line_price_max - goods_price_min) as discount']); // 排序条件 $sort1 = $this->setQuerySort($param); // var_dump($sort); // exit(); $order = request()->get()['order'] ?? ''; $sort = request()->get()['sort'] ?? ''; //商家端商品列表排序 if ($order && $sort) { $sort = [ $sort => $order, ]; } else { //小程序前台商品排序 if ($sort1) { $sort = $sort1; } else { $sort = [ $this->getPk() => 'desc', ]; } } $field = $this->getAliasFields($this->name, ['content']); $field[] = 'selling_point'; //关键字搜索 wanghousheng if (!empty($param['keyword'])) { $query = $query->whereRaw('goods_name like ? or goods_no like ?', ["%{$param['keyword']}%", "%{$param['keyword']}%"]); } if (!empty($param['keywords'])) { $where = explode(',',$param['keywords']); foreach ($where as $key => $val){ $query = $query->whereRaw('goods_name like ? or goods_no like ?', ["%{$val}%", "%{$val}%"]); } } //数据渠道过滤特殊处理 if (isset($param['fliter_condition']) && $param['fliter_condition']) { $fliter_condition = json_decode($param['fliter_condition'], true); $categorys = []; $str = ""; foreach ($fliter_condition as $value) { $categorys = array_merge($categorys, $value['category']); $strConditon = "("; $strConditon .= "goods_category_rel.category_id in (".implode(",", $value['category']).")"; $strConditon .= " and goods.profit >= ".$value['profit']; $strConditon .= " and goods.profit_rate >= ".$value['profit_rate'] . ") or "; $str .= $strConditon; } $str = trim($str, "or "); //筛选的分类不在当前筛选条件里面就无需带上过滤条件 if (!$param['categoryId'] || in_array($param['categoryId'], $categorys)) { // 执行查询 $list = $query->with(['images.file']) ->alias($this->name) ->field($field) ->where('is_delete', '=', 0) ->where($str) ->group("goods.goods_id") ->order($sort) ->paginate($listRows); } else { // 执行查询 $list = $query->with(['images.file']) ->alias($this->name) ->field($field) ->where('is_delete', '=', 0) ->group("goods.goods_id") ->order($sort) ->paginate($listRows); } } else { // 执行查询 $list = $query->with(['images.file']) ->alias($this->name) ->field($field) ->where('is_delete', '=', 0) ->group("goods.goods_id") ->order($sort) ->paginate($listRows); } // 整理列表数据并返回 return $this->setGoodsListData($list); } /** * 检索排序条件 * @param array $param * @return array|string[] */ private function setQuerySort(array $param = []): array { $params = $this->setQueryDefaultValue($param, [ 'sortType' => 'all', // 排序类型 (all默认 sales销量 price价格) 'sortPrice' => false, // 价格排序 (true高到低 false低到高) ]); // 排序规则 $sort = []; if ($params['sortType'] === 'all') { $sort = ['sort' => 'asc']; } elseif ($params['sortType'] === 'sales') { $sort = ['goods_sales' => 'desc']; } elseif ($params['sortType'] === 'price') { $sort = $params['sortPrice'] ? ['goods_price_max' => 'desc'] : ['goods_price_min' => 'asc']; } return array_merge($sort, [$this->getPk() => 'desc']); } /** * 检索查询条件 * @param array $param * @return BaseQuery */ private function getQueryFilter(array $param): BaseQuery { // 商品列表获取条件 $params = $this->setQueryDefaultValue($param, [ 'listType' => 'all', // 列表模式 (全部:all 出售中:on_sale 已下架:off_sale 已售罄:sold_out) 'categoryId' => null, // 商品分类ID 'goodsName' => null, // 商品名称 'goodsNo' => null, // 商品编码 'status' => 0, // 商品状态(0全部 10上架 20下架) 'goods_price_min' => '', 'goods_price_max' => '', 'date_type' => '', 'is_pool' => '', 'is_self' => '', 'is_sale' => '', 'keywords' => '', 'role' => '', ]); // 实例化新查询对象 $query = $this->getNewQuery(); // 筛选条件 $filter = []; // 列表模式 if ($params['listType'] === 'on_sale') { $filter[] = ['status', '=', GoodsStatusEnum::ON_SALE]; // 出售中 } elseif ($params['listType'] === 'off_sale') { $filter[] = ['status', '=', GoodsStatusEnum::OFF_SALE]; // 已下架 } elseif ($params['listType'] === 'sold_out') { $filter[] = ['stock_total', '=', 0]; // 已售罄 } elseif ($params['listType'] === 'out_pool') { $filter[] = ['is_pool', '=', 2]; // 已售罄 } elseif ($params['listType'] === 'in_pool') { $filter[] = ['is_pool', '=', 1]; // 已售罄 } if (isset($params['is_sale']) && $params['is_sale'] != "") { if ($params['is_sale'] == 1) { $filter[] = ['is_sale', '=', 1]; // 出售中 } elseif ($params['is_sale'] == 0) { $filter[] = ['is_sale', '=', 0]; // 已下架 } elseif ($params['is_sale'] == 2) { $filter[] = ['stock_total', '=', 0]; // 已售罄 } } if (isset($params['data_type']) && $params['data_type'] != "") { if ($params['data_type'] == 1) { $filter[] = ['data_type', '=', 1]; // 出售中 } elseif ($params['data_type'] == 0) { $filter[] = ['data_type', '=', 0]; // 已下架 } elseif ($params['data_type'] == 2) { $filter[] = ['is_pool', '=', 1]; // 已售罄 } } // 商品状态 $params['status'] > 0 && $filter[] = ['status', '=', (int)$params['status']]; //$a = 1; // 商品分类 if ($params['categoryId'] > 0 || (isset($params['fliter_condition']) && $params['fliter_condition'])) { // 关联商品与分类关系记录表 $GoodsCategoryRelName = (new GoodsCategoryRelModel())->getName(); $query->join($GoodsCategoryRelName, "{$GoodsCategoryRelName}.goods_id = {$this->name}.goods_id"); if ($params['categoryId'] > 0) { // 设置分类ID条件 $query->where('goods_category_rel.category_id', 'in', explode(",", (string)$params['categoryId'])); } } if (isset($param['goodsIds']) && $param['goodsIds'] !== '') { $filter[] = ['goods.goods_id', 'in', explode(",", $param['goodsIds'])]; } // 商品名称 !empty($params['goodsName']) && $filter[] = ['goods_name', 'like', "%{$params['goodsName']}%"]; // 商品编码 !empty($params['goodsNo']) && $filter[] = ['goods_no', 'like', "%{$params['goodsNo']}%"]; //wmc // if (!empty($param['is_brand'])) { // $filter[] = ['is_brand', '=', intval($param['is_brand'])]; // } // if (!empty($param['is_new'])) { // $filter[] = ['is_new', '=', intval($param['is_new'])]; // } if (isset($param['spec_type']) && $param['spec_type'] !== '') { $filter[] = ['spec_type', '=', $params['spec_type']]; } //gj -前台小程序 if (isset($param['merchantId']) && $param['merchantId']) { $filter[] = ['goods.merchant_id', '=', $param['merchantId']]; } //后台管理 if (isset($param['merchant_id']) && $param['merchant_id']) { $filter[] = ['goods.merchant_id', '=', $param['merchant_id']]; } //是否店内 //if (isset($param['is_in_store']) && $param['is_in_store'] !== '') { // if (isset($param['is_in_store']) && $param['is_in_store']) { // $filter[] = ['is_in_store', '=', $params['is_in_store']]; // } if (isset($param['store_id']) && $param['store_id'] !== '') { $filter[] = ['goods.store_id', '=', $params['store_id']]; } //不同的渠道查看不同的数据 if (isset($param['channel']) && $param['channel'] !== '') { $filter[] = ['goods.channel', '=', $params['channel']]; } else { if (isset($param['channels']) && $param['channels']) { $filter[] = ['goods.channel', 'in', $param['channels']]; } } if (isset($param['is_grab']) && $param['is_grab'] !== '') { $filter[] = ['goods.is_grab', '=', intval($params['is_grab'])]; } if (isset($param['is_self']) && $param['is_self'] !== '') { $filter[] = ['goods.is_self', '=', $params['is_self']]; } // if (isset($params['data_type']) && $params['data_type'] != '') { // $filter[] = ['goods.data_type', '=', $params['data_type']]; // } if (isset($param['is_pool']) && $param['is_pool'] !== '') { $filter[] = ['goods.is_pool', '=', $params['is_pool']]; } if (isset($param['is_jingpin']) && $param['is_jingpin'] !== '') { $filter[] = ['goods.is_jingpin', '=', $params['is_jingpin']]; } if (isset($param['is_brand']) && $param['is_brand'] !== '') { $filter[] = ['goods.is_brand', '=', $params['is_brand']]; } if (isset($param['is_new']) && $param['is_new'] !== '') { $filter[] = ['goods.is_new', '=', $params['is_new']]; } if (isset($param['is_in_store']) && $param['is_in_store'] !== '') { $filter[] = ['goods.is_in_store', '=', $params['is_in_store']]; } // if (!empty($param['paihang'])) { // $filter[] = ['paihang', '>', 0]; // $query->order('paihang asc'); // } if (isset($param['is_paihang']) && $param['is_paihang'] !== '') { if ($param['is_paihang'] > 0 ) { $filter[] = ['goods.paihang', '>', 0]; $query->order('paihang asc'); } else { $filter[] = ['goods.paihang', '=', 0]; } } if (isset($param['start_time']) && $param['start_time'] !== '') { $filter[] = ['goods.create_time', '>=', strtotime($params['start_time'])]; } if (isset($param['end_time']) && $param['end_time'] !== '') { $filter[] = ['goods.create_time', '<=', strtotime($params['end_time'])]; } if (isset($param['goods_price_min']) && $param['goods_price_min'] !== '') { $filter[] = ['goods.goods_price_min', '>=', $params['goods_price_min']]; } if (isset($param['goods_price_max']) && $param['goods_price_max'] !== '') { $filter[] = ['goods.goods_price_min', '<=', $params['goods_price_max']]; } if (isset($param['distribute_price_max']) && $param['distribute_price_max'] !== '') { $filter[] = ['goods.distribute_price', '<=', $params['distribute_price_max']]; } if (isset($param['distribute_price_min']) && $param['distribute_price_min'] !== '') { $filter[] = ['goods.distribute_price', '>=', $params['distribute_price_min']]; } if (isset($param['shop_price_min']) && $param['shop_price_min'] !== '') { $filter[] = ['goods.shop_price', '>=', $params['shop_price_min']]; } if (isset($param['shop_price_max']) && $param['shop_price_max'] !== '') { $filter[] = ['goods.shop_price', '<=', $params['shop_price_max']]; } if (isset($param['profit_rate_min']) && $param['profit_rate_min'] !== '') { $filter[] = ['goods.profit_rate', '>=', $params['profit_rate_min']]; } if (isset($param['profit_rate_max']) && $param['profit_rate_max'] !== '') { $filter[] = ['goods.profit_rate', '<=', $params['profit_rate_max']]; } if (isset($param['goods_source']) && $param['goods_source'] !== '') { $filter[] = ['goods.goods_source', '=', $params['goods_source']]; } if (isset($param['is_has_banner']) && $param['is_has_banner'] !== '') { $filter[] = ['goods.is_has_banner', '=', $params['is_has_banner']]; } if (isset($param['is_has_detail']) && $param['is_has_detail'] !== '') { $filter[] = ['goods.is_has_detail', '=', $params['is_has_detail']]; } if (isset($param['is_jd_remove']) && $param['is_jd_remove'] !== '') { $filter[] = ['goods.is_jd_remove', '=', $params['is_jd_remove']]; } // 实例化新查询对象 return $query->where($filter); } /** * 使用总后台的商品的图片作为商城商品的图片 * [storeUsePlatformGoodsImage description] * @param array $origin_goods_ids [description] * @return [type] [description] */ public function storeUsePlatformGoodsImage(array $origin_goods_ids){ $goods_images = GoodsImageModel::withoutGlobalScope()->whereIn('goods_id', $origin_goods_ids)->select()->toArray(); $goods_image_ids = array_column($goods_images, "image_id"); $files = UploadFileModel::withoutGlobalScope()->whereIn('file_id', $goods_image_ids)->select()->toArray(); $files = array_column($files, null, "file_id"); $arr = []; foreach ($goods_images as &$goods_image) { $goods_image['file'] = $files[$goods_image['image_id']] ?? []; $arr[$goods_image['goods_id']][] = $goods_image; } return $arr; } /** * 使用总后台的商品信息 * [storeUsePlatformGoodsImage description] * @param array $origin_goods_ids [description] * @return [type] [description] */ public function storeUsePlatformGoods(array $origin_goods_ids){ $goods = self::withoutGlobalScope()->whereIn('goods_id', $origin_goods_ids)->field('goods_id,goods_name,goods_no,content')->select()->toArray(); $goods = array_column($goods, null, "goods_id"); return $goods; } /** * 设置商品展示的数据 * @param Collection|Paginator $list 商品列表 * @param callable|null $callback 回调函数 * @return mixed */ protected function setGoodsListData($list, callable $callback = null) { if ($list->isEmpty()) { return $list; } //使用总后台的商品的图片作为商城商品的图片 $goodsList = $list->toArray()['data'] ?? $list->toArray(); $origin_goods_ids = array_column($goodsList, 'origin_goods_id'); $goods_images = $this->storeUsePlatformGoodsImage($origin_goods_ids); $goodsList = $this->storeUsePlatformGoods($origin_goods_ids); // 遍历商品列表整理数据 foreach ($list as &$goods) { if ($goods->origin_goods_id && $goods->store_id > 0) { $goods->images = $goods_images[$goods->origin_goods_id] ?? []; $goods->content = $goodsList[$goods->origin_goods_id]['content'] ?? ""; $goods->goods_no = $goodsList[$goods->origin_goods_id]['goods_no'] ?? ""; $goods->goods_name = $goodsList[$goods->origin_goods_id]['goods_name'] ?? ""; } $goods = $this->setGoodsData($goods, $callback); } return $list; } /** * 整理商品数据 * @param Collection|static $goodsInfo * @param callable|null $callback * @return mixed */ protected function setGoodsData($goodsInfo, callable $callback = null) { $channel = Channel::withoutGlobalScope()->where('code', $goodsInfo['channel'])->find(); if (isset($goodsInfo['dic']) && $goodsInfo['dic'] == 'admin') { $goodsInfo['channel_name'] = (isset($channel['name'])&& $channel['name']) ? $channel['alias']."-".$channel['name'] : ""; } else { $goodsInfo['channel_name'] = $channel['alias'] ?? ""; } $goodsInfo['selling_point'] = ""; $goodsInfo['goods_images'] = helper::getArrayColumn($goodsInfo['images'], 'file'); // 商品主图 $goodsInfo['goods_image'] = $goodsInfo['goods_images'] ? current($goodsInfo['goods_images'])['preview_url'] : ""; // 商品销量(实际显示=初始虚拟销量+实际销量) $goodsInfo['goods_sales'] = $goodsInfo['sales_initial'] + $goodsInfo['sales_actual']; if (in_array($goodsInfo['channel'], ['sn','sn1']) && $goodsInfo['link_other']) { $goodsInfo['link'] = $goodsInfo['link_other']; } // 回调函数 is_callable($callback) && call_user_func($callback, $goodsInfo); return $goodsInfo; } /** * 根据商品id集获取商品列表 * @param array $goodsIds * @param null $status * @return array|mixed */ public function getListByIds(array $goodsIds, $status = null) { // 筛选条件 $filter = [['goods_id', 'in', $goodsIds]]; // 商品状态 $status > 0 && $filter[] = ['status', '=', $status]; // 获取商品列表数据 $data = $this->withoutField(['content']) ->with(['images.file']) ->where($filter) ->where('is_delete', '=', 0) ->orderRaw('field(goods_id, ' . implode(',', $goodsIds) . ')') ->select(); // 整理列表数据并返回 return $this->setGoodsListData($data); } /** * 获取商品记录 * @param int $goodsId * @param array $with * @return static|array|null */ public static function detail(int $goodsId, array $with = []) { // var_dump($with); // exit(); return static::get($goodsId, $with); } }