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.
yanzong/app/common/model/Goods.php

724 lines
26 KiB

<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
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);
// 整理列表数据并返回
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 = $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);
}
}