UserProductService.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <?php
  2. namespace app\common\service;
  3. use think\facade\Db;
  4. use think\facade\Log;
  5. class UserProductService
  6. {
  7. /**
  8. * 创建用户产品使用记录(支付成功后调用)
  9. */
  10. public function createUserProductRecord($userId, $orderItem, $package)
  11. {
  12. Db::startTrans();
  13. try {
  14. // 1. 首先检查是否已存在相同的记录
  15. $existing = Db::table('ba_user_product_usage')
  16. ->where('order_item_id', $orderItem['id'])
  17. ->where('user_id', $userId)
  18. ->lock(true)
  19. ->find();
  20. if ($existing) {
  21. Db::commit();
  22. Log::info('用户产品记录已存在,跳过创建', [
  23. 'order_item_id' => $orderItem['id'],
  24. 'user_id' => $userId,
  25. 'existing_id' => $existing['id']
  26. ]);
  27. return $existing['id'];
  28. }
  29. // 2. 创建使用记录(原有逻辑)
  30. $usageData = [
  31. 'user_id' => $userId,
  32. 'order_id' => $orderItem['order_id'],
  33. 'order_item_id' => $orderItem['id'],
  34. 'product_id' => $orderItem['product_id'],
  35. 'package_price_id' => $orderItem['package_price_id'],
  36. 'package_type' => $package['type'],
  37. 'card_type' => $package['card_type'],
  38. 'start_time' => date('Y-m-d H:i:s'),
  39. 'status' => 1,
  40. 'create_time' => date('Y-m-d H:i:s'),
  41. 'update_time' => date('Y-m-d H:i:s')
  42. ];
  43. // 3. 根据卡类型和购买数量计算初始值
  44. $quantity = $orderItem['quantity'] ?? 1;
  45. switch ($package['card_type']) {
  46. case 1: // 次卡
  47. $unitValue = $package['unit_value'] ?? 0;
  48. $usageData['remaining_times'] = $unitValue * $quantity;
  49. break;
  50. case 2: // 积分卡
  51. $pointsValue = $package['points_value'] ?? 0;
  52. $usageData['remaining_points'] = $pointsValue * $quantity;
  53. break;
  54. case 3: // 日卡
  55. $usageData['end_time'] = date('Y-m-d 23:59:59', strtotime('+1 day'));
  56. break;
  57. case 4: // 周卡
  58. $usageData['end_time'] = date('Y-m-d 23:59:59', strtotime('+7 days'));
  59. break;
  60. case 5: // 季卡
  61. $usageData['end_time'] = date('Y-m-d 23:59:59', strtotime('+90 days'));
  62. break;
  63. case 6: // 月卡
  64. $usageData['end_time'] = date('Y-m-d 23:59:59', strtotime('+30 days'));
  65. break;
  66. case 7: // 年卡
  67. $usageData['end_time'] = date('Y-m-d 23:59:59', strtotime('+365 days'));
  68. break;
  69. }
  70. $usageId = Db::table('ba_user_product_usage')->insertGetId($usageData);
  71. // 4. 如果是个人套餐,同时更新ba_user_product表
  72. if ($package['type'] == 1) {
  73. $this->updateUserProductTable($userId, $orderItem, $package, $usageData);
  74. }
  75. Db::commit();
  76. Log::info('用户产品记录创建成功', [
  77. 'usage_id' => $usageId,
  78. 'user_id' => $userId,
  79. 'order_item_id' => $orderItem['id']
  80. ]);
  81. return $usageId;
  82. } catch (\Throwable $e) {
  83. Db::rollback();
  84. Log::error('创建用户产品记录失败: ' . $e->getMessage());
  85. throw $e;
  86. }
  87. }
  88. /**
  89. * 更新用户产品表(兼容原有表)
  90. */
  91. private function updateUserProductTable($userId, $orderItem, $package, $usageData)
  92. {
  93. try {
  94. // 检查是否已有记录
  95. $userProduct = Db::table('ba_user_product')
  96. ->where('user_id', $userId)
  97. ->where('product_id', $orderItem['product_id'])
  98. ->where('package_price_id', $orderItem['package_price_id'])
  99. ->find();
  100. if ($userProduct) {
  101. // 更新现有记录
  102. $updateData = [
  103. 'update_time' => date('Y-m-d H:i:s')
  104. ];
  105. // 根据卡类型累加
  106. if ($package['card_type'] == 1) { // 次卡
  107. $updateData['remaining_times'] = Db::raw('remaining_times + ' . $usageData['remaining_times']);
  108. } elseif ($package['card_type'] == 2) { // 积分卡
  109. $updateData['remaining_points'] = Db::raw('remaining_points + ' . $usageData['remaining_points']);
  110. } elseif ($package['card_type'] >= 3) { // 日期卡
  111. // 日期卡取最新的结束时间
  112. if (empty($userProduct['expire_time']) ||
  113. strtotime($usageData['end_time']) > strtotime($userProduct['expire_time'])) {
  114. $updateData['expire_time'] = $usageData['end_time'];
  115. }
  116. }
  117. Db::table('ba_user_product')
  118. ->where('id', $userProduct['id'])
  119. ->update($updateData);
  120. } else {
  121. // 创建新记录
  122. $newData = [
  123. 'user_id' => $userId,
  124. 'product_id' => $orderItem['product_id'],
  125. 'package_price_id' => $orderItem['package_price_id'],
  126. 'remaining_points' => $usageData['remaining_points'] ?? 0,
  127. 'remaining_times' => $usageData['remaining_times'] ?? 0,
  128. 'expire_time' => $usageData['end_time'] ?? null,
  129. 'create_time' => date('Y-m-d H:i:s')
  130. ];
  131. Db::table('ba_user_product')->insert($newData);
  132. }
  133. } catch (\Throwable $e) {
  134. Log::error('更新用户产品表失败: ' . $e->getMessage());
  135. // 这里不抛出异常,因为这是辅助功能,主功能不受影响
  136. }
  137. }
  138. /**
  139. * 企业套餐分配时创建用户产品记录
  140. */
  141. public function createCompanyProductUsage($allocationRecord, $assignedToUserId,$card_type)
  142. {
  143. try {
  144. // 从分配记录创建用户产品使用记录
  145. $usageData = [
  146. 'user_id' => $assignedToUserId,
  147. 'order_id' => $allocationRecord['order_id'],
  148. 'order_item_id' => $allocationRecord['order_item_id'],
  149. 'product_id' => $allocationRecord['product_id'],
  150. 'package_price_id' => $allocationRecord['package_price_id'],
  151. 'package_type' => 2, // 企业套餐
  152. 'card_type' => $card_type,
  153. 'company_id' => $allocationRecord['company_id'],
  154. 'allocation_id' => $allocationRecord['id'],
  155. 'is_allocated' => 1,
  156. 'start_time' => date('Y-m-d H:i:s'),
  157. 'status' => 1,
  158. 'create_time' => date('Y-m-d H:i:s'),
  159. 'update_time' => date('Y-m-d H:i:s')
  160. ];
  161. // 设置有效期和剩余量
  162. if ($card_type >= 3) { // 日期卡
  163. $usageData['end_time'] = $this->calculateEndTime($card_type);
  164. } elseif ($card_type == 1) { // 次卡
  165. $usageData['remaining_times'] = $allocationRecord['remaining_times'];
  166. } elseif ($card_type == 2) { // 积分卡
  167. $usageData['remaining_points'] = $allocationRecord['remaining_points'];
  168. }
  169. return Db::table('ba_user_product_usage')->insertGetId($usageData);
  170. } catch (\Throwable $e) {
  171. Log::error('创建企业产品使用记录失败: ' . $e->getMessage());
  172. throw $e;
  173. }
  174. }
  175. /**
  176. * 获取我的产品列表(整合个人套餐和企业套餐)
  177. */
  178. public function getUserProducts($userId)
  179. {
  180. try {
  181. // 1. 查询个人套餐(直接从使用记录表)
  182. $personalProducts = Db::table('ba_user_product_usage')
  183. ->alias('upu')
  184. ->leftJoin('ba_product p', 'p.id = upu.product_id')
  185. ->leftJoin('ba_package_price pp', 'pp.id = upu.package_price_id')
  186. ->where('upu.user_id', $userId)
  187. ->where('upu.package_type', 1) // 个人套餐
  188. ->where('upu.status', 1) // 有效
  189. ->field('upu.*,
  190. p.name as product_name, p.type as product_type, p.link,
  191. pp.spec_name, pp.type as package_type, pp.card_type,pp.image')
  192. ->select()
  193. ->toArray();
  194. // 2. 查询已分配给用户的企业套餐
  195. $companyProducts = Db::table('ba_user_product_usage')
  196. ->alias('upu')
  197. ->leftJoin('ba_product p', 'p.id = upu.product_id')
  198. ->leftJoin('ba_package_price pp', 'pp.id = upu.package_price_id')
  199. ->leftJoin('ba_company_package_allocation cpa', 'cpa.id = upu.allocation_id')
  200. ->where('upu.user_id', $userId)
  201. ->where('upu.package_type', 2) // 企业套餐
  202. ->where('upu.status', 1) // 有效
  203. ->where('upu.is_allocated', 1) // 已分配
  204. ->field('upu.*,
  205. p.name as product_name, p.type as product_type, p.link,
  206. pp.spec_name, pp.type as package_type, pp.card_type,
  207. cpa.assigned_by_user_id,pp.image')
  208. ->select()
  209. ->toArray();
  210. // 3. 合并结果
  211. $allProducts = array_merge($personalProducts, $companyProducts);
  212. // 4. 按产品ID和套餐规格ID分组,合并相同产品(只合并次卡和积分卡)
  213. $groupedProducts = [];
  214. foreach ($allProducts as $product) {
  215. $key = $product['product_id'] . '_' . $product['package_price_id'];
  216. // 日期卡不合并,每次分配单独显示
  217. if ($product['card_type'] >= 3) {
  218. $groupedProducts[] = $this->formatProductItem($product, false);
  219. continue;
  220. }
  221. // 次卡和积分卡合并
  222. if (!isset($groupedProducts[$key])) {
  223. $groupedProducts[$key] = $this->formatProductItem($product, true);
  224. } else {
  225. // 累加剩余次数/积分
  226. $groupedProducts[$key]['remaining_times'] += $product['remaining_times'];
  227. $groupedProducts[$key]['remaining_points'] += $product['remaining_points'];
  228. $groupedProducts[$key]['total_quantity'] += 1;
  229. }
  230. }
  231. // 5. 处理合并后的数组
  232. $result = [];
  233. foreach ($groupedProducts as $product) {
  234. $result[] = $product;
  235. }
  236. return $result;
  237. } catch (\Throwable $e) {
  238. Log::error('获取用户产品列表失败: ' . $e->getMessage());
  239. return [];
  240. }
  241. }
  242. /**
  243. * 格式化产品项
  244. */
  245. private function formatProductItem($product, $isMerged = false)
  246. {
  247. $item = [
  248. 'product_id' => $product['product_id'],
  249. 'product_name' => $product['product_name'],
  250. 'product_type' => $product['product_type'] ?? 1, // 默认1:网页
  251. 'product_link' => $product['link'] ?? '',
  252. 'image' => $product['image'] ?? '',
  253. 'package_spec' => $product['spec_name'],
  254. 'package_type' => $product['package_type'],
  255. 'package_type_text' => $product['package_type'] == 1 ? '个人套餐' : '企业套餐',
  256. 'card_type' => $product['card_type'],
  257. 'card_type_text' => $this->getCardTypeText($product['card_type']),
  258. 'total_quantity' => $isMerged ? 1 : 1,
  259. 'effective_end_time' => $product['end_time'] ?? null,
  260. 'remaining_times' => $product['remaining_times'] ?? 0,
  261. 'remaining_points' => $product['remaining_points'] ?? 0,
  262. 'display_info' => $this->getDisplayInfo($product),
  263. 'usage_id' => $product['id'],
  264. 'start_time' => $product['start_time'] ?? null,
  265. 'last_use_time' => $product['last_use_time'] ?? null,
  266. 'status' => $product['status'] ?? 1,
  267. 'buy_time' => $product['create_time']
  268. ];
  269. // 企业套餐特有字段
  270. if ($product['package_type'] == 2) {
  271. $item['is_allocated'] = $product['is_allocated'] ?? 0;
  272. $item['company_id'] = $product['company_id'] ?? null;
  273. $item['allocation_id'] = $product['allocation_id'] ?? null;
  274. // 获取企业套餐的席位信息(如果有)
  275. if ($item['company_id']) {
  276. $seatsInfo = Db::name('company_package_seats')
  277. ->where('company_id', $item['company_id'])
  278. ->where('package_price_id', $product['package_price_id'])
  279. ->find();
  280. if ($seatsInfo) {
  281. $item['total_seats'] = $seatsInfo['total_seats'];
  282. $item['assigned_seats'] = $seatsInfo['assigned_seats'];
  283. $item['available_seats'] = $seatsInfo['available_seats'];
  284. } else {
  285. $item['total_seats'] = 0;
  286. $item['assigned_seats'] = 0;
  287. $item['available_seats'] = 0;
  288. }
  289. } else {
  290. $item['total_seats'] = null;
  291. $item['assigned_seats'] = null;
  292. $item['available_seats'] = null;
  293. }
  294. } else {
  295. // 个人套餐没有席位信息
  296. $item['is_allocated'] = 0;
  297. $item['company_id'] = null;
  298. $item['allocation_id'] = null;
  299. $item['total_seats'] = null;
  300. $item['assigned_seats'] = null;
  301. $item['available_seats'] = null;
  302. }
  303. return $item;
  304. }
  305. /**
  306. * 获取显示信息
  307. */
  308. private function getDisplayInfo($product)
  309. {
  310. if ($product['card_type'] >= 3) { // 日期卡
  311. $endTime = $product['end_time'] ?? null;
  312. return $endTime ? '到期: ' . date('Y-m-d', strtotime($endTime)) : '永久有效';
  313. } elseif ($product['card_type'] == 1) { // 次卡
  314. $remainingTimes = $product['remaining_times'] ?? 0;
  315. return '剩余次数: ' . $remainingTimes;
  316. } elseif ($product['card_type'] == 2) { // 积分卡
  317. $remainingPoints = $product['remaining_points'] ?? 0;
  318. return '剩余积分: ' . $remainingPoints;
  319. }
  320. return '';
  321. }
  322. /**
  323. * 获取卡类型文本
  324. */
  325. private function getCardTypeText($type)
  326. {
  327. $map = [
  328. 1 => '次卡',
  329. 2 => '积分卡',
  330. 3 => '日卡',
  331. 4 => '周卡',
  332. 5 => '季卡',
  333. 6 => '月卡',
  334. 7 => '年卡'
  335. ];
  336. return $map[$type] ?? '其他';
  337. }
  338. /**
  339. * 计算到期时间
  340. */
  341. private function calculateEndTime($cardType)
  342. {
  343. $start = time();
  344. switch ($cardType) {
  345. case 3: // 日卡
  346. return date('Y-m-d 23:59:59', strtotime('+1 day', $start));
  347. case 4: // 周卡
  348. return date('Y-m-d 23:59:59', strtotime('+7 days', $start));
  349. case 5: // 季卡
  350. return date('Y-m-d 23:59:59', strtotime('+90 days', $start));
  351. case 6: // 月卡
  352. return date('Y-m-d 23:59:59', strtotime('+30 days', $start));
  353. case 7: // 年卡
  354. return date('Y-m-d 23:59:59', strtotime('+365 days', $start));
  355. default:
  356. return null;
  357. }
  358. }
  359. }