562 lines
20 KiB
PHP
562 lines
20 KiB
PHP
<?php
|
|
namespace App\Domains\Virtual\Services;
|
|
|
|
use App\Dicts;
|
|
use Carbon\Carbon;
|
|
use App\Core\Service;
|
|
use Illuminate\Validation\Rule;
|
|
use App\Models\Virtual\FlowPool;
|
|
use Illuminate\Support\Facades\DB;
|
|
use App\Models\Virtual\FlowPoolData;
|
|
use App\Exceptions\NotExistException;
|
|
use App\Models\Virtual\FlowPoolMonth;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use App\Exceptions\NotAllowedException;
|
|
use App\Models\Virtual\FlowPoolSetting;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use App\Exceptions\InvalidArgumentException;
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
use App\Domains\Virtual\Services\CompanyService;
|
|
use App\Domains\Virtual\Services\PackageService;
|
|
use App\Domains\Virtual\Repositories\ProductRepository;
|
|
use App\Domains\Virtual\Repositories\FlowPoolRepository;
|
|
use App\Domains\Virtual\Repositories\OrderCardRepository;
|
|
use App\Domains\Virtual\Repositories\FlowPoolSettingRepository;
|
|
use App\Domains\Virtual\Repositories\OrderCardPartitionRepository;
|
|
use App\Domains\Real\Repositories\FlowPoolRepository as RealFlowPoolRepository;
|
|
|
|
class FlowPoolService extends Service
|
|
{
|
|
protected $realFlowPoolRepository;
|
|
protected $flowPoolRepository;
|
|
protected $flowPoolPackageRepository;
|
|
protected $flowPoolSettingRepository;
|
|
|
|
protected static $pools;
|
|
|
|
/**
|
|
* 构造函数
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct(
|
|
RealFlowPoolRepository $realFlowPoolRepository,
|
|
FlowPoolRepository $flowPoolRepository,
|
|
FlowPoolSettingRepository $flowPoolSettingRepository
|
|
) {
|
|
$this->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 products(array $conditions = [])
|
|
{
|
|
$used = $this->flowPoolRepository->select(['id', 'product_ids'])->get();
|
|
|
|
$array = [];
|
|
|
|
foreach ($used as $item) {
|
|
$item['product_ids'] = $item['product_ids'] ?? [];
|
|
|
|
foreach ($item['product_ids'] as $key => $value) {
|
|
$array[$value] = $item['id'];
|
|
}
|
|
}
|
|
|
|
$products = app(ProductService::class)->index($conditions);
|
|
|
|
$products = $products->where('status', 0)->where('flowed', 1);
|
|
|
|
$products->map(function ($item) use ($array) {
|
|
$item->virtual_pool_id = $array[$item['id']] ?? 0;
|
|
});
|
|
|
|
return $products->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);
|
|
|
|
|
|
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', 'product_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' => ['required', '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');
|
|
|
|
if ($attributes['start_at'] > $attributes['end_at']) {
|
|
throw new InvalidArgumentException('开始时间必须小于结束时间');
|
|
}
|
|
|
|
$rule = [
|
|
'pool_id' => ['required', Rule::exists($this->flowPoolRepository->getTable(), 'id')],
|
|
'first_month_price' => ['required', 'integer'],
|
|
'other_month_price' => ['required', 'integer'],
|
|
'gradient' => ['required', 'integer'],
|
|
'gradient_unit' => ['required', 'in:0,1'],
|
|
];
|
|
|
|
$message = [
|
|
'pool_id.required' => '请输入流量池ID',
|
|
'first_month_price.required' => '请输入首月单价',
|
|
'other_month_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'][*]['product_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']);
|
|
|
|
$settings = $conditions['settings'];
|
|
|
|
foreach ($settings as &$setting) {
|
|
$setting = array_only($setting, ['product_id', 'product_name', 'total', 'cards']);
|
|
|
|
$cards = $setting['cards'];
|
|
|
|
foreach ($cards as &$card) {
|
|
$card = array_only($card, ['counts', 'flow_range']);
|
|
}
|
|
|
|
$setting['cards'] = $cards;
|
|
}
|
|
|
|
$conditions['settings'] = $settings;
|
|
|
|
$pool_id = $conditions['pool_id'];
|
|
$month = Carbon::parse($conditions['month']);
|
|
$settings = array_keyBy($conditions['settings'], 'product_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('当前月份计费规则未设置或套餐保底流量未设置');
|
|
}
|
|
|
|
$cards = app(OrderCardPartitionRepository::class)->select([
|
|
DB::raw('distinct sim as sim, product_id'),
|
|
])->withConditions([
|
|
'month' => $month,
|
|
'product_id' => array_keys($settings),
|
|
])->get()->groupBy('product_id')->toArray();
|
|
|
|
$data = [];
|
|
|
|
foreach ($cards as $product_id => $array) {
|
|
if (!$setting = $settings[$product_id]) {
|
|
throw new NotAllowedException("定价 ID:{$product_id} 未设置");
|
|
}
|
|
|
|
$total = array_sum(array_pluck($setting['cards'], 'counts'));
|
|
|
|
if ($total !== count($array)) {
|
|
throw new NotAllowedException("定价 ID:{$product_id} 数量设置不匹配");
|
|
}
|
|
|
|
$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);
|
|
|
|
if ($range_end < $range_start) {
|
|
throw new NotAllowedException("流量范围结束必须不小于开始");
|
|
}
|
|
|
|
foreach ($simArray as $sim) {
|
|
$kilobyte = rand($range_start, $range_end);
|
|
|
|
$data[] = [
|
|
'month' => $month->format('Ym'),
|
|
'sim' => $sim['sim'],
|
|
'product_id' => $product_id,
|
|
'pool_id' => $pool_id,
|
|
'kilobyte' => $kilobyte,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
$table = app(FlowPoolMonth::class)->getTable() . '_' . $month->format('Ym');
|
|
|
|
if (!Schema::hasTable($table)) {
|
|
Schema::table(app(FlowPoolMonth::class)->getTable(), function ($blueprint) use ($table, $month) {
|
|
$blueprint->addPartition($table, 'list', [$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) {
|
|
try {
|
|
app(FlowPoolMonth::class)->setTable($table)->where('pool_id', $pool_id)->delete();
|
|
|
|
foreach (array_chunk($data, 10000) as $chunk) {
|
|
app(FlowPoolMonth::class)->setTable($table)->insert($chunk);
|
|
}
|
|
} catch (\Exception $e) {
|
|
throw $e;
|
|
}
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 流量池卡详情
|
|
*
|
|
* @param array $conditions
|
|
* @return void
|
|
*/
|
|
public function show(array $conditions = [])
|
|
{
|
|
$limit = $conditions['limit'] ?? 20;
|
|
$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', 'product_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->forPage($page, $limit)->get();
|
|
} else {
|
|
$cards = collect();
|
|
}
|
|
|
|
$cards->map(function ($item) use ($minimum_flows) {
|
|
$item->package_name = PackageService::load($item->product_id)['name'];
|
|
$item->minimum_flows = $minimum_flows[$item->product_id] ?? 0;
|
|
});
|
|
|
|
$cards = new LengthAwarePaginator($cards, $total, $limit);
|
|
|
|
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->product_ids = empty($item->product_ids) ? [] : $item->product_ids;
|
|
$item->status = $item->end_at ? 1 : 0;
|
|
|
|
$products = [];
|
|
|
|
foreach ($item->product_ids as $value) {
|
|
$products[] = [
|
|
'product_id' => $value,
|
|
'product_name' => ProductService::load($value)['name'],
|
|
];
|
|
}
|
|
|
|
$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 ?? [];
|
|
|
|
foreach ($products as $product) {
|
|
if (!in_array($product['product_id'], array_pluck($minimum_settings, 'product_id'))) {
|
|
$setting_status = 0;
|
|
|
|
$minimum_settings[] = [
|
|
'product_id' => $product['product_id'],
|
|
'product_name' => ProductService::load($value)['name'],
|
|
'flows' => 0,
|
|
'price' => 0
|
|
];
|
|
}
|
|
}
|
|
|
|
$setting->minimum_settings = $minimum_settings;
|
|
}
|
|
}
|
|
} else {
|
|
$settings = [];
|
|
$setting_status = 0;
|
|
}
|
|
|
|
$item->settings = $settings;
|
|
$item->setting_status = $setting_status;
|
|
});
|
|
|
|
return $flowPools;
|
|
}
|
|
|
|
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];
|
|
}
|
|
}
|