DiscountService.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. <?php
  2. namespace app\common\Service;
  3. use think\Db;
  4. use think\Model;
  5. use app\common\Enum\StatusEnum;
  6. use app\common\Enum\ActivityEnum;
  7. use app\common\Enum\GoodsEnum;
  8. /**
  9. * 折扣活动服务类
  10. */
  11. class DiscountService
  12. {
  13. /**
  14. * 查询当前时间段内的商品折扣信息
  15. * @param array $goodsIds 商品ID数组,为空则查询所有参与折扣的商品
  16. * @param int $limit 限制数量
  17. * @return array
  18. */
  19. public static function getCurrentDiscountGoods($goodsIds = [], $limit = 0)
  20. {
  21. $currentTime = time();
  22. // 第一步:查询当前时间有效的活动ID
  23. $activeActivityIds = Db::table('shop_activity')
  24. ->where('start_time', '<=', $currentTime)
  25. ->where('end_time', '>=', $currentTime)
  26. ->where('activity_status', ActivityEnum::ACTIVITY_STATUS_ONGOING)
  27. ->where('status', StatusEnum::ENABLED)
  28. ->column('id');
  29. if (empty($activeActivityIds)) {
  30. return [];
  31. }
  32. // 第二步:查询这些活动的SKU数据
  33. $query = Db::table('shop_activity_sku')
  34. ->alias('sku')
  35. ->join('shop_goods goods', 'sku.goods_id = goods.id')
  36. ->join('shop_goods_sku spec', 'sku.sku_id = spec.id', 'left')
  37. ->join(['shop_activity'=> 'activity'], 'sku.activity_id = activity.id')
  38. ->where('sku.activity_id', 'in', $activeActivityIds)
  39. ->where('goods.status', GoodsEnum::STATUS_ON_SALE) // 已上架的商品
  40. ->where('sku.stocks', '>', 0) // 有折扣库存的商品
  41. ->field([
  42. 'goods.id as goods_id',
  43. 'goods.title',
  44. 'goods.sub_title',
  45. 'goods.image',
  46. 'goods.price as original_price',
  47. 'goods.market_price',
  48. 'goods.stocks as goods_stocks',
  49. 'goods.sales',
  50. 'goods.spec_type',
  51. 'sku.id as activity_sku_id',
  52. 'sku.sku_id',
  53. 'sku.discount',
  54. 'sku.discount_price',
  55. 'sku.stocks as discount_stocks',
  56. 'sku.sale_quantity',
  57. 'activity.id as activity_id',
  58. 'activity.name as activity_name',
  59. 'activity.start_time',
  60. 'activity.end_time',
  61. 'spec.price as sku_price',
  62. 'spec.image as sku_image',
  63. // 'spec.specs as sku_specs'
  64. ]);
  65. // 如果指定了商品ID,则添加条件
  66. if (!empty($goodsIds)) {
  67. $query->where('sku.goods_id', 'in', $goodsIds);
  68. }
  69. // 如果设置了限制数量
  70. if ($limit > 0) {
  71. $query->limit($limit);
  72. }
  73. // 按折扣力度排序,折扣越大越靠前
  74. $query->order('sku.discount', 'asc');
  75. $result = $query->select();
  76. // 处理返回数据
  77. $processedData = [];
  78. foreach ($result as $item) {
  79. $goodsId = $item['goods_id'];
  80. $skuId = $item['sku_id'];
  81. // 如果商品还没有在结果中,初始化
  82. if (!isset($processedData[$goodsId])) {
  83. $processedData[$goodsId] = [
  84. 'goods_id' => $goodsId,
  85. 'title' => $item['title'],
  86. 'sub_title' => $item['sub_title'],
  87. 'image' => $item['image'],
  88. 'original_price' => $item['original_price'],
  89. 'market_price' => $item['market_price'],
  90. 'goods_stocks' => $item['goods_stocks'],
  91. 'sales' => $item['sales'],
  92. 'spec_type' => $item['spec_type'],
  93. 'activity_id' => $item['activity_id'],
  94. 'activity_name' => $item['activity_name'],
  95. 'start_time' => $item['start_time'],
  96. 'end_time' => $item['end_time'],
  97. 'discount_info' => []
  98. ];
  99. }
  100. // 添加折扣信息
  101. $discountInfo = [
  102. 'activity_sku_id' => $item['activity_sku_id'],
  103. 'sku_id' => $skuId,
  104. 'discount' => $item['discount'],
  105. 'discount_price' => $item['discount_price'],
  106. 'discount_stocks' => $item['discount_stocks'],
  107. 'sale_quantity' => $item['sale_quantity']
  108. ];
  109. // 如果是多规格商品,添加规格信息
  110. if ($skuId > 0) {
  111. $discountInfo['sku_price'] = $item['sku_price'];
  112. $discountInfo['sku_image'] = $item['sku_image'];
  113. // $discountInfo['sku_specs'] = $item['sku_specs'];
  114. }
  115. $processedData[$goodsId]['discount_info'][] = $discountInfo;
  116. }
  117. return array_values($processedData);
  118. }
  119. /**
  120. * 限时查询一个当前活动的信息和商品信息 活动信息直接加一个商品数组字段
  121. * @param mixed $activityId
  122. * @return array|bool|Model|string|\PDOStatement|null
  123. */
  124. public static function getActivityWithGoods()
  125. {
  126. $currentTime = time();
  127. // 查询活动信息
  128. $activity = Db::table('shop_activity')
  129. // ->where('id', $activityId)
  130. ->where('start_time', '<=', $currentTime)
  131. ->where('end_time', '>=', $currentTime)
  132. ->where('activity_status', ActivityEnum::ACTIVITY_STATUS_ONGOING)
  133. ->where('status', StatusEnum::ENABLED)
  134. ->find();
  135. if (!$activity) {
  136. return null;
  137. }
  138. // 查询该活动下的商品信息
  139. $goods = Db::table('shop_activity_sku')
  140. ->alias('sku')
  141. ->join('shop_goods goods', 'sku.goods_id = goods.id')
  142. ->join('shop_goods_sku spec', 'sku.sku_id = spec.id', 'left')
  143. ->where('sku.activity_id', $activity->id)
  144. ->where('goods.status', GoodsEnum::STATUS_ON_SALE)
  145. ->where('sku.stocks', '>', 0)
  146. ->field([
  147. 'goods.id as goods_id',
  148. 'goods.title',
  149. 'goods.sub_title',
  150. 'goods.image',
  151. 'goods.price as original_price',
  152. 'goods.market_price',
  153. 'goods.stocks as goods_stocks',
  154. 'goods.sales',
  155. 'goods.spec_type',
  156. 'sku.id as activity_sku_id',
  157. 'sku.sku_id',
  158. 'sku.discount',
  159. 'sku.discount_price',
  160. 'sku.stocks as discount_stocks',
  161. 'sku.sale_quantity',
  162. 'spec.price as sku_price',
  163. 'spec.image as sku_image'
  164. ])
  165. ->order('sku.discount', 'asc')
  166. ->select();
  167. // 处理商品数据
  168. $processedGoods = [];
  169. foreach ($goods as $item) {
  170. $goodsId = $item['goods_id'];
  171. if (!isset($processedGoods[$goodsId])) {
  172. $processedGoods[$goodsId] = [
  173. 'goods_id' => $goodsId,
  174. 'title' => $item['title'],
  175. 'sub_title' => $item['sub_title'],
  176. 'image' => $item['image'],
  177. 'original_price' => $item['original_price'],
  178. 'market_price' => $item['market_price'],
  179. 'goods_stocks' => $item['goods_stocks'],
  180. 'sales' => $item['sales'],
  181. 'spec_type' => $item['spec_type'],
  182. 'discount_info' => []
  183. ];
  184. }
  185. $discountInfo = [
  186. 'activity_sku_id' => $item['activity_sku_id'],
  187. 'sku_id' => $item['sku_id'],
  188. 'discount' => $item['discount'],
  189. 'discount_price' => $item['discount_price'],
  190. 'discount_stocks' => $item['discount_stocks'],
  191. 'sale_quantity' => $item['sale_quantity']
  192. ];
  193. if ($item['sku_id'] > 0) {
  194. $discountInfo['sku_price'] = $item['sku_price'];
  195. $discountInfo['sku_image'] = $item['sku_image'];
  196. }
  197. $processedGoods[$goodsId]['discount_info'][] = $discountInfo;
  198. }
  199. // 在活动信息中添加商品数组字段
  200. $activity['goods'] = array_values($processedGoods);
  201. return $activity;
  202. }
  203. // 限时zhe'kou一个当前活动的信息和商品信息 活动信息直接加一个商品数组字段
  204. public static function getCurrentActivityInfoAndPrize($activityId)
  205. {
  206. $activity = Db::table('shop_activity')->where('id', $activityId)->find();
  207. $prize = Db::table('shop_activity_sku_prize')->where('activity_id', $activityId)->find();
  208. return ['activity' => $activity, 'prize' => $prize];
  209. }
  210. /**
  211. * 根据商品ID和SKU ID获取折扣信息
  212. * @param int $goodsId 商品ID
  213. * @param int $skuId SKU ID,单规格商品传0
  214. * @return array|null
  215. */
  216. public static function getGoodsDiscountInfo($goodsId, $skuId = 0)
  217. {
  218. $currentTime = time();
  219. // 第一步:查询当前时间有效的活动ID
  220. $activeActivityIds = Db::table('shop_activity')
  221. ->where('start_time', '<=', $currentTime)
  222. ->where('end_time', '>=', $currentTime)
  223. ->where('activity_status', ActivityEnum::ACTIVITY_STATUS_ONGOING)
  224. ->where('status', 'normal')
  225. ->column('id');
  226. if (empty($activeActivityIds)) {
  227. return null;
  228. }
  229. // 第二步:查询指定商品的折扣信息
  230. $discountInfo = Db::table('shop_activity_sku_prize')
  231. ->alias('sku')
  232. ->join('shop_activity activity', 'sku.activity_id = activity.id')
  233. ->where('sku.goods_id', $goodsId)
  234. ->where('sku.sku_id', $skuId)
  235. ->where('sku.activity_id', 'in', $activeActivityIds)
  236. ->where('sku.status', 1)
  237. ->where('sku.stocks', '>', 0)
  238. ->field([
  239. 'sku.id as activity_sku_id',
  240. 'sku.discount',
  241. 'sku.discount_price',
  242. 'sku.stocks as discount_stocks',
  243. 'sku.sale_quantity',
  244. 'activity.id as activity_id',
  245. 'activity.name as activity_name',
  246. 'activity.start_time',
  247. 'activity.end_time'
  248. ])
  249. ->find();
  250. return $discountInfo ?: null;
  251. }
  252. /**
  253. * 批量获取商品的折扣信息
  254. * @param array $goodsIds 商品ID数组
  255. * @return array 键为商品ID,值为折扣信息数组
  256. */
  257. public static function getBatchGoodsDiscountInfo($goodsIds)
  258. {
  259. if (empty($goodsIds)) {
  260. return [];
  261. }
  262. $currentTime = time();
  263. // 第一步:查询当前时间有效的活动ID
  264. $activeActivityIds = Db::table('shop_activity')
  265. ->where('start_time', '<=', $currentTime)
  266. ->where('end_time', '>=', $currentTime)
  267. ->where('activity_status', ActivityEnum::ACTIVITY_STATUS_ONGOING)
  268. ->where('status', 'normal')
  269. ->column('id');
  270. if (empty($activeActivityIds)) {
  271. return [];
  272. }
  273. // 第二步:查询指定商品的折扣信息
  274. $result = Db::table('shop_activity_sku_prize')
  275. ->alias('sku')
  276. ->join('shop_activity activity', 'sku.activity_id = activity.id')
  277. ->where('sku.goods_id', 'in', $goodsIds)
  278. ->where('sku.activity_id', 'in', $activeActivityIds)
  279. ->where('sku.status', 1)
  280. ->where('sku.stocks', '>', 0)
  281. ->field([
  282. 'sku.goods_id',
  283. 'sku.sku_id',
  284. 'sku.id as activity_sku_id',
  285. 'sku.discount',
  286. 'sku.discount_price',
  287. 'sku.stocks as discount_stocks',
  288. 'sku.sale_quantity',
  289. 'activity.id as activity_id',
  290. 'activity.name as activity_name'
  291. ])
  292. ->order('sku.discount', 'asc')
  293. ->select();
  294. // 按商品ID分组
  295. $discountData = [];
  296. foreach ($result as $item) {
  297. $goodsId = $item['goods_id'];
  298. if (!isset($discountData[$goodsId])) {
  299. $discountData[$goodsId] = [];
  300. }
  301. $discountData[$goodsId][] = $item;
  302. }
  303. return $discountData;
  304. }
  305. /**
  306. * 检查商品是否参与当前折扣活动
  307. * @param int $goodsId 商品ID
  308. * @param int $skuId SKU ID
  309. * @return bool
  310. */
  311. public static function hasActiveDiscount($goodsId, $skuId = 0)
  312. {
  313. $discountInfo = self::getGoodsDiscountInfo($goodsId, $skuId);
  314. return !empty($discountInfo);
  315. }
  316. /**
  317. * 计算折扣后的价格
  318. * @param float $originalPrice 原价
  319. * @param float $discount 折扣(如9.0表示9折)
  320. * @return float
  321. */
  322. public static function calculateDiscountPrice($originalPrice, $discount)
  323. {
  324. return round($originalPrice * $discount / 10, 2);
  325. }
  326. /**
  327. * 获取活动剩余时间(秒)
  328. * @param int $endTime 活动结束时间戳
  329. * @return int
  330. */
  331. public static function getActivityRemainingTime($endTime)
  332. {
  333. return max(0, $endTime - time());
  334. }
  335. /**
  336. * 格式化活动剩余时间
  337. * @param int $endTime 活动结束时间戳
  338. * @return array
  339. */
  340. public static function formatRemainingTime($endTime)
  341. {
  342. $remaining = self::getActivityRemainingTime($endTime);
  343. if ($remaining <= 0) {
  344. return ['days' => 0, 'hours' => 0, 'minutes' => 0, 'seconds' => 0];
  345. }
  346. $days = floor($remaining / 86400);
  347. $hours = floor(($remaining % 86400) / 3600);
  348. $minutes = floor(($remaining % 3600) / 60);
  349. $seconds = $remaining % 60;
  350. return [
  351. 'days' => $days,
  352. 'hours' => $hours,
  353. 'minutes' => $minutes,
  354. 'seconds' => $seconds
  355. ];
  356. }
  357. }