diff --git a/app/Core/AbstractExport.php b/app/Core/AbstractExport.php index 9e40e270..074fd7e1 100644 --- a/app/Core/AbstractExport.php +++ b/app/Core/AbstractExport.php @@ -24,6 +24,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize public static $classes = [ \App\Domains\Virtual\Exports\CardExport::class => '客户列表', + \App\Domains\Virtual\Exports\FlowPoolExport::class => '流量池列表', \App\Domains\Stats\Exports\CompanyCountExport::class => '企业统计', \App\Domains\Stats\Exports\OrderExport::class => '订单统计', \App\Domains\Stats\Exports\OrderDetailExport::class => '订单明细', @@ -154,6 +155,10 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize $title .= Carbon::parse($conditions['starttime'])->format('Ymd') . '-' . Carbon::parse($conditions['endtime'])->format('Ymd'); } + if (($conditions = $this->conditions) && $conditions['month']) { + $title .= Carbon::parse($conditions['month'])->format('Ym'); + } + return $title ?? '列表'; } diff --git a/app/Domains/Virtual/Exports/FlowPoolExport.php b/app/Domains/Virtual/Exports/FlowPoolExport.php new file mode 100644 index 00000000..a74d746e --- /dev/null +++ b/app/Domains/Virtual/Exports/FlowPoolExport.php @@ -0,0 +1,87 @@ +conditions = $conditions; + parent::__construct(); + } + + public function collection() + { + $this->conditions['limit'] = 0; + + $list = app(FlowPoolService::class)->index($this->conditions)->map(function ($item) { + return collect([ + 'id' => $item->id, + 'name' => $item->name, + 'carrier_operator_name' => $item->carrier_operator_name, + 'shared_name' => $item->shared_name, + 'company_name' => $item->company_name, + 'minimum_flows' => $item->minimum_flows, + 'excess_flows' => $item->excess_flows, + 'minimum_price' => $item->minimum_price, + 'excess_price' => $item->excess_price, + 'members' => $item->members, + 'total_price' => $item->total_price, + ]); + }); + + $list->push([ + '总计', + '', + '', + '', + '', + '', + '', + array_sum($list->pluck('minimum_price')->toArray()) ?: 0, + array_sum($list->pluck('excess_price')->toArray()) ?: 0, + array_sum($list->pluck('members')->toArray()) ?: 0, + array_sum($list->pluck('total_price')->toArray()) ?: 0, + ]); + + return $list; + } + + public function headings(): array + { + return [ + 'ID', + '名称', + '运营商', + '共享类型', + '客户名称', + '保底流量', + '超出流量', + '保底收入(元)', + '超出收入(元)', + '收费用户数', + '总收入(元)', + ]; + } + + /** + * @return array + */ + public function columnFormats(): array + { + return [ + 'H' => NumberFormat::FORMAT_NUMBER_00, + 'I' => NumberFormat::FORMAT_NUMBER_00, + 'K' => NumberFormat::FORMAT_NUMBER_00, + ]; + } +} diff --git a/app/Domains/Virtual/Http/Controllers/FlowPoolController.php b/app/Domains/Virtual/Http/Controllers/FlowPoolController.php index 7b733f7a..b33eb3e4 100644 --- a/app/Domains/Virtual/Http/Controllers/FlowPoolController.php +++ b/app/Domains/Virtual/Http/Controllers/FlowPoolController.php @@ -10,6 +10,8 @@ use App\Exceptions\NotExistException; use Illuminate\Support\Facades\Validator; use App\Models\Virtual\OrderCardPartition; use App\Exceptions\InvalidArgumentException; +use App\Domains\Export\Services\ExportService; +use App\Domains\Virtual\Exports\FlowPoolExport; use App\Domains\Virtual\Imports\FlowCardImport; use App\Domains\Virtual\Services\ProductService; use App\Domains\Virtual\Services\FlowPoolService; @@ -175,7 +177,7 @@ class FlowPoolController extends Controller $cards->map(function ($item) { $item->product_name = ProductService::load($item->product_id)['name']; }); - }else{ + } else { $cards = collect(); } @@ -201,10 +203,21 @@ class FlowPoolController extends Controller 'news' => $card['total'], ]; } else { + $chunk = $settings[$card['product_id']]['cards']; + + foreach ($chunk as &$value) { + $value['flow_range'][0] = sprintf('%.02f', $value['flow_range'][0]/1024); + $value['flow_range'][1] = sprintf('%.02f', $value['flow_range'][1]/1024); + } + + $settings[$card['product_id']]['cards'] = $chunk; + $settings[$card['product_id']]['news'] = $card['total'] - array_sum(array_pluck($settings[$card['product_id']]['cards'], 'counts')); } } + $flowPoolData['total_flows'] = sprintf('%.02f', $flowPoolData['total_flows']/1024); + return res(['flowPool' => $flowPool, 'settings' => array_values($settings), 'total' => array_sum(array_pluck($cards, 'total')), 'total_flows' => $flowPoolData['total_flows']], '数据设置'); } @@ -214,4 +227,24 @@ class FlowPoolController extends Controller return res($res, '设置成功'); } + + /** + * 导出. + * + * @return \Illuminate\Http\Response + */ + public function export() + { + $conditions = $this->request->except(['page', 'limit']); + $conditions['limit'] = 0; + + try { + $export = new FlowPoolExport($conditions); + $url = ExportService::store($export, $disk = 'public'); + } catch (\Exception $e) { + throw $e; + } + + return res($url, '导出成功', 201); + } } diff --git a/app/Domains/Virtual/Routes/api.php b/app/Domains/Virtual/Routes/api.php index 8a5d0cb9..1e2cc83f 100644 --- a/app/Domains/Virtual/Routes/api.php +++ b/app/Domains/Virtual/Routes/api.php @@ -57,6 +57,7 @@ $router->group(['prefix' => 'virtual', 'as' => 'virtual', 'middleware' => ['admi $router->get('/flow-pools/real', ['as' => 'flow-pools.real', 'uses' => 'FlowPoolController@real']); $router->get('/flow-pools/products', ['as' => 'flow-pools.products', 'uses' => 'FlowPoolController@products']); $router->get('/flow-pools/index', ['as' => 'flow-pools.index', 'uses' => 'FlowPoolController@index']); + $router->get('/flow-pools/export', ['as' => 'flow-pools.export', 'uses' => 'FlowPoolController@export']); $router->get('/flow-pools/show', ['as' => 'flow-pools.show', 'uses' => 'FlowPoolController@show']); $router->post('/flow-pools/create', ['as' => 'flow-pools.create', 'uses' => 'FlowPoolController@create']); $router->post('/flow-pools/update/{id}', ['as' => 'flow-pools.update', 'uses' => 'FlowPoolController@update']); diff --git a/app/Domains/Virtual/Services/FlowPoolService.php b/app/Domains/Virtual/Services/FlowPoolService.php index 980f5bef..f8c47aed 100644 --- a/app/Domains/Virtual/Services/FlowPoolService.php +++ b/app/Domains/Virtual/Services/FlowPoolService.php @@ -123,6 +123,90 @@ class FlowPoolService extends Service $flowPools = $this->transformer($flowPools, $month); + $table = app(FlowPoolMonth::class)->getTable() . '_' . $month->format('Ym'); + + $select = [ + "$table.sim", + 'product_id', + 'pool_id', + 'kilobyte', + 'cards.virtual_activated_at' + ]; + + $flows = app(FlowPoolMonth::class)->setTable($table)->leftJoin('cards', 'cards.sim', '=', "{$table}.sim")->select($select) + ->whereIn('pool_id', $flowPools->pluck('id')->toArray())->get()->groupBy('pool_id'); + + // 流量统计 + $flowPools->map(function ($flowPool) use ($month, $flows) { + $monthInt = $month->format('Ym'); + $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; + } + + $setting = $flowPool->settings->where('start_at', '<=', $month)->where('end_at', '>=', $month)->first(); + + // 无计费规则 + if (!$setting) { + $flowPool->members = $flowArry->count(); + $flowPool->minimum_flows = $this->humanFlows(0); + $flowPool->excess_flows = $this->humanFlows(array_sum($flowArry->pluck('kilobyte'))); + $flowPool->minimum_price = sprintf('%.02f', 0); + $flowPool->excess_price = sprintf('%.02f', 0); + $flowPool->total_price = sprintf('%.02f', 0); + return $flowPool; + } + + $gradient = $setting['gradient'] * pow(1024, $setting['gradient_unit']); + $first_month_price = $setting['first_month_price'] / pow(1024, $setting['gradient_unit'] + 1); + $other_month_price = $setting['other_month_price'] / pow(1024, $setting['gradient_unit'] + 1); + + $minimum_settings = array_keyBy($setting['minimum_settings'], 'product_id'); + + $flowGroup = $flowArry->groupBy('product_id'); + + $flowPool->members = $flowArry->count(); + + $minimum_flows = 0; + $excess_flows = 0; + $minimum_price = 0; + $excess_price = 0; + + foreach ($flowGroup as $product_id => $flowItems) { + $minimum_setting = $minimum_settings[$product_id]; + + foreach ($flowItems as $item) { + $itemMinimumFlows = $minimum_setting['flows'] ?? 0; + $minimum_flows += $itemMinimumFlows; + $minimum_price += $minimum_setting['price'] ?? 0; + + $excess_flow = $item->kilobyte < $itemMinimumFlows ? 0 : $item->kilobyte - $itemMinimumFlows; + $excess_flows += ceil($excess_flow / $gradient) * $gradient; + $price = (date('Ym', strtotime($item->virtual_activated_at)) == $monthInt) ? $first_month_price : $other_month_price; + $excess_price += $excess_flow * $price; + } + } + + $flowPool->minimum_flows = $this->humanFlows($minimum_flows); + $flowPool->excess_flows = $this->humanFlows($excess_flows); + $flowPool->minimum_price = sprintf('%.02f', $minimum_price / 100); + $flowPool->excess_price = sprintf('%.02f', $excess_price / 100); + $flowPool->total_price = sprintf('%.02f', ($minimum_price + $excess_price) / 100); + + return $flowPool; + }); + + $flowPools->map(function ($item) { + $item = $this->transformerSettings($item); + }); return $flowPools; } @@ -203,6 +287,23 @@ class FlowPoolService extends Service { $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['first_month_price'] = intval($attributes['first_month_price'] * 100); + $attributes['other_month_price'] = intval($attributes['other_month_price'] * 100); + $attributes['gradient'] = intval($attributes['gradient'] * 1024); + + if (!is_array($attributes['minimum_settings'])) { + throw new InvalidArgumentException('保底流量设置必须为数组'); + } + + $minimum_settings = $attributes['minimum_settings']; + + foreach ($minimum_settings as &$item) { + $item = array_only($item, ['product_id', 'price', 'flows']); + $item['price'] = intval($item['price'] * 100); + $item['flows'] = intval($item['flows'] * 1024); + } + + $attributes['minimum_settings'] = $minimum_settings; if ($attributes['start_at'] > $attributes['end_at']) { throw new InvalidArgumentException('开始时间必须小于结束时间'); @@ -327,9 +428,13 @@ class FlowPoolService extends Service public function flows(array $conditions) { $conditions = array_only($conditions, ['pool_id', 'month', 'total_flows', 'settings']); + $conditions['total_flows'] = intval($conditions['total_flows'] * 1024); $settings = $conditions['settings']; + $min_total_flows = 0; + $max_total_flows = 0; + foreach ($settings as &$setting) { $setting = array_only($setting, ['product_id', 'product_name', 'total', 'cards']); @@ -337,13 +442,22 @@ class FlowPoolService extends Service foreach ($cards as &$card) { $card = array_only($card, ['counts', 'flow_range']); + $card['flow_range'][0] = intval($card['flow_range'][0] * 1024); + $card['flow_range'][1] = intval($card['flow_range'][1] * 1024); + $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'], 'product_id'); @@ -363,7 +477,11 @@ class FlowPoolService extends Service ])->withConditions([ 'month' => $month, 'product_id' => array_keys($settings), - ])->get()->groupBy('product_id')->toArray(); + ])->get(); + + $allTotal = $cards->count(); + + $cards = $cards->groupBy('product_id')->toArray(); $data = []; @@ -381,19 +499,23 @@ class FlowPoolService extends Service $offset = 0; foreach ($setting['cards'] as $cardSetting) { $simArray = array_slice($array, $offset, $cardSetting['counts']); + $offset += $cardSetting['counts']; - $range_start = intval($cardSetting['flow_range'][0] * 1024); - $range_end = intval($cardSetting['flow_range'][1] * 1024); + $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) { $kilobyte = rand($range_start, $range_end); - $data[] = [ + $dataItems[] = [ 'month' => $month->format('Ym'), 'sim' => $sim['sim'], 'product_id' => $product_id, @@ -401,6 +523,18 @@ class FlowPoolService extends Service 'kilobyte' => $kilobyte, ]; } + + $itemKilobyte = array_sum(array_pluck($dataItems, 'kilobyte')); + + if ($itemKilobyte) { + $k = $flows/$itemKilobyte; + + foreach ($dataItems as &$value) { + $value['kilobyte'] = round($value['kilobyte'] * $k); + } + } + + $data = array_merge($data, $dataItems); } } @@ -417,7 +551,7 @@ class FlowPoolService extends Service FlowPoolData::updateOrCreate(array_only($flowPoolData, ['pool_id', 'month']), $flowPoolData); - DB::transaction(function () use ($table, $data) { + DB::transaction(function () use ($table, $data, $pool_id) { try { app(FlowPoolMonth::class)->setTable($table)->where('pool_id', $pool_id)->delete(); @@ -471,6 +605,8 @@ class FlowPoolService extends Service $cards = new LengthAwarePaginator($cards, $total, $limit); + $flowPool = $this->transformerSettings($flowPool); + return ['flowPool' => $flowPool, 'cards' => $cards]; } @@ -510,31 +646,30 @@ class FlowPoolService extends Service $item->products = $products; - if ($settings = $allSettings[$item->id]) { foreach ($settings as $setting) { if ($setting->start_at <= $month && $setting->end_at >= $month) { $setting_status = 1; - $minimum_settings = $setting->minimum_settings ?? []; + $minimum_settings = array_keyBy($setting->minimum_settings ?? [], 'product_id'); foreach ($products as $product) { - if (!in_array($product['product_id'], array_pluck($minimum_settings, 'product_id'))) { - $setting_status = 0; - - $minimum_settings[] = [ + if (!isset($minimum_settings[$product['product_id']])) { + $minimum_settings[$product['product_id']] = [ 'product_id' => $product['product_id'], - 'product_name' => ProductService::load($value)['name'], + 'product_name' => $product['product_name'], 'flows' => 0, 'price' => 0 ]; + } else { + $minimum_settings[$product['product_id']]['product_name'] = $product['product_name']; } } - $setting->minimum_settings = $minimum_settings; + $setting->minimum_settings = array_values($minimum_settings); } } } else { - $settings = []; + $settings = collect(); $setting_status = 0; } @@ -545,6 +680,35 @@ class FlowPoolService extends Service return $flowPools; } + /** + * 流量池格式转化 + * + * @param mixed $item + * @return void + */ + public static function transformerSettings($item) + { + $settings = $item->settings; + + foreach ($settings as &$setting) { + $setting['first_month_price'] = sprintf('%.02f', $setting['first_month_price']/100); + $setting['other_month_price'] = sprintf('%.02f', $setting['other_month_price']/100); + $setting['gradient'] = sprintf('%.02f', $setting['gradient']/1024); + $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']/1024); + } + + $setting['minimum_settings'] = $minimum_settings; + } + + $item->settings = $settings; + + return $item; + } + public static function sn($id) { return sprintf('FP%011d', $id); @@ -558,4 +722,11 @@ class FlowPoolService extends Service return self::$pools[$id]; } + + public static function humanFlows($int) + { + return human_filesize($int, 2, ['unit' => 'KB', 'min' => 'MB', 'max' => 'PB']); + } + + } diff --git a/app/Domains/Virtual/Services/tempCodeRunnerFile.php b/app/Domains/Virtual/Services/tempCodeRunnerFile.php new file mode 100644 index 00000000..572fac0a --- /dev/null +++ b/app/Domains/Virtual/Services/tempCodeRunnerFile.php @@ -0,0 +1,6 @@ +hasOne(Card::class, 'sim', 'sim'); + } } diff --git a/frontend/src/api/virtual/flow_pools.js b/frontend/src/api/virtual/flow_pools.js index 8a74d007..b84641d8 100644 --- a/frontend/src/api/virtual/flow_pools.js +++ b/frontend/src/api/virtual/flow_pools.js @@ -31,6 +31,17 @@ export function index(data) { }); } +/** + * [exportList 流量池列表导出] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function exportList(data) { + return service.get('api/virtual/flow-pools/export', { + params: data + }); +} + /** * [show 流量池详情] * @param {[type]} id [description] diff --git a/frontend/src/views/virtual/flow_pools/index.vue b/frontend/src/views/virtual/flow_pools/index.vue index 6d7b2459..6c6cccaa 100644 --- a/frontend/src/views/virtual/flow_pools/index.vue +++ b/frontend/src/views/virtual/flow_pools/index.vue @@ -26,6 +26,10 @@
{{message}}
\n\t选择图标
\n\n{{CONFIG.title}}
\n \n