diff --git a/Server/application/common.php b/Server/application/common.php index 4fd7c7c9..29010f44 100644 --- a/Server/application/common.php +++ b/Server/application/common.php @@ -104,7 +104,7 @@ if (!function_exists('setHeader')) { if (!function_exists('errorJson')) { function errorJson($error = '', $code = 500) { - return json_encode([ + return json([ 'code' => $code, 'msg' => $error, ]); @@ -115,7 +115,7 @@ if (!function_exists('errorJson')) { if (!function_exists('successJson')) { function successJson($data = [] ,$msg = '操作成功', $code = 200) { - return json_encode([ + return json([ 'data' => $data, 'code' => $code, 'msg' => $msg, diff --git a/Server/application/common/controller/Api.php b/Server/application/common/controller/Api.php new file mode 100644 index 00000000..cbe0df80 --- /dev/null +++ b/Server/application/common/controller/Api.php @@ -0,0 +1,132 @@ +requestType = Request::method(); + + // 控制器初始化 + $this->_initialize(); + + // 跨域请求检测 + $this->_checkCors(); + } + + /** + * 初始化操作 + */ + protected function _initialize() + { + // 初始化操作 + } + + /** + * 跨域检测 + */ + protected function _checkCors() + { + // 允许跨域 + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); + header('Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With'); + + // 对OPTIONS请求直接返回 + if ($this->requestType === 'OPTIONS') { + Response::create()->send(); + exit; + } + } + + /** + * 操作成功返回的数据 + * @param string $msg 提示信息 + * @param mixed $data 返回的数据 + * @param int $code 错误码,默认为1 + * @param string $type 输出类型 + * @param array $header 发送的header信息 + */ + protected function success($msg = '', $data = null, $code = 1, $type = null, array $header = []) + { + $this->result($msg, $data, $code, $type, $header); + } + + /** + * 操作失败返回的数据 + * @param string $msg 提示信息 + * @param mixed $data 返回的数据 + * @param int $code 错误码,默认为0 + * @param string $type 输出类型 + * @param array $header 发送的header信息 + */ + protected function error($msg = '', $data = null, $code = 0, $type = null, array $header = []) + { + $this->result($msg, $data, $code, $type, $header); + } + + /** + * 返回封装后的API数据 + * @param string $msg 提示信息 + * @param mixed $data 要返回的数据 + * @param int $code 错误码,默认为0 + * @param string $type 输出类型,支持json/xml/jsonp + * @param array $header 发送的header信息 + */ + protected function result($msg, $data = null, $code = 0, $type = null, array $header = []) + { + $result = [ + 'code' => $code, + 'msg' => $msg, + 'time' => time(), + 'data' => $data, + ]; + + // 返回数据格式 + $type = $type ?: $this->responseType; + + // 发送响应 + $response = Response::create($result, $type)->header($header); + $response->send(); + exit; + } +} \ No newline at end of file diff --git a/Server/application/store/config/route.php b/Server/application/store/config/route.php new file mode 100644 index 00000000..fbf355be --- /dev/null +++ b/Server/application/store/config/route.php @@ -0,0 +1,14 @@ +middleware(['jwt'])*/; \ No newline at end of file diff --git a/Server/application/store/controller/FlowPackageController.php b/Server/application/store/controller/FlowPackageController.php new file mode 100644 index 00000000..e3d9f72c --- /dev/null +++ b/Server/application/store/controller/FlowPackageController.php @@ -0,0 +1,164 @@ +request->param(); + + // 查询条件 + $where = []; + + // 只获取未删除的数据 + $where[] = ['isDel', '=', 0]; + + // 套餐模型 + $model = new FlowPackageModel(); + + // 查询数据 + $list = $model->where($where) + ->field('id, name, tag, originalPrice, price, monthlyFlow, duration, privileges') + ->order('sort', 'asc') + ->select(); + + // 格式化返回数据,添加计算字段 + $result = []; + foreach ($list as $item) { + $result[] = [ + 'id' => $item['id'], + 'name' => $item['name'], + 'tag' => $item['tag'], + 'originalPrice' => $item['originalPrice'], + 'price' => $item['price'], + 'monthlyFlow' => $item['monthlyFlow'], + 'duration' => $item['duration'], + 'discount' => $item->discount, + 'totalFlow' => $item->totalFlow, + 'privileges' => $item['privileges'], + ]; + } + + return successJson($result, '获取成功'); + } + + /** + * 获取流量套餐详情 + * + * @param int $id 套餐ID + * @return \think\Response + */ + public function detail($id) + { + if (empty($id)) { + return errorJson('参数错误'); + } + + // 套餐模型 + $model = new FlowPackageModel(); + + // 查询数据 + $info = $model->where('id', $id)->where('isDel', 0)->find(); + + if (empty($info)) { + return errorJson('套餐不存在'); + } + + // 格式化返回数据,添加计算字段 + $result = [ + 'id' => $info['id'], + 'name' => $info['name'], + 'tag' => $info['tag'], + 'originalPrice' => $info['originalPrice'], + 'price' => $info['price'], + 'monthlyFlow' => $info['monthlyFlow'], + 'duration' => $info['duration'], + 'discount' => $info->discount, + 'totalFlow' => $info->totalFlow, + 'privileges' => $info['privileges'], + ]; + + return successJson($result, '获取成功'); + } + + /** + * 展示用户流量套餐使用情况 + * + * @return \think\Response + */ + public function remainingFlow() + { + $params = $this->request->param(); + + // 获取用户ID,通常应该从会话或令牌中获取 + $userId = isset($params['userId']) ? intval($params['userId']) : 0; + + if (empty($userId)) { + return errorJson('请先登录'); + } + + // 获取用户当前有效的流量套餐 + $userPackage = UserFlowPackageModel::getUserActivePackage($userId); + + if (empty($userPackage)) { + return errorJson('您没有有效的流量套餐'); + } + + // 获取套餐详情 + $packageId = $userPackage['packageId']; + $flowPackage = FlowPackageModel::where('id', $packageId)->where('isDel', 0)->find(); + + if (empty($flowPackage)) { + return errorJson('套餐信息不存在'); + } + + // 计算剩余流量 + $totalFlow = $userPackage['totalFlow'] ?? $flowPackage->totalFlow; // 总流量 + $usedFlow = $userPackage['usedFlow'] ?? 0; // 已使用流量 + $remainingFlow = $totalFlow - $usedFlow; // 剩余流量 + $remainingFlow = $remainingFlow > 0 ? $remainingFlow : 0; // 确保不为负数 + + // 计算剩余天数 + $now = time(); + $expireTime = $userPackage['expireTime']; + $remainingDays = ceil(($expireTime - $now) / 86400); // 向上取整,剩余天数 + $remainingDays = $remainingDays > 0 ? $remainingDays : 0; // 确保不为负数 + + // 剩余百分比 + $flowPercentage = $totalFlow > 0 ? round(($remainingFlow / $totalFlow) * 100, 1) : 0; + $timePercentage = $userPackage['duration'] > 0 ? + round(($remainingDays / ($userPackage['duration'] * 30)) * 100, 1) : 0; + + // 返回数据 + $result = [ + 'packageName' => $flowPackage['name'], // 套餐名称 + 'remainingFlow' => $remainingFlow, // 剩余流量(人) + 'totalFlow' => $totalFlow, // 总流量(人) + 'flowPercentage' => $flowPercentage, // 剩余流量百分比 + 'remainingDays' => $remainingDays, // 剩余天数 + 'totalDays' => $userPackage['duration'] * 30, // 总天数(按30天/月计算) + 'timePercentage' => $timePercentage, // 剩余时间百分比 + 'expireTime' => date('Y-m-d', $expireTime), // 到期日期 + 'startTime' => date('Y-m-d', $userPackage['startTime']), // 开始日期 + ]; + + return successJson($result, '获取成功'); + } +} diff --git a/Server/application/store/model/FlowPackageModel.php b/Server/application/store/model/FlowPackageModel.php new file mode 100644 index 00000000..a4bf24da --- /dev/null +++ b/Server/application/store/model/FlowPackageModel.php @@ -0,0 +1,63 @@ + 'array', + ]; + + /** + * 特权字段获取器 - 将多行文本转换为数组 + * @param $value + * @return array + */ + public function getPrivilegesAttr($value) + { + if (empty($value)) { + return []; + } + + // 如果已经是数组则直接返回 + if (is_array($value)) { + return $value; + } + + // 按行分割文本 + return array_filter(explode("\n", $value)); + } + + /** + * 折扣获取器 - 根据原价和售价计算折扣 + * @param $value + * @param $data + * @return string + */ + public function getDiscountAttr($value, $data) + { + if (empty($data['originalPrice']) || $data['originalPrice'] <= 0) { + return '原价'; + } + + $discount = round(($data['price'] / $data['originalPrice']) * 10, 1); + return $discount . '折'; + } + + /** + * 总流量获取器 - 计算套餐总流量 + * @param $value + * @param $data + * @return int + */ + public function getTotalFlowAttr($value, $data) + { + return isset($data['monthlyFlow']) && isset($data['duration']) ? + intval($data['monthlyFlow']) * intval($data['duration']) : 0; + } +} \ No newline at end of file diff --git a/Server/application/store/model/UserFlowPackageModel.php b/Server/application/store/model/UserFlowPackageModel.php new file mode 100644 index 00000000..b477f558 --- /dev/null +++ b/Server/application/store/model/UserFlowPackageModel.php @@ -0,0 +1,104 @@ +where('status', 1) // 1表示有效 + ->where('expireTime', '>', time()) // 未过期 + ->order('expireTime', 'asc') // 按到期时间排序,最先到期的排在前面 + ->find(); + } + + /** + * 创建用户套餐订阅记录 + * + * @param int $userId 用户ID + * @param int $packageId 套餐ID + * @param int $duration 套餐时长(月) + * @return bool 是否创建成功 + */ + public static function createSubscription($userId, $packageId, $duration = 0) + { + if (empty($userId) || empty($packageId)) { + return false; + } + + // 获取套餐信息 + $package = FlowPackageModel::where('id', $packageId)->where('isDel', 0)->find(); + if (empty($package)) { + return false; + } + + // 如果未指定时长,则使用套餐默认时长 + if (empty($duration)) { + $duration = $package['duration']; + } + + // 计算开始时间和到期时间 + $now = time(); + $startTime = $now; + $expireTime = strtotime("+{$duration} month", $now); + + // 创建新订阅 + $data = [ + 'userId' => $userId, + 'packageId' => $packageId, + 'duration' => $duration, + 'totalFlow' => $package->totalFlow, + 'usedFlow' => 0, + 'status' => 1, // 1表示有效 + 'startTime' => $startTime, + 'expireTime' => $expireTime, + 'createTime' => $now, + 'updateTime' => $now + ]; + + return self::create($data) ? true : false; + } + + /** + * 更新用户已使用流量 + * + * @param int $id 用户套餐ID + * @param int $usedFlow 已使用流量 + * @return bool 是否更新成功 + */ + public static function updateUsedFlow($id, $usedFlow) + { + if (empty($id)) { + return false; + } + + $userPackage = self::where('id', $id)->find(); + if (empty($userPackage)) { + return false; + } + + // 确保使用量不超过总量 + $maxFlow = $userPackage['totalFlow']; + $usedFlow = $usedFlow > $maxFlow ? $maxFlow : $usedFlow; + + return self::where('id', $id)->update([ + 'usedFlow' => $usedFlow, + 'updateTime' => time() + ]) ? true : false; + } +} \ No newline at end of file diff --git a/Server/database/seeds/FlowPackageSeeder.php b/Server/database/seeds/FlowPackageSeeder.php new file mode 100644 index 00000000..a2dc27dd --- /dev/null +++ b/Server/database/seeds/FlowPackageSeeder.php @@ -0,0 +1,61 @@ + '基础套餐', + 'tag' => '入门级', + 'original_price' => 999.00, + 'price' => 899.00, + 'monthly_flow' => 20, + 'duration' => 1, + 'privileges' => "基础客服支持\n自动化任务\n每日数据报表", + 'sort' => 1, + 'status' => 1 + ], + [ + 'name' => '标准套餐', + 'tag' => '热销', + 'original_price' => 2799.00, + 'price' => 2499.00, + 'monthly_flow' => 50, + 'duration' => 3, + 'privileges' => "优先客服支持\n高级自动化任务\n每日数据报表\n好友数据分析\n一对一培训支持", + 'sort' => 2, + 'status' => 1 + ], + [ + 'name' => '专业套餐', + 'tag' => '推荐', + 'original_price' => 5999.00, + 'price' => 4999.00, + 'monthly_flow' => 100, + 'duration' => 6, + 'privileges' => "24小时专属客服\n全部自动化任务\n实时数据报表\n深度数据分析\n个性化培训支持\n专属策略顾问\n优先功能更新", + 'sort' => 3, + 'status' => 1 + ], + [ + 'name' => '企业套餐', + 'tag' => '高级', + 'original_price' => 11999.00, + 'price' => 9999.00, + 'monthly_flow' => 200, + 'duration' => 12, + 'privileges' => "24小时专属客服\n全部自动化任务\n实时数据报表\n深度数据分析\n个性化培训支持\n专属策略顾问\n优先功能更新\n企业API对接\n专属功能定制\n全平台数据打通", + 'sort' => 4, + 'status' => 1 + ] + ]; + + $this->table('flow_package')->insert($data)->save(); + } +} \ No newline at end of file diff --git a/Server/route/route.php b/Server/route/route.php index 69c69327..1885d66d 100644 --- a/Server/route/route.php +++ b/Server/route/route.php @@ -17,4 +17,7 @@ include __DIR__ . '/../application/common/config/route.php'; // 加载Devices模块路由配置 include __DIR__ . '/../application/devices/config/route.php'; +// 加载Store模块路由配置 +include __DIR__ . '/../application/store/config/route.php'; + return [];