realFlowPoolRepository = $realFlowPoolRepository; $this->flowPoolRepository = $flowPoolRepository; $this->flowPoolSettingRepository = $flowPoolSettingRepository; } /** * RD流量池列表 * * @return void */ public function real() { $used = $this->flowPoolRepository->select(['id', 'real_pool_ids'])->get(); $array = []; foreach ($used as $item) { $item['real_pool_ids'] = $item['real_pool_ids'] ?? []; foreach ($item['real_pool_ids'] as $key => $value) { $array[$value] = $item['id']; } } $list = $this->realFlowPoolRepository->get(); $list->map(function ($item) use ($array) { $item->virtual_pool_id = $array[$item['id']] ?? 0; }); return $list; } /** * 定价列表 * * @return void */ public function packages(array $conditions = []) { $used = $this->flowPoolRepository->select(['id', 'company_id', 'package_ids'])->get(); $array = []; foreach ($used as $item) { $item['package_ids'] = $item['package_ids'] ?? []; foreach ($item['package_ids'] as $key => $value) { if (isset($array[$value])) { array_push($array[$value], $item['company_id']); } else { $array[$value] =[$item['company_id']]; } } } $packages = app(PackageService::class)->index(['limit' => 0]); $packages = $packages->where('status', 0)->where('flowed', 1); $packages->map(function ($item) use ($array) { $item->company_ids = $array[$item['id']] ?? []; }); return $packages->values(); } /** * 流量池列表 * * @param array $conditions * @return mixed */ public function index(array $conditions = []) { $limit = $conditions['limit'] ?? 20; $month = Carbon::parse($conditions['month']); $flowPools = $this->flowPoolRepository->withConditions($conditions) ->applyConditions()->paginate($limit); $flowPools = $this->transformer($flowPools, $month); $flows = DB::select('select * from get_flow_pool_month_stat(?)', [$month->format('Ym')]); $flows = collect($flows)->collect()->keyBy('pool_id')->toArray(); // 流量统计 $flowPools->map(function ($flowPool) use ($flows) { $flowArry = $flows[$flowPool->id]; // 无使用流量 if (!$flowArry) { $flowPool->members = 0; $flowPool->minimum_flows = $this->humanFlows(0); $flowPool->excess_flows = $this->humanFlows(0); $flowPool->minimum_price = sprintf('%.02f', 0); $flowPool->excess_price = sprintf('%.02f', 0); $flowPool->total_price = sprintf('%.02f', 0); return $flowPool; } $flowPool->members = $flowArry['members']; $flowPool->minimum_flows = $this->humanFlows($flowArry['minimum_flows']); $flowPool->excess_flows = $this->humanFlows($flowArry['excess_flows']); $flowPool->minimum_price = sprintf('%.02f', $flowArry['minimum_price'] / 100); $flowPool->excess_price = sprintf('%.02f', $flowArry['excess_price'] / 100); $flowPool->total_price = sprintf('%.02f', $flowPool->minimum_price + $flowPool->excess_price); return $flowPool; }); $flowPools->map(function ($item) { $item = $this->transformerSettings($item); }); return $flowPools; } /** * 存储流量池 * * @param array $attributes * @return FlowPool */ public function store(array $attributes = []) { $attributes['start_at'] = $attributes['start_at'] ? Carbon::parse($attributes['start_at'])->startOfMonth()->format('Y-m-d H:i:s') : Carbon::now()->startOfMonth()->format('Y-m-d H:i:s'); $attributes['end_at'] = $attributes['status'] ? Carbon::now()->endOfMonth()->format('Y-m-d H:i:s') : null; $attributes = array_only($attributes, ['id', 'company_id', 'name', 'flows', 'carrier_operator', 'shared', 'package_ids', 'real_pool_ids', 'remark', 'start_at', 'end_at']); $rule = [ 'name' => ['required', 'between:2,32', Rule::unique($this->flowPoolRepository->getTable(), 'name')->ignore($attributes['id'])->whereNUll('deleted_at')], 'carrier_operator' => ['required', 'in:0,1,2,3'], 'shared' => ['in:1,2'], ]; $message = [ 'name.required' => '请输入流量池名称', 'name.between' => '流量池名称长度不合法', 'name.unique' => '流量池名称已经被其他用户所使用', 'carrier_operator.required' => '请选择运营商', 'carrier_operator.in' => '运营商不合法', // 'shared.required' => '请选择共享类型', 'shared.in' => '共享类型不合法', ]; Validator::validate($attributes, $rule, $message); if (!$attributes['id']) { $maxId = FlowPool::withTrashed()->max('id'); $attributes['id'] = $maxId ? $maxId + 1 : 1; $attributes['sn'] = self::sn($attributes['id']); $node = $this->flowPoolRepository->create($attributes); } if ($attributes['id']) { $node = $this->flowPoolRepository->find($attributes['id']); if (!$node) { throw new NotExistException('流量池不存在或已删除'); } $this->flowPoolRepository->setModel($node)->update($attributes); } return $node; } /** * 删除 * * @return bool */ public function destroy($ids) { $ids = is_array($ids) ? $ids : [$ids]; $this->flowPoolRepository->destroy($ids); return true; } /** * 计费规则 * * @param array $attributes * @return FlowPool */ public function setting(array $attributes = []) { $attributes['start_at'] = Carbon::parse($attributes['start_at'])->startOfMonth()->format('Y-m-d H:i:s'); $attributes['end_at'] = Carbon::parse($attributes['end_at'])->endOfMonth()->format('Y-m-d H:i:s'); $attributes['gradient_price'] = intval($attributes['gradient_price'] * 100); $attributes['gradient'] = floatval($attributes['gradient']); if (!is_array($attributes['minimum_settings'])) { throw new InvalidArgumentException('保底流量设置必须为数组'); } $minimum_settings = $attributes['minimum_settings']; foreach ($minimum_settings as &$item) { $item = array_only($item, ['package_id', 'price', 'flows']); $item['price'] = intval($item['price'] * 100); $item['flows'] = floatval($item['flows']); } $attributes['minimum_settings'] = $minimum_settings; if ($attributes['start_at'] > $attributes['end_at']) { throw new InvalidArgumentException('开始时间必须小于结束时间'); } $rule = [ 'pool_id' => ['required', Rule::exists($this->flowPoolRepository->getTable(), 'id')], 'gradient_price' => ['required', 'integer'], 'gradient' => ['required', 'integer'], 'gradient_unit' => ['required', 'in:0,1'], ]; $message = [ 'pool_id.required' => '请输入流量池ID', 'gradient_price.required' => '请输入次月单价', 'gradient.required' => '请输入梯度', 'gradient_unit.required' => '请选择梯度单位', ]; Validator::validate($attributes, $rule, $message); if (!$attributes['id']) { DB::transaction(function () use ($attributes) { $settings = $this->flowPoolSettingRepository->withConditions(['pool_id' => $attributes['pool_id']])->get(); $creates = []; if (!$settings->isEmpty()) { foreach ($settings as $item) { $result = range_compare([$attributes['start_at'], $attributes['end_at']], [$item['start_at'], $item['end_at']]); switch ($result) { case 0: $this->flowPoolSettingRepository->where(['id' => $item['id']])->update($attributes); $creates = []; break 2; case 1: $this->flowPoolSettingRepository->destroy($item['id']); $creates[0] = $attributes; break; case 2: $this->flowPoolSettingRepository->destroy($item['id']); $creates[0] = $attributes; unset($item['id']); $creates[1] = $item->toArray(); $creates[1]['end_at'] = Carbon::parse($attributes['start_at'])->subMonth()->endOfMonth()->format('Y-m-d H:i:s'); $creates[2] = $item->toArray(); $creates[2]['start_at'] = Carbon::parse($attributes['end_at'])->addMonth()->startOfMonth()->format('Y-m-d H:i:s'); break 2; case 3: case 4: $creates[0] = $attributes; break; case 5: $creates[0] = $attributes; $item['start_at'] = Carbon::parse($attributes['end_at'])->addMonth()->startOfMonth()->format('Y-m-d H:i:s'); $this->flowPoolSettingRepository->where(['id' => $item['id']])->update($item->toArray()); break; case 6: $creates[0] = $attributes; $item['end_at'] = Carbon::parse($attributes['start_at'])->subMonth()->startOfMonth()->format('Y-m-d H:i:s'); $this->flowPoolSettingRepository->where(['id' => $item['id']])->update($item->toArray()); break; default: $creates[0] = $attributes; break; } } } else { $creates[0] = $attributes; $creates[0]['start_at'] = '2000-01-01 00:00:00'; $creates[0]['end_at'] !== '3000-01-01 23:59:59'; } $maxId = FlowPoolSetting::withTrashed()->max('id') ?? 0; foreach ($creates as $key => $create) { $create['id'] = ++$maxId; if ($key) { $this->flowPoolSettingRepository->create($create); } else { $node =$this->flowPoolSettingRepository->create($create); } } }); } if ($attributes['id']) { $node = $this->flowPoolSettingRepository->find($attributes['id']); if (!$node) { throw new NotExistException('规则不存在或已删除'); } if ($node->start_at !== $attributes['start_at'] || $node->end_at !== $attributes['end_at']) { throw new InvalidArgumentException('起止时间不能修改'); } $this->flowPoolSettingRepository->setModel($node)->update($attributes); } return $node; } /** * 数据生成 * * $conditions['pool_id']; * $conditions['month']; * $conditions['total_flows']; * $conditions['settings']; * $conditions['settings'][*]['package_id']; * $conditions['settings'][*]['total']; * $conditions['settings'][*]['cards']; * $conditions['settings'][*]['cards'][*]['counts']; * $conditions['settings'][*]['cards'][*]['flow_range']; * * @return void */ public function flows(array $conditions) { $conditions = array_only($conditions, ['pool_id', 'month', 'total_flows', 'settings']); $conditions['total_flows'] = floatval($conditions['total_flows']); $settings = $conditions['settings']; $min_total_flows = 0; $max_total_flows = 0; foreach ($settings as &$setting) { $setting = array_only($setting, ['package_id', 'package_name', 'total', 'cards']); $cards = $setting['cards']; foreach ($cards as &$card) { $card = array_only($card, ['counts', 'flow_range']); $card['flow_range'][0] = floatval($card['flow_range'][0]); $card['flow_range'][1] = floatval($card['flow_range'][1]); $min_total_flows += $card['counts'] * $card['flow_range'][0]; $max_total_flows += $card['counts'] * $card['flow_range'][1]; } $setting['cards'] = $cards; } unset($setting); $conditions['settings'] = $settings; if ($min_total_flows > $conditions['total_flows'] || $max_total_flows < $conditions['total_flows']) { throw new NotAllowedException('流量范围设置不正确,最小流量大于总流量或最大流量小于总流量'); } $pool_id = $conditions['pool_id']; $month = Carbon::parse($conditions['month']); $settings = array_keyBy($conditions['settings'], 'package_id'); if (!$flowPool = $this->flowPoolRepository->find($pool_id)) { throw new NotExistException('流量池不存在或已删除'); } $flowPool = $this->transformer(collect([$flowPool]), $month)->first(); if (!$flowPool->setting_status) { throw new NotAllowedException('当前月份计费规则未设置或套餐保底流量未设置'); } $cardConditions = [ 'type' => [0, 1, 2], 'month' => $month, 'company_id' => $flowPool->company_id, 'package_id' => array_keys($settings), 'unit_price' => 0 ]; $cards = app(OrderCardPartitionRepository::class)->selectRaw('distinct sim as sim, package_id')->withConditions($cardConditions)->get(); $allTotal = $cards->count(); $cards = $cards->groupBy('package_id')->toArray(); $data = []; foreach ($cards as $package_id => $array) { if (!$setting = $settings[$package_id]) { throw new NotAllowedException("套餐 ID:{$package_id} 未设置"); } $total = array_sum(array_pluck($setting['cards'], 'counts')); if ($total !== count($array)) { throw new NotAllowedException("套餐 ID:{$package_id} 数量设置不匹配"); } $offset = 0; foreach ($setting['cards'] as $cardSetting) { $simArray = array_slice($array, $offset, $cardSetting['counts']); $offset += $cardSetting['counts']; $range_start = $cardSetting['flow_range'][0]; $range_end = $cardSetting['flow_range'][1]; if ($range_end < $range_start) { throw new NotAllowedException("流量范围结束必须不小于开始"); } $dataItems = []; $flows = $conditions['total_flows'] * $cardSetting['counts'] / $allTotal; foreach ($simArray as $sim) { $mebibyte = rand($range_start * 100, $range_end * 100) / 100; $rowObj = [ 'month' => $month->format('Ym'), 'sim' => $sim['sim'], 'package_id' => $package_id, 'pool_id' => $pool_id, 'mebibyte' => $mebibyte, ]; $dataItems[] = $rowObj; } $itemMebibyte = array_sum(array_pluck($dataItems, 'mebibyte')); if ($itemMebibyte) { $k = $flows/$itemMebibyte; foreach ($dataItems as &$value) { $value['mebibyte'] = round($value['mebibyte'] * $k); } } $data = array_merge($data, $dataItems); } } $table = self::checkTable($month->format('Ym')); $flowPoolData = $conditions; $flowPoolData['month'] = $month->format('Ym'); FlowPoolData::updateOrCreate(array_only($flowPoolData, ['pool_id', 'month']), $flowPoolData); DB::transaction(function () use ($table, $data, $pool_id) { try { app(FlowPoolMonth::class)->setTable($table)->where('pool_id', $pool_id)->delete(); foreach (array_chunk($data, 10000) as $chunk) { app(FlowPoolMonth::class)->setTable($table)->upsert($chunk, ['sim', 'month']); } } catch (\Exception $e) { throw $e; } }); return true; } /** * 流量池卡详情 * * @param array $conditions * @return void */ public function show(array $conditions = []) { $limit = $conditions['limit'] ?? 20; $page = $conditions['page'] ?? 1; $pool_id = $conditions['pool_id']; $month = Carbon::parse($conditions['month']); if (!$flowPool = $this->flowPoolRepository->find($pool_id)) { throw new NotExistException('流量池不存在或已删除'); } $flowPool = $this->transformer(collect([$flowPool]), $month)->first(); $minimum_settings = $flowPool['settings'][0]['minimum_settings'] ?? []; $minimum_flows = array_pluck($minimum_settings, 'flows', 'package_id'); $table = app(FlowPoolMonth::class)->getTable() . '_' . $month->format('Ym'); $query = app(FlowPoolMonth::class)->setTable($table)->where('pool_id', $pool_id); if (Schema::hasTable($table) && $total = $query->count()) { $cards = $query->select(['package_id', 'sim', 'mebibyte'])->forPage($page, $limit)->get(); } else { $cards = collect(); } $cards->map(function ($item) use ($minimum_flows) { $item->package_name = PackageService::load($item->package_id)['name']; $item->mebibyte = $this->humanFlows($item->mebibyte); $item->minimum_flows = $this->humanFlows($minimum_flows[$item->package_id] ?? 0); }); $cards = new LengthAwarePaginator($cards, $total, $limit); $flowPool = $this->transformerSettings($flowPool); return ['flowPool' => $flowPool, 'cards' => $cards]; } /** * 流量池格式转化 * * @param mixed $flowPools * @param Carbon $month * @return void */ public static function transformer($flowPools, $month) { $allSettings = app(FlowPoolSettingRepository::class)->withConditions(['pool_id' => $flowPools->pluck('id')->toArray()]) ->orderBy('start_at', 'desc')->get()->groupBy('pool_id'); $carrierOperators = app(Dicts::class)->get('carrier_operator'); $shares = app(Dicts::class)->get('shares'); $flowPools->map(function ($item) use ($carrierOperators, $shares, $allSettings, $month) { $setting_status = 0; $item->company_name = CompanyService::load($item['company_id'])['name']; $item->carrier_operator_name = $carrierOperators[$item['carrier_operator']]; $item->shared_name = $shares[$item['shared']]; $item->real_pool_ids = empty($item->real_pool_ids) ? [] : $item->real_pool_ids; $item->package_ids = empty($item->package_ids) ? [] : $item->package_ids; $item->status = $item->end_at ? 1 : 0; $packages = []; foreach ($item->package_ids as $value) { $package = PackageService::load($value); $packages[] = [ 'type' => $package['type'], 'package_id' => $value, 'package_name' => $package['name'], 'carrier_operator' => $package['carrier_operator'], ]; } $item->packages = $packages; $packages = array_keyBy($packages, 'package_id'); if ($settings = $allSettings[$item->id]) { foreach ($settings as $setting) { $minimum_settings = array_keyBy($setting->minimum_settings ?? [], 'package_id'); if ($setting->start_at <= $month && $setting->end_at >= $month) { $setting_status = 1; foreach ($packages as $package) { if (!isset($minimum_settings[$package['package_id']])) { $setting_status = 0; $minimum_settings[$package['package_id']] = [ 'package_id' => $package['package_id'], 'package_name' => PackageService::load($package['package_id'])['name'], 'flows' => 0, 'price' => 0 ]; } } foreach ($minimum_settings as $package_id => $minimum_setting) { if (!isset($packages[$package_id])) { unset($minimum_settings[$package_id]); } } } foreach ($minimum_settings as $package_id => $minimum_setting) { $minimum_settings[$package_id]['package_name'] = PackageService::load($package_id)['name']; } $setting->minimum_settings = array_values($minimum_settings); } } else { $settings = collect(); $setting_status = 0; } $item->settings = $settings; $item->setting_status = $setting_status; }); return $flowPools; } /** * 流量池格式转化 * * @param mixed $item * @return void */ public static function transformerSettings($item) { $settings = $item->settings; foreach ($settings as &$setting) { $setting['gradient_price'] = sprintf('%.02f', $setting['gradient_price']/100); $setting['gradient'] = sprintf('%.02f', $setting['gradient']); $minimum_settings = $setting['minimum_settings'] ?? []; foreach ($minimum_settings as &$minimum_setting) { $minimum_setting['price'] = sprintf('%.02f', $minimum_setting['price']/100); $minimum_setting['flows'] = floatval($minimum_setting['flows']); } $setting['minimum_settings'] = $minimum_settings; } $item->settings = $settings; return $item; } public static function sn($id) { return sprintf('FP%011d', $id); } public static function load($id) { if (!self::$pools) { self::$pools = app(FlowPoolRepository::class)->withTrashed()->get()->keyBy('id'); } return self::$pools[$id]; } public static function humanFlows($int) { return human_filesize($int, 2, ['unit' => 'MB', 'min' => 'MB', 'max' => 'MB']); } /** * 检查当月分区表是否已创建 * * @param string $month * @return string */ public static function checkTable(string $month) { $table = app(FlowPoolMonth::class)->getTable() . '_' . $month; if (!Schema::hasTable($table)) { Schema::table(app(FlowPoolMonth::class)->getTable(), function ($blueprint) use ($table, $month) { $blueprint->addPartition($table, 'list', [$month]); }); Schema::table($table, function ($blueprint) { $blueprint->unique(['sim', 'month']); }); } return $table; } }