Bladeren bron

更新版本1
1:修改购买产品过期不展示问题
2:添加定时更新用户购买产品日期状态
3:产品接口对接

PC-7\Administrator 7 maanden geleden
bovenliggende
commit
472188adc0

+ 3 - 1
app/admin/controller/package/Package.php

@@ -334,6 +334,7 @@ class Package extends Backend
         $cardType = $this->request->post('card_type', 1);
         $unitValue = $this->request->post('unit_value', 0);
         $pointsValue = $this->request->post('points_value', 0);
+        $cost_value = $this->request->post('cost_value', 0);
         $description = $this->request->post('description', '');
         $image = $this->request->post('image', '');
         $sort = $this->request->post('sort', 0);
@@ -351,7 +352,7 @@ class Package extends Backend
             $this->error(__('次卡类型必须填写有效次数'));
         }
 
-        if ($cardType == 2 && $pointsValue <= 0) {
+        if ($cardType == 2 && $pointsValue <= 0&&$cost_value<=0) {
             $this->error(__('积分类型必须填写有效积分数量'));
         }
 
@@ -387,6 +388,7 @@ class Package extends Backend
             'card_type' => $cardType,
             'unit_value' => $unitValue,
             'points_value' => $pointsValue,
+            'cost_value' => $cost_value,
             'description' => $description,
             'sort' => $sort,
             'update_time' => date('Y-m-d H:i:s')

+ 134 - 1
app/api/controller/Index.php

@@ -11,7 +11,7 @@ use app\common\library\token\TokenExpirationException;
 
 class Index extends Frontend
 {
-    protected array $noNeedLogin = ['index'];
+    protected array $noNeedLogin = ['index','checkExpiredProducts'];
 
     public function initialize(): void
     {
@@ -81,4 +81,137 @@ class Index extends Frontend
             'menus'            => $menus,
         ]);
     }
+
+
+    /*
+    * 批量更新过期记录
+    */
+    /**
+     * 检查并更新过期套餐
+     * @return int 更新的记录数
+     */
+    public function checkExpiredProducts()
+    {
+        $now = date('Y-m-d H:i:s');
+
+        Db::startTrans();
+        try {
+            // 1. 查询所有已过期且状态仍有效的 usage 记录(加锁)
+            $expiredList = Db::table('ba_user_product_usage')
+                ->where('status', 1)
+                ->where('end_time', '<=', $now)
+                ->whereNotNull('end_time')
+                ->lock(true)
+                ->select()
+                ->toArray();
+
+            if (empty($expiredList)) {
+                Db::commit();
+                return 0;
+            }
+
+            $ids = array_column($expiredList, 'id');
+
+            // 提取企业套餐的 allocation_id
+            $allocationIds = [];
+            foreach ($expiredList as $item) {
+                if (!empty($item['allocation_id'])) {
+                    $allocationIds[] = $item['allocation_id'];
+                }
+            }
+            $allocationIds = array_unique($allocationIds);
+
+            // 2. 更新 usage 表状态
+            Db::table('ba_user_product_usage')
+                ->whereIn('id', $ids)
+                ->update([
+                    'status' => 2,
+                    'update_time' => $now
+                ]);
+
+            // 3. 更新企业套餐分配记录状态(如果业务需要)
+            if (!empty($allocationIds)) {
+                Db::table('ba_company_package_allocation')
+                    ->whereIn('id', $allocationIds)
+                    ->where('allocation_status', 1) // 假设 1 表示已分配/有效
+                    ->update([
+                        'allocation_status' => 2,   // 2 表示已过期或取消
+                        'update_time' => $now
+                    ]);
+
+                // 可选:更新企业套餐席位表的 assigned_seats / available_seats
+                // 这需要根据分配记录找到对应的企业套餐席位,然后减少 assigned_seats 并增加 available_seats
+                // 此处省略,可根据实际业务补充
+            }
+
+            // 4. 处理个人套餐的 ba_user_product 表
+            $personalCombos = [];
+            foreach ($expiredList as $item) {
+                if ($item['package_type'] == 1) { // 个人套餐
+                    $key = $item['user_id'] . '_' . $item['product_id'] . '_' . $item['package_price_id'];
+                    $personalCombos[$key] = [
+                        'user_id'          => $item['user_id'],
+                        'product_id'       => $item['product_id'],
+                        'package_price_id' => $item['package_price_id']
+                    ];
+                }
+            }
+
+            foreach ($personalCombos as $combo) {
+                // 检查该组合是否还有任何有效的 usage 记录
+                $hasValid = Db::table('ba_user_product_usage')
+                    ->where('user_id', $combo['user_id'])
+                    ->where('product_id', $combo['product_id'])
+                    ->where('package_price_id', $combo['package_price_id'])
+                    ->where('package_type', 1)
+                    ->where('status', 1)
+                    ->where(function ($query) use ($now) {
+                        $query->where('end_time', '>', $now)
+                            ->whereOr('end_time', 'null'); // 永久有效
+                    })
+                    ->count();
+
+                if ($hasValid == 0) {
+                    // 无有效记录,将汇总表状态置为过期
+                    Db::table('ba_user_product')
+                        ->where('user_id', $combo['user_id'])
+                        ->where('product_id', $combo['product_id'])
+                        ->where('package_price_id', $combo['package_price_id'])
+                        ->update([
+                            'status' => 2,               // 已过期
+                            'update_time' => $now
+                        ]);
+                } else {
+                    // 还有有效记录,更新 expire_time 为最新的有效记录
+                    $latestEndTime = Db::table('ba_user_product_usage')
+                        ->where('user_id', $combo['user_id'])
+                        ->where('product_id', $combo['product_id'])
+                        ->where('package_price_id', $combo['package_price_id'])
+                        ->where('package_type', 1)
+                        ->where('status', 1)
+                        ->order('end_time', 'desc')
+                        ->value('end_time');
+
+                    if ($latestEndTime) {
+                        Db::table('ba_user_product')
+                            ->where('user_id', $combo['user_id'])
+                            ->where('product_id', $combo['product_id'])
+                            ->where('package_price_id', $combo['package_price_id'])
+                            ->update([
+                                'expire_time' => $latestEndTime,
+                                'update_time' => $now
+                            ]);
+                    }
+                }
+            }
+
+            Db::commit();
+
+            return count($expiredList);
+
+        } catch (\Throwable $e) {
+            Db::rollback();
+            throw $e;
+        }
+    }
 }

+ 2 - 1
app/api/controller/invitation/Order.php

@@ -59,7 +59,7 @@ class Order extends Frontend
             }
 
             if (!empty($star_time) && !empty($end_time)) {
-                $where[] = ['order_time', 'between', [$star_time, $end_time]];
+                $where[] = ['order_time', 'between', [$star_time.' 00:00:00', $end_time.' 23:59:59']];
                 $query->where($where); // 塞选时间
             } else {
                 $yesterday = date('Y-m-d 00:00:00', strtotime('-1 day'));
@@ -70,6 +70,7 @@ class Order extends Frontend
 
             // 获取订单总数
             $total = $query->count();
+
             // 获取订单列表(只取必要字段)
             $orders = $query->field('order_no, pay_amount, pay_method, pay_status, order_time, pay_time')
                 ->order('order_time', 'desc')

+ 64 - 1
app/api/controller/invitation/Product.php

@@ -7,7 +7,7 @@ use think\facade\Db;
 
 class Product extends Frontend
 {
-    protected array $noNeedPermission = ['purchasedList', 'detail', 'myProducts'];
+    protected array $noNeedPermission = ['purchasedList', 'detail', 'myProducts','test_project'];
     protected $userProductService;
 
     public function initialize(): void
@@ -58,6 +58,37 @@ class Product extends Frontend
         $this->myProducts();
     }
 
+
+    /**
+     * @return void
+     * 判断用户是否有使用这个产品的权限
+     */
+    public function test_project(): void
+    {
+        $userId = $this->auth->id;
+        if (!$userId) {
+            $this->error('用户未登录');
+        }
+        $productId = $this->request->get('project_id', 0);
+        if ($productId <= 0) {
+            $this->error('产品ID错误');
+        }
+        $products = $this->userProductService->getUserProducts($userId);
+        foreach ($products as $v){
+
+            if ($v['product_id'] == $productId) {
+                // 找到匹配项,直接返回数据并终止循环
+                $this->success('用户已购买产品',['status'=>'true']);
+                // 或者如果只是想终止循环并返回数据,也可以用 break + return
+                // break;
+            }
+
+        }
+          // 如果循环结束都没找到,返回未找到提示
+         $this->error('未找到对应产品,请购买对应的产品',['status'=>'false']);
+    }
+
+
     /**
      * 获取产品详情(包含购买记录)
      */
@@ -144,6 +175,37 @@ class Product extends Frontend
         return '';
     }
 
+
+    /**
+     * 用户使用产品接口
+     * @ApiMethod (POST)
+     * @ApiParams (name="product_id", type="integer", required=true, description="产品ID")
+     */
+    public function useProduct()
+    {
+        $userId = $this->auth->id;
+        if (!$userId) {
+            $this->error('用户未登录');
+        }
+
+        $productId = $this->request->post('product_id', 0);
+        if ($productId <= 0) {
+            $this->error('产品ID不能为空');
+        }
+//        dump($userId);
+//        dump($productId);die;
+        try {
+            $result = $this->userProductService->useProductByProductId($userId, $productId);
+        } catch (\Exception $e) {
+            $this->error($e->getMessage());
+        }
+        if ($result) {
+            $this->success('使用成功');
+        } else {
+            $this->error('使用失败');
+        }
+    }
+
     /**
      * 获取卡类型文本
      */
@@ -160,4 +222,5 @@ class Product extends Frontend
         ];
         return $map[$type] ?? '其他';
     }
+
 }

+ 0 - 2
app/api/controller/product/Purchase.php

@@ -75,7 +75,6 @@ class Purchase extends Frontend
         }else{
             $companyId =0;
         }
-
             // 查找关联的产品
             $product = Db::table('ba_product')
                 ->where('package_id', $package['package_id'])
@@ -137,7 +136,6 @@ class Purchase extends Frontend
                 ];
 
                 Db::table('ba_order_item')->insert($itemData);
-
                 // 3. 如果存在分销,创建分销记录
                 if ($hasCommission && $distributorId > 0) {
                     $commissionService->createDistributionRecord([

+ 2 - 0
app/common/service/CompanyPackageService.php

@@ -1476,4 +1476,6 @@ class CompanyPackageService
         }
     }
 
+
+
 }

+ 161 - 2
app/common/service/UserProductService.php

@@ -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);
+    }
+
+
     /**
      * 格式化产品项
      */