StoreCartDao.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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\order;
  12. use app\common\dao\BaseDao;
  13. use app\common\model\BaseModel;
  14. use app\common\model\store\order\StoreCart;
  15. use app\common\model\user\UserAddress;
  16. use app\common\model\store\shipping\ShippingTemplate;
  17. use app\common\repositories\store\order\StoreCartRepository;
  18. use think\model\Relation;
  19. class StoreCartDao extends BaseDao
  20. {
  21. protected function getModel(): string
  22. {
  23. return StoreCart::class;
  24. }
  25. /**
  26. * 检查购物车项是否有效并存在交叉
  27. *
  28. * 本函数用于验证给定的购物车ID列表中,是否存在属于特定用户($uid)且满足特定条件(如未删除、未失败、未支付)的购物车项。
  29. * 如果提供了$merId,则还会检查这些购物车项是否属于指定的商家。
  30. *
  31. * @param array $ids 购物车ID列表
  32. * @param int $uid 用户ID
  33. * @param int|null $merId 商家ID(可选),用于过滤属于特定商家的购物车项
  34. * @return array 有效并存在交叉的购物车ID列表
  35. */
  36. public function validIntersection(array $ids, $uid, int $merId = null, string $touristUniqueKey = ''): array
  37. {
  38. // 从数据库中获取满足条件的购物车项的cart_id列
  39. return StoreCart::getDB()->whereIn('cart_id', $ids)
  40. // 如果提供了商家ID,则进一步过滤出属于该商家的购物车项
  41. ->when($merId, function ($query, $merId) {
  42. $query->where('mer_id', $merId);
  43. })
  44. ->when($touristUniqueKey, function ($query, $touristUniqueKey) {
  45. $query->where('tourist_unique_key', $touristUniqueKey);
  46. })
  47. // 过滤出未删除、未失败、未支付的购物车项
  48. ->where('is_del', 0)->where('is_fail', 0)->where('is_pay', 0)
  49. // 过滤出属于指定用户的购物车项
  50. ->where('uid', $uid)->column('cart_id');
  51. }
  52. /**
  53. * 获取用户的所有购物车商品
  54. *
  55. * 本函数用于根据用户ID查询该用户购物车中所有未删除、未标记为新、未支付的商品。
  56. * 查询时会包括商品的基本信息、属性信息,以及(注释掉的)商家信息。
  57. * 查询结果按照创建时间降序排列,并限制返回结果的数量。
  58. *
  59. * @param int $uid 用户ID
  60. * @return \think\Paginator 返回查询结果的分页器对象
  61. */
  62. public function getAll(int $uid)
  63. {
  64. // 构建查询条件,查询用户购物车中符合条件的商品
  65. $query = ($this->getModel())::where(['uid' => $uid, 'is_del' => 0, 'is_new' => 0, 'is_pay' => 0])
  66. ->with([
  67. // 加载商品相关信息,包括商品基础信息和库存价格等
  68. 'product' => function ($query) {
  69. $query->field('product_id,image,store_name,is_show,status,is_del,unit_name,price,mer_status,is_used,product_type,once_max_count,once_min_count,pay_limit,mer_svip_status,svip_price_type');
  70. },
  71. // 加载商品属性信息,包括SKU和特殊价格等
  72. 'productAttr' => function ($query) {
  73. $query->field('product_id,stock,price,unique,sku,image,svip_price,bar_code');
  74. },
  75. // 注释掉的商家信息加载部分,可能用于未来扩展
  76. // 'merchant' => function ($query) {
  77. // $query->field('mer_id,mer_name,mer_state,mer_avatar,is_trader,type_id')->with(['type_name']);
  78. // }
  79. ])
  80. // 限制返回的购物车项目数量
  81. ->limit(StoreCartRepository::CART_LIMIT_COUNT)
  82. // 按照创建时间降序排列
  83. ->order('create_time DESC')
  84. // 执行查询并返回结果
  85. ->select();
  86. return $query;
  87. }
  88. /**
  89. * 根据传入的用户ID和购物车ID数组,以及用户地址,获取购物车商品信息。
  90. * 此函数详细说明了如何从数据库中获取与用户购物车相关的商品数据,
  91. * 包括商品的基本信息、属性信息以及配送方式和费用等。
  92. *
  93. * @param array $ids 购物车项ID数组
  94. * @param int $uid 用户ID
  95. * @param UserAddress|null $address 用户地址对象,可能为null
  96. * @return array 返回符合查询条件的购物车商品数据集合
  97. * @throws \think\db\exception\DataNotFoundException
  98. * @throws \think\db\exception\DbException
  99. * @throws \think\db\exception\ModelNotFoundException
  100. */
  101. public function cartIbByData(array $ids, int $uid, ?UserAddress $address)
  102. {
  103. // 通过用户ID和购物车ID数组从数据库获取购物车商品信息
  104. return StoreCart::getDb()->where('uid', $uid)->with([
  105. // 获取商品基本信息,包括产品ID、分类ID、图片、商店名称等
  106. 'product' => function (Relation $query) use ($address) {
  107. $query->field('product_id,cate_id,image,store_name,is_show,status,is_del,unit_name,price,mer_status,temp_id,give_coupon_ids,is_gift_bag,is_used,product_type,old_product_id,integral_rate,delivery_way,delivery_free,type,extend,pay_limit,once_max_count,once_min_count,mer_svip_status,svip_price_type,refund_switch,mer_form_id,active_id');
  108. $query->with([
  109. 'storeCategory' => function (Relation $query) {
  110. $query->field('store_category_id,cate_name,path');
  111. }
  112. ]);
  113. // 如果用户地址存在,进一步获取与地址相关的配送模板和免运费模板信息
  114. if ($address) {
  115. $cityIds = array_filter([$address->province_id, $address->city_id, $address->district_id, $address->street_id]);
  116. $query->with([
  117. 'temp' => [
  118. 'region' => function (Relation $query) use ($cityIds) {
  119. // 根据用户地址的省市ID,查询适用的配送区域
  120. $query->where(function ($query) use ($cityIds) {
  121. foreach ($cityIds as $v) {
  122. $query->whereOr('city_id', 'like', "%/{$v}/%");
  123. }
  124. $query->whereOr('city_id', '0');
  125. })->order('shipping_template_region_id DESC')->withLimit(1);
  126. },
  127. 'undelives' => function ($query) use ($cityIds) {
  128. // 查询不能配送的区域
  129. foreach ($cityIds as $v) {
  130. $query->whereOr('city_id', 'like', "%/{$v}/%");
  131. }
  132. },
  133. 'free' => function (Relation $query) use ($cityIds) {
  134. // 查询免运费模板
  135. foreach ($cityIds as $v) {
  136. $query->whereOr('city_id', 'like', "%/{$v}/%");
  137. }
  138. $query->order('shipping_template_free_id DESC')->withLimit(1);
  139. }
  140. ]
  141. ]);
  142. }
  143. },
  144. // 获取商品属性信息,包括属性值ID、图片、库存、价格等
  145. 'productAttr' => function (Relation $query) {
  146. $query->field('value_id,image,extension_one,extension_two,product_id,stock,price,unique,sku,volume,weight,ot_price,cost,svip_price,library_id,bar_code,bar_code_number')->with(['product'])->append(['bc_extension_one', 'bc_extension_two']);
  147. },
  148. 'reservation',
  149. 'reservationAttr'
  150. // 注释掉的商家信息部分,原注释说明了这部分代码的作用是获取商家的基本信息、优惠券、配置信息等
  151. // 'merchant' => function (Relation $query) use ($uid) {
  152. // ...
  153. // }
  154. ])->whereIn('cart_id', $ids)->order('product_type DESC,cart_id DESC')->select();
  155. }
  156. public function getCartItemsByIds(array $ids, int $uid, $cityIds = [])
  157. {
  158. if (!$ids && !$uid) return ;
  159. // 查询购物车并加载关联
  160. $cartItems = StoreCart::getDb()
  161. //->where('uid', $uid)
  162. ->whereIn('cart_id', $ids)
  163. ->with(['product', 'productAttr' => function($query){
  164. $query->field('value_id,image,extension_one,extension_two,product_id,stock,price,unique,sku,volume,weight,ot_price,cost,svip_price,library_id,bar_code,bar_code_number');
  165. }])
  166. ->order('product_type DESC,cart_id DESC')
  167. ->select();
  168. // 批量加载配送模板
  169. $this->batchLoadShippingTemplates($cartItems, $cityIds);
  170. return $cartItems;
  171. }
  172. private function batchLoadShippingTemplates($cartItems, array $cityIds)
  173. {
  174. if (empty($cityIds)) {
  175. return;
  176. }
  177. // 获取所有模板 ID
  178. $templateIds = $cartItems->column('product.temp_id'); // 提取模板 ID
  179. $templateIds = array_unique($templateIds); // 去重
  180. // 一次性查询所有模板数据,包括 region、free 和 undelives
  181. $shippingTemplates = ShippingTemplate::whereIn('id', $templateIds)
  182. ->with([
  183. 'region' => function ($query) use ($cityIds) {
  184. foreach ($cityIds as $cityId) {
  185. $query->whereOr('city_id', 'like', "%/{$cityId}/%");
  186. }
  187. $query->whereOr('city_id', '0'); // 适配所有区域
  188. $query->order('shipping_template_region_id DESC')->withLimit(1);
  189. },
  190. 'free' => function ($query) use ($cityIds) {
  191. foreach ($cityIds as $cityId) {
  192. $query->whereOr('city_id', 'like', "%/{$cityId}/%");
  193. }
  194. $query->order('shipping_template_free_id DESC')->withLimit(1);
  195. },
  196. 'undelives' => function ($query) use ($cityIds) {
  197. foreach ($cityIds as $cityId) {
  198. $query->whereOr('city_id', 'like', "%/{$cityId}/%");
  199. }
  200. }
  201. ])
  202. ->select()
  203. ->column(null, 'id'); // 按模板 ID 索引结果
  204. // 将模板数据分配给购物车项
  205. foreach ($cartItems as $item) {
  206. $item->product->shipping_template = $shippingTemplates[$item->product->temp_id] ?? null;
  207. }
  208. }
  209. /**
  210. * 批量删除购物车中指定ID的商品
  211. *
  212. * 此方法通过传入的用户ID和购物车ID数组,批量从数据库中删除对应的购物车记录。
  213. * 主要用于处理用户批量清除购物车商品的场景,或者在特定情况下批量清理数据库中的购物车数据。
  214. *
  215. * @param array $cartIds 购物车项ID数组,代表需要被删除的购物车记录的ID。
  216. * @param int $uid 用户ID,用于指定哪些购物车记录属于该用户,从而进行精准删除。
  217. * @return int 返回删除操作的影响行数,即被删除的购物车记录数量。
  218. */
  219. public function batchDelete(array $cartIds, int $uid)
  220. {
  221. // 通过模型获取数据库实例,并构造删除条件,最终执行删除操作。
  222. return ($this->getModel()::getDB())->where('uid', $uid)->whereIn('cart_id', $cartIds)->delete();
  223. }
  224. /**
  225. * 获取用户购物车中商品的数量
  226. *
  227. * 本函数用于查询指定用户ID的购物车中,未删除、未标记为新、未支付的商品总数。
  228. * 这对于显示用户的购物车总数或者进行相关统计非常有用。
  229. *
  230. * @param int $uid 用户ID,用于指定查询哪个用户的购物车数据。
  231. * @return array 返回一个包含商品总数的数组,如果商品数量为0,则数组中的计数为0。
  232. */
  233. public function getCartCount(int $uid)
  234. {
  235. // 通过模型获取数据库实例,并根据条件查询购物车商品数量,条件包括用户ID、未删除、未标记为新、未支付。
  236. $data = ($this->getModel()::getDB())->where(['uid' => $uid, 'is_del' => 0, 'is_new' => 0, 'is_pay' => 0])->field('SUM(cart_num) as count')->select();
  237. // 确保返回的数据中count字段不为空,如果为空则默认为0。
  238. $data[0]['count'] = $data[0]['count'] ? $data[0]['count'] : 0;
  239. // 返回查询结果。
  240. return $data;
  241. }
  242. /**
  243. * 获取用户购物车中商品的数量
  244. *
  245. * 本函数用于查询指定用户ID的购物车中,未删除、未标记为新、未支付的商品总数。
  246. * 这对于显示用户的购物车总数或者进行相关统计非常有用。
  247. *
  248. * @param int $uid 用户ID,用于指定查询哪个用户的购物车数据。
  249. * @return array 返回一个包含商品总数的数组,如果商品数量为0,则数组中的计数为0。
  250. */
  251. public function getMerchantCartCount(int $uid, $cartIds)
  252. {
  253. // 通过模型获取数据库实例,并根据条件查询购物车商品数量,条件包括用户ID、未删除、未标记为新、未支付。
  254. $cartTotalNum = ($this->getModel()::getDB())->hasWhere('product', function($query) {
  255. $query->where('is_show', 1)->where('status', 1)->whereColumn('stock', '>=', 'StoreCart.cart_num');
  256. })->whereIn('StoreCart.cart_id', $cartIds)->where(['StoreCart.uid' => $uid, 'StoreCart.is_del' => 0, 'StoreCart.is_new' => 0, 'StoreCart.is_pay' => 0])->sum('StoreCart.cart_num');
  257. $data[0]['count'] = $cartTotalNum ?? 0;
  258. // 返回查询结果。
  259. return $data;
  260. }
  261. /**
  262. * 获取指定来源的支付信息
  263. *
  264. * 本函数用于查询指定来源($source)的支付详情,包括支付数量和支付金额。
  265. * 如果提供了$ids参数,则只会查询这些ID对应的支付信息。
  266. *
  267. * @param string $source 来源标识,用于指定查询哪个来源的支付信息。
  268. * @param array|null $ids 可选参数,指定查询的来源ID列表。如果不提供,则查询所有来源。
  269. * @return array 返回一个包含支付信息的数组,每个元素包含支付数量(pay_num)、支付金额(pay_price)和来源ID(source_id)。
  270. */
  271. public function getSourcePayInfo($source, ?array $ids = null)
  272. {
  273. // 使用数据库查询工具,指定别名为A,查询满足条件的支付信息。
  274. return StoreCart::getDB()->alias('A')
  275. // 筛选来源为指定值且已支付的购物车项。
  276. ->where('A.source', $source)->where('A.is_pay', 1)
  277. // 如果提供了ID列表,则进一步筛选来源ID在列表中的项。
  278. ->when($ids, function ($query, $ids) {
  279. $query->whereIn('A.source_id', $ids);
  280. })
  281. // 左连接订单产品表,以获取购物车项对应的订单产品信息。
  282. ->leftJoin('StoreOrderProduct B', 'A.cart_id = B.cart_id')
  283. // 选择计算总支付数量和总支付金额的字段,以及来源ID。
  284. ->field('sum(B.product_num) as pay_num,sum(B.product_price) as pay_price,A.source_id')
  285. // 按来源ID分组,以聚合支付数量和金额。
  286. ->group('A.source_id')
  287. // 执行查询并返回结果集。
  288. ->select();
  289. }
  290. }