|
|
|
<?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\common\model\Channel;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 商品模型
|
|
|
|
* 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'] + $data['sales_actual'];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 商品详情: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')->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 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);
|
|
|
|
|
|
|
|
// 整理列表数据并返回
|
|
|
|
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']);
|
|
|
|
// 排序条件
|
|
|
|
$sort = $this->setQuerySort($param);
|
|
|
|
$order = request()->get()['order'] ?? '';
|
|
|
|
|
|
|
|
if (!empty($order)) {
|
|
|
|
if ($order == 1) {
|
|
|
|
$sort = ['goods_price_min' => 'asc'];
|
|
|
|
}
|
|
|
|
if ($order == 2) {
|
|
|
|
$sort = ['goods_price_min' => 'desc'];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($order == 3) {
|
|
|
|
$sort = ['discount' => 'asc'];
|
|
|
|
}
|
|
|
|
if ($order == 4) {
|
|
|
|
$sort = ['discount' => 'desc'];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($order == 5) {
|
|
|
|
$sort = ['goods_sales' => 'asc'];
|
|
|
|
|
|
|
|
}
|
|
|
|
if ($order == 6) {
|
|
|
|
$sort = ['goods_sales' => 'desc'];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($order == 7) {
|
|
|
|
$sort = ['goods_sales' => 'asc'];
|
|
|
|
}
|
|
|
|
if ($order == 8) {
|
|
|
|
$sort = ['goods_sales' => '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);
|
|
|
|
|
|
|
|
// 整理列表数据并返回
|
|
|
|
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下架)
|
|
|
|
'keywords' => ''
|
|
|
|
]);
|
|
|
|
|
|
|
|
// 实例化新查询对象
|
|
|
|
$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]; // 已售罄
|
|
|
|
}
|
|
|
|
// 商品状态
|
|
|
|
$params['status'] > 0 && $filter[] = ['status', '=', (int)$params['status']];
|
|
|
|
$a = 1;
|
|
|
|
// 商品分类
|
|
|
|
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']);
|
|
|
|
$query->where([
|
|
|
|
'goods_category_rel.category_id' => (int)$params['categoryId'],
|
|
|
|
//'goods.store_id' => (int)$params['store_id'],
|
|
|
|
]);
|
|
|
|
$a = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
// 商品名称
|
|
|
|
foreach (explode(",", $params['keywords']?? '') as $val) {
|
|
|
|
$filter[] = ['goods_name', 'like', "%{$val}%"];
|
|
|
|
}
|
|
|
|
|
|
|
|
// 商品名称
|
|
|
|
!empty($params['goodsName']) && $filter[] = ['goods_name', 'like', "%{$params['goodsName']}%"];
|
|
|
|
// 商品编码
|
|
|
|
!empty($params['goodsNo']) && $filter[] = ['goods_no', 'like', "%{$params['goodsNo']}%"];
|
|
|
|
|
|
|
|
//wmc
|
|
|
|
if (isset($param['is_brand']) && $param['is_brand'] !== '') {
|
|
|
|
$filter[] = ['is_brand', '=', $param['is_brand']];
|
|
|
|
}
|
|
|
|
if (isset($param['is_new']) && $param['is_new'] !== '') {
|
|
|
|
$filter[] = ['is_new', '=', $param['is_new']];
|
|
|
|
}
|
|
|
|
if (!empty($param['store_id']) && $a == 0) {
|
|
|
|
$filter[] = ['store_id', '=', $param['store_id']];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($param['paihang'])) {
|
|
|
|
$filter[] = ['paihang', '>', 0];
|
|
|
|
$query->order('paihang asc');
|
|
|
|
}
|
|
|
|
//是否店内
|
|
|
|
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']];
|
|
|
|
}
|
|
|
|
// 实例化新查询对象
|
|
|
|
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|static $goodsInfo
|
|
|
|
* @param callable|null $callback
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
protected function setGoodsData($goodsInfo, callable $callback = null)
|
|
|
|
{
|
|
|
|
$channel = Channel::where('code', $goodsInfo['channel'])->find();
|
|
|
|
$goodsInfo['channel_name'] = $channel['name'] ?? "";
|
|
|
|
|
|
|
|
$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 (UserService::isLogin()) {
|
|
|
|
// if (UserService::isStore()) {//店主
|
|
|
|
// $goodsInfo['goods_price_min_plus'] = 0;
|
|
|
|
// $goodsInfo['goods_price_min_dealer'] = 0;
|
|
|
|
// } elseif (UserService::isDealerMember()) { //分销商
|
|
|
|
// $goodsInfo['goods_price_min_dealer'] = 0;
|
|
|
|
// } elseif (UserService::isPlusMember()) {//升级会员
|
|
|
|
// $goodsInfo['goods_price_min_plus'] = 0;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// 回调函数
|
|
|
|
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 = [])
|
|
|
|
{
|
|
|
|
return static::get($goodsId, $with);
|
|
|
|
}
|
|
|
|
}
|