SkuService.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php
  2. namespace addons\TinyShop\services\product;
  3. use addons\TinyShop\common\models\order\Order;
  4. use Yii;
  5. use yii\db\ActiveQuery;
  6. use yii\helpers\Json;
  7. use yii\web\UnprocessableEntityHttpException;
  8. use common\helpers\ArrayHelper;
  9. use common\enums\StatusEnum;
  10. use common\components\Service;
  11. use addons\TinyShop\common\models\product\Product;
  12. use addons\TinyShop\common\models\product\Sku;
  13. /**
  14. * Class SkuService
  15. * @package addons\TinyShop\services\product
  16. * @author jianyan74 <751393839@qq.com>
  17. */
  18. class SkuService extends Service
  19. {
  20. /**
  21. * @param Product $product
  22. * @param $data
  23. * @throws \yii\db\Exception
  24. */
  25. public function create(Product $product, $data)
  26. {
  27. !is_array($data) && $data = Json::decode($data);
  28. if (empty($data)) {
  29. throw new UnprocessableEntityHttpException('请至少添加一个规格');
  30. }
  31. $skus = $this->findByProductId($product->id);
  32. $oldData = ArrayHelper::getColumn($skus, 'data');
  33. $newData = ArrayHelper::getColumn($data, 'data');
  34. list($updatedIds, $deleteData) = ArrayHelper::comparisonIds($oldData, $newData);
  35. // 获取规格Map
  36. $skus = ArrayHelper::arrayKey($skus, 'data');
  37. // 让购物车里面的sku失效
  38. if (!empty($deleteData)) {
  39. $deleteIds = [];
  40. foreach ($deleteData as $deleteDatum) {
  41. isset($skus[$deleteDatum]['id']) && $deleteIds[] = $skus[$deleteDatum]['id'];
  42. }
  43. // 删除失效的sku
  44. Sku::deleteAll([
  45. 'and',
  46. ['product_id' => $product->id, 'merchant_id' => $product->merchant_id],
  47. ['in', 'id', $deleteIds],
  48. ]);
  49. Yii::$app->tinyShopService->memberCartItem->loseBySkus($deleteIds);
  50. }
  51. $rows = [];
  52. $field = [];
  53. $using = false;
  54. foreach ($data as $datum) {
  55. $datum['name'] = implode(' ', ArrayHelper::getColumn($datum['child'], 'title'));
  56. $datum['product_id'] = $product->id;
  57. $datum['merchant_id'] = $product->merchant_id;
  58. $datum['created_at'] = time();
  59. $datum['updated_at'] = time();
  60. // 校验数据是否规范
  61. $model = new Sku();
  62. $model->loadDefaultValues();
  63. $model->attributes = $datum;
  64. $model->sku_no = (string)$model->sku_no;
  65. !$model->validate() && $this->error($model);
  66. if ($model->status == StatusEnum::ENABLED) {
  67. $using = true;
  68. }
  69. // 更新数据
  70. $row = ArrayHelper::toArray($model);
  71. if (in_array($model->data, $updatedIds)) {
  72. $skuModel = $skus[$datum['data']];
  73. $skuModel->attributes = $row;
  74. !$skuModel->save() && $this->error($skuModel);
  75. } else {
  76. $rows[] = $row;
  77. empty($field) && $field = array_keys($row);
  78. }
  79. }
  80. if ($using === false) {
  81. throw new UnprocessableEntityHttpException('请至少启用一个规格');
  82. }
  83. !empty($rows) && Yii::$app->db->createCommand()->batchInsert(Sku::tableName(), $field, $rows)->execute();
  84. }
  85. /**
  86. * @param $product_id
  87. * @param $data
  88. * @throws \yii\db\Exception
  89. */
  90. public function createByCopy($product_id, $data)
  91. {
  92. $rows = $field = [];
  93. foreach ($data as $datum) {
  94. $rows[] = [
  95. 'product_id' => $product_id,
  96. 'merchant_id' => $datum['merchant_id'],
  97. 'name' => $datum['name'],
  98. 'picture' => $datum['picture'],
  99. 'price' => $datum['price'],
  100. 'data' => $datum['data'],
  101. 'market_price' => $datum['market_price'],
  102. 'cost_price' => $datum['cost_price'],
  103. 'stock' => $datum['stock'],
  104. 'sku_no' => $datum['sku_no'],
  105. 'barcode' => $datum['barcode'],
  106. 'weight' => $datum['weight'],
  107. 'volume' => $datum['volume'],
  108. 'status' => $datum['status'],
  109. 'created_at' => time(),
  110. 'updated_at' => time(),
  111. ];
  112. empty($field) && $field = array_keys($rows[0]);
  113. }
  114. !empty($rows) && Yii::$app->db->createCommand()->batchInsert(Sku::tableName(), $field, $rows)->execute();
  115. }
  116. /**
  117. * 扣除库存
  118. *
  119. * @param Order $order
  120. * @param $orderProduct
  121. * @param $stockDeductionType
  122. * @param $force
  123. * @return bool|void
  124. * @throws UnprocessableEntityHttpException
  125. */
  126. public function decrRepertory(Order $order, $orderProducts, $stockDeductionType = null, $force = true)
  127. {
  128. // 扣减库存数量
  129. $skuNums = [];
  130. foreach ($orderProducts as $item) {
  131. // 扣减库存类型判断
  132. if (
  133. $stockDeductionType != null &&
  134. $stockDeductionType != $item['stock_deduction_type']
  135. ) {
  136. continue;
  137. }
  138. if (!isset($skuNums[$item['sku_id']])) {
  139. $skuNums[$item['sku_id']] = 0;
  140. }
  141. $skuNums[$item['sku_id']] += $item['num'];
  142. }
  143. if (empty($skuNums)) {
  144. return false;
  145. }
  146. $ids = array_keys($skuNums);
  147. $models = Sku::find()
  148. ->select(['id', 'product_id', 'stock', 'price', 'name'])
  149. ->with(['product'])
  150. ->where(['in', 'id', $ids])
  151. ->asArray()
  152. ->all();
  153. // 判断是否下架
  154. foreach ($models as $model) {
  155. if (
  156. $model['product']['audit_status'] != StatusEnum::ENABLED ||
  157. $model['product']['status'] != StatusEnum::ENABLED
  158. ) {
  159. throw new UnprocessableEntityHttpException($model['product']['name'] . '已下架或不存在');
  160. }
  161. if (
  162. $model['stock'] < abs($skuNums[$model['id']]) ||
  163. $model['product']['stock'] < abs($skuNums[$model['id']])
  164. ) {
  165. throw new UnprocessableEntityHttpException($model['product']['name'] . '库存不足');
  166. }
  167. }
  168. // 判断是否扣减库存
  169. if ($force == false) {
  170. return false;
  171. }
  172. // 扣减数量
  173. foreach ($models as $model) {
  174. $num = abs($skuNums[$model['id']]);
  175. if (!Sku::updateAllCounters(['stock' => -$num], [
  176. 'and',
  177. ['id' => $model['id']],
  178. ['>=', 'stock', $num],
  179. ]
  180. )) {
  181. throw new UnprocessableEntityHttpException($model['product']['name'] . '库存不足');
  182. }
  183. if (!Product::updateAllCounters([
  184. 'stock' => -$num,
  185. 'real_sales' => $num,
  186. 'total_sales' => $num
  187. ],
  188. [
  189. 'and',
  190. ['id' => $model['product_id']],
  191. ['>=', 'stock', $num],
  192. ]
  193. )) {
  194. throw new UnprocessableEntityHttpException($model['product']['name'] . '库存不足');
  195. }
  196. }
  197. }
  198. /**
  199. * @param $productId
  200. * @param $pitchOn
  201. * @return array
  202. */
  203. public function getJsData($productId, $pitchOn)
  204. {
  205. $pitchOn = ArrayHelper::arrayKey($pitchOn, 'id');
  206. $sku = $this->findByProductId($productId);
  207. $sku = ArrayHelper::toArray($sku);
  208. foreach ($sku as &$item) {
  209. $item['child'] = [];
  210. $dataArr = explode('-', $item['data']);
  211. foreach ($dataArr as $value) {
  212. if (isset($pitchOn[$value])) {
  213. $item['child'][] = [
  214. 'id' => $pitchOn[$value]['id'],
  215. 'title' => $pitchOn[$value]['title'],
  216. ];
  217. }
  218. }
  219. }
  220. return ArrayHelper::arrayKey($sku, 'data');
  221. }
  222. /**
  223. * 写入
  224. *
  225. * @param $product_id
  226. * @param $data
  227. */
  228. public function saveByProductId($product_id, $data)
  229. {
  230. if (!($model = Sku::find()->where(['product_id' => $product_id])->one())) {
  231. $model = new Sku();
  232. $model = $model->loadDefaultValues();
  233. $model->merchant_id = Yii::$app->services->merchant->getNotNullId();
  234. }
  235. $model->attributes = $data;
  236. $model->name = '';
  237. $model->product_id = $product_id;
  238. $model->save();
  239. }
  240. /**
  241. * 获取单个商品库存
  242. *
  243. * @param $product_id
  244. * @return false|null|string
  245. */
  246. public function getStockByProductId($product_id)
  247. {
  248. $stock = Sku::find()
  249. ->select(['sum(stock) as all_stock'])
  250. ->where(['product_id' => $product_id])
  251. ->andWhere(['>', 'stock', 0])
  252. ->asArray()
  253. ->scalar();
  254. return $stock ?? 0;
  255. }
  256. /**
  257. * @param $id
  258. * @return array|null|\yii\db\ActiveRecord
  259. */
  260. public function findWithProductById($id, $member_id)
  261. {
  262. return Sku::find()
  263. ->where(['id' => $id])
  264. ->with([
  265. 'product.marketingProduct',
  266. 'product.cateMap',
  267. 'product.myGet' => function (ActiveQuery $query) use ($member_id) {
  268. return $query->andWhere(['buyer_id' => $member_id]);
  269. }
  270. ])
  271. ->asArray()
  272. ->one();
  273. }
  274. /**
  275. * @param $id
  276. * @return array|null|\yii\db\ActiveRecord
  277. */
  278. public function findWithProductByIds($ids, $member_id)
  279. {
  280. return Sku::find()
  281. ->where(['in', 'id', $ids])
  282. ->with([
  283. 'product.marketingProduct',
  284. 'product.cateMap',
  285. 'product.myGet' => function (ActiveQuery $query) use ($member_id) {
  286. return $query->andWhere(['buyer_id' => $member_id]);
  287. }
  288. ])
  289. ->asArray()
  290. ->all();
  291. }
  292. /**
  293. * @param $sku_id
  294. * @return array|\yii\db\ActiveRecord|null
  295. */
  296. public function findById($sku_id)
  297. {
  298. return Sku::find()
  299. ->andFilterWhere(['id' => $sku_id])
  300. ->with('product')
  301. ->one();
  302. }
  303. /**
  304. * @param array $sku_ids
  305. * @return array|\yii\db\ActiveRecord[]
  306. */
  307. public function findByIds(array $sku_ids)
  308. {
  309. return Sku::find()
  310. ->where(['in', 'id', $sku_ids])
  311. ->with('product.cateMap')
  312. ->asArray()
  313. ->all();
  314. }
  315. /**
  316. * @param $product_id
  317. * @return array|\yii\db\ActiveRecord[]
  318. */
  319. public function findByProductId($product_id)
  320. {
  321. return Sku::find()
  322. ->where(['product_id' => $product_id])
  323. ->andWhere(['>=', 'status', StatusEnum::DISABLED])
  324. ->all();
  325. }
  326. /**
  327. * @param $barcode
  328. * @return array|\yii\db\ActiveRecord|null
  329. */
  330. public function findByBarcode($barcode, $merchant_id, $store_id)
  331. {
  332. return Sku::find()
  333. ->where(['barcode' => $barcode])
  334. ->andFilterWhere(['merchant_id' => $merchant_id])
  335. ->with(['product', 'repertoryStock' => function (ActiveQuery $query) use ($merchant_id, $store_id) {
  336. return $query->andWhere([
  337. 'merchant_id' => $merchant_id,
  338. 'store_id' => $store_id
  339. ]);
  340. }])
  341. ->asArray()
  342. ->one();
  343. }
  344. }
粤ICP备19079148号