request->param('page', 1)); $limit = intval($this->request->param('limit', 10)); // 确保分页参数有效 if ($page <= 0) $page = 1; if ($limit <= 0) $limit = 10; $where = [ ['isDel', '=', 0], ['status', '=', 1], ]; $query = TokensPackageModel::where($where); $total = $query->count(); $list = $query->page($page, $limit)->order('sort ASC,id desc')->select(); // 格式化数据 $result = []; foreach ($list as $item) { $originalPrice = floatval($item['originalPrice'] ?? 0) / 100; // 分转元 $price = floatval($item['price'] ?? 0) / 100; // 分转元 $tokens = intval($item['tokens'] ?? 0); // 计算折扣 $discount = 0; if ($originalPrice > 0) { $discount = round((($originalPrice - $price) / $originalPrice) * 100, 2); } // 计算单价 $unitPrice = $tokens > 0 ? round($price / $tokens, 6) : 0; $result[] = [ 'id' => intval($item['id']), 'name' => $item['name'] ?? '', 'tokens' => number_format($tokens), 'price' => round($price, 2), 'originalPrice' => round($originalPrice, 2), 'discount' => $discount, 'unitPrice' => $unitPrice, 'description' => $item->description, 'sort' => intval($item['sort'] ?? 50), 'isTrial' => intval($item['isTrial'] ?? 0), 'isRecommend' => intval($item['isRecommend'] ?? 0), 'isHot' => intval($item['isHot'] ?? 0), 'isVip' => intval($item['isVip'] ?? 0), ]; } return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'list' => $result, 'total' => $total, 'page' => $page, 'limit' => $limit ] ]); } catch (\Exception $e) { Log::error('获取算力套餐列表失败: ' . $e->getMessage()); return json(['code' => 500, 'msg' => '获取失败:' . $e->getMessage()]); } } /** * 购买算力 * POST /v2/store/tokens/pay * * @return \think\response\Json */ public function pay() { try { $id = intval($this->request->param('id', 0)); $price = $this->request->param('price', ''); $payType = $this->request->param('payType', 'qrCode'); $userId = $this->userInfo['id'] ?? 0; $companyId = $this->userInfo['companyId'] ?? 0; if (empty($userId)) { return json(['code' => 401, 'msg' => '请先登录']); } if (!in_array($payType, ['wechat', 'alipay', 'qrCode'])) { return json(['code' => 400, 'msg' => '付款类型不正确']); } if (empty($id) && empty($price)) { return json(['code' => 400, 'msg' => '套餐和自定义购买金额必须选一个']); } // 处理套餐或自定义购买 if (!empty($id)) { $package = TokensPackageModel::where(['id' => $id, 'status' => 1, 'isDel' => 0])->find(); if (empty($package)) { return json(['code' => 404, 'msg' => '套餐不存在或者已禁用']); } if ($package['price'] <= 0) { return json(['code' => 400, 'msg' => '套餐金额异常']); } $specs = [ 'id' => intval($package['id']), 'name' => $package['name'], 'price' => intval($package['price']), // 单位:分 'tokens' => intval($package['tokens']), ]; } else { // 获取配置的tokens比例 $tokens_multiple = Env::get('payment.tokens_multiple', 20); $specs = [ 'id' => 0, 'name' => '自定义购买算力', 'price' => intval(floatval($price) * 100), // 元转分 'tokens' => intval(floatval($price) * $tokens_multiple), ]; } // 生成订单号 $orderNo = date('YmdHis') . rand(100000, 999999); $order = [ 'companyId' => $companyId, 'userId' => $userId, 'orderNo' => $orderNo, 'goodsId' => $specs['id'], 'goodsName' => $specs['name'], 'goodsSpecs' => $specs, 'orderType' => 1, // 1=购买算力 'money' => $specs['price'], 'service' => $payType ]; $paymentService = new PaymentService(); $res = $paymentService->createOrder($order); $res = json_decode($res, true); if ($res['code'] == 200) { return json([ 'code' => 200, 'msg' => '订单创建成功', 'data' => [ 'orderNo' => $orderNo, 'code_url' => $res['data'] ?? '' ] ]); } else { return json(['code' => 500, 'msg' => $res['msg'] ?? '订单创建失败']); } } catch (\Exception $e) { Log::error('购买算力失败: ' . $e->getMessage()); return json(['code' => 500, 'msg' => '购买失败:' . $e->getMessage()]); } } /** * 查询订单状态 * GET /v2/store/tokens/order * * @return \think\response\Json */ public function queryOrder() { try { $orderNo = $this->request->param('orderNo', ''); if (empty($orderNo)) { return json(['code' => 400, 'msg' => '订单号不能为空']); } $order = Order::where('orderNo', $orderNo)->find(); if (!$order) { return json(['code' => 404, 'msg' => '该订单不存在']); } // 如果订单已支付,直接返回 if ($order->status == 1) { return json([ 'code' => 200, 'msg' => '订单已支付', 'data' => [ 'orderNo' => $order->orderNo, 'status' => $order->status, 'payTime' => !empty($order->payTime) && is_numeric($order->payTime) ? date('Y-m-d H:i:s', intval($order->payTime)) : '', ] ]); } // 查询支付状态 $paymentService = new PaymentService(); $res = $paymentService->queryOrder($orderNo); $res = json_decode($res, true); if ($res['code'] == 200) { return json([ 'code' => 200, 'msg' => '订单已支付', 'data' => [ 'orderNo' => $order->orderNo, 'status' => 1, 'payTime' => !empty($order->payTime) && is_numeric($order->payTime) ? date('Y-m-d H:i:s', intval($order->payTime)) : '', ] ]); } else { $errorMsg = !empty($order['payInfo']) ? $order['payInfo'] : '订单未支付'; return json([ 'code' => 200, 'msg' => $errorMsg, 'data' => [ 'orderNo' => $order->orderNo, 'status' => $order->status, ] ]); } } catch (\Exception $e) { Log::error('查询订单失败: ' . $e->getMessage()); return json(['code' => 500, 'msg' => '查询失败:' . $e->getMessage()]); } } /** * 获取订单列表 * GET /v2/store/tokens/orders * * @return \think\response\Json */ public function getOrderList() { try { $page = intval($this->request->param('page', 1)); $limit = intval($this->request->param('limit', 10)); $status = $this->request->param('status', ''); $keyword = $this->request->param('keyword', ''); $orderType = $this->request->param('orderType', ''); $payType = $this->request->param('payType', ''); $startTime = $this->request->param('startTime', ''); $endTime = $this->request->param('endTime', ''); // 确保分页参数有效 if ($page <= 0) $page = 1; if ($limit <= 0) $limit = 10; $userId = $this->userInfo['id'] ?? 0; $companyId = $this->userInfo['companyId'] ?? 0; if (empty($userId)) { return json(['code' => 401, 'msg' => '请先登录']); } // 构建查询条件 $where = [ ['userId', '=', $userId], ['companyId', '=', $companyId] ]; // 关键词搜索(订单号、商品名称) if (!empty($keyword)) { $where[] = ['orderNo|goodsName', 'like', '%' . $keyword . '%']; } // 状态筛选 (0-待支付 1-已付款 2-已退款 3-付款失败) if ($status !== '') { $where[] = ['status', '=', intval($status)]; } // 订单类型筛选 if ($orderType !== '') { $where[] = ['orderType', '=', intval($orderType)]; } // 支付类型筛选 if ($payType !== '') { $where[] = ['payType', '=', intval($payType)]; } // 时间范围筛选 if (!empty($startTime)) { $where[] = ['createTime', '>=', strtotime($startTime)]; } if (!empty($endTime)) { $where[] = ['createTime', '<=', strtotime($endTime . ' 23:59:59')]; } // 分页查询 $query = Order::where($where) ->where(function ($query) { $query->whereNull('deleteTime')->whereOr('deleteTime', 0); }); $total = $query->count(); $list = $query->field('id,orderNo,goodsId,goodsName,goodsSpecs,orderType,money,status,payType,payTime,createTime') ->order('id desc') ->page($page, $limit) ->select(); // 格式化数据 $result = []; foreach ($list as $item) { // 金额转换(分转元) $money = round(floatval($item['money'] ?? 0) / 100, 2); // 解析商品规格 $specs = []; if (!empty($item['goodsSpecs'])) { $specs = is_string($item['goodsSpecs']) ? json_decode($item['goodsSpecs'], true) : $item['goodsSpecs']; } // 状态文本 $statusText = [ 0 => '待支付', 1 => '已付款', 2 => '已退款', 3 => '付款失败' ]; // 订单类型文本 $orderTypeText = [ 1 => '购买算力' ]; // 支付类型文本 $payTypeText = [ 1 => '微信支付', 2 => '支付宝' ]; $result[] = [ 'id' => intval($item['id']), 'orderNo' => $item['orderNo'], 'goodsId' => intval($item['goodsId'] ?? 0), 'goodsName' => $item['goodsName'] ?? '', 'goodsSpecs' => $specs, 'tokens' => isset($specs['tokens']) ? number_format(intval($specs['tokens'])) : '0', 'orderType' => intval($item['orderType'] ?? 0), 'orderTypeText' => $orderTypeText[$item['orderType'] ?? 0] ?? '其他', 'money' => $money, 'status' => intval($item['status'] ?? 0), 'statusText' => $statusText[$item['status'] ?? 0] ?? '未知', 'payType' => intval($item['payType'] ?? 0), 'payTypeText' => !empty($item['payType']) ? ($payTypeText[$item['payType']] ?? '未知') : '', 'payTime' => !empty($item['payTime']) && is_numeric($item['payTime']) ? date('Y-m-d H:i:s', intval($item['payTime'])) : '', 'createTime' => !empty($item['createTime']) && is_numeric($item['createTime']) ? date('Y-m-d H:i:s', intval($item['createTime'])) : '', ]; } return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'list' => $result, 'total' => $total, 'page' => $page, 'limit' => $limit ] ]); } catch (\Exception $e) { Log::error('获取订单列表失败: ' . $e->getMessage()); return json(['code' => 500, 'msg' => '获取失败:' . $e->getMessage()]); } } /** * 获取算力统计信息 * GET /v2/store/tokens/statistics * * @return \think\response\Json */ public function getTokensStatistics() { try { $userId = $this->userInfo['id'] ?? 0; $companyId = $this->userInfo['companyId'] ?? 0; if (empty($companyId)) { return json(['code' => 400, 'msg' => '公司信息获取失败']); } // 获取公司算力余额 $tokensCompany = TokensCompanyModel::where(['companyId' => $companyId, 'userId' => $userId])->find(); $remainingTokens = $tokensCompany ? intval($tokensCompany->tokens ?? 0) : 0; // 获取今日开始和结束时间戳 $todayStart = strtotime(date('Y-m-d 00:00:00')); $todayEnd = strtotime(date('Y-m-d 23:59:59')); // 获取本月开始和结束时间戳 $monthStart = strtotime(date('Y-m-01 00:00:00')); $monthEnd = strtotime(date('Y-m-t 23:59:59')); // 统计今日消费(type=0表示消费) $todayUsed = TokensRecordModel::where([ ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 0], // 0为减少(消费) ['createTime', '>=', $todayStart], ['createTime', '<=', $todayEnd] ])->sum('tokens'); $todayUsed = intval($todayUsed); // 统计本月消费 $monthUsed = TokensRecordModel::where([ ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 0], // 0为减少(消费) ['createTime', '>=', $monthStart], ['createTime', '<=', $monthEnd] ])->sum('tokens'); $monthUsed = intval($monthUsed); // 计算总算力(当前剩余 + 历史总消费) $totalConsumed = TokensRecordModel::where([ ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 0] ])->sum('tokens'); $totalConsumed = intval($totalConsumed); // 总充值算力 $totalRecharged = TokensRecordModel::where([ ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 1] // 1为增加(充值) ])->sum('tokens'); $totalRecharged = intval($totalRecharged); // 计算预计可用天数(基于过去一个月的平均消耗) $estimatedDays = $this->calculateEstimatedDays($userId, $companyId, $remainingTokens); return json([ 'code' => 200, 'msg' => '获取成功', 'data' => [ 'totalTokens' => $totalRecharged, // 总算力(累计充值) 'todayUsed' => $todayUsed, // 今日使用 'monthUsed' => $monthUsed, // 本月使用 'remainingTokens' => $remainingTokens, // 剩余算力 'totalConsumed' => $totalConsumed, // 累计消费 'estimatedDays' => $estimatedDays, // 预计可用天数 ] ]); } catch (\Exception $e) { Log::error('获取算力统计失败: ' . $e->getMessage()); return json(['code' => 500, 'msg' => '获取失败:' . $e->getMessage()]); } } /** * 计算预计可用天数(基于过去一个月的平均消耗) * * @param int $userId 用户ID * @param int $companyId 公司ID * @param int $remainingTokens 当前剩余算力 * @return int 预计可用天数,-1表示无法计算(无消耗记录或余额为0) */ private function calculateEstimatedDays($userId, $companyId, $remainingTokens) { // 如果余额为0或负数,无法计算 if ($remainingTokens <= 0) { return -1; } // 计算过去30天的消耗总量(只统计减少的记录,type=0) $oneMonthAgo = time() - (30 * 24 * 60 * 60); // 30天前的时间戳 $totalConsumed = TokensRecordModel::where([ ['userId', '=', $userId], ['companyId', '=', $companyId], ['type', '=', 0], // 只统计减少的记录 ['createTime', '>=', $oneMonthAgo] ])->sum('tokens'); $totalConsumed = intval($totalConsumed); // 如果过去30天没有消耗记录,无法计算 if ($totalConsumed <= 0) { return -1; } // 计算平均每天消耗量 $avgDailyConsumption = $totalConsumed / 30; // 如果平均每天消耗为0,无法计算 if ($avgDailyConsumption <= 0) { return -1; } // 计算预计可用天数 = 当前余额 / 平均每天消耗量 $estimatedDays = floor($remainingTokens / $avgDailyConsumption); return $estimatedDays; } }