// +---------------------------------------------------------------------- declare (strict_types=1); namespace app\common\model; use app\common\enum\goods\GoodsType; use think\model\relation\BelongsTo; use think\Paginator; use think\model\Collection; use app\common\library\helper; use app\store\model\GoodsCategoryRel as GoodsCategoryRelModel; use app\common\enum\goods\Status as GoodsStatusEnum; use app\common\model\chef\ChefAreas as RegionModel; use app\common\model\store\Setting as SettingModel; use app\api\service\User as UserService; use think\facade\Db; /** * 商品模型 * Class Goods * @package app\common\model */ class Goods extends BaseModel { // 定义表名 protected $name = 'goods'; // 定义主键 protected $pk = 'goods_id'; // 追加字段 protected $append = ['goods_sales','province_text','city_text','region_text', 'category_name','cascader']; const PACKAGETYPE = 30; // const UNIT = [ // 1 => '件', // 2 => '盒', // 3 => '包', // 4 => '份', // 5 => '箱', // 6 => '袋', // 7 => '斤', // 8 => '桶', // 9 => '瓶', // 10 => '罐', // 11 => '个', // ]; /** * 省市区id集 * @param $value * @param $data * @return array */ public function getCascaderAttr($value, $data) { return [$data['province_id']??0, $data['city_id']??0, $data['region_id']??0]; } /** * 计算显示销量 (初始销量 + 实际销量 + 门店销量) * @param $value * @param $data * @return mixed */ public function getGoodsSalesAttr($value, $data) { return ($data['sales_initial']??0) + ($data['sales_actual']??0) + ($data['sales_shops']??0); } /** * Notes:产地:省份 * Author: zhangs * DateTime: 2021/9/24 14:20 * @param $value * @param $data * @return mixed */ public function getProvinceTextAttr($value, $data) { if (empty($data['province_id'])) return ''; return RegionModel::getNameById($data['province_id'] ?? 0); } /** * Notes:产地:城市 * Author: zhangs * DateTime: 2021/9/24 14:21 * @param $value * @param $data * @return mixed */ public function getCityTextAttr($value, $data) { if (empty($data['city_id'])) return ''; return RegionModel::getNameById($data['city_id'] ?? 0); } /** * Notes:产地:地区 * Author: zhangs * DateTime: 2021/9/24 14:21 * @param $value * @param $data * @return mixed */ public function getRegionTextAttr($value, $data) { if (empty($data['region_id'])) return ''; return RegionModel::getNameById($data['region_id'] ?? 0); } /** * Notes:商品分类名称 * Author: zhangs * DateTime: 2021/9/24 16:41 * @param $value * @param $data * @return string */ public function getCategoryNameAttr($value, $data) { if(isset($data['goods_id'])){ $category_ids = GoodsCategoryRel::where('goods_id', '=', $data['goods_id'])->column('category_id'); if (empty($category_ids)) { return ''; } $names = Category::whereIn('category_id', $category_ids)->column('name'); return $names ? implode(',', $names) : ''; } } /** * 商品详情:HTML实体转换回普通字符 * @param $value * @return string */ public function getContentAttr($value) { return htmlspecialchars_decode($value); } /** * 获取器:单独设置折扣的配置 * @param $json * @return mixed */ public function getAloneGradeEquityAttr($json) { return helper::jsonDecode($json); } /** * 修改器:单独设置折扣的配置 * @param $data * @return mixed */ public function setAloneGradeEquityAttr($data) { return helper::jsonEncode($data); } /** * 关联商品规格表 * @return \think\model\relation\HasMany */ public function skuList() { return $this->hasMany('GoodsSku')->order(['id' => 'asc']); } /** * * @return \think\model\relation\HasMany * @author: zjwhust * @Time: 2021/12/28 15:43 */ public function category() { return $this->hasOne('GoodsCategoryRel','goods_id','goods_id'); } /** * 关联商品规格关系表 * @return \think\model\relation\HasMany */ public function specRel() { return $this->hasMany('GoodsSpecRel'); } /** * 关联商品图片表 * @return \think\model\relation\HasMany */ public function images() { return $this->hasMany('GoodsImage')->order(['id']); } /** * 关联运费模板表 * @return BelongsTo */ public function delivery() { return $this->belongsTo('Delivery'); } /** * 关联订单评价表 * @return \think\model\relation\HasMany */ public function commentData() { return $this->hasMany('Comment'); } /** * 关联供应商表/商铺 * @return BelongsTo */ public function provider() { return $this->belongsTo(Provider::class, 'provider_id', 'provider_id'); } /** * 获取商品列表 * @param array $param 查询条件 * @param int $listRows 分页数量 * @param bool $showVip 会员商品列表 * @return mixed * @throws \think\db\exception\DbException */ public function getList(array $param = [], int $listRows = 15,bool $showVip = false) { // 筛选条件 $query = $this->getQueryFilter($param); // 设置显示的销量 goods_sales // 购买数*1.5+加入购物车数+收藏数*0.8)之和排倒序 $query->field(['(sales_initial + sales_actual) as online_sales','(sales_initial + sales_actual + sales_shops) as goods_sales','(shops_stock_total+stock_total) as goods_stocks','(collect_num*0.8+cart_num+(sales_initial+sales_actual)*1.5) as common_sort']); // 排序条件 //$sort = $this->setQuerySort($param); $sort = $this->setQuerySortZq($param); //dd($sort); // 执行查询 $list = $query->with(['images.file','provider']) ->alias($this->name); if ($showVip){ $list = $list->whereExists('select id from yoshop_member_goods where goods_id=goods.goods_id and status=0'); } $list = $list->field($this->getAliasFields($this->name, ['content'])) ->where('is_delete', '=', 0) ->order($sort) ->paginate($listRows); // 整理列表数据并返回 return $this->setGoodsListData($list); } /** * 获取商品列表 * @param array $param 查询条件 * @param int $listRows 分页数量 * @param bool $showVip 会员商品列表 * @param bool $enableExchange 我能兑换 * @param string $userPonitsMoney 我能兑换 * @return mixed * @throws \think\db\exception\DbException */ public function getListFront(array $param = [], int $listRows = 15,bool $showVip = false,$enableExchange = false) { // 筛选条件 $query = $this->getQueryFilter($param); // 设置显示的销量 goods_sales // 购买数*1.5+加入购物车数+收藏数*0.8)之和排倒序 $query->field(['(sales_initial + sales_actual) as online_sales','(sales_initial + sales_actual + sales_shops) as goods_sales','(shops_stock_total+stock_total) as goods_stocks','(collect_num*0.8+cart_num+(sales_initial+sales_actual)*1.5) as common_sort']); // 排序条件 //$sort = $this->setQuerySort($param); $sort = $this->setQuerySortZq($param); //dd($sort); // 执行查询 $list = $query->with(['images.file','provider']) ->alias($this->name); if ($showVip){ $list = $list->whereExists('select id from yoshop_member_goods where goods_id=goods.goods_id and status=0'); } if ($enableExchange){ $user = UserService::getCurrentLoginUser(); $rate = SettingModel::getItem('points_rate')['points_2_money']; $pointsToMoney = bcdiv(strval($user['accumulate_points'] ?? '0'), $rate, 2); $list = $list->where('goods_price_min','<',$pointsToMoney); } $list = $list->field($this->getAliasFields($this->name, ['content'])) ->where('is_delete', '=', 0) ->where('is_show', '=', 1) ->order($sort) ->paginate($listRows); // 整理列表数据并返回 return $this->setGoodsListData($list); } /** * 检索排序条件 * @param array $param * @return array|string[] */ private function setQuerySort(array $param = []) { $params = $this->setQueryDefaultValue($param, [ 'sortType' => 'all', // 排序类型 'sortPrice' => false, // 价格排序 (true高到低 false低到高) ]); // 排序规则 $sort = []; if ($params['sortType'] === 'all') { $sort = ['common_sort' => 'desc']; } elseif ($params['sortType'] === 'sales') { $sort = ['goods_sales' => 'desc']; } elseif ($params['sortType'] === 'price') { $sort = $params['sortPrice'] ? ['goods_price_max' => 'desc'] : ['goods_price_min' => 'asc']; } elseif ($params['sortType'] == 'create_time') { // 按上架时间倒序 $sort = ['create_time' => 'desc']; } return array_merge($sort, [$this->getPk() => 'desc']); } /** * 检索排序条件 * @param array $param * @return array|string[] */ private function setQuerySortZq(array $param = []) { $params = $this->setQueryDefaultValue($param, [ 'sortStyle' => 'all', // 排序类型 'sortSort' => false, // 价格排序 (true高到低 false低到高) ]); // 排序规则 $sort = []; $sortSort = ($params['sortSort'] == 'false' || !$params['sortSort'])?false:true; if ($params['sortStyle'] == 'all') {//综合 $sort = $sortSort ?['goods_sales' => 'desc']:['goods_sales' => 'asc']; } elseif ($params['sortStyle'] == 'sales') { $sort = $sortSort ?['goods_sales' => 'desc']:['goods_sales' => 'asc']; } elseif ($params['sortStyle'] == 'price') { $sort = $sortSort ? ['goods_price_max' => 'desc'] : ['goods_price_min' => 'asc']; } elseif ($params['sortStyle'] == 'create_time') { // 按上架时间倒序 $sort = $sortSort ? ['create_time' => 'desc'] : ['create_time' => 'asc']; } return array_merge($sort, [$this->getPk() => 'desc']); } /** * 检索查询条件 * @param array $param * @return \think\db\BaseQuery */ private function getQueryFilter(array $param) { // 商品列表获取条件 $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下架) 'have_stock' => 0, // 是否查找有库存商品 'stock_status' => -1, // 库存状态 0-预警 1-正常 'spec_type'=>0 ]); // 实例化新查询对象 $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]; // 已售罄 } if(!empty($params['not_goods_id_arr'])){ $filter[] = ['goods.goods_id','not in',$params['not_goods_id_arr']]; } if(!empty($params['spec_type'])){ $filter[] = ['spec_type','=',$params['spec_type']]; } if(isset($params['goods_id_arr'])){ $filter[] = ['goods_id','in',$params['goods_id_arr']]; } // 起止时间 if (!empty($params['betweenTime'])) { $times = between_time($params['betweenTime']); $filter[] = ['goods.create_time', '>=', $times['start_time']]; $filter[] = ['goods.create_time', '<', $times['end_time'] + 86400]; } if (isset($params['stock_status']) && $params['stock_status'] != -1) { // 库存预警 $filter[] = ['status', '=', GoodsStatusEnum::ON_SALE]; // 出售中 $filterSku[] = ['stock_num', 'exp', Db::raw('< `alarm_stock_num`')]; $goodsIds = GoodsSku::where($filterSku)->column('goods_id'); if ($params['stock_status'] == 1) { if (!empty($goodsIds)) { $filter[] = ['goods_id', 'not in', $goodsIds]; } } else { $filter[] = ['goods_id', 'in', $goodsIds]; } } // 商品状态 $params['status'] > 0 && $filter[] = ['status', '=', (int)$params['status']]; $params['have_stock'] > 0 && $filter[] = ['stock_total', '>', 0]; // 有库存 // 商品分类 if ($params['categoryId'] > 0) { // 关联商品与分类关系记录表 $GoodsCategoryRelName = (new GoodsCategoryRelModel)->getName(); $query->join($GoodsCategoryRelName, "{$GoodsCategoryRelName}.goods_id = {$this->name}.goods_id"); // 设置分类ID条件 $query->where('goods_category_rel.category_id', '=', (int)$params['categoryId']); } // 商品名称 !empty($params['goodsName']) && $filter[] = ['goods_name', 'like', "%{$params['goodsName']}%"]; // 商品编码 !empty($params['goodsNo']) && $filter[] = ['goods_no', 'like', "%{$params['goodsNo']}%"]; //普通商品还是赠品 //in_array($params['goods_type'],[10,20]) && $filter[] = ['goods_type','=',$params['goods_type']]; if(isset($params['goods_type']) && ($params['goods_type'] == GoodsType::GIFT || $params['goods_type'] == GoodsType::NORMAL)){ $filter[] = ['goods_type','=',$params['goods_type']]; $filter[] = ['stock_total','>',0]; }elseif(isset($params['goods_type']) && $params['goods_type'] == 90){ //需要单规格的商品或赠品 $filter[] = ['goods_type','<',GoodsType::MULTI]; $filter[] = ['stock_total','>',0]; }elseif(isset($params['goods_type']) && $params['goods_type'] == GoodsType::EXCHANGE){ //积分兑换商品 $filter[] = ['goods_type','=',GoodsType::EXCHANGE]; $filter[] = ['stock_total','>',0]; }else{ if (isset($params['is_backend']) && $params['is_backend'] == 1){ }else{ $filter[] = ['goods_type','<>',GoodsType::GIFT]; } } // // 是否显示在商品列表 // $filter[] = ['display', '=', 1]; // 可以显示的 // 实例化新查询对象 return $query->where($filter); } /** * 设置商品展示的数据 * @param Collection|Paginator $list 商品列表 * @param callable|null $callback 回调函数 * @return mixed */ protected function setGoodsListData($list, callable $callback = null) { if ($list->isEmpty()) return $list; // 遍历商品列表整理数据 foreach ($list as &$goods) { $goods = $this->setGoodsData($goods, $callback); } return $list; } /** * 设置商品展示的结算价格列表 * @param Collection|Paginator $list 商品列表 * @param callable|null $callback 回调函数 * @return mixed */ protected function setGoodsListDataFinance($list, callable $callback = null) { if ($list->isEmpty()) return $list; // 遍历商品列表整理数据 foreach ($list as &$goods) { $goods = $this->setGoodsData($goods, $callback); $goodsArr = $goods->toArray(); $clearing_price_arr = array_column($goodsArr['skues'],'clearing_price'); //dd($clearing_price_arr); if (count($clearing_price_arr) == 0){ $goods['clearing_price_range'] = ''; } elseif (count($clearing_price_arr) == 1){ $goods['clearing_price_range'] = (string)$clearing_price_arr[0]; }else{ $clearing_price_arr = array_unique($clearing_price_arr); asort($clearing_price_arr); $clearing_price_arr = array_values($clearing_price_arr); $goods['clearing_price_range'] = $clearing_price_arr[0].'~'.$clearing_price_arr[count($clearing_price_arr)-1]; } $rate_arr = array_column($goodsArr['skues'],'platform_rate'); if (count($rate_arr) == 0){ $goods['platform_rate_range'] = ''; } elseif (count($rate_arr) == 1){ $goods['platform_rate_range'] = (string)$rate_arr[0]; }else{ $rate_arr = array_unique($rate_arr); asort($rate_arr); $rate_arr = array_values($rate_arr); $goods['platform_rate_range'] = $rate_arr[0].'~'.$rate_arr[count($rate_arr)-1]; } } return $list; } /** * 整理商品数据 * @param Collection|static $goodsInfo * @param callable|null $callback * @return mixed */ protected function setGoodsData($goodsInfo, callable $callback = null) { // 商品图片列表 $goodsInfo['goods_images'] = helper::getArrayColumn($goodsInfo['images'], 'file'); $goodsImages = []; if (!empty($goodsInfo['goods_images'])) { foreach ($goodsInfo['goods_images'] as $item) { $item = $item->toArray(); $item['preview_url'] = config('chef.sso_domain_text').$item['file_path']; $item['external_url'] = config('chef.sso_domain_text').$item['file_path']; $goodsImages[] = $item; } } $goodsInfo['goods_images'] = $goodsImages; // 商品主图 $goodsInfo['goods_image'] = current($goodsInfo['goods_images'])['preview_url']; // 商品主图 生成海报用 $goodsInfo['goods_image_text'] = current($goodsInfo['goods_images'])['preview_url']; // 商品销量(实际显示=初始虚拟销量+实际销量) $goodsInfo['goods_sales'] = $goodsInfo['sales_initial'] + $goodsInfo['sales_actual']; // 商品库存(总库存=线上库存+门店库存) $goodsInfo['goods_stocks'] = $goodsInfo['stock_total'] + $goodsInfo['shops_stock_total']; // 回调函数 is_callable($callback) && call_user_func($callback, $goodsInfo); return $goodsInfo->hidden(array_merge($this->hidden, ['specRel', 'images'])); } /** * 根据商品id集获取商品列表 * @param array $goodsIds * @param null $status * @return array|mixed */ public function getListByIds(array $goodsIds, $status = null) { if (empty($goodsIds)) { return []; } // 筛选条件 $filter = [['goods_id', 'in', $goodsIds]]; // 商品状态 $status > 0 && $filter[] = ['status', '=', $status]; // 获取商品列表数据 $data = $this // ->withoutField(['content']) ->with(['images.file','category']) ->where($filter) ->where('is_delete', '=', 0) ->orderRaw('field(goods_id, ' . implode(',', $goodsIds) . ')') ->select(); // 整理列表数据并返回 return $this->setGoodsListData($data); } /** * 商品多规格信息 * @param \think\Collection $specRel * @param \think\Collection $skuData * @return array */ public function getManySpecData($specRel, $skuData) { // spec_attr $specAttrData = []; foreach ($specRel as $item) { if (!isset($specAttrData[$item['spec_id']])) { $specAttrData[$item['spec_id']] = [ 'group_id' => $item['spec']['spec_id'], 'name' => $item['spec']['spec_name'], 'spec_items' => [], ]; } $specAttrData[$item['spec_id']]['spec_items'][] = [ 'item_id' => $item['spec_value_id'], 'spec_value' => $item['spec_value'], ]; } // spec_list $specListData = []; foreach ($skuData as $item) { $image = (isset($item['images']) && !empty($item['images'])) ? $item['images'] : ['file_id' => 0, 'external_url' => '']; $specListData[] = [ 'id' => $item['id'], 'goods_sku_id' => $item['goods_sku_id'], 'rows' => [], 'form' => [ 'image_id' => $image['file_id'], 'image_path' => $image['external_url'], 'goods_sku_no' => $item['goods_sku_no'], 'goods_price' => $item['goods_price'], 'goods_weight' => $item['goods_weight'], 'line_price' => $item['line_price'], 'stock_num' => $item['stock_num'], ], ]; } return ['spec_attr' => array_values($specAttrData), 'spec_list' => $specListData]; } /** * 获取商品记录 * @param int $goodsId * @param array $with * @return static */ public static function detail(int $goodsId, array $with = []) { return static::get($goodsId, $with); } /** * 累积收藏数 * @param int $goodsId 商品ID * @param int $num 递增的数量 * @return mixed */ public static function setIncByField(int $goodsId, $field = '', int $num = 1) { return (new static)->setInc([['goods_id', '=', $goodsId]], $field, $num); } /** * 累积收藏数 * @param int $goodsId 商品ID * @param int $num 递减的数量 * @return mixed */ public static function setDecByField(int $goodsId, $field = '', int $num = 1) { return (new static)->setDec([['goods_id', '=', $goodsId]], $field, $num); } // /** // * 指定的商品规格信息 // * @param static $goods 商品详情 // * @param string $goodsSkuId // * @return array|bool // */ // public static function getGoodsSku(self $goods, string $goodsSkuId) // { // // 获取指定的sku // $goodsSku = []; // foreach ($goods['skuList'] as $item) { // if ($item['goods_sku_id'] == $goodsSkuId) { // $goodsSku = $item; // break; // } // } // if (empty($goodsSku)) { // return false; // } // // 多规格文字内容 // $goodsSku['goods_attr'] = ''; // if ($goods['spec_type'] == 20) { // $specRelData = helper::arrayColumn2Key($goods['spec_rel'], 'spec_value_id'); // $attrs = explode('_', $goodsSku['goods_sku_id']); // foreach ($attrs as $specValueId) { // $goodsSku['goods_attr'] .= $specRelData[$specValueId]['spec']['spec_name'] . ':' // . $specRelData[$specValueId]['spec_value'] . '; '; // } // } // return $goodsSku; // } public function getCostsList(array $param = [], int $listRows = 15) { // 筛选条件 $query = $this->getQueryFilter($param); // 排序条件 //$sort = $this->setQuerySort($param); // 执行查询 $list = $query->with(['provider','skues']) ->alias($this->name) ->field($this->getAliasFields($this->name, ['content'])) ->where('is_delete', '=', 0) ->paginate($listRows); // 整理列表数据并返回 return $this->setGoodsListDataFinance($list); } /** * 关联供应商表/商铺 * @return \think\model\relation\HasMany */ public function skues() { return $this->hasMany(\app\store\model\GoodsSku::class, 'goods_id', 'goods_id')->field('id,goods_id,clearing_price,platform_rate,goods_props'); } /** * 导出提现列表 * @param array $param * @author: zq * @Time: 2021/10/15 13:51 */ public function listExport(array $param) { //$ids = $param['goods_id']; $data['header'] = ['商品序号', '商品编码', '商品名称', '供应商名称', '商品分类','售价', '结算价']; $data['filename'] = '财务成本管理列表导出'; $data['data'] = []; $query = $this->getQueryFilter($param); $listI = $query->with(['provider','skues']) ->alias($this->name) ->field($this->getAliasFields($this->name, ['content'])) ->where('is_delete', '=', 0)->select(); //->whereIn('goods_id',$ids) //->paginate(500); // 整理列表数据并返回 $list = $this->setGoodsListDataFinance($listI); /* $list = $listPage->toArray(); dd($listPage);*/ foreach ($list as $arr){ $new_list['goods_id'] = $arr['goods_id']; $new_list['goods_no'] = $arr['goods_no']; $new_list['goods_name'] = $arr['goods_name']; $new_list['provider'] = $arr['provider']['provider_name']; $new_list['category_name'] = $arr['category_name']; $new_list['goods_prices'] = $arr['goods_price_min'].'~'.$arr['goods_price_max']; $new_list['clearing_price_range'] = $arr['clearing_price_range']; $data['data'][] = $new_list; } return $data; } /** * 获取商品列表 * @param array $param 查询条件 * @return mixed * @throws \think\db\exception\DbException */ public function getListNoPage() { $param['sortType'] = 'create_time'; $param['listType'] = 'all'; $param['status'] = 10; // 筛选条件 $query = $this->getQueryFilter($param); // 设置显示的销量 goods_sales // 购买数*1.5+加入购物车数+收藏数*0.8)之和排倒序 $query->field(['(sales_initial + sales_actual + sales_shops) as goods_sales']); // 排序条件 $sort = $this->setQuerySortZq($param); // 执行查询 return $query->with(['images.file']) ->alias($this->name) ->field($this->getAliasFields($this->name, ['content'])) ->where('status', '=', 10) ->where('is_delete', '=', 0) ->order($sort) ->select(); /* return $list; dd($list->toArray()); // 整理列表数据并返回 return $this->setGoodsListData($list);*/ } }