|
|
@@ -205,7 +205,7 @@ class UserProductService
|
|
|
->leftJoin('ba_package_price pp', 'pp.id = upu.package_price_id')
|
|
|
->where('upu.user_id', $userId)
|
|
|
->where('upu.package_type', 1) // 个人套餐
|
|
|
- ->where('upu.status', 1) // 有效
|
|
|
+// ->where('upu.status', 1) // 有效
|
|
|
->field('upu.*,
|
|
|
p.name as product_name, p.type as product_type, p.link,
|
|
|
pp.spec_name, pp.type as package_type, pp.card_type,pp.image')
|
|
|
@@ -219,7 +219,7 @@ class UserProductService
|
|
|
->leftJoin('ba_company_package_allocation cpa', 'cpa.id = upu.allocation_id')
|
|
|
->where('upu.user_id', $userId)
|
|
|
->where('upu.package_type', 2) // 企业套餐
|
|
|
- ->where('upu.status', 1) // 有效
|
|
|
+// ->where('upu.status', 1) // 有效
|
|
|
->where('upu.is_allocated', 1) // 已分配
|
|
|
->field('upu.*,
|
|
|
p.name as product_name, p.type as product_type, p.link,
|
|
|
@@ -265,6 +265,165 @@ class UserProductService
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户使用产品(根据产品ID自动匹配可用套餐,优先级:日期卡 > 次卡 > 积分卡)
|
|
|
+ * @param int $userId
|
|
|
+ * @param int $productId
|
|
|
+ * @return bool
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function useProductByProductId($userId, $productId)
|
|
|
+ {
|
|
|
+ Db::startTrans();
|
|
|
+ try {
|
|
|
+ // 1. 获取该产品关联的所有套餐规格ID
|
|
|
+ $packagePriceIds = Db::table('ba_product')
|
|
|
+ ->alias('p')
|
|
|
+ ->leftJoin('ba_package_price pp', 'pp.package_id = p.package_id')
|
|
|
+ ->where('p.id', $productId)
|
|
|
+ ->where('p.status', 1)
|
|
|
+ ->column('pp.id');
|
|
|
+
|
|
|
+ if (empty($packagePriceIds)) {
|
|
|
+ throw new \Exception('产品未配置套餐');
|
|
|
+ }
|
|
|
+
|
|
|
+ $now = date('Y-m-d H:i:s');
|
|
|
+
|
|
|
+ // 2. 查询用户所有可能有效的使用记录(加锁防止并发)
|
|
|
+ $usageList = Db::table('ba_user_product_usage')
|
|
|
+ ->where('user_id', $userId)
|
|
|
+ ->whereIn('package_price_id', $packagePriceIds)
|
|
|
+ ->where('status', 1) // 有效
|
|
|
+ ->lock(true)
|
|
|
+ ->select()
|
|
|
+ ->toArray();
|
|
|
+
|
|
|
+ if (empty($usageList)) {
|
|
|
+ throw new \Exception('没有可用的套餐');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 批量获取所有套餐规格的 cost_value 映射(用于积分卡)
|
|
|
+ $priceIds = array_column($usageList, 'package_price_id');
|
|
|
+ $costMap = Db::table('ba_package_price')
|
|
|
+ ->whereIn('id', $priceIds)
|
|
|
+ ->column('cost_value', 'id');
|
|
|
+
|
|
|
+ // 4. 按卡类型分类并过滤出真正可用的记录
|
|
|
+ $dateCardUsages = []; // 日期卡 (card_type >= 3)
|
|
|
+ $timesCardUsages = []; // 次卡 (card_type = 1)
|
|
|
+ $pointsCardUsages = []; // 积分卡 (card_type = 2)
|
|
|
+
|
|
|
+ foreach ($usageList as $usage) {
|
|
|
+ $cardType = $usage['card_type'];
|
|
|
+ if ($cardType >= 3) { // 日期卡
|
|
|
+ if (empty($usage['end_time']) || $usage['end_time'] > $now) {
|
|
|
+ $dateCardUsages[] = $usage;
|
|
|
+ }
|
|
|
+ } elseif ($cardType == 1) { // 次卡
|
|
|
+ if ($usage['remaining_times'] > 0) {
|
|
|
+ $timesCardUsages[] = $usage;
|
|
|
+ }
|
|
|
+ } elseif ($cardType == 2) { // 积分卡
|
|
|
+ $cost = $costMap[$usage['package_price_id']] ?? 0;
|
|
|
+ if ($cost > 0 && $usage['remaining_points'] >= $cost) {
|
|
|
+ $pointsCardUsages[] = $usage;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 按优先级选择一条记录
|
|
|
+ $selectedUsage = null;
|
|
|
+ $consumeValue = 0; // 积分卡本次消耗值
|
|
|
+
|
|
|
+ if (!empty($dateCardUsages)) {
|
|
|
+ // 日期卡按到期时间升序,优先使用即将到期的
|
|
|
+ usort($dateCardUsages, function ($a, $b) {
|
|
|
+ $aEnd = $a['end_time'] ?? '9999-12-31 23:59:59';
|
|
|
+ $bEnd = $b['end_time'] ?? '9999-12-31 23:59:59';
|
|
|
+ return strcmp($aEnd, $bEnd);
|
|
|
+ });
|
|
|
+ $selectedUsage = $dateCardUsages[0];
|
|
|
+ } elseif (!empty($timesCardUsages)) {
|
|
|
+ // 次卡按剩余次数升序,优先使用剩余少的
|
|
|
+ usort($timesCardUsages, function ($a, $b) {
|
|
|
+ return $a['remaining_times'] <=> $b['remaining_times'];
|
|
|
+ });
|
|
|
+ $selectedUsage = $timesCardUsages[0];
|
|
|
+ } elseif (!empty($pointsCardUsages)) {
|
|
|
+ // 积分卡按剩余积分升序,优先使用剩余少的
|
|
|
+ usort($pointsCardUsages, function ($a, $b) {
|
|
|
+ return $a['remaining_points'] <=> $b['remaining_points'];
|
|
|
+ });
|
|
|
+ $selectedUsage = $pointsCardUsages[0];
|
|
|
+ $consumeValue = $costMap[$selectedUsage['package_price_id']];
|
|
|
+ } else {
|
|
|
+ throw new \Exception('没有可用的套餐');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 执行扣减操作
|
|
|
+ $updateData = [
|
|
|
+ 'last_use_time' => $now,
|
|
|
+ 'update_time' => $now
|
|
|
+ ];
|
|
|
+
|
|
|
+ $cardType = $selectedUsage['card_type'];
|
|
|
+ switch ($cardType) {
|
|
|
+ case 1: // 次卡
|
|
|
+ $updateData['remaining_times'] = Db::raw('remaining_times - 1');
|
|
|
+ if ($selectedUsage['remaining_times'] - 1 <= 0) {
|
|
|
+ $updateData['status'] = 3; // 已用完
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 2: // 积分卡
|
|
|
+ $updateData['remaining_points'] = Db::raw('remaining_points - ' . $consumeValue);
|
|
|
+ if ($selectedUsage['remaining_points'] - $consumeValue <= 0) {
|
|
|
+ $updateData['status'] = 3; // 已用完
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ // 日期卡无需扣减,仅记录使用时间
|
|
|
+ }
|
|
|
+
|
|
|
+ Db::table('ba_user_product_usage')
|
|
|
+ ->where('id', $selectedUsage['id'])
|
|
|
+ ->update($updateData);
|
|
|
+
|
|
|
+ // 7. 记录使用明细日志(可选)
|
|
|
+ $this->logUsage($userId, $selectedUsage['id'], $productId, $consumeValue, $selectedUsage);
|
|
|
+
|
|
|
+ Db::commit();
|
|
|
+ return true;
|
|
|
+
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Db::rollback();
|
|
|
+ Log::error('用户使用产品失败:' . $e->getMessage(), [
|
|
|
+ 'user_id' => $userId,
|
|
|
+ 'product_id' => $productId
|
|
|
+ ]);
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录使用明细日志
|
|
|
+ */
|
|
|
+ private function logUsage($userId, $usageId, $productId, $consumeValue, $usage)
|
|
|
+ {
|
|
|
+ $logData = [
|
|
|
+ 'user_id' => $userId,
|
|
|
+ 'usage_id' => $usageId,
|
|
|
+ 'product_id' => $productId,
|
|
|
+ 'consume_type' => $usage['card_type'] == 1 ? 1 : 2, // 1=次数,2=积分
|
|
|
+ 'consume_value' => $consumeValue,
|
|
|
+ 'before_value' => $usage['card_type'] == 1 ? $usage['remaining_times'] : $usage['remaining_points'],
|
|
|
+ 'after_value' => $usage['card_type'] == 1 ? $usage['remaining_times'] - 1 : $usage['remaining_points'] - $consumeValue,
|
|
|
+ 'create_time' => date('Y-m-d H:i:s')
|
|
|
+ ];
|
|
|
+ Db::table('ba_user_usage_log')->insert($logData);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 格式化产品项
|
|
|
*/
|