ProductAttrValueDao.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace app\common\dao\store\product;
  12. use app\common\dao\BaseDao;
  13. use app\common\model\store\product\ProductAttrValue as model;
  14. use app\common\repositories\store\product\CdkeyLibraryRepository;
  15. use app\common\repositories\store\product\ProductCdkeyRepository;
  16. use app\common\repositories\store\product\ProductRepository;
  17. use think\db\exception\DbException;
  18. use think\facade\Db;
  19. /**
  20. * Class ProductAttrValueDao
  21. * @package app\common\dao\store\product
  22. * @author xaboy
  23. * @day 2020/6/9
  24. */
  25. class ProductAttrValueDao extends BaseDao
  26. {
  27. /**
  28. * @return string
  29. * @author xaboy
  30. * @day 2020/6/9
  31. */
  32. protected function getModel(): string
  33. {
  34. return model::class;
  35. }
  36. /**
  37. * 商品规格添加操作
  38. * @param $data
  39. * @param $isType
  40. * @return void
  41. * @author Qinii
  42. */
  43. public function add($data,$isType)
  44. {
  45. switch ($isType) {
  46. case 2: //网盘信息
  47. $cdkey = [];
  48. foreach ($data as $datum) {
  49. $sku_cdkey = $datum['cdkey'][0];
  50. unset($datum['cdkey']);
  51. $sku = $this->create($datum);
  52. $sku_cdkey['value_id'] = $sku['value_id'];
  53. $cdkey[] = $sku_cdkey;
  54. }
  55. if ($cdkey) {
  56. app()->make(ProductCdkeyRepository::class)->insertAll($cdkey);
  57. }
  58. break;
  59. case 3: // 一次性卡密关联
  60. $cdkeyLibraryRepository = app()->make(CdkeyLibraryRepository::class);
  61. foreach ($data as $datum) {
  62. $sku = $this->create($datum);
  63. if ($datum['library_id']) {
  64. $cdkeyLibraryRepository->update($datum['library_id'], [
  65. 'product_id' => $datum['product_id'],
  66. 'product_attr_value_id' => $sku['value_id']
  67. ]);
  68. }
  69. }
  70. break;
  71. case 4: // 预约商品
  72. $sku = $this->create($data);
  73. $this->createReservation($sku, $data['reservation']);
  74. break;
  75. default:
  76. $this->insertAll($data);
  77. break;
  78. }
  79. }
  80. /**
  81. * 清楚所有规格
  82. * @Author:Qinii
  83. * @Date: 2020/5/9
  84. * @param int $productId
  85. * @return mixed
  86. */
  87. public function clearAttr(int $productId)
  88. {
  89. ($this->getModel())::where('product_id', $productId)->delete();
  90. }
  91. /**
  92. * 根据条件获取某字段的值合集
  93. * @Author:Qinii
  94. * @Date: 2020/5/9
  95. * @param int $merId
  96. * @param $field
  97. * @param $value
  98. * @param null $except
  99. * @return mixed
  100. */
  101. public function getFieldColumnt($key, $value, $field, $except = null)
  102. {
  103. return ($this->getModel()::getDB())->when($except, function ($query, $except) use ($field) {
  104. $query->where($field, '<>', $except);
  105. })->where($key, $value)->column($field);
  106. }
  107. /**
  108. * 计算指定字段的总和,排除特定值。
  109. *
  110. * 此函数用于根据给定的条件计算数据表中某个字段的总和,
  111. * 支持排除特定的值以获得更精确的计算结果。
  112. *
  113. * @param string $key 查询条件的关键字。
  114. * @param mixed $value 查询条件的关键字对应的值。
  115. * @param string $field 需要计算总和的字段名。
  116. * @param mixed $except 排除的特定值,可选参数。
  117. * @return int 返回计算得到的字段总和。
  118. */
  119. public function getFieldSum($key, $value, $field, $except = null)
  120. {
  121. // 使用模型的数据库实例,并根据$except参数应用排除条件
  122. return ($this->getModel()::getDB())->when($except, function ($query, $except) use ($field) {
  123. // 如果$except有值,则添加不等于($field, '<>', $except)的条件
  124. $query->where($field, '<>', $except);
  125. })->where($key, $value)->sum($field);
  126. }
  127. /**
  128. * 插入数据到数据库。
  129. *
  130. * 本函数用于将给定的数据数组批量插入到数据库。它首先通过getModel方法获取模型对象,
  131. * 然后调用该对象的getDB方法来获取数据库连接对象,最后通过调用insertAll方法来执行数据插入操作。
  132. *
  133. * @param array $data 包含多条待插入数据的数组,每条数据是一个子数组。
  134. * @return mixed 返回数据库操作的结果。具体类型取决于数据库库的实现。
  135. */
  136. public function insert(array $data)
  137. {
  138. // 通过模型获取数据库对象,并执行批量插入操作
  139. return ($this->getModel()::getDB())->insertAll($data);
  140. }
  141. /**
  142. * 检查指定字段的值在数据库中是否存在。
  143. *
  144. * 此函数用于确定给定字段的特定值是否在数据库中出现。它支持条件筛选,可以通过`merId`来限定查询范围,也可以通过`except`参数排除特定值。
  145. * 主要用于在进行数据操作前验证数据的唯一性或存在性,以避免重复数据或错误的数据操作。
  146. *
  147. * @param int|null $merId 商户ID,用于限定查询的范围。如果为null,则不进行商户ID的筛选。
  148. * @param string $field 要检查的字段名。
  149. * @param mixed $value 要检查的字段值。
  150. * @param mixed|null $except 排除的值,如果不为null,则查询时会排除掉这个值。
  151. * @return bool 如果存在返回true,否则返回false。
  152. */
  153. public function merFieldExists(?int $merId, $field, $value, $except = null)
  154. {
  155. // 获取数据库实例,并根据条件构建查询。
  156. return ($this->getModel())::getDB()->when($except, function ($query, $except) use ($field) {
  157. // 如果有排除值,则添加不等于条件。
  158. $query->where($field, '<>', $except);
  159. })->when($merId, function ($query, $merId) {
  160. // 如果有商户ID,则添加等于条件。
  161. $query->where('mer_id', $merId);
  162. })->where($field, $value)->count() > 0;
  163. }
  164. /**
  165. * 根据产品ID获取SKU
  166. *
  167. * 本函数旨在通过产品ID检索与之相关联的SKU。SKU (Stock Keeping Unit) 是一种产品库存管理单位,通常用于唯一标识一个产品变体。
  168. * 使用本函数需要提供一个产品ID,然后它将返回一个查询构建器实例,该实例已配置为查询与指定产品ID相关联的SKU。
  169. *
  170. * @param int $id 产品的唯一标识ID。这个ID用于在数据库查询中定位特定的产品。
  171. * @return \Illuminate\Database\Eloquent\Builder|static 返回一个Eloquent查询构建器实例,该实例已准备好根据产品ID查询SKU。
  172. */
  173. public function getSku($id)
  174. {
  175. // 通过调用$this->getModel()获取模型实例,并立即使用where子句过滤查询结果,只包括product_id为$id的记录。
  176. return ($this->getModel())::where('product_id', $id);
  177. }
  178. /**
  179. * 检查指定字段的值是否存在,可选地排除特定值或限制商户ID。
  180. *
  181. * 此方法用于查询数据库中是否存在指定字段的值,可以根据需要排除特定值或限制查询的商户。
  182. * 主要用于数据验证或数据存在性检查场景。
  183. *
  184. * @param int|null $merId 商户ID,用于限制查询的范围。
  185. * @param string $field 要检查的字段名。
  186. * @param string $value 要检查的字段值。
  187. * @param string|null $except 要排除的特定值,如果不为空,则查询时不包括此值。
  188. * @return bool 查询结果,如果存在则返回true,否则返回false。
  189. */
  190. public function getFieldExists(?int $merId, $field, $value, $except = null)
  191. {
  192. // 获取模型对应的数据库实例
  193. return ($this->getModel())::getDB()->when($except, function ($query, $except) use ($field) {
  194. // 如果有需要排除的值,则添加不等于条件
  195. $query->where($field, '<>', $except);
  196. })->when($merId, function ($query, $merId) {
  197. // 如果提供了商户ID,则添加条件限制查询结果只包含指定商户
  198. $query->where('mer_id', $merId);
  199. })->where($field, $value);
  200. // 添加字段等于条件,完成查询条件构建
  201. }
  202. /**
  203. * 减少商品库存并增加销售数量
  204. *
  205. * 该方法用于更新数据库中指定商品的库存和销售数量。它通过传入的产品ID和唯一标识符
  206. * 来定位特定的记录,并对库存进行减少,同时对销售数量进行增加。这种方法适用于
  207. * 实时更新库存系统,例如在处理订单时减少可用库存并记录已销售数量。
  208. *
  209. * @param int $productId 商品ID,用于在数据库中定位商品记录。
  210. * @param string $unique 商品的唯一标识符,用于确保操作的准确性。
  211. * @param int $desc 库存减少的数量,同时也是销售数量增加的数量。
  212. * @return mixed 返回数据库更新操作的结果,可能是布尔值或影响的行数。
  213. * @throws DbException
  214. */
  215. public function descStock(int $productId, string $unique, int $desc)
  216. {
  217. // 使用模型获取数据库实例,并通过WHERE子句定位到特定的商品记录。
  218. // 然后更新该记录的'stock'和'sales'字段,分别进行库存减少和销售数量增加的操作。
  219. return model::getDB()->where('product_id', $productId)->where('unique', $unique)->update([
  220. 'stock' => Db::raw('stock-' . $desc), // 直接使用数据库原生语法减少库存
  221. 'sales' => Db::raw('sales+' . $desc) // 直接使用数据库原生语法增加销售数量
  222. ]);
  223. }
  224. /**
  225. * 更新SKU库存和销量
  226. *
  227. * 该方法用于减少指定SKU的库存并增加其销售量。通过传入产品ID、SKU和销售数量,
  228. * 直接对数据库进行更新操作,避免了不必要的数据检索和处理步骤,提高了数据库操作的效率。
  229. *
  230. * @param int $productId 产品ID,用于定位特定产品的SKU信息。
  231. * @param string $sku 特定产品的SKU,用于唯一标识产品的一个变种。
  232. * @param int $desc 销售描述数量,表示此次销售的数量,将从库存中扣除,并加到销售量中。
  233. * @return mixed 返回数据库更新操作的结果,可能是布尔值或影响行数。
  234. * @throws DbException
  235. */
  236. public function descSkuStock(int $productId, string $sku, int $desc)
  237. {
  238. // 使用模型的数据库访问方法,直接构造并执行更新语句
  239. // 通过where子句定位到特定的产品ID和SKU,然后更新库存和销量字段
  240. return model::getDB()->where('product_id', $productId)->where('sku', $sku)->update([
  241. 'stock' => Db::raw('stock-' . $desc), // 直接从库存字段减去销售数量
  242. 'sales' => Db::raw('sales+' . $desc) // 直接将销售数量加到销量字段
  243. ]);
  244. }
  245. /**
  246. * 增加商品销售量
  247. *
  248. * 该方法用于更新数据库中指定产品的销售量。它通过传入的产品ID和SKU来定位特定的产品行,
  249. * 然后将销售量字段的值增加指定的数量。
  250. *
  251. * @param int $productId 产品ID,用于定位特定产品
  252. * @param string $sku 产品的SKU(库存单位),进一步唯一标识产品
  253. * @param int $desc 销售量增加的数量,表示要将当前销售量增加的值
  254. *
  255. * @return int 返回更新操作的影响行数,用于确认更新是否成功
  256. * @throws DbException
  257. */
  258. public function incSales(int $productId, string $sku, int $desc)
  259. {
  260. // 使用Db::raw()来构建SQL的增量更新语句,确保销售量字段正确增加
  261. return model::getDB()->where('product_id', $productId)->where('sku', $sku)->update([
  262. 'sales' => Db::raw('sales+' . $desc)
  263. ]);
  264. }
  265. /**
  266. * 增加商品库存并减少对应销量
  267. *
  268. * 本函数用于在数据库中增加指定商品的库存量,并同时减少相同数量的已销售量。
  269. * 这样做的目的是为了准确跟踪商品的库存和销售情况,确保数据的准确性。
  270. *
  271. * @param int $productId 商品ID,用于定位特定商品
  272. * @param string $unique 商品的唯一标识,用于进一步确认特定商品
  273. * @param int $inc 需要增加的库存数量,同时也是需要减少的销售数量
  274. */
  275. public function incStock(int $productId, string $unique, int $inc)
  276. {
  277. // 增加商品库存
  278. model::getDB()->where('product_id', $productId)->where('unique', $unique)->inc('stock', $inc)->update();
  279. // 减少商品销量,但只针对销量大于等于增加的库存数量的部分进行减少
  280. model::getDB()->where('product_id', $productId)->where('unique', $unique)->where('sales', '>=', $inc)->dec('sales', $inc)->update();
  281. }
  282. /**
  283. * 增加SKU库存并调整销售数量
  284. *
  285. * 该方法用于在数据库中增加指定产品的指定SKU的库存数量,并且如果该SKU的销售数量大于增加的库存数量,
  286. * 则相应地减少销售数量。这种方法适用于处理库存和销售数据的同步更新,确保数据的一致性。
  287. *
  288. * @param int $productId 产品ID,用于定位特定产品的SKU
  289. * @param string $sku 指定产品的SKU,用于唯一标识一个产品的SKU
  290. * @param int $inc 需要增加的库存数量,这个数量将被加到SKU的库存上
  291. */
  292. public function incSkuStock(int $productId, string $sku, int $inc)
  293. {
  294. // 增加SKU的库存数量
  295. model::getDB()->where('product_id', $productId)->where('sku', $sku)->inc('stock', $inc)->update();
  296. // 如果SKU的销售数量大于增加的库存数量,则减少销售数量
  297. // 这里假设销售数量不应减少到小于增加的库存数量,确保数据的合理性
  298. model::getDB()->where('product_id', $productId)->where('sku', $sku)->where('sales', '>', $inc)->dec('sales', $inc)->update();
  299. }
  300. /**
  301. * 检查产品属性是否存在
  302. *
  303. * 本函数用于查询数据库中是否存在指定产品ID和唯一标识符对应的属性记录。
  304. * 主要用于验证某个产品的特定属性是否已经被定义。
  305. *
  306. * @param int $productId 产品ID,用于查询产品属性时的条件过滤。
  307. * @param string $unique 唯一标识符,用于查询产品属性时的条件过滤。
  308. * @return bool 如果找到匹配的属性记录,则返回true,否则返回false。
  309. * @throws DbException
  310. */
  311. public function attrExists(int $productId, string $unique): bool
  312. {
  313. // 通过模型获取数据库实例,并构造查询条件,检查是否存在指定产品ID和唯一标识符的属性记录。
  314. return model::getDB()->where('product_id', $productId)->where('unique', $unique)->count() > 0;
  315. }
  316. /**
  317. * 检查SKU是否存在于数据库中。
  318. *
  319. * 本函数用于确定给定的产品ID和SKU组合是否在数据库中存在记录。
  320. * 通过查询产品ID和SKU匹配的记录数量,如果数量大于0,则表示SKU存在。
  321. *
  322. * @param int $productId 产品ID,用于查询特定产品的SKU记录。
  323. * @param string $sku 商品SKU,用于唯一标识一个商品。
  324. * @return bool 如果SKU存在则返回true,否则返回false。
  325. * @throws DbException
  326. */
  327. public function skuExists(int $productId, string $sku): bool
  328. {
  329. // 使用模型获取数据库实例,并构造查询条件,检查是否存在指定产品ID和SKU的记录。
  330. return model::getDB()->where('product_id', $productId)->where('sku', $sku)->count() > 0;
  331. }
  332. /**
  333. * 商品佣金是否大于设置佣金比例
  334. * @param $productId
  335. * @return bool
  336. * @author Qinii
  337. * @day 2020-06-25
  338. */
  339. public function checkExtensionById($productId)
  340. {
  341. $extension_one_rate = systemConfig('extension_one_rate');
  342. $extension_two_rate = systemConfig('extension_two_rate');
  343. $count = ($this->getModel()::getDb())->where(function($query)use($productId,$extension_one_rate){
  344. $query->where('product_id',$productId)->whereRaw('price * '.$extension_one_rate.' > extension_one');
  345. })->whereOr(function($query)use($productId,$extension_two_rate){
  346. $query->where('product_id',$productId)->whereRaw('price * '.$extension_two_rate.' > extension_two');
  347. })->count();
  348. return $count ? false : true;
  349. }
  350. /**
  351. * 根据条件搜索记录。
  352. *
  353. * 该方法用于构建查询条件,根据传入的数组$where动态添加查询条件到查询语句中。
  354. * 支持根据产品ID、SKU和唯一标识符进行搜索。只有当相应字段的值被设置且不为空时,才会添加对应的查询条件。
  355. *
  356. * @param array $where 搜索条件数组,可能包含产品ID、SKU和唯一标识符。
  357. * @return \yii\db\Query 查询对象,带有应用了搜索条件的查询语句。
  358. */
  359. public function search(array $where)
  360. {
  361. // 获取数据库查询对象,并根据$where数组中的条件动态构建查询语句
  362. $query = ($this->getModel()::getDb())
  363. // 当产品ID存在且不为空时,添加到查询条件中
  364. ->when(isset($where['product_id']) && $where['product_id'] !== '',function($query)use($where){
  365. $query->where('product_id',$where['product_id']);
  366. })
  367. // 当SKU存在且不为空时,添加到查询条件中
  368. ->when(isset($where['sku']) && $where['sku'] !== '',function($query)use($where){
  369. $query->where('sku',$where['sku']);
  370. })
  371. // 当唯一标识符存在且不为空时,添加到查询条件中
  372. ->when(isset($where['unique']) && $where['unique'] !== '',function($query)use($where){
  373. $query->where('unique',$where['unique']);
  374. });
  375. return $query;
  376. }
  377. /**
  378. * 批量更新指定产品ID的数据
  379. *
  380. * 本函数通过传入的产品ID数组和数据数组,更新数据库中对应产品ID的所有记录。
  381. * 它使用了whereIn查询语句来筛选需要更新的记录,然后应用update方法进行更新。
  382. * 这种方法适用于需要一次性更新多个记录的场景,可以减少数据库操作的次数,提高效率。
  383. *
  384. * @param array $ids 产品ID数组,指定需要更新的产品范围
  385. * @param array $data 数据数组,包含需要更新的字段及其值
  386. */
  387. public function updates(array $ids, array $data)
  388. {
  389. // 获取模型实例并调用其数据库连接方法,然后使用whereIn和update进行批量更新
  390. $this->getModel()::getDb()->whereIn('product_id',$ids)->update($data);
  391. }
  392. /**
  393. * 批量更新产品的扩展字段
  394. *
  395. * 此方法用于根据提供的产品ID数组和更新数据数组,批量更新指定产品ID的扩展字段。
  396. * 首先,它将所有指定产品的扩展类型设置为1,然后根据产品ID分批获取产品列表,
  397. * 并对每个产品计算并更新其两个扩展字段的值。
  398. *
  399. * @param array $ids 产品ID数组,指定需要更新扩展字段的产品
  400. * @param array $data 更新数据数组,包含扩展字段'extension_one'和'extension_two'的倍数值
  401. */
  402. public function updatesExtension(array $ids, array $data)
  403. {
  404. // 设置指定产品ID的扩展类型为1
  405. app()->make(ProductRepository::class)->updates($ids,['extension_type' => 1]);
  406. // 构建查询,准备分批更新产品扩展字段
  407. $query = $this->getModel()::getDb()->where('product_id','in',$ids);
  408. // 分批处理产品列表,每次处理100条,用于更新扩展字段
  409. $query->chunk(100, function($list) use($data){
  410. foreach ($list as $item) {
  411. // 计算扩展字段的新值,并更新到数据库
  412. $arr['extension_one'] = bcmul($item->price,$data['extension_one'],2);
  413. $arr['extension_two'] = bcmul($item->price,$data['extension_two'],2);
  414. $this->getModel()::getDb()->where('unique',$item->unique)->update($arr);
  415. }
  416. },'product_id');
  417. }
  418. public function createReservation($value, array $reservationData)
  419. {
  420. foreach ($reservationData as &$item) {
  421. $item['attr_value_id'] = $value['value_id'];
  422. $item['product_id'] = $value['product_id'];
  423. $item['unique'] = $value['unique'];
  424. }
  425. ($this->getModel()::find($value['value_id']))->reservation()->saveAll($reservationData);
  426. }
  427. public function deleteReservation(int $productId)
  428. {
  429. $res = $this->getModel()::where('product_id', $productId)->with('reservation')->select();
  430. foreach ($res as $item) {
  431. $item->reservation()->delete();
  432. }
  433. }
  434. }