| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- <?php
- /*
- * FecShop file.
- *
- * @link http://www.fecshop.com/
- * @copyright Copyright (c) 2016 FecShop Software LLC
- * @license http://www.fecshop.com/license/
- */
- namespace fecshop\services;
- use yii\base\InvalidCallException;
- use yii\base\InvalidConfigException;
- use Yii;
- /**
- * Product Service is the component that you can get product info from it.
- *
- * @property \fecshop\services\Image | \fecshop\services\Product\Image $image image service or product image sub-service
- * @property \fecshop\services\product\Info $info product info sub-service
- * @property \fecshop\services\product\Stock $stock stock sub-service of product service
- *
- * @method getByPrimaryKey($primaryKey) get product model by primary key
- * @see \fecshop\services\Product::actionGetByPrimaryKey()
- * @method getEnableStatus() get enable status
- * @see \fecshop\services\Product::actionGetEnableStatus()
- *
- * @author Terry Zhao <2358269014@qq.com>
- * @since 1.0
- */
- class Product extends Service
- {
- /**
- * @var array 自定义的属性组配置数组
- */
- public $customAttrGroup;
- public $categoryAggregateMaxCount; // Yii::$service->product->categoryAggregateMaxCount;
- /**
- * 分类页面的产品,如果一个spu下面由多个sku同时在这个分类,
- * 那么,是否只显示一个sku(score最高),而不是全部sku
- * true: 代表只显示一个sku
- * false: 代表产品全部显示
- */
- public $productSpuShowOnlyOneSku = true;
-
- /**
- * $storagePrex , $storage , $storagePath 为找到当前的storage而设置的配置参数
- * 可以在配置中更改,更改后,就会通过容器注入的方式修改相应的配置值
- */
- public $storage = 'ProductMongodb'; // 当前的storage,如果在config中配置,那么在初始化的时候会被注入修改
- /**
- * 设置storage的path路径,
- * 如果不设置,则系统使用默认路径
- * 如果设置了路径,则使用自定义的路径
- */
- public $storagePath = '';
- /**
- * @var \fecshop\services\product\ProductInterface 根据 $storage 及 $storagePath 配置的 Product 的实现
- */
- protected $_product;
- /**
- * @var string 默认属性组名称
- */
- protected $_defaultAttrGroup = 'default';
- public function init()
- {
- parent::init();
- $currentService = $this->getStorageService($this);
- $this->_product = new $currentService();
- }
- protected function actionGetEnableStatus()
- {
- return $this->_product->getEnableStatus();
- }
-
- /**
- * 得到产品的所有的属性组。
- */
- protected function actionGetCustomAttrGroup()
- {
- $customAttrGroup = $this->customAttrGroup;
- $arr = array_keys($customAttrGroup);
- $arr[] = $this->_defaultAttrGroup;
- return $arr;
- }
- /**
- * @param $productAttrGroup|string
- * 得到这个产品属性组里面的所有的产品属性详细,
- * 注解:不同类型的产品,对应不同的属性组,譬如衣服有颜色尺码,电脑类型的有不同cpu型号等
- * 属性组,以及属性组对应的属性,是在Product Service config中配置的。
- */
- protected function actionGetGroupAttrInfo($productAttrGroup)
- {
- $arr = [];
- if ($productAttrGroup == $this->_defaultAttrGroup) {
- return [];
- }
- // 得到普通属性
- if (isset($this->customAttrGroup[$productAttrGroup]['general_attr'])
- && is_array($this->customAttrGroup[$productAttrGroup]['general_attr'])
- ) {
- $arr = array_merge($arr, $this->customAttrGroup[$productAttrGroup]['general_attr']);
- }
- // 得到用于spu,细分sku的属性,譬如颜色尺码之类。
- if (isset($this->customAttrGroup[$productAttrGroup]['spu_attr'])
- && is_array($this->customAttrGroup[$productAttrGroup]['spu_attr'])
- ) {
- $arr = array_merge($arr, $this->customAttrGroup[$productAttrGroup]['spu_attr']);
- }
- return $arr;
- }
-
- /**
- * @param $productAttrGroup|string
- * 得到这个产品属性组里面的所有的产品属性,
- * 注解:不同类型的产品,对应不同的属性组,譬如衣服有颜色尺码,电脑类型的有不同cpu型号等
- * 属性组,以及属性组对应的属性,是在Product Service config中配置的。
- */
- protected function actionGetGroupAttr($productAttrGroup)
- {
- $arr = [];
-
- // 得到普通属性
- if (isset($this->customAttrGroup[$productAttrGroup]['general_attr'])
- && is_array($this->customAttrGroup[$productAttrGroup]['general_attr'])
- ) {
- $general_attr = $this->customAttrGroup[$productAttrGroup]['general_attr'];
- if (is_array($general_attr)) {
- foreach ($general_attr as $attr => $info) {
- $arr[] = $attr;
- }
- }
- }
- // 得到用于spu,细分sku的属性,譬如颜色尺码之类。
- if (isset($this->customAttrGroup[$productAttrGroup]['spu_attr'])
- && is_array($this->customAttrGroup[$productAttrGroup]['spu_attr'])
- ) {
- $spu_attr = $this->customAttrGroup[$productAttrGroup]['spu_attr'];
- if (is_array($spu_attr)) {
- foreach ($spu_attr as $attr => $info) {
- $arr[] = $attr;
- }
- }
- }
- return $arr;
- }
- /**
- * @param $productAttrGroup|string
- * @return array 一维数组
- * 得到这个产品属性组里面的属性,也就是原来的产品属性+属性组对应的属性
- */
- protected function actionGetSpuAttr($productAttrGroup)
- {
- $arr = [];
- if ($productAttrGroup == $this->_defaultAttrGroup) {
- return [];
- }
- // 得到用于spu,细分sku的属性,譬如颜色尺码之类。
- if (isset($this->customAttrGroup[$productAttrGroup]['spu_attr'])
- && is_array($this->customAttrGroup[$productAttrGroup]['spu_attr'])
- ) {
- $arr = array_merge($arr, $this->customAttrGroup[$productAttrGroup]['spu_attr']);
- }
- return array_keys($arr);
- }
- /**
- * @param $productAttrGroup | String
- * @return string 显示图片的spu属性。
- */
- protected function actionGetSpuImgAttr($productAttrGroup)
- {
- if ($productAttrGroup == $this->_defaultAttrGroup) {
- return '';
- }
- // 得到用于spu,细分sku的属性,譬如颜色尺码之类。
- if (isset($this->customAttrGroup[$productAttrGroup]['spu_attr'])
- && is_array($this->customAttrGroup[$productAttrGroup]['spu_attr'])
- ) {
- foreach ($this->customAttrGroup[$productAttrGroup]['spu_attr'] as $attr => $one) {
- if (isset($one['showAsImg']) && $one['showAsImg']) {
- return $attr;
- }
- }
- }
- return '';
- }
- /**
- * 产品状态是否是 active
- * @param int $status
- * @return boolean 如果产品状态是 active 返回 true, 否则返回 false
- */
- protected function actionIsActive($status)
- {
- return ($status == 1) ? true : false;
- }
- /**
- * @param $productAttrGroup | String 产品属性组
- * 通过产品属性组,从配置中得到对应的custom_options部分的配置
- * @return array
- */
- protected function actionGetCustomOptionAttrInfo($productAttrGroup)
- {
- if ($productAttrGroup == $this->_defaultAttrGroup) {
- return [];
- }
- if (isset($this->customAttrGroup[$productAttrGroup]['custom_options'])
- && is_array($this->customAttrGroup[$productAttrGroup]['custom_options'])
- ) {
- return $this->customAttrGroup[$productAttrGroup]['custom_options'];
- }
- return [];
- }
- /**
- * 得到默认的产品属性组。
- */
- protected function actionGetDefaultAttrGroup()
- {
- return $this->_defaultAttrGroup;
- }
- /**
- * 得到主键的名称.
- */
- protected function actionGetPrimaryKey()
- {
- return $this->_product->getPrimaryKey();
- }
- /**
- * get Product model by primary key.
- */
- protected function actionGetByPrimaryKey($primaryKey)
- {
- return $this->_product->getByPrimaryKey($primaryKey);
- }
- /**
- * @param $attr_group | String , 属性组名称
- * 给product model 增加相应的属性组对应的属性。
- */
- protected function actionAddGroupAttrs($attr_group)
- {
- return $this->_product->addGroupAttrs($attr_group);
- }
- /**
- * api部分
- * 和coll()的不同在于,该方式不走active record,因此可以获取产品的所有数据的。
- */
- protected function actionApicoll()
- {
- return $this->_product->apicoll();
- }
- /**
- * api部分
- */
- protected function actionApiGetByPrimaryKey($primaryKey)
- {
- return $this->_product->apiGetByPrimaryKey($primaryKey);
- }
- /**
- * api部分
- */
- protected function actionApiSave($product_one)
- {
- return $this->_product->apiSave($product_one);
- }
- /**
- * api部分
- */
- protected function actionApiDelete($primaryKey)
- {
- return $this->_product->apiDelete($primaryKey);
- }
- /**
- * 得到Product model的全名.
- */
- protected function actionGetModelName()
- {
- return get_class($this->_product->getByPrimaryKey());
- }
- /**
- * @param $sku | string
- * @param $returnArr | boolean , 是否返回数组格式
- * 通过sku查询产品
- */
- protected function actionGetBySku($sku, $returnArr = true)
- {
- return $this->_product->getBySku($sku, $returnArr);
- }
- /**
- * @param $spu | string
- * 通过spu查询产品
- */
- protected function actionGetBySpu($spu)
- {
- return $this->_product->getBySpu($spu);
- }
- /**
- * @param $filter|array
- * get artile collection by $filter
- * example filter:
- * [
- * 'numPerPage' => 20,
- * 'pageNum' => 1,
- * 'orderBy' => ['_id' => SORT_DESC, 'sku' => SORT_ASC ],
- * 'where' => [
- * ['>','price',1],
- * ['<=','price',10]
- * ['sku' => 'uk10001'],
- * ],
- * 'asArray' => true,
- * ]
- * 根据传入的查询条件,得到产品的列表
- */
- protected function actionColl($filter = [])
- {
- return $this->_product->coll($filter);
- }
- protected function actionCollCount($filter = [])
- {
- return $this->_product->collCount($filter);
- }
- /**
- * 通过where条件 和 查找的select 字段信息,得到产品的列表信息,
- * 这里一般是用于前台的区块性的不分页的产品查找。
- * 结果数据没有进行进一步处理,需要前端获取数据后在处理。
- */
- protected function actionGetProducts($filter)
- {
- return $this->_product->getProducts($filter);
- }
- /**
- * @param $product_id_arr | Array
- * @param $category_id | String
- * 在给予的产品id数组$product_id_arr中,找出来那些产品属于分类 $category_id
- * 该功能是后台分类编辑中,对应的分类产品列表功能
- * 也就是在当前的分类下,查看所有的产品,属于当前分类的产品,默认被勾选。
- */
- protected function actionGetCategoryProductIds($product_id_arr, $category_id)
- {
- return $this->_product->getCategoryProductIds($product_id_arr, $category_id);
- }
- /**
- * @param $one|array , 产品数据数组
- * @param $originUrlKey|string , 分类的原来的url key ,也就是在前端,分类的自定义url。
- * 保存产品(插入和更新),以及保存产品的自定义url
- * 如果提交的数据中定义了自定义url,则按照自定义url保存到urlkey中,如果没有自定义urlkey,则会使用name进行生成。
- */
- protected function actionSave($one, $originUrlKey = 'catalog/product/index')
- {
- return $this->_product->save($one, $originUrlKey);
- }
- /**
- * @param $ids | Array or String
- * 删除产品,如果ids是数组,则删除多个产品,如果是字符串,则删除一个产品
- * 在产品产品的同时,会在url rewrite表中删除对应的自定义url数据。
- */
- protected function actionRemove($ids)
- {
- return $this->_product->remove($ids);
- }
- /**
- * @param $category_id | String 分类的id的值
- * @param $addCateProductIdArr | Array 分类中需要添加的产品id数组,也就是给这个分类增加这几个产品。
- * @param $deleteCateProductIdArr | Array 分类中需要删除的产品id数组,也就是在这个分类下面去除这几个产品的对应关系。
- * 这个函数是后台分类编辑功能中使用到的函数,在分类中可以一次性添加多个产品,也可以删除多个产品,产品和分类是多对多的关系。
- */
- protected function actionAddAndDeleteProductCategory($category_id, $addCateProductIdArr, $deleteCateProductIdArr)
- {
- return $this->_product->addAndDeleteProductCategory($category_id, $addCateProductIdArr, $deleteCateProductIdArr);
- }
- /**
- * [
- * 'category_id' => 1,
- * 'pageNum' => 2,
- * 'numPerPage' => 50,
- * 'orderBy' => 'name',
- * 'where' => [
- * ['>','price',11],
- * ['<','price',22],
- * ],
- * 'select' => ['xx','yy'],
- * 'group' => '$spu',
- * ]
- * 得到分类下的产品,在这里需要注意的是:
- * 1.同一个spu的产品,有很多sku,但是只显示score最高的产品,这个score可以通过脚本取订单的销量(最近一个月,或者
- * 最近三个月等等),或者自定义都可以。
- * 2.结果按照filter里面的orderBy排序
- * 3.由于使用的是mongodb的aggregate(管道)函数,因此,此函数有一定的限制,就是该函数
- * 处理后的结果不能大约32MB,因此,如果一个分类下面的产品几十万的时候可能就会出现问题,
- * 这种情况可以用专业的搜索引擎做聚合工具。
- * 不过,对于一般的用户来说,这个不会成为瓶颈问题,一般一个分类下的产品不会出现几十万的情况。
- * 4.最后就得到spu唯一的产品列表(多个spu相同,sku不同的产品,只要score最高的那个).
- */
- protected function actionGetFrontCategoryProducts($filter)
- {
- return $this->_product->getFrontCategoryProducts($filter);
- }
- /**
- * @param $filter_attr | String 需要进行统计的字段名称
- * @propertuy $where | Array 搜索条件。这个需要些mongodb的搜索条件。
- * 得到的是个属性,以及对应的个数。
- * 这个功能是用于前端分类侧栏进行属性过滤。
- */
- protected function actionGetFrontCategoryFilter($filter_attr, $where)
- {
- return $this->_product->getFrontCategoryFilter($filter_attr, $where);
- }
- /**
- * 全文搜索
- * $filter Example:
- * $filter = [
- * 'pageNum' => $this->getPageNum(),
- * 'numPerPage' => $this->getNumPerPage(),
- * 'where' => $this->_where,
- * 'product_search_max_count' => Yii::$app->controller->module->params['product_search_max_count'],
- * 'select' => $select,
- * ];
- * 因为mongodb的搜索涉及到计算量,因此产品过多的情况下,要设置 product_search_max_count的值。减轻服务器负担
- * 因为对客户来说,前10页的产品已经足矣,后面的不需要看了,限定一下产品个数,减轻服务器的压力。
- * 多个spu,取score最高的那个一个显示。
- * 按照搜索的匹配度来进行排序,没有其他排序方式.
- */
- //protected function actionFullTearchText($filter){
- // return $this->_product->fullTearchText($filter);
- //}
- /**
- * @param $ids | Array
- * 通过产品ids得到产品sku
- */
- protected function actionGetSkusByIds($ids)
- {
- $skus = [];
- $_id = $this->getPrimaryKey();
- if (!empty($ids) && is_array($ids)) {
- $ids_ob_arr = [];
- foreach ($ids as $id) {
- $ids_ob_arr[] = new \MongoDB\BSON\ObjectId($id);
- }
- $filter = [
- 'where' => [
- ['in', $_id, $ids_ob_arr],
- ],
- 'asArray' => true,
- ];
- $coll = $this->coll($filter);
- $data = $coll['coll'];
- if (!empty($data) && is_array($data)) {
- foreach ($data as $one) {
- $skus[(string) $one[$_id]] = $one['sku'];
- }
- }
- }
- return $skus;
- }
- /**
- * @param $spu | String
- * @param $avag_rate | Int 产品的总平均得分
- * @param $count | Int 产品的总评论数
- * @param $avag_lang_rate | 当前语言的总平均得分
- * @param $lang_count | 当前语言的总评论数
- */
- protected function actionUpdateProductReviewInfo($spu, $avag_rate, $count, $lang_code, $avag_lang_rate, $lang_count, $rate_total_arr, $rate_lang_total_arr)
- {
- return $this->_product->updateProductReviewInfo($spu, $avag_rate, $count, $lang_code, $avag_lang_rate, $lang_count, $rate_total_arr, $rate_lang_total_arr);
- }
- public function updateAllScoreToZero()
- {
- return $this->_product->updateAllScoreToZero();
- }
- }
|