381 lines
9.9 KiB
PHP
381 lines
9.9 KiB
PHP
<?php
|
|
|
|
namespace app\common\service\app\physical;
|
|
|
|
use app\admin\model\app\physical\Goods;
|
|
use app\admin\model\app\physical\Category;
|
|
use think\Db;
|
|
|
|
/**
|
|
* 商品服务类
|
|
*/
|
|
class GoodsService
|
|
{
|
|
protected $query = null;
|
|
protected $order = [];
|
|
protected $md5s = [];
|
|
protected $format = null;
|
|
|
|
public function __construct(\Closure $format = null, \think\db\Query $query = null)
|
|
{
|
|
$this->query = $query ?: new Goods();
|
|
$this->format = $format;
|
|
}
|
|
|
|
/**
|
|
* with 关联
|
|
*
|
|
* @param mixed $with
|
|
* @return self
|
|
*/
|
|
public function with($with)
|
|
{
|
|
$this->query->with($with);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 查询上架的商品,包含隐藏商品
|
|
*
|
|
* @return self
|
|
*/
|
|
public function show()
|
|
{
|
|
$this->md5s[] = 'show';
|
|
$this->query = $this->query->whereIn('status', [1]);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 查询上架的商品,不包含隐藏商品
|
|
*
|
|
* @return self
|
|
*/
|
|
public function up()
|
|
{
|
|
$this->md5s[] = 'up';
|
|
$this->query = $this->query->where('status', 1);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 查询库存大于0的商品
|
|
*
|
|
* @return self
|
|
*/
|
|
public function inStock()
|
|
{
|
|
$this->md5s[] = 'inStock';
|
|
$this->query = $this->query->where('stock', '>', 0);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 搜索商品
|
|
*
|
|
* @param string $keyword
|
|
* @return self
|
|
*/
|
|
public function search($keyword)
|
|
{
|
|
$keyword = is_string($keyword) ? $keyword : json_encode($keyword);
|
|
$this->md5s[] = 'search:' . $keyword;
|
|
|
|
$this->query = $this->query->where('name', 'like', '%' . $keyword . '%');
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 根据 ids 获取商品
|
|
*
|
|
* @param string|array $ids ids 数组或者 , 隔开的字符串
|
|
* @return self
|
|
*/
|
|
public function whereIds($ids = '')
|
|
{
|
|
$ids = array_map('intval', is_array($ids) ? $ids : explode(',', $ids));
|
|
$this->md5s[] = 'ids:' . implode(',', $ids);
|
|
|
|
if (!empty($ids)) {
|
|
$this->query = $this->query->orderRaw('field(id, ' . implode(',', $ids) . ')');
|
|
$this->query = $this->query->whereIn('id', $ids);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 根据商品分类获取商品
|
|
*
|
|
* @param integer $category_id
|
|
* @param boolean $is_category_deep 是否深度搜索子分类
|
|
* @return self
|
|
*/
|
|
public function category($category_id, $is_category_deep = true)
|
|
{
|
|
$category_id = intval($category_id);
|
|
$this->md5s[] = 'category_id:' . $category_id;
|
|
$category_ids = [];
|
|
|
|
if (isset($category_id) && $category_id != 0) {
|
|
if ($is_category_deep) {
|
|
// 查询分类所有子分类,包括自己
|
|
$category_ids = $this->getChildIds($category_id);
|
|
} else {
|
|
$category_ids = [$category_id];
|
|
}
|
|
}
|
|
|
|
$this->query->where(function ($query) use ($category_ids) {
|
|
// 所有子分类使用 find_in_set or 匹配
|
|
foreach ($category_ids as $key => $category_id) {
|
|
$query->whereOrRaw("find_in_set($category_id, category_id)");
|
|
}
|
|
});
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 获取分类及其所有子分类ID
|
|
*
|
|
* @param int $categoryId
|
|
* @return array
|
|
*/
|
|
protected function getChildIds($categoryId)
|
|
{
|
|
$categoryIds = [$categoryId];
|
|
$this->getChildCategoryIds($categoryId, $categoryIds);
|
|
return $categoryIds;
|
|
}
|
|
|
|
/**
|
|
* 递归获取子分类ID
|
|
*
|
|
* @param int $parentId
|
|
* @param array $categoryIds
|
|
*/
|
|
protected function getChildCategoryIds($parentId, &$categoryIds)
|
|
{
|
|
$children = Category::where('parent_id', $parentId)
|
|
->where('status', 1)
|
|
->where('uniacid', UNIACID)
|
|
->column('id');
|
|
|
|
foreach ($children as $childId) {
|
|
$categoryIds[] = $childId;
|
|
$this->getChildCategoryIds($childId, $categoryIds);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 排序,方法参数同 thinkphp order 方法
|
|
*
|
|
* @param string $sort
|
|
* @param string $order
|
|
* @return self
|
|
*/
|
|
public function order($sort = 'weigh', $order = 'desc')
|
|
{
|
|
$sort = $sort == 'price' ? Db::raw('convert(`price`, DECIMAL(10, 2)) ' . $order) : $sort;
|
|
$this->query->order($sort, $order);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 获取商品列表
|
|
*
|
|
* @param bool $is_cache 是否缓存
|
|
* @return array|\think\model\Collection
|
|
*/
|
|
public function select($is_cache = false)
|
|
{
|
|
$this->md5s[] = 'select';
|
|
|
|
// 默认排序
|
|
$this->order();
|
|
|
|
$goods = $this->query->select();
|
|
|
|
// 格式化数据
|
|
foreach ($goods as $key => $gd) {
|
|
$gd = $this->defaultFormat($gd);
|
|
if ($this->format instanceof \Closure) {
|
|
$gd = ($this->format)($gd, $this);
|
|
}
|
|
$goods[$key] = $gd;
|
|
}
|
|
|
|
return $goods;
|
|
}
|
|
|
|
/**
|
|
* 获取商品列表(分页)
|
|
*
|
|
* @param bool $is_cache 是否缓存
|
|
* @return \think\Paginator
|
|
*/
|
|
public function paginate($is_cache = false)
|
|
{
|
|
$this->md5s[] = 'paginate';
|
|
|
|
// 默认排序
|
|
$this->order();
|
|
|
|
$goods = $this->query->paginate(request()->param('list_rows', 10));
|
|
|
|
// 格式化数据
|
|
$goods->each(function ($god) {
|
|
$god = $this->defaultFormat($god);
|
|
if ($this->format instanceof \Closure) {
|
|
($this->format)($god, $this);
|
|
}
|
|
});
|
|
|
|
return $goods;
|
|
}
|
|
|
|
/**
|
|
* 获取单个商品
|
|
*
|
|
* @param bool $is_cache
|
|
* @return Goods
|
|
*/
|
|
public function find($is_cache = false)
|
|
{
|
|
$this->md5s[] = 'find';
|
|
$goods = $this->query->find();
|
|
|
|
if ($goods && $this->format instanceof \Closure) {
|
|
// 格式化数据
|
|
$goods = $this->defaultFormat($goods);
|
|
($this->format)($goods, $this);
|
|
}
|
|
|
|
return $goods;
|
|
}
|
|
|
|
/**
|
|
* 获取单个商品,找不到抛出异常
|
|
*
|
|
* @param bool $is_cache
|
|
* @return Goods
|
|
*/
|
|
public function findOrFail($is_cache = false)
|
|
{
|
|
$this->md5s[] = 'find';
|
|
$goods = $this->query->find();
|
|
if (!$goods) {
|
|
throw new \Exception('商品不存在');
|
|
}
|
|
|
|
if ($this->format instanceof \Closure) {
|
|
// 格式化数据
|
|
$goods = $this->defaultFormat($goods);
|
|
($this->format)($goods, $this);
|
|
}
|
|
|
|
return $goods;
|
|
}
|
|
|
|
/**
|
|
* 默认格式化数据
|
|
*
|
|
* @param array|object $goods
|
|
* @return array
|
|
*/
|
|
public function defaultFormat($goods)
|
|
{
|
|
// 获取配置
|
|
$physicalConfig = \app\common\model\app\Config::getConfig('physical');
|
|
$showSales = $physicalConfig['show_sales'] ?? 1;
|
|
|
|
// 处理图片
|
|
if (isset($goods['carousel']) && is_string($goods['carousel'])) {
|
|
$goods['carousel'] = json_decode($goods['carousel'], true) ?: [];
|
|
}
|
|
|
|
// 处理分类ID
|
|
if (isset($goods['category_id']) && is_string($goods['category_id'])) {
|
|
$goods['category_id'] = array_map('intval', explode(',', $goods['category_id']));
|
|
}
|
|
|
|
// 处理参数
|
|
if (isset($goods['params']) && is_string($goods['params'])) {
|
|
$goods['params'] = json_decode($goods['params'], true) ?: [];
|
|
}
|
|
|
|
// 根据配置决定是否显示销量
|
|
if ($showSales != 1) {
|
|
unset($goods['total_sales']);
|
|
}
|
|
|
|
// 获取最低价格、库存和销量
|
|
if (isset($goods['id'])) {
|
|
$goodsId = $goods['id'];
|
|
|
|
// 优先使用商品表中的 price 字段
|
|
if (isset($goods['price']) && $goods['price'] > 0) {
|
|
$goods['price'] = floatval($goods['price']);
|
|
} else {
|
|
// 如果商品表中没有价格,则从 SKU 价格表中计算
|
|
$skuPrices = \app\admin\model\app\physical\SkuPrice::where('goods_id', $goodsId)
|
|
->where('uniacid', UNIACID)
|
|
->where('status', 1)
|
|
->select();
|
|
|
|
if ($skuPrices) {
|
|
if (is_array($skuPrices)) {
|
|
$skuPricesArray = $skuPrices;
|
|
} else {
|
|
$skuPricesArray = $skuPrices->toArray();
|
|
}
|
|
|
|
$goods['price'] = min(array_column($skuPricesArray, 'price'));
|
|
} else {
|
|
$goods['price'] = 0;
|
|
}
|
|
}
|
|
|
|
// 计算库存和销量
|
|
$skuPrices = \app\admin\model\app\physical\SkuPrice::where('goods_id', $goodsId)
|
|
->where('uniacid', UNIACID)
|
|
->where('status', 1)
|
|
->select();
|
|
|
|
if ($skuPrices) {
|
|
if (is_array($skuPrices)) {
|
|
$skuPricesArray = $skuPrices;
|
|
} else {
|
|
$skuPricesArray = $skuPrices->toArray();
|
|
}
|
|
|
|
$goods['stock'] = array_sum(array_column($skuPricesArray, 'stock'));
|
|
|
|
// 根据配置决定是否显示销量
|
|
if ($showSales != 1) {
|
|
unset($goods['sales']);
|
|
}
|
|
} else {
|
|
$goods['stock'] = 0;
|
|
$goods['sales'] = 0;
|
|
}
|
|
}
|
|
|
|
return $goods;
|
|
}
|
|
|
|
/**
|
|
* 默认调用 query 中的方法,比如 withTrashed()
|
|
*
|
|
* @param string $funcname
|
|
* @param array $arguments
|
|
* @return self
|
|
*/
|
|
public function __call($funcname, $arguments)
|
|
{
|
|
$this->query->{$funcname}(...$arguments);
|
|
return $this;
|
|
}
|
|
}
|