定价优化

This commit is contained in:
邓皓元 2019-03-28 18:12:39 +08:00
parent 450b833a70
commit 179a0cb37c
65 changed files with 783 additions and 489 deletions

View File

@ -45,7 +45,8 @@ class CardController extends Controller
->whereIn('sim', $cards->pluck('sim')->toArray())->groupBy('sim')->get()->keyBy('sim');
$time = Carbon::now()->format('Y-m-d H:i:s');
$packages = $this->orderCardPartitionRepository->selectRaw('distinct on (sim) sim, package_id, product_id')
$packages = $this->orderCardPartitionRepository->selectRaw('distinct on (sim) sim, package_id')
->whereIn('type', $conditions['type'])
->whereIn('sim', $cards->pluck('sim')->toArray())
->where('service_start_at', '<=', $time)->orderBy('sim')->orderBy('service_start_at', 'desc')
@ -54,11 +55,17 @@ class CardController extends Controller
$cards->map(function ($item) use ($services, $packages) {
$item->service_start_at = $services[$item->sim]['service_start_at'] ?? '';
$item->service_end_at = $services[$item->sim]['service_end_at'] ?? '';
$item->product_id = $packages[$item->sim]['product_id'] ?? 0;
$item->package_id = $packages[$item->sim]['package_id'] ?? 0;
$item->package_name = PackageService::load($item->package_id)['name'] ?? '';
$item->price = ProductService::load($item->product_id)['price'] ?? 0;
$item->renew_price = ProductService::load($item->product_id)['renew_price'] ?? 0;
$package = PackageService::load($item->package_id);
$item->package_name = $package['name'] ?? '';
$product = ProductService::load($package['type'], $this->account->company_id, $item->package_id);
$item->price = $product['price'] ?? 9999;
$item->renew_price = $product['renew_price'] ?? 9999;
});
return res($cards, '卡列表', 201);

View File

@ -53,7 +53,6 @@ class OrderService extends Service
DB::raw("array_to_string(array_agg(id), ',') as order_id"),
'company_id',
'package_id',
'product_id',
'unit_price',
'pay_channel',
DB::raw('SUM(counts) as counts'),
@ -61,12 +60,11 @@ class OrderService extends Service
];
$orders = $this->orderRepository->select($select)->withConditions($conditions)->applyConditions()
->groupBy(['company_id', 'package_id', 'product_id', 'unit_price', 'pay_channel'])->paginate($conditions['limit']);
->groupBy(['company_id', 'package_id', 'unit_price', 'pay_channel'])->paginate($conditions['limit']);
$order_ids = [];
foreach ($orders as $order) {
$ids = str_to_array($order->order_id);
$order_ids = array_merge($order_ids, str_to_array($order->order_id));
}

View File

@ -54,6 +54,7 @@ class PackageSync extends Command
'effect_months' => 0,
'delay_months' => 0,
'description' => $item->description ?: '',
'flowed' => strpos($item->name, 'S') === 0,
'created_at' => date('Y-m-d H:i:s', $item->create_time),
'updated_at' => date('Y-m-d H:i:s', $item->update_time),
'deleted_at' => $item->del ? date('Y-m-d H:i:s', $item->update_time) : null,
@ -84,6 +85,7 @@ class PackageSync extends Command
'effect_months' => ($item->buy_enabled == 10) ? 0 : 1,
'delay_months' => ($item->service_extend == 10) ? 1 : 0,
'description' => $item->package_text ?: '',
'flowed' => strpos($item->package_name, 'S') === 0,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'deleted_at' => $item->del ? date('Y-m-d H:i:s') : null,
@ -114,6 +116,7 @@ class PackageSync extends Command
'effect_months' => ($item->buy_enabled == 10) ? 0 : 1,
'delay_months' => ($item->service_extend == 10) ? 1 : 0,
'description' => $item->package_text ?: '',
'flowed' => strpos($item->package_name, 'S') === 0,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'deleted_at' => $item->del ? date('Y-m-d H:i:s') : null,

View File

@ -2,12 +2,7 @@
namespace App\Domains\Virtual\Commands\Sync;
use App\Models\Virtual\Product;
use Illuminate\Support\Facades\DB;
use App\Domains\Virtual\Services\CommonService;
use App\Domains\Virtual\Services\ProductService;
use App\Domains\Virtual\Repositories\CompanyRepository;
use App\Domains\Virtual\Repositories\PackageRepository;
use App\Domains\Virtual\Repositories\ProductRepository;
class ProductSync extends Command
@ -16,62 +11,9 @@ class ProductSync extends Command
protected $description = '同步VD定价';
protected static $types = [11 => 0, 13 => 0, 14 => 2, 15 => 3];
public function handle()
{
DB::table('virtual_products')->truncate();
$packages = app(PackageRepository::class)->withTrashed()->get()->keyBy('sn')->toArray();
$companies = app(CompanyRepository::class)->withTrashed()->get()->keyBy('sn');
$fields = ['company', 'content', 'order_account', 'type'];
$list = DB::connection('vd_old')->table('ckb_custom_handle_log')->select($fields)
->whereIn('type', [11, 13, 14, 15])
->groupBy($fields)->get();
$products = [];
foreach ($list as $key => $value) {
$value = (array)$value;
if (!$package = $packages[$value['content']]) {
throw new \Exception('套餐不存在');
}
if (!$company = $companies[CommonService::stringifyCompanyId($value['company'])]) {
throw new \Exception('企业不存在');
}
$price = intval($value['order_account'] * 100);
$sn = ProductService::sn($package['sn'], $company['id'], $price);
$products[$sn] = [
'type' => self::$types[$value['type']],
'sn' => $sn,
'company_id' => $company['id'],
'package_id' => $package['id'],
'name' => $package['name'] . ' ' . $value['order_account'],
'price' => $price,
'renew_price' => $price,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'deleted_at' => null,
];
}
Product::upsert($products, ['sn', 'deleted_at'], []);
$subSql = 'SELECT DISTINCT ON (company_id, package_id) id FROM virtual_products ORDER BY company_id, package_id, updated_at DESC';
Product::whereRaw("id not in ($subSql)")->update(['status' => 1]);
DB::statement("select gen_products();");
app(ProductRepository::class)->forgetCached();
}
}

View File

@ -35,7 +35,7 @@ class FlowPoolExportDetailExport extends AbstractExport implements FromQuery, Wi
$table = app(FlowPoolMonth::class)->getTable() . '_' . $month->format('Ym');
return app(FlowPoolMonth::class)->setTable($table)->where('pool_id', $pool_id)->select(['product_id', 'sim', 'kilobyte']);
return app(FlowPoolMonth::class)->setTable($table)->where('pool_id', $pool_id)->select(['package_id', 'sim', 'kilobyte']);
}
public function headings(): array
@ -68,15 +68,15 @@ class FlowPoolExportDetailExport extends AbstractExport implements FromQuery, Wi
$minimum_settings = $flowPool['settings'][0]['minimum_settings'] ?? [];
$minimum_flows = array_pluck($minimum_settings, 'flows', 'product_id');
$minimum_flows = array_pluck($minimum_settings, 'flows', 'package_id');
$array = [];
foreach ($rows as $item) {
$array[] = [
$item['sim'],
PackageService::load($item['product_id'])['name'],
FlowPoolService::humanFlows($minimum_flows[$item['product_id']] ?? 0),
PackageService::load($item['package_id'])['name'],
FlowPoolService::humanFlows($minimum_flows[$item['package_id']] ?? 0),
FlowPoolService::humanFlows($item['kilobyte']),
];
}

View File

@ -28,7 +28,7 @@ class CompanyController extends Controller
*/
public function index()
{
$conditions = [];
$conditions = $this->request->all();
$conditions['limit'] = $this->request->get('limit', 20);
$companies = $this->companyService->index($conditions);

View File

@ -12,10 +12,11 @@ use Illuminate\Support\Facades\Schema;
use App\Exceptions\NotAllowedException;
use App\Domains\Export\Services\ExportService;
use App\Domains\Virtual\Exports\FlowPoolExport;
use App\Domains\Virtual\Services\PackageService;
use App\Domains\Virtual\Services\ProductService;
use App\Domains\Virtual\Services\FlowPoolService;
use App\Domains\Virtual\Repositories\FlowPoolRepository;
use App\Domains\Virtual\Repositories\FlowPoolRepository;
use App\Domains\Virtual\Exports\FlowPoolExportDetailExport;
use App\Domains\Virtual\Repositories\OrderCardPartitionRepository;
@ -45,14 +46,14 @@ class FlowPoolController extends Controller
}
/**
* 后向定价列表
* 后向套餐列表
*
* @return void
*/
public function products()
public function packages()
{
$list = $this->flowPoolService->products();
return res($list, '后向定价列表', 201);
$list = $this->flowPoolService->packages();
return res($list, '后向套餐列表', 201);
}
/**
@ -194,21 +195,22 @@ class FlowPoolController extends Controller
$minimum_settings = $flowPool['settings'][0]['minimum_settings'] ?? [];
$product_ids = array_pluck($minimum_settings, 'product_id');
$package_ids = array_pluck($minimum_settings, 'package_id');
$conditions = [
'type' => [0, 1, 2],
'month' => $month,
'product_id' => $product_ids
'company_id' => $flowPool->company_id,
'package_id' => $package_ids
];
$cards = $repository->select([
'product_id',
'package_id',
DB::raw('count(distinct sim) as total'),
])->withConditions($conditions)->groupBy('product_id')->get();
])->withConditions($conditions)->groupBy('package_id')->get();
$cards->map(function ($item) {
$item->product_name = ProductService::load($item->product_id)['name'];
$item->package_name = PackageService::load($item->package_id)['name'];
});
} else {
$cards = collect();
@ -225,27 +227,27 @@ class FlowPoolController extends Controller
];
}
$settings = array_keyBy($flowPoolData['settings'], 'product_id');
$settings = array_keyBy($flowPoolData['settings'], 'package_id');
foreach ($cards as $card) {
if (!isset($settings[$card['product_id']])) {
$settings[$card['product_id']] = [
'product_id' => $card['product_id'],
'product_name' => $card['product_name'],
if (!isset($settings[$card['package_id']])) {
$settings[$card['package_id']] = [
'package_id' => $card['package_id'],
'package_name' => $card['package_name'],
'total' => $card['total'],
'news' => $card['total'],
];
} else {
$chunk = $settings[$card['product_id']]['cards'];
$chunk = $settings[$card['package_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['package_id']]['cards'] = $chunk;
$settings[$card['product_id']]['news'] = $card['total'] - array_sum(array_pluck($settings[$card['product_id']]['cards'], 'counts'));
$settings[$card['package_id']]['news'] = $card['total'] - array_sum(array_pluck($settings[$card['package_id']]['cards'], 'counts'));
}
}

View File

@ -36,6 +36,20 @@ class ProductController extends Controller
return res($products, '定价列表', 201);
}
/**
* 历史定价.
*
* @return \Illuminate\Http\Response
*/
public function history()
{
$conditions = $this->request->all();
$products = $this->productService->history($conditions);
return res($products, '历史定价', 201);
}
/**
* 创建.

View File

@ -3,6 +3,7 @@
namespace App\Domains\Virtual\Repositories\Concerns;
use Illuminate\Support\Carbon;
use App\Models\Virtual\OrderCardPartition;
trait OrderCardConcern
{
@ -41,11 +42,8 @@ trait OrderCardConcern
}
if (isset($conditions['package_id'])) {
$query->where('package_id', $conditions['package_id']);
}
if (isset($conditions['product_id'])) {
$query->whereIn('product_id', $conditions['product_id']);
$conditions['package_id'] = array_wrap($conditions['package_id']);
$query->whereIn('package_id', $conditions['package_id']);
}
if (isset($conditions['carrier_operator'])) {
@ -108,6 +106,56 @@ trait OrderCardConcern
// $query->whereRaw("timelines_index(id, sim) @> '{{$conditions['month']}}'");
}
if (isset($conditions['activated_starttime']) && isset($conditions['activated_endtime'])) {
$query->whereHas('card', function ($relation) use ($conditions) {
$relation->where('activated_at', '>=', Carbon::parse($conditions['activated_starttime']))
->where('activated_at', '<=', Carbon::parse($conditions['activated_endtime']));
});
}
if (isset($conditions['card_status'])) {
switch ($conditions['card_status']) {
case 0:
$query->whereNull('service_start_at')->whereHas('card', function ($relation) {
$relation->whereNull('cancelled_at');
});
break;
case 1:
$query->whereExists(function ($subQuery) {
$table = with(new OrderCardPartition)->getTable();
$subQuery->from($table)->select('sim')
->where('service_start_at', '<=', date('Y-m-d H:i:s'))
->where('service_end_at', '>=', date('Y-m-d H:i:s'))
->whereRaw("sim = {$table}.sim");
})->whereHas('card', function ($relation) {
$relation->whereNull('cancelled_at');
});
break;
case 2:
$query->whereNotNull('service_start_at')->whereNotIn('sim', function ($subQuery) {
$table = with(new OrderCardPartition)->getTable();
$subQuery->from($table)->select('sim')
->where('service_start_at', '<=', date('Y-m-d H:i:s'))
->where('service_end_at', '>=', date('Y-m-d H:i:s'));
})->whereHas('card', function ($relation) {
$relation->whereNull('cancelled_at');
});
break;
case 3:
$query->whereHas('card', function ($relation) {
$relation->whereNotNull('cancelled_at');
});
break;
default:
# code...
break;
}
}
if (isset($conditions['sn'])) {
$query->whereHas('order', function ($relation) use ($conditions) {
$relation->where('sn', 'like', "%{$conditions['sn']}%");

View File

@ -76,7 +76,15 @@ class ProductRepository extends Repository
}
if (isset($conditions['status'])) {
$query->where('status', $conditions['status']);
$query->where(function ($subQuery) use ($conditions) {
$subQuery->where('status', $conditions['status']);
if ($conditions['status'] == 1) {
$subQuery->orWhereHas('package', function ($relation) use ($conditions) {
$relation->withTrashed()->where('status', $conditions['status']);
});
}
});
}
if (!empty($conditions['name'])) {

View File

@ -38,6 +38,7 @@ $router->group(['prefix' => 'virtual', 'as' => 'virtual', 'middleware' => ['admi
// 企业定价管理
$router->get('/products/index', ['as' => 'products.index', 'uses' => 'ProductController@index']);
$router->get('/products/history', ['as' => 'products.history', 'uses' => 'ProductController@history']);
$router->post('/products/create', ['as' => 'products.create', 'uses' => 'ProductController@create']);
$router->post('/products/update/{id}', ['as' => 'products.update', 'uses' => 'ProductController@update']);
$router->post('/products/destroy', ['as' => 'products.destroy', 'uses' => 'ProductController@destroy']);
@ -59,7 +60,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/packages', ['as' => 'flow-pools.packages', 'uses' => 'FlowPoolController@packages']);
$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']);

View File

@ -38,6 +38,10 @@ class CompanyService extends Service
$companies = $this->companyRepository->withConditions($conditions)
->applyConditions()->paginate($limit);
$companies->map(function ($item) {
$item->status = $item->deleted_at ? 2 : $item->status;
});
return $companies;
}

View File

@ -81,29 +81,33 @@ class FlowPoolService extends Service
*
* @return void
*/
public function products(array $conditions = [])
public function packages(array $conditions = [])
{
$used = $this->flowPoolRepository->select(['id', 'product_ids'])->get();
$used = $this->flowPoolRepository->select(['id', 'company_id', 'package_ids'])->get();
$array = [];
foreach ($used as $item) {
$item['product_ids'] = $item['product_ids'] ?? [];
$item['package_ids'] = $item['package_ids'] ?? [];
foreach ($item['product_ids'] as $key => $value) {
$array[$value] = $item['id'];
foreach ($item['package_ids'] as $key => $value) {
if (isset($array[$value])) {
array_push($array[$value], $item['company_id']);
}else{
$array[$value] =[$item['company_id']];
}
}
}
$products = app(ProductService::class)->index($conditions);
$packages = app(PackageService::class)->index(['limit' => 0]);
$products = $products->where('status', 0)->where('flowed', 1);
$packages = $packages->where('status', 0)->where('flowed', 1);
$products->map(function ($item) use ($array) {
$item->virtual_pool_id = $array[$item['id']] ?? 0;
$packages->map(function ($item) use ($array) {
$item->company_ids = $array[$item['id']] ?? [];
});
return $products->values();
return $packages->values();
}
/**
@ -126,7 +130,7 @@ class FlowPoolService extends Service
$select = [
"$table.sim",
'product_id',
'package_id',
'pool_id',
'kilobyte',
'cards.virtual_activated_at'
@ -172,9 +176,9 @@ class FlowPoolService extends Service
$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');
$minimum_settings = array_keyBy($setting['minimum_settings'], 'package_id');
$flowGroup = $flowArry->groupBy('product_id');
$flowGroup = $flowArry->groupBy('package_id');
$flowPool->members = $flowArry->count();
@ -183,8 +187,8 @@ class FlowPoolService extends Service
$minimum_price = 0;
$excess_price = 0;
foreach ($flowGroup as $product_id => $flowItems) {
$minimum_setting = $minimum_settings[$product_id];
foreach ($flowGroup as $package_id => $flowItems) {
$minimum_setting = $minimum_settings[$package_id];
foreach ($flowItems as $item) {
$itemMinimumFlows = $minimum_setting['flows'] ?? 0;
@ -225,12 +229,12 @@ class FlowPoolService extends Service
$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']);
$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' => ['required', 'in:1,2'],
'shared' => ['in:1,2'],
];
$message = [
@ -239,7 +243,7 @@ class FlowPoolService extends Service
'name.unique' => '流量池名称已经被其他用户所使用',
'carrier_operator.required' => '请选择运营商',
'carrier_operator.in' => '运营商不合法',
'shared.required' => '请选择共享类型',
// 'shared.required' => '请选择共享类型',
'shared.in' => '共享类型不合法',
];
@ -301,7 +305,7 @@ class FlowPoolService extends Service
$minimum_settings = $attributes['minimum_settings'];
foreach ($minimum_settings as &$item) {
$item = array_only($item, ['product_id', 'price', 'flows']);
$item = array_only($item, ['package_id', 'price', 'flows']);
$item['price'] = intval($item['price'] * 100);
$item['flows'] = intval($item['flows'] * 1024);
}
@ -420,7 +424,7 @@ class FlowPoolService extends Service
* $conditions['month'];
* $conditions['total_flows'];
* $conditions['settings'];
* $conditions['settings'][*]['product_id'];
* $conditions['settings'][*]['package_id'];
* $conditions['settings'][*]['total'];
* $conditions['settings'][*]['cards'];
* $conditions['settings'][*]['cards'][*]['counts'];
@ -439,7 +443,7 @@ class FlowPoolService extends Service
$max_total_flows = 0;
foreach ($settings as &$setting) {
$setting = array_only($setting, ['product_id', 'product_name', 'total', 'cards']);
$setting = array_only($setting, ['package_id', 'package_name', 'total', 'cards']);
$cards = $setting['cards'];
@ -463,7 +467,7 @@ class FlowPoolService extends Service
$pool_id = $conditions['pool_id'];
$month = Carbon::parse($conditions['month']);
$settings = array_keyBy($conditions['settings'], 'product_id');
$settings = array_keyBy($conditions['settings'], 'package_id');
if (!$flowPool = $this->flowPoolRepository->find($pool_id)) {
throw new NotExistException('流量池不存在或已删除');
@ -475,28 +479,27 @@ class FlowPoolService extends Service
throw new NotAllowedException('当前月份计费规则未设置或套餐保底流量未设置');
}
$cards = OrderCardPartition::select([
DB::raw('distinct sim as sim, product_id'),
])->withConditions([
$cards = app(OrderCardPartitionRepository::class)->selectRaw('distinct sim as sim, package_id')->withConditions([
'month' => $month,
'product_id' => array_keys($settings),
'company_id' => $flowPool->company_id,
'package_id' => array_keys($settings),
])->get();
$allTotal = $cards->count();
$cards = $cards->groupBy('product_id')->toArray();
$cards = $cards->groupBy('package_id')->toArray();
$data = [];
foreach ($cards as $product_id => $array) {
if (!$setting = $settings[$product_id]) {
throw new NotAllowedException("定价 ID:{$product_id} 未设置");
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:{$product_id} 数量设置不匹配");
throw new NotAllowedException("套餐 ID:{$package_id} 数量设置不匹配");
}
$offset = 0;
@ -521,7 +524,7 @@ class FlowPoolService extends Service
$dataItems[] = [
'month' => $month->format('Ym'),
'sim' => $sim['sim'],
'product_id' => $product_id,
'package_id' => $package_id,
'pool_id' => $pool_id,
'kilobyte' => $kilobyte,
];
@ -590,22 +593,22 @@ class FlowPoolService extends Service
$minimum_settings = $flowPool['settings'][0]['minimum_settings'] ?? [];
$minimum_flows = array_pluck($minimum_settings, 'flows', 'product_id');
$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(['product_id', 'sim', 'kilobyte'])->forPage($page, $limit)->get();
$cards = $query->select(['package_id', 'sim', 'kilobyte'])->forPage($page, $limit)->get();
} else {
$cards = collect();
}
$cards->map(function ($item) use ($minimum_flows) {
$item->product_name = PackageService::load($item->product_id)['name'];
$item->package_name = PackageService::load($item->package_id)['name'];
$item->kilobyte = $this->humanFlows($item->kilobyte);
$item->minimum_flows = $this->humanFlows($minimum_flows[$item->product_id] ?? 0);
$item->minimum_flows = $this->humanFlows($minimum_flows[$item->package_id] ?? 0);
});
$cards = new LengthAwarePaginator($cards, $total, $limit);
@ -637,51 +640,51 @@ class FlowPoolService extends Service
$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->package_ids = empty($item->package_ids) ? [] : $item->package_ids;
$item->status = $item->end_at ? 1 : 0;
$products = [];
$packages = [];
foreach ($item->product_ids as $value) {
$products[] = [
'product_id' => $value,
'type' => ProductService::load($value)['type'],
'product_name' => ProductService::load($value)['name'],
foreach ($item->package_ids as $value) {
$packages[] = [
'package_id' => $value,
'type' => PackageService::load($value)['type'],
'package_name' => PackageService::load($value)['name'],
];
}
$item->products = $products;
$item->packages = $packages;
$products = array_keyBy($products, 'product_id');
$packages = array_keyBy($packages, 'package_id');
if ($settings = $allSettings[$item->id]) {
foreach ($settings as $setting) {
$minimum_settings = array_keyBy($setting->minimum_settings ?? [], 'product_id');
$minimum_settings = array_keyBy($setting->minimum_settings ?? [], 'package_id');
if ($setting->start_at <= $month && $setting->end_at >= $month) {
$setting_status = 1;
foreach ($products as $product) {
if (!isset($minimum_settings[$product['product_id']])) {
foreach ($packages as $package) {
if (!isset($minimum_settings[$package['package_id']])) {
$setting_status = 0;
$minimum_settings[$product['product_id']] = [
'product_id' => $product['product_id'],
'product_name' => ProductService::load($product['product_id'])['name'],
$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 $product_id => $minimum_setting) {
if (!isset($products[$product_id])) {
unset($minimum_settings[$product_id]);
foreach ($minimum_settings as $package_id => $minimum_setting) {
if (!isset($packages[$package_id])) {
unset($minimum_settings[$package_id]);
}
}
}
foreach ($minimum_settings as $product_id => $minimum_setting) {
$minimum_settings[$product_id]['product_name'] = ProductService::load($product_id)['name'];
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);

View File

@ -147,20 +147,18 @@ class OrderService extends Service
$attributes['transaction_no'] = $attributes['transaction_no'] ?: $this->generateTransactionNo($attributes['pay_channel']);
if ($attributes['company_id'] && $attributes['package_id'] && isset($attributes['unit_price'])) {
$product = ProductService::getProduct($attributes['company_id'], $attributes['package_id'], $attributes['unit_price']);
$product = ProductService::getProduct($attributes['type'], $attributes['company_id'], $attributes['package_id'], $attributes['unit_price']);
} elseif ($attributes['product_id']) {
$product = app(ProductRepository::class)->find($attributes['product_id']);
$attributes['unit_price'] = $product['price'];
}
if (!$product) {
throw new NotExistException('请选择套餐');
}
$attributes['product_id'] = $product->id;
$rule['type'][] = 'required';
$rule['company_id'][] = 'required';
$rule['product_id'][] = 'required';
$rule['counts'][] = 'required';
$rule['pay_channel'][] = 'required';
@ -287,7 +285,6 @@ class OrderService extends Service
'order_id' => $node['id'],
'company_id' => $node['company_id'],
'package_id' => $node['package_id'],
'product_id' => $node['product_id'],
'unit_price' => $node['unit_price'],
'created_at' => $node['order_at'],
'updated_at' => date('Y-m-d H:i:s'),
@ -353,6 +350,10 @@ class OrderService extends Service
$res = $this->orderCardPartitionRepository->selectRaw('distinct on (sim) *')
->withConditions($conditions)->orderBy('sim')->orderBy('created_at', 'desc')->get();
$res->map(function ($item) {
$item->groupKey = $item->company_id . '_' . $item->package_id;
});
$errors = [];
foreach ($res as $card) {
if ($card->company_id !== $attributes['company_id']) {
@ -380,7 +381,7 @@ class OrderService extends Service
$order_at = date('Y-m-d H:i:s');
if ($attributes['type'] === 1) {
foreach ($res->groupBy('product_id') as $product_id => $value) {
foreach ($res->groupBy('groupKey') as $key => $value) {
$orderId = ++$maxId;
$realCards = array_filter($attributes['cards'], function ($item) use ($value) {
@ -389,14 +390,13 @@ class OrderService extends Service
$counts = array_sum(array_pluck($realCards, 'counts'));
$orders[$product_id] = [
$orders[$key] = [
'id' => $orderId,
'sn' => $this->generateSn(),
'source' => 0,
'type' => $attributes['type'],
'company_id' => $attributes['company_id'],
'package_id' => $value[0]['package_id'],
'product_id' => $product_id,
'transaction_no' => $this->generateTransactionNo($attributes['pay_channel']),
'pay_channel' => $attributes['pay_channel'],
'unit_price' => $value[0]['unit_price'],
@ -419,7 +419,6 @@ class OrderService extends Service
'order_id' => $orderId,
'company_id' => $attributes['company_id'],
'package_id' => $card['package_id'],
'product_id' => $product_id,
'counts' => $cardCounts[$card->sim],
'unit_price' => $card->unit_price,
'created_at' => $order_at,
@ -447,7 +446,6 @@ class OrderService extends Service
'type' => $attributes['type'],
'company_id' => $attributes['company_id'],
'package_id' => $product['package_id'],
'product_id' => $attributes['product_id'],
'transaction_no' => $this->generateTransactionNo($attributes['pay_channel']),
'pay_channel' => $attributes['pay_channel'],
'unit_price' => $product['price'],
@ -470,7 +468,6 @@ class OrderService extends Service
'order_id' => $orderId,
'company_id' => $attributes['company_id'],
'package_id' => $product['package_id'],
'product_id' => $attributes['product_id'],
'counts' => $cardCounts[$card->sim],
'unit_price' => $product['price'],
'transaction_status' => 0,

View File

@ -45,6 +45,7 @@ class PackageService extends Service
$item->cost_price = sprintf('%.02f', $item->cost_price/100);
$item->guide_price = sprintf('%.02f', $item->guide_price/100);
$item->carrier_operator_name = $carrierOperators[$item->carrier_operator];
$item->status = $item->deleted_at ? 2 : $item->status;
});
return $packages;
@ -152,7 +153,7 @@ class PackageService extends Service
{
if (!self::$packages) {
self::$packages = app(PackageRepository::class)
->select(['id', 'sn', 'name', 'carrier_operator', 'flows', 'service_months', 'status'])
->select(['id', 'type', 'sn', 'name', 'carrier_operator', 'flows', 'service_months', 'status'])
->withTrashed()->get()->keyBy('id');
}

View File

@ -5,6 +5,7 @@ use App\Dicts;
use App\Core\Service;
use App\Models\Virtual\Product;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
use App\Exceptions\NotExistException;
use App\Exceptions\NotAllowedException;
use Illuminate\Support\Facades\Validator;
@ -53,6 +54,27 @@ class ProductService extends Service
$item->status = $item['package']['status'] ? 1 : $item->status;
$item->carrier_operator = $item['package']['carrier_operator'];
$item->carrier_operator_name = $carrierOperators[$item->carrier_operator] ?? '未知';
$item->status = $item->deleted_at ? 2 : $item->status;
});
return $list;
}
/**
* 获取历史定价列表
*
* @param array $conditions
* @return Collection
*/
public function history(array $conditions = [])
{
$select = ['id', 'company_id', 'package_id', 'price', 'renew_price', 'created_at', 'updated_at'];
$list = $this->productRepository->select($select)->withTrashed()->withConditions($conditions)->orderBy('created_at', 'desc')->get();
$list->map(function ($item) {
$item->price = sprintf('%.02f', $item->price/100);
$item->renew_price = sprintf('%.02f', $item->renew_price/100);
});
return $list;
@ -66,8 +88,13 @@ class ProductService extends Service
*/
public function store(array $attributes = [])
{
$attributes['price'] = intval($attributes['price'] * 100);
$attributes['renew_price'] = intval($attributes['renew_price'] * 100);
if (isset($attributes['price'])) {
$attributes['price'] = intval($attributes['price'] * 100);
}
if (isset($attributes['renew_price'])) {
$attributes['renew_price'] = intval($attributes['renew_price'] * 100);
}
$rule = [
'name' => ['required', 'between:2,32', Rule::unique($this->productRepository->getTable(), 'name')->ignore($attributes['id'])->whereNUll('deleted_at')->where('company_id', $attributes['company_id'])],
@ -92,39 +119,46 @@ class ProductService extends Service
throw new NotExistException('套餐不存在或已删除');
}
$attributes['sn'] = self::sn($package['sn'], $attributes['company_id'], $attributes['price']);
DB::beginTransaction();
// 禁用相同套餐的其他定价
if ($attributes['status']) {
$this->productRepository->where('package_id', $attributes['package_id'])->update(['status' => 1]);
}
try {
// 上一次定价
$newest = $this->productRepository->where('company_id', $attributes['company_id'])
->where('package_id', $attributes['package_id'])->first();
if (!$attributes['id']) {
if ($this->productRepository->where('sn', $attributes['sn'])->count()) {
throw new NotAllowedException('已存在相同定价,请勿重复添加');
}
$node = $this->productRepository->create($attributes);
}
if ($newest && (isset($attributes['price']) && $newest->price !== $attributes['price']) || (isset($attributes['renew_price']) && $newest->renew_price !== $attributes['renew_price'])) {
unset($attributes['id']);
$newest->delete();
if ($attributes['id']) {
if (!$node = $this->productRepository->find($attributes['id'])) {
throw new NotExistException('定价不存在或已删除');
$attributes['renew_price'] = $attributes['renew_price'] ?? $newest->renew_price;
$attributes['price'] = $attributes['price'] ?? $newest->price;
}
if ($attributes['status'] == 0) {
$node->load(['package:id,status']);
if ($node['package']['status'] === 1) {
throw new NotAllowedException('套餐已被禁用,不能启用');
if ($attributes['id']) {
if (!$node = $this->productRepository->find($attributes['id'])) {
throw new NotExistException('定价不存在或已删除');
}
if ($attributes['status'] == 0) {
$node->load(['package:id,status']);
if ($node['package']['status'] === 1) {
throw new NotAllowedException('套餐已被禁用,不能启用');
}
}
$this->productRepository->setModel($node)->update($attributes);
return $node;
} else {
$attributes['sn'] = self::sn($package['sn'], $attributes['company_id']);
$node = $this->productRepository->create($attributes);
}
if ($this->productRepository->where('sn', $attributes['sn'])->where('id', '<>', $attributes['id'])->count()) {
throw new NotAllowedException('已存在相同定价,请核对后重试');
}
$this->productRepository->setModel($node)->update($attributes);
DB::commit();
} catch (\Exception $e) {
DB::rollback();
throw $e;
}
return $node;
@ -144,44 +178,49 @@ class ProductService extends Service
return true;
}
public static function load($id)
public static function load($type, $companyId, $packageId)
{
$type = $type === 1 ? 0 : $type;
if (!self::$products) {
self::$products = app(ProductRepository::class)->select(['id', 'type', 'name', 'company_id', 'package_id', 'price', 'renew_price', 'status'])
->withTrashed()->get()->keyBy('id');
self::$products = app(ProductRepository::class)
->select(['id', 'type', 'name', 'company_id', 'package_id', 'price', 'renew_price', 'status'])
->get();
}
return self::$products[$id];
return self::$products->filter(function ($item) use ($type, $companyId, $packageId) {
return $item->type === $type && $item->company_id === $companyId && $item->package_id === $packageId;
})->first();
}
public static function sn($packageSn, $companyId, $price)
public static function sn($packageSn, $companyId)
{
return strtoupper($packageSn . '_' . $companyId . '_' . $price);
return strtoupper($packageSn . '_' . $companyId);
}
/**
* 获取定价
*
* @param string $sn
* @param int $type
* @param int $companyId
* @param int $packageId
* @param int $price
* @return void
*/
public static function getProduct($companyId, $packageId, $price)
public static function getProduct($type, $companyId, $packageId, $price)
{
$package = PackageService::load($packageId);
$product = app(ProductRepository::class)->withConditions([
'company_id' => $companyId,
'package_id' => $packageId,
'price' => $price,
])->first();
$product = self::load($type, $companyId, $packageId);
if (!$product) {
$field = $type === 1 ? 'renew_price' : 'price';
if (!$product || $product[$field] !== $price) {
$product = app(ProductService::class)->store([
'name' => $package['name'] . ' ' . $price,
'name' => $package['name'],
'company_id' => $companyId,
'package_id' => $package['id'],
'price' => $price/100,
'renew_price' => $price/100,
$field => $price/100,
]);
}

View File

@ -39,7 +39,7 @@ class FlowPool extends Model
protected $table = 'real_flow_pools';
protected $casts = [
'product_ids' => 'array',
'package_ids' => 'array',
'real_pool_ids' => 'array',
];
}

View File

@ -17,7 +17,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int $flows 流量值 -1不限流量 单位MB
* @property int $carrier_operator 运营商(0:联通 1:移动 2:电信)
* @property int $shared 共享类型 0:未知 1纵向共享 2横向共享
* @property array|null $product_ids 包含定价
* @property array|null $package_ids 包含套餐
* @property array|null $real_pool_ids RD流量池ID
* @property string|null $remark 流量池备注
* @property string|null $start_at 开始时间
@ -60,7 +60,7 @@ class FlowPool extends Model
protected $table = 'virtual_flow_pools';
protected $casts = [
'product_ids' => 'array',
'package_ids' => 'array',
'real_pool_ids' => 'array',
];

View File

@ -11,7 +11,7 @@ use App\Models\Card\Card;
* @property int $id 订单ID
* @property int $month 月份
* @property int $sim SIM号
* @property int $product_id 定价ID
* @property int $package_id 套餐ID
* @property int $pool_id 流量池ID
* @property int $kilobyte 使用流量 单位KB
* @property-read \App\Models\Card\Card $card

View File

@ -15,7 +15,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int $type 订单类型0:基础订单 1:套餐续费 2:续费包 3:加油包 4:可选包 5:附加包)
* @property int $company_id 企业ID
* @property int $package_id 套餐ID
* @property int $product_id 定价ID
* @property string $transaction_no 交易流水号
* @property string $pay_channel 支付频道
* @property int $unit_price 单价
@ -96,7 +95,6 @@ class Order extends Model
'type',
'company_id',
'package_id',
'product_id',
'transaction_no',
'pay_channel',
'unit_price',

View File

@ -50,7 +50,6 @@ use App\Models\Virtual\Relations\OrderRelations;
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderCard withTrashed()
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderCard withoutTrashed()
* @mixin \Eloquent
* @property int $product_id
* @property int $unit_price
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderCard whereProductId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderCard whereUnitPrice($value)

View File

@ -47,7 +47,6 @@ use App\Models\Virtual\Scopes\OrderCardPartitionNotRefundedScope;
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderCardPartition withTrashed()
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderCardPartition withoutTrashed()
* @mixin \Eloquent
* @property int $product_id 定价ID
* @property int $unit_price 单价
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderCardPartition whereProductId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderCardPartition whereUnitPrice($value)

View File

@ -46,7 +46,6 @@ use App\Models\Virtual\Relations\OrderRelations;
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderFlowPackageCards withTrashed()
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderFlowPackageCards withoutTrashed()
* @mixin \Eloquent
* @property int $product_id
* @property int $unit_price
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderFlowPackageCards whereProductId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderFlowPackageCards whereUnitPrice($value)

View File

@ -46,7 +46,6 @@ use App\Models\Virtual\Relations\OrderRelations;
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderRenewalCard withTrashed()
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderRenewalCard withoutTrashed()
* @mixin \Eloquent
* @property int $product_id
* @property int $unit_price
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderRenewalCard whereProductId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderRenewalCard whereUnitPrice($value)

View File

@ -46,7 +46,6 @@ use App\Models\Virtual\Relations\OrderRelations;
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderRenewalPackageCard withTrashed()
* @method static \Illuminate\Database\Query\Builder|\App\Models\Virtual\OrderRenewalPackageCard withoutTrashed()
* @mixin \Eloquent
* @property int $product_id
* @property int $unit_price
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderRenewalPackageCard whereProductId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Virtual\OrderRenewalPackageCard whereUnitPrice($value)

View File

@ -83,6 +83,7 @@ class Package extends PackageBase
'effect_months',
'delay_months',
'description',
'flowed',
'status',
];
}

View File

@ -37,6 +37,7 @@ class CreateVirtualPackagesTable extends Migration
$table->tinyInteger('effect_months')->unsigned()->default(0)->comment('生效延迟周期(月)');
$table->tinyInteger('delay_months')->unsigned()->default(0)->comment('服务延长周期(月)');
$table->text('description')->nullable()->comment('描述');
$table->tinyInteger('flowed')->unsigned()->default(0)->comment('是否是后向流量池套餐 0:否 1:是');
$table->tinyInteger('status')->unsigned()->default(0)->comment('状态 0:正常 1:禁用');
$table->timestamps();
$table->softDeletes();

View File

@ -27,7 +27,6 @@ class CreateVirtualProductsTable extends Migration
$table->integer('price')->unsigned()->default(0)->comment('价格');
$table->integer('renew_price')->unsigned()->default(0)->comment('续费价格');
$table->text('remark')->nullable()->comment('备注');
$table->tinyInteger('flowed')->unsigned()->default(0)->comment('是否是后向流量池套餐 0:否 1:是');
$table->tinyInteger('status')->unsigned()->default(0)->comment('状态 0:正常 1:禁用');
$table->timestamps();
$table->softDeletes();

View File

@ -24,7 +24,6 @@ class CreateVirtualOrdersTable extends Migration
$table->tinyInteger('type')->unsigned()->default(0)->comment('订单类型0:基础订单 1:套餐续费 2续费包 3:加油包 4:可选包 5:附加包)');
$table->integer('company_id')->unsigned()->default(0)->comment("企业ID");
$table->integer('package_id')->unsigned()->default(0)->comment('套餐ID');
$table->integer('product_id')->unsigned()->default(0)->comment('定价ID');
$table->string('transaction_no', 64)->default('')->comment('交易流水号');
$table->string('pay_channel', 20)->default('')->comment('支付频道');
$table->integer('unit_price')->unsigned()->default(0)->comment('单价');

View File

@ -19,8 +19,7 @@ class CreateVirtualOrderCardsTables extends Migration
return;
}
DB::unprepared(File::get(__DIR__ . '/create_virtaul_order_cards_func.pgsql'));
// DB::unprepared(File::get(__DIR__ . '/create_virtual_order_cards_table.pgsql'));
DB::unprepared(File::get(__DIR__ . '/create_virtual_order_cards_func.pgsql'));
Schema::create('virtual_order_cards_partition', function (Blueprint $table) {
$table->increments('id')->comment('自增ID');
@ -29,7 +28,6 @@ class CreateVirtualOrderCardsTables extends Migration
$table->integer('order_id')->unsigned()->default(0)->comment('订单ID');
$table->integer('company_id')->unsigned()->default(0)->comment('企业ID');
$table->integer('package_id')->unsigned()->default(0)->comment('套餐ID');
$table->integer('product_id')->unsigned()->default(0)->comment('定价ID');
$table->integer('counts')->unsigned()->default(0)->comment('数量');
$table->integer('unit_price')->unsigned()->default(0)->comment('单价');
$table->timestamp('service_start_at')->nullable()->comment('服务开始时间');
@ -51,14 +49,12 @@ class CreateVirtualOrderCardsTables extends Migration
Schema::table('virtual_order_cards', function (Blueprint $table) {
$table->unique(['sim', 'order_id', 'deleted_at']);
$table->index(['type', 'product_id']);
$table->index(['type', 'company_id', 'package_id', 'unit_price']);
$table->index(['service_start_at', 'service_end_at']);
});
Schema::table('virtual_order_renewal_cards', function (Blueprint $table) {
$table->unique(['sim', 'order_id', 'deleted_at']);
$table->index(['type', 'product_id']);
$table->index(['type', 'company_id', 'package_id', 'unit_price']);
$table->index(['service_start_at', 'service_end_at']);
});
@ -71,7 +67,6 @@ class CreateVirtualOrderCardsTables extends Migration
Schema::table('virtual_order_flows_package_cards', function (Blueprint $table) {
$table->unique(['sim', 'order_id', 'deleted_at']);
$table->index(['type', 'product_id']);
$table->index(['type', 'company_id', 'package_id', 'unit_price']);
$table->index(['service_start_at', 'service_end_at']);
});

View File

@ -41,7 +41,7 @@ class CreateFlowPoolTables extends Migration
$table->integer('flows')->default(255)->comment('流量值 -1不限流量 单位MB');
$table->tinyInteger('carrier_operator')->unsigned()->default(255)->comment('运营商(0:联通 1:移动 2:电信)');
$table->tinyInteger('shared')->unsigned()->default(0)->comment('共享类型 0:未知 1纵向共享 2横向共享');
$table->text('product_ids')->nullable()->comment('包含定价');
$table->text('package_ids')->nullable()->comment('包含套餐');
$table->text('real_pool_ids')->nullable()->comment('RD流量池ID');
$table->text('remark')->nullable()->comment('流量池备注');
$table->timestamp('start_at')->nullable()->comment('开始时间');
@ -95,7 +95,7 @@ class CreateFlowPoolTables extends Migration
$table->increments('id')->comment('订单ID');
$table->integer('month')->unsigned()->default(0)->comment('月份');
$table->bigInteger('sim')->unsigned()->default(0)->comment('SIM号');
$table->integer('product_id')->unsigned()->default(0)->comment('定价ID');
$table->integer('package_id')->unsigned()->default(0)->comment('套餐ID');
$table->integer('pool_id')->unsigned()->default(0)->comment('流量池ID');
$table->integer('kilobyte')->unsigned()->default(0)->comment('使用流量 单位KB');
$table->comment('VD卡月流量');

View File

@ -1,3 +1,17 @@
CREATE OR REPLACE VIEW real_virtual_relations AS
SELECT
r.type as type,
r.company_id as real_company_id,
r.package_id as real_package_id,
v.company_id as virtual_company_id,
v.package_id as virtual_package_id,
COUNT(*) as times,
MAX(v.created_at) as updated_at
FROM real_order_cards_partition as r
JOIN virtual_order_cards as v ON v.id = r.virtual_order_id
WHERE r.virtual_order_id <> 0
GROUP BY r.type,r.company_id,r.package_id,v.company_id,v.package_id
CREATE OR REPLACE FUNCTION GET_TIMELINES(INT8[])
RETURNS TABLE
(
@ -183,3 +197,40 @@ BEGIN
END;
$$ LANGUAGE plpgsql
SET synchronous_commit TO OFF;
CREATE OR REPLACE FUNCTION GEN_PRODUCTS()
RETURNS void
AS
$$
DECLARE
order_row RECORD;
product_row RECORD;
package_row RECORD;
_ids INT4[];
BEGIN
TRUNCATE virtual_products RESTART IDENTITY;
FOR order_row IN SELECT * FROM virtual_orders ORDER BY created_at
LOOP
SELECT * INTO package_row FROM virtual_packages WHERE id = order_row.package_id LIMIT 1;
SELECT * INTO product_row FROM virtual_products WHERE company_id = order_row.company_id AND package_id = order_row.package_id AND deleted_at IS NULL LIMIT 1;
IF NOT FOUND THEN
INSERT INTO virtual_products (type, sn, "name", company_id, package_id, price, renew_price, remark, status, created_at, updated_at)
VALUES (CASE order_row.type WHEN 1 THEN 0 ELSE order_row.type END, package_row.sn || '_' || order_row.company_id, package_row.name, order_row.company_id, order_row.package_id, order_row.unit_price, order_row.unit_price, package_row.description, 0, order_row.created_at, order_row.created_at);
ELSE
IF order_row.type = 2 THEN
IF order_row.unit_price <> product_row.renew_price THEN
UPDATE virtual_products SET updated_at = order_row.created_at, deleted_at = order_row.created_at + (product_row.id || ' second')::INTERVAL WHERE id = product_row.id;
INSERT INTO virtual_products (type, sn, "name", company_id, package_id, price, renew_price, remark, status, created_at, updated_at)
VALUES (product_row.type, product_row.sn, product_row.name, product_row.company_id, product_row.package_id, product_row.price, order_row.unit_price, product_row.remark, 0, order_row.created_at, order_row.created_at);
END IF;
ELSE
IF order_row.unit_price <> product_row.price THEN
UPDATE virtual_products SET updated_at = order_row.created_at, deleted_at = order_row.created_at + (product_row.id || ' second')::INTERVAL WHERE id = product_row.id;
INSERT INTO virtual_products (type, sn, "name", company_id, package_id, price, renew_price, remark, status, created_at, updated_at)
VALUES (product_row.type, product_row.sn, product_row.name, product_row.company_id, product_row.package_id, order_row.unit_price, product_row.renew_price, product_row.remark, 0, order_row.created_at, order_row.created_at);
END IF;
END IF;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;

View File

@ -1,47 +0,0 @@
-- 服务周期索引
CREATE INDEX "virtual_order_cards_timelines_index" ON "virtual_order_cards" USING GIN (TIMELINES_INDEX(id, sim));
CREATE INDEX "virtual_order_renewal_cards_timelines_index" ON "virtual_order_renewal_cards" USING GIN (TIMELINES_INDEX(id, sim));
CREATE INDEX "virtual_order_renewal_package_cards_timelines_index" ON "virtual_order_renewal_package_cards" USING GIN (TIMELINES_INDEX(id, sim));
CREATE INDEX "virtual_order_flows_package_cards_index" ON "virtual_order_flows_package_cards" USING GIN (TIMELINES_INDEX(id, sim));
-- 增删改卡表时更新索引
CREATE OR REPLACE FUNCTION REINDEX_TIMELINES ()
RETURNS TRIGGER
AS $$
BEGIN
IF (TG_OP = 'INSERT') THEN
UPDATE vd.virtual_order_cards_partition SET updated_at = CURRENT_TIMESTAMP WHERE virtual_order_cards_partition.sim = NEW.sim;
ELSIF (TG_OP = 'UPDATE') THEN
UPDATE vd.virtual_order_cards_partition SET updated_at = CURRENT_TIMESTAMP WHERE virtual_order_cards_partition.sim = NEW.sim;
ELSIF (TG_OP = 'DELETE') THEN
UPDATE vd.virtual_order_cards_partition SET updated_at = CURRENT_TIMESTAMP WHERE virtual_order_cards_partition.sim = OLD.sim;
END IF;
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS REINDEX_TIMELINES ON cards;
CREATE TRIGGER "reindex_timelines" AFTER INSERT
OR UPDATE OF "virtual_activated_at"
OR DELETE
ON cards
FOR EACH ROW
EXECUTE PROCEDURE REINDEX_TIMELINES ();
CREATE OR REPLACE VIEW real_virtual_relations AS
SELECT
r.type as type,
r.company_id as real_company_id,
r.package_id as real_package_id,
v.company_id as virtual_company_id,
v.package_id as virtual_package_id,
COUNT(*) as times,
MAX(v.created_at) as updated_at
FROM real_order_cards_partition as r
JOIN virtual_order_cards as v ON v.id = r.virtual_order_id
WHERE r.virtual_order_id <> 0
GROUP BY r.type,r.company_id,r.package_id,v.company_id,v.package_id

View File

@ -1,12 +1,6 @@
-- 从旧平台迁移数据
-- 第一步:建立外部服务器 ckb_custom 与 ckb_custom_handle_log
CREATE EXTENSION IF NOT EXISTS mysql_fdw;
CREATE SERVER IF NOT EXISTS vd_old FOREIGN DATA WRAPPER mysql_fdw OPTIONS (host 'rm-bp1i7dsf6fi1bc5y2o.mysql.rds.aliyuncs.com', port '3306');
CREATE USER MAPPING IF NOT EXISTS FOR root SERVER vd_old OPTIONS (username 'vduser', password 'fxft@123');
DROP FOREIGN TABLE IF EXISTS ckb_custom;
DROP FOREIGN TABLE IF EXISTS ckb_custom_handle_log;
IMPORT FOREIGN SCHEMA "vd-new" limit to ("ckb_custom","ckb_custom_handle_log") FROM SERVER vd_old INTO vd_old;
-- 第一步:复制旧平台 ckb_custom 与 ckb_custom_handle_log 两表至新数据库
-- 第二步:建立物化视图
CREATE MATERIALIZED VIEW logs AS
@ -48,7 +42,6 @@ CREATE MATERIALIZED VIEW logs AS
END, lpad((virtual_companies.id)::text, 3, '0'::text), lpad((virtual_packages.id)::text, 4, '0'::text), lpad((((ckb_custom_handle_log.order_account)::float * 100))::text, 6, '0'::text)) AS order_sn,
COALESCE(virtual_companies.id, 0) AS company_id,
COALESCE(virtual_packages.id, 0) AS package_id,
COALESCE(min(virtual_products.id), 0) AS product_id,
CASE ckb_custom_handle_log.pay_type
WHEN 10 THEN
'wx'::text
@ -78,11 +71,10 @@ CREATE MATERIALIZED VIEW logs AS
END AS service_end_at,
to_timestamp((ckb_custom_handle_log.create_time)::double precision) AS created_at,
count(*) AS counts
FROM ((((vd_old.ckb_custom_handle_log
LEFT JOIN vd_old.ckb_custom ON (((ckb_custom.custom_no)::text = (ckb_custom_handle_log.custom_no)::text)))
FROM ((((vd.ckb_custom_handle_log
LEFT JOIN vd.ckb_custom ON (((ckb_custom.custom_no)::text = (ckb_custom_handle_log.custom_no)::text)))
LEFT JOIN vd.virtual_companies ON ((((virtual_companies.sn)::TEXT = concat('No', lpad((ckb_custom_handle_log.company)::TEXT, 11, '0'::TEXT))) AND (virtual_companies.deleted_at IS NULL))))
LEFT JOIN vd.virtual_packages ON ((((virtual_packages.sn)::text = (ckb_custom_handle_log.content)::text) AND (virtual_packages.deleted_at IS NULL))))
LEFT JOIN vd.virtual_products ON (((virtual_products.sn)::text = concat(ckb_custom_handle_log.content, '_', virtual_companies.id, '_', ((ckb_custom_handle_log.order_account)::float * 100)))))
WHERE (ckb_custom_handle_log.type = ANY (ARRAY[11, 13, 14, 15]))
GROUP BY
ckb_custom_handle_log.create_time,
@ -113,7 +105,7 @@ INSERT INTO vd.cards (sim, imsi, iccid, carrier_operator, "type", virtual_activa
cancelled_at=excluded.cancelled_at;
-- 第四步:同步订单数据
INSERT INTO vd.virtual_orders ("type", sn, "source", company_id, package_id, product_id, pay_channel, unit_price, counts, total_price, custom_price, order_at, order_status, transaction_status, created_at, updated_at)
INSERT INTO vd.virtual_orders ("type", sn, "source", company_id, package_id, pay_channel, unit_price, counts, total_price, custom_price, order_at, order_status, transaction_status, created_at, updated_at)
(
SELECT
order_type,
@ -121,7 +113,6 @@ INSERT INTO vd.virtual_orders ("type", sn, "source", company_id, package_id, pro
1,
MIN(company_id),
MIN(package_id),
MIN(product_id),
MIN(pay_channel),
MIN(unit_price),
SUM(counts) AS counts,
@ -139,7 +130,7 @@ INSERT INTO vd.virtual_orders ("type", sn, "source", company_id, package_id, pro
) ON CONFLICT (sn, COALESCE(deleted_at::TIMESTAMP, '1970-01-01 08:00:00'::TIMESTAMP)) DO UPDATE SET counts = excluded.counts, total_price = excluded.total_price, custom_price = excluded.custom_price;
-- 第五步:同步订单详情数据
INSERT INTO vd.virtual_order_cards ("type", sim, order_id, company_id, package_id, product_id, counts, unit_price, service_start_at, service_end_at, created_at, updated_at)
INSERT INTO vd.virtual_order_cards ("type", sim, order_id, company_id, package_id, counts, unit_price, service_start_at, service_end_at, created_at, updated_at)
(
SELECT
logs.order_type AS "type",
@ -147,7 +138,6 @@ INSERT INTO vd.virtual_order_cards ("type", sim, order_id, company_id, package_i
virtual_orders.ID AS order_id,
logs.company_id,
logs.package_id,
logs.product_id,
logs.counts,
logs.unit_price,
logs.service_start_at,
@ -161,7 +151,7 @@ INSERT INTO vd.virtual_order_cards ("type", sim, order_id, company_id, package_i
logs.order_type = 0
) ON CONFLICT (sim, order_id, COALESCE(deleted_at::TIMESTAMP, '1970-01-01 08:00:00'::TIMESTAMP)) DO UPDATE SET counts = excluded.counts, service_start_at = excluded.service_start_at, service_end_at = excluded.service_end_at;
INSERT INTO vd.virtual_order_renewal_cards ("type", sim, order_id, company_id, package_id, product_id, counts, unit_price, service_start_at, service_end_at, created_at, updated_at)
INSERT INTO vd.virtual_order_renewal_cards ("type", sim, order_id, company_id, package_id, counts, unit_price, service_start_at, service_end_at, created_at, updated_at)
(
SELECT
logs.order_type AS "type",
@ -169,7 +159,6 @@ INSERT INTO vd.virtual_order_renewal_cards ("type", sim, order_id, company_id, p
virtual_orders.ID AS order_id,
logs.company_id,
logs.package_id,
logs.product_id,
logs.counts,
logs.unit_price,
logs.service_start_at,
@ -183,7 +172,7 @@ INSERT INTO vd.virtual_order_renewal_cards ("type", sim, order_id, company_id, p
logs.order_type = 1
) ON CONFLICT (sim, order_id, COALESCE(deleted_at::TIMESTAMP, '1970-01-01 08:00:00'::TIMESTAMP)) DO UPDATE SET counts = excluded.counts, service_start_at = excluded.service_start_at, service_end_at = excluded.service_end_at;
INSERT INTO vd.virtual_order_renewal_package_cards ("type", sim, order_id, company_id, package_id, product_id, counts, unit_price, service_start_at, service_end_at, created_at, updated_at)
INSERT INTO vd.virtual_order_renewal_package_cards ("type", sim, order_id, company_id, package_id, counts, unit_price, service_start_at, service_end_at, created_at, updated_at)
(
SELECT
logs.order_type AS "type",
@ -191,7 +180,6 @@ INSERT INTO vd.virtual_order_renewal_package_cards ("type", sim, order_id, compa
virtual_orders.ID AS order_id,
logs.company_id,
logs.package_id,
logs.product_id,
logs.counts,
logs.unit_price,
logs.service_start_at,
@ -205,7 +193,7 @@ INSERT INTO vd.virtual_order_renewal_package_cards ("type", sim, order_id, compa
logs.order_type = 2
) ON CONFLICT (sim, order_id, COALESCE(deleted_at::TIMESTAMP, '1970-01-01 08:00:00'::TIMESTAMP)) DO UPDATE SET counts = excluded.counts, service_start_at = excluded.service_start_at, service_end_at = excluded.service_end_at;
INSERT INTO vd.virtual_order_flows_package_cards ("type", sim, order_id, company_id, package_id, product_id, counts, unit_price, service_start_at, service_end_at, created_at, updated_at)
INSERT INTO vd.virtual_order_flows_package_cards ("type", sim, order_id, company_id, package_id, counts, unit_price, service_start_at, service_end_at, created_at, updated_at)
(
SELECT
logs.order_type AS "type",
@ -213,7 +201,6 @@ INSERT INTO vd.virtual_order_flows_package_cards ("type", sim, order_id, company
virtual_orders.ID AS order_id,
logs.company_id,
logs.package_id,
logs.product_id,
logs.counts,
logs.unit_price,
logs.service_start_at,

View File

@ -12,12 +12,12 @@ export function real() {
}
/**
* [products 后向定价列表]
* [packages 后向套餐列表]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function products() {
return service.get('api/virtual/flow-pools/products');
export function packages() {
return service.get('api/virtual/flow-pools/packages');
}
/**

View File

@ -13,6 +13,17 @@ export function index(data) {
});
}
/**
* [history 定价历史]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function history(data) {
return service.get('api/virtual/products/history', {
params: data
});
}
/**
* [create 创建定价]
* @param {[type]} data [description]

View File

@ -30,19 +30,47 @@
<div class="search-wrap" v-show="search.show">
<ul class="handle-wraper">
<li class="handle-item w-250">
<AutoComplete @on-search="handleCompleteCompanies" icon="ios-search" placeholder="企业名称" v-model.trim="params.company_name">
<Option :key="item.id" :value="item.name" v-for="item in completeHandledCompanies">{{ item.name }}</Option>
<Input clearable placeholder="客户编号" v-model.trim="params.id"></Input>
</li>
<li class="handle-item w-250">
<AutoComplete
@on-search="handleCompleteCompanies"
icon="ios-search"
placeholder="企业名称"
v-model.trim="params.company_name"
>
<Option
:key="item.id"
:value="item.name"
v-for="item in completeHandledCompanies"
>{{ item.name }}</Option>
</AutoComplete>
</li>
<li class="handle-item w-250">
<AutoComplete @on-search="handleCompletePackages" icon="ios-search" placeholder="套餐名称" v-model.trim="params.package_name">
<Option :key="item.id" :value="item.name" v-for="item in completeHandledPackages">{{ item.name }}</Option>
<AutoComplete
@on-search="handleCompletePackages"
icon="ios-search"
placeholder="套餐名称"
v-model.trim="params.package_name"
>
<Option
:key="item.id"
:value="item.name"
v-for="item in completeHandledPackages"
>{{ item.name }}</Option>
</AutoComplete>
</li>
<li class="handle-item w-250">
<DatePicker :editable="false" placeholder="创建时间" placement="bottom-start" type="daterange" v-model.trim="params.time"></DatePicker>
<DatePicker
:editable="false"
placeholder="激活时间"
placement="bottom-start"
type="daterange"
v-model.trim="params.activated_time"
></DatePicker>
</li>
</ul>
@ -56,13 +84,28 @@
</li>
<li class="handle-item w-250">
<Input clearable placeholder="客户编号" v-model.trim="params.id"></Input>
<Select clearable placeholder="卡状态" v-model="params.card_status">
<Option :value="0">沉默期</Option>
<Option :value="1">服务期</Option>
<Option :value="2">服务到期</Option>
<Option :value="3">注销期</Option>
</Select>
</li>
<li class="handle-item w-250">
<Input placeholder="SIM" type="textarea" v-model="params.sim"/>
</li>
<li class="handle-item w-250">
<DatePicker
:editable="false"
placeholder="创建时间"
placement="bottom-start"
type="daterange"
v-model.trim="params.time"
></DatePicker>
</li>
<li class="f-r">
<div class="handle-item">
<Button @click="index(1)" ghost type="primary">立即搜索</Button>
@ -80,7 +123,14 @@
</div>
<div class="page-turn-wrap" v-if="list_data">
<Page :current="Number(list_data.current_page)" :page-size="Number(list_data.per_page)" :total="Number(list_data.total)" @on-change="index" show-elevator show-total></Page>
<Page
:current="Number(list_data.current_page)"
:page-size="Number(list_data.per_page)"
:total="Number(list_data.total)"
@on-change="index"
show-elevator
show-total
></Page>
</div>
<ui-detail :data="detailObj.data" :show.sync="detailObj.show"></ui-detail>

View File

@ -13,7 +13,9 @@ export default {
company_name: '',
package_name: '',
carrier_operator: '',
time: []
card_status: '',
time: [],
activated_time: []
},
list_data: null,
detailObj: {
@ -118,6 +120,42 @@ export default {
* @return {[type]} [description]
*/
index(page = 1) {
let params = this.getParams({ page });
this.isShowLoading(true);
API.index(params).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
this.list_data = res.data;
}
}).catch(() => {
this.isShowLoading(false);
});
},
exportExcel() {
let params = this.getParams({ limit: 0 });
this.isShowLoading(true);
API.exportExcel(params).then(res => {
this.isShowLoading(false);
if (res.code === 0) {
if (res.data) {
this.downloadFile(res.data);
} else {
this.$Modal.success({
title: '提示',
content: '当前导出数据量大,已进入后台队列导出模式,请稍后至导出列表查看下载。'
});
}
}
}).catch(() => {
this.isShowLoading(false);
});
},
getParams({ page, limit }) {
let params = Object.assign({
orderBy: 'id',
sortedBy: 'asc'
@ -127,18 +165,17 @@ export default {
params.sim = this.params.sim.split(/[\s|,|;]+/);
}
let data = this.searchDataHandle({}, {
page
}, params);
this.isShowLoading(true);
API.index(data).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
this.list_data = res.data;
}
}).catch(() => {
this.isShowLoading(false);
});
if (this.params.activated_time.length && this.params.activated_time[0] && this.params.activated_time[1]) {
let activated_time = this.parseTime(this.params.activated_time);
params.activated_starttime = activated_time.starttime;
params.activated_endtime = activated_time.endtime;
}
params.activated_time = undefined;
let data = this.searchDataHandle({}, { page, limit }, params);
return data;
},
/**
@ -158,46 +195,13 @@ export default {
resetSearch() {
for (let k in this.params) {
if (k === 'time') {
if (k === 'time' || k === 'activated_time') {
this.params[k] = [];
} else {
this.params[k] = '';
}
}
this.index(1);
},
exportExcel() {
let params = Object.assign({
orderBy: 'id',
sortedBy: 'asc'
}, this.params);
if (this.params.sim) {
params.sim = this.params.sim.split(/[\s|,|;]+/);
}
let data = this.searchDataHandle({}, {
limit: 0
}, params);
this.isShowLoading(true);
API.exportExcel(data).then(res => {
this.isShowLoading(false);
if (res.code === 0) {
if (res.data) {
this.downloadFile(res.data);
} else {
this.$Modal.success({
title: '提示',
content: '当前导出数据量大,已进入后台队列导出模式,请稍后至导出列表查看下载。'
});
}
}
}).catch(() => {
this.isShowLoading(false);
});
}
}
};

View File

@ -27,15 +27,25 @@
<div class="search-wrap" v-show="search.show">
<ul class="handle-wraper">
<li class="handle-item w-250">
<AutoComplete @on-search="handleCompleteCompanies" icon="ios-search" placeholder="请输入企业名称" v-model.trim="params.name">
<Option :key="item.id" :value="item.name" v-for="item in completeHandledCompanies">{{ item.name }}</Option>
<AutoComplete
@on-search="handleCompleteCompanies"
icon="ios-search"
placeholder="请输入企业名称"
v-model.trim="params.name"
>
<Option
:key="item.id"
:value="item.name"
v-for="item in completeHandledCompanies"
>{{ item.name }}</Option>
</AutoComplete>
</li>
<li class="handle-item w-250">
<Select clearable v-model="trashed">
<Option :value="'without'">使用中</Option>
<Option :value="'only'">已删除</Option>
<Select clearable placeholder="套餐状态" v-model="params.status">
<Option :value="0">已启用</Option>
<Option :value="1">已禁用</Option>
<Option :value="2">已删除</Option>
</Select>
</li>
</ul>
@ -58,10 +68,22 @@
</div>
<div class="page-turn-wrap" v-if="list_data">
<Page :current="Number(list_data.current_page)" :page-size="Number(list_data.per_page)" :total="Number(list_data.total)" @on-change="index" show-elevator show-total></Page>
<Page
:current="Number(list_data.current_page)"
:page-size="Number(list_data.per_page)"
:total="Number(list_data.total)"
@on-change="index"
show-elevator
show-total
></Page>
</div>
<ui-edit :data="editObj.data" :show.sync="editObj.show" @add-success="index" @update-success="index(list_data.current_page)"></ui-edit>
<ui-edit
:data="editObj.data"
:show.sync="editObj.show"
@add-success="index"
@update-success="index(list_data.current_page)"
></ui-edit>
<ui-detail :data="detailObj.data" :show.sync="detailObj.show"></ui-detail>
</div>

View File

@ -8,7 +8,8 @@ export default {
data() {
return {
params: {
name: ''
name: '',
status: ''
},
trashed: null,
list_data: null,
@ -51,12 +52,15 @@ export default {
key: '',
width: 100,
render: (h, { row, column, index }) => {
let type = ['primary', 'warning', 'error'];
let text = ['已启用', '已禁用', '已删除'];
return h('Button', {
props: {
type: row.status ? 'error' : 'primary',
type: type[row.status],
size: 'small'
}
}, row.status ? '已禁用' : '启用中');
}, text[row.status]);
}
},
{
@ -163,7 +167,17 @@ export default {
* @return {[type]} [description]
*/
index(page = 1) {
let data = this.searchDataHandle(this.params, { page }, { 'trashed': this.trashed, 'orderBy': 'id', 'sortedBy': 'asc' });
let params = Object.assign(this.params, { 'orderBy': 'id', 'sortedBy': 'asc' });
if (params.status === 2) {
params.status = undefined;
params.trashed = 'only';
} else {
params.trashed = 'without';
}
let data = this.searchDataHandle({}, { page }, params);
this.isShowLoading(true);
API.index(data).then(res => {
this.isShowLoading(false);

View File

@ -27,11 +27,6 @@
<div class="ui-list-title">运营商:</div>
<div class="ui-list-content">{{flowPool.carrier_operator_name}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">共享类型:</div>
<div class="ui-list-content">{{flowPool.shared_name}}</div>
</li>
</ul>
</Col>
<Col span="12">
@ -41,9 +36,9 @@
<div class="ui-list-content">
<Tag
color="blue"
v-for="(item,index) in products"
v-for="(item,index) in packages"
:key="index"
>{{item.product_name}}</Tag>
>{{item.package_name}}</Tag>
</div>
</li>
@ -52,9 +47,9 @@
<div class="ui-list-content">
<Tag
color="blue"
v-for="(item,index) in renewPackageProducts"
v-for="(item,index) in renewPackages"
:key="index"
>{{item.product_name}}</Tag>
>{{item.package_name}}</Tag>
</div>
</li>

View File

@ -64,34 +64,19 @@
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">
<span class="title-require">*</span>共享类型:
</div>
<div class="ui-list-content">
<Select
:disabled="data ? true : false"
v-model="params.shared"
:style="'width:' + listStyle.width"
>
<Option :value="1">纵向共享</Option>
<Option :value="2">横向共享</Option>
</Select>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">
<span class="title-require">*</span>企业名称:
</div>
<div class="ui-list-content">
<Select
:disabled="data ? true : false"
filterable
icon="ios-search"
placeholder="企业名称"
v-model.trim="params.company_id"
:style="'width:' + listStyle.width"
@on-change="filterProducts"
@on-change="filterPackages"
>
<Option :value="item.id" :key="item.id" v-for="item in companies">{{ item.name }}</Option>
</Select>
@ -106,8 +91,8 @@
<Transfer
:titles="['备选套餐', '已选套餐']"
:list-style="listStyle"
:data="productFilters"
:target-keys="product_ids"
:data="packageFilters"
:target-keys="package_ids"
@on-change="transferPackages"
></Transfer>
</div>

View File

@ -63,7 +63,7 @@
</Row>
<Row v-for="(obj, objIndex) in params.settings" :key="objIndex">
<Row v-for="(item, index) in obj.cards" :key="index" class="umar-tb5">
<Col span="4">{{!index ? obj.product_name : '&nbsp;'}}</Col>
<Col span="4">{{!index ? obj.package_name : '&nbsp;'}}</Col>
<Col span="4">
{{!index ? obj.total : '&nbsp;'}}
<Tooltip v-if="!index" content="新增的卡(未设置流量)">

View File

@ -32,15 +32,15 @@ export default {
my_show: false,
flowPool: null,
cards: null,
products: [],
renewPackageProducts: [],
packages: [],
renewPackages: [],
cardsColumns: [{
title: 'SIM',
key: 'sim'
},
{
title: '套餐名称',
key: 'product_name'
key: 'package_name'
},
{
title: '保底流量',
@ -75,10 +75,10 @@ export default {
if (res.code == 0) {
this.flowPool = res.data.flowPool;
this.cards = res.data.cards;
this.products = this.flowPool.products.filter(item => {
this.packages = this.flowPool.packages.filter(item => {
return item.type === 0;
});
this.renewPackageProducts = this.flowPool.products.filter(item => {
this.renewPackages = this.flowPool.packages.filter(item => {
return item.type === 2;
});

View File

@ -8,7 +8,7 @@ export default {
},
data: {
type: Object,
default() {
default () {
return null;
}
}
@ -20,9 +20,9 @@ export default {
height: '300px'
},
companies: [],
products: [],
productFilters: [],
product_ids: [],
packages: [],
packageFilters: [],
package_ids: [],
reals: [],
realFilters: [],
real_pool_ids: [],
@ -32,13 +32,12 @@ export default {
params: {
name: '',
carrier_operator: '',
shared: '',
company_id: '',
real_pool_ids: [],
product_ids: [],
package_ids: [],
status: 0,
remark: '',
start_at: this.moment().format('YYYY-MM')
start_at: this.moment().subtract('1', 'months').format('YYYY-MM')
}
};
},
@ -53,7 +52,7 @@ export default {
}
}
this.product_ids = this.data.product_ids;
this.package_ids = this.data.package_ids;
this.real_pool_ids = this.data.real_pool_ids;
}
@ -83,25 +82,25 @@ export default {
this.filterReals();
}
if (!this.products.length) {
API.products().then(res => {
if (!this.packages.length) {
API.packages().then(res => {
if (res.code == 0) {
this.products = res.data.map(item => {
this.packages = res.data.map(item => {
return {
'key': item.id,
'label': item.name,
'label': item.name + (item.type === 0 ? '(基础)' : '(续费包)'),
'disabled': false,
'company_id': item.company_id,
'company_ids': item.company_ids,
'virtual_pool_id': item.virtual_pool_id,
'carrier_operator': item.carrier_operator
};
});
this.filterProducts();
this.filterPackages();
}
});
} else {
this.filterProducts();
this.filterPackages();
}
}
}
@ -124,30 +123,36 @@ export default {
return false;
});
},
filterProducts() {
this.productFilters = this.products.filter(item => {
filterPackages() {
this.packageFilters = this.packages.filter(item => {
if ((this.params.carrier_operator !== '' && this.params.carrier_operator !== undefined) && this.params.carrier_operator !== item.carrier_operator) {
return false;
}
if ((this.params.company_id !== '' && this.params.company_id !== undefined) && this.params.company_id !== item.company_id) {
if (item.company_ids.length === 0) {
return true;
}
if (this.data && item.company_ids.indexOf(this.data.company_id) !== -1) {
return true;
}
if ((this.params.company_id !== '' && this.params.company_id !== undefined) && item.company_ids.indexOf(this.params.company_id) !== -1) {
let index = this.params.package_ids.indexOf(item.id);
if (index !== -1) {
this.params.package_ids.splice(index, 1);
}
return false;
}
if (item.virtual_pool_id === 0) {
return true;
}
if (this.data && item.virtual_pool_id === this.data.id) {
return true;
}
return false;
return true;
});
},
selectCO() {
this.filterReals();
this.filterProducts();
this.filterPackages();
},
ok() {
if (this.params.company_id === '') {
@ -158,10 +163,6 @@ export default {
this.$Message.info('请选择运营商');
}
if (this.params.shared === '') {
this.$Message.info('请选择共享类型');
}
this.params.start_at = this.moment(this.params.start_at).format('YYYY-MM');
if (this.data) {
@ -200,45 +201,45 @@ export default {
for (let k in this.params) {
if (k == 'status') {
this.params[k] = 0;
} else if (k == 'real_pool_ids' || k == 'product_ids') {
} else if (k == 'real_pool_ids' || k == 'package_ids') {
this.params[k] = [];
} else if (k == 'start_at') {
this.params[k] = this.moment().format('YYYY-MM');
this.params[k] = this.moment().subtract('1', 'months').format('YYYY-MM');
} else {
this.params[k] = '';
}
}
this.my_show = false;
this.product_ids = [];
this.package_ids = [];
this.real_pool_ids = [];
this.packages = [];
},
transferPackages(ids) {
if (ids.length) {
this.products.filter(item => {
this.packages.map(item => {
if (item.key === ids[0]) {
this.params.company_id = item.company_id;
this.params.carrier_operator = item.carrier_operator;
}
});
}
if (this.product_ids.length > ids.length) {
if (this.package_ids.length > ids.length) {
this.$Modal.confirm({
title: '请谨慎操作!',
content: '移除已选套餐,可能会引起已有数据的变化。',
onOk: () => {
this.product_ids = ids;
this.params.product_ids = ids;
this.package_ids = ids;
this.params.package_ids = ids;
}
});
} else {
this.product_ids = ids;
this.params.product_ids = ids;
this.package_ids = ids;
this.params.package_ids = ids;
}
this.filterReals();
this.filterProducts();
this.filterPackages();
},
transferRealFlowPools(ids) {
if (ids.length) {
@ -253,7 +254,7 @@ export default {
this.params.real_pool_ids = ids;
this.filterReals();
this.filterProducts();
this.filterPackages();
}
}
};

View File

@ -84,8 +84,8 @@ export default {
}
let obj = {
product_id: item.product_id,
product_name: item.product_name,
package_id: item.package_id,
package_name: item.package_name,
total: item.total,
news: item.news,
cards: cards
@ -109,7 +109,7 @@ export default {
}
if (total !== obj.total) {
this.$Message.error(`套餐(${obj.product_name})卡数量设置不正确`);
this.$Message.error(`套餐(${obj.package_name})卡数量设置不正确`);
return;
}
}
@ -135,7 +135,7 @@ export default {
clear() {
for (let k in this.params) {
if (k == 'month') {
this.params[k] = this.moment().format('YYYY-MM');
this.params[k] = this.moment().subtract('1', 'months').format('YYYY-MM');
} else if (k == 'total_flows') {
this.params[k] = 0;
} else {

View File

@ -79,11 +79,6 @@ export default {
key: 'carrier_operator_name',
width: 80
},
{
title: '共享类型',
key: 'shared_name',
width: 100
},
{
title: '客户名称',
key: 'company_name',

View File

@ -33,9 +33,9 @@ export default {
}
},
{
text: '月',
text: '月',
value: () => {
return this.moment().format('YYYY-MM');
return this.moment().subtract('1', 'months').format('YYYY-MM');
}
}
],
@ -144,10 +144,10 @@ export default {
} else {
let minimum_settings = [];
this.data.products.map(item => {
this.data.packages.map(item => {
minimum_settings.push({
product_id: item.product_id,
product_name: item.product_name,
package_id: item.package_id,
package_name: item.package_name,
flows: 0,
price: 0
});
@ -155,8 +155,8 @@ export default {
this.params = {
pool_id: this.data.id,
start_at: this.data.settings.length ? this.moment().format('YYYY-MM') : this.start_at,
end_at: this.data.settings.length ? this.moment().format('YYYY-MM') : this.end_at,
start_at: this.data.settings.length ? this.moment().subtract('1', 'months').format('YYYY-MM') : this.start_at,
end_at: this.data.settings.length ? this.moment().subtract('1', 'months').format('YYYY-MM') : this.end_at,
first_month_price: 0,
other_month_price: 0,
gradient: 0,

View File

@ -110,7 +110,7 @@
</Row>
<Row v-for="(item, index) in params.minimum_settings" :key="index">
<Col span="8">{{item.product_name}}</Col>
<Col span="8">{{item.package_name}}</Col>
<Col span="8">
<FormItem>
<InputNumber :max="99999" :min="0" v-model="params.minimum_settings[index]['flows']"></InputNumber>

View File

@ -94,6 +94,13 @@
</div>
</li>
<li class="ui-list" v-if="[0, 2].indexOf(type) !== -1">
<div class="ui-list-title">后向套餐</div>
<div class="ui-list-content">
<Switch :false-value="0" :true-value="1" v-model="params.flowed"/>
</div>
</li>
<li class="ui-list" v-if="type">
<div class="ui-list-title">立即生效</div>
<div class="ui-list-content">

View File

@ -45,9 +45,10 @@
</li>
<li class="handle-item w-250">
<Select clearable placeholder="套餐状态" v-model="trashed">
<Option :value="'without'">使用中</Option>
<Option :value="'only'">已删除</Option>
<Select clearable placeholder="套餐状态" v-model="params.status">
<Option :value="0">已启用</Option>
<Option :value="1">已禁用</Option>
<Option :value="2">已删除</Option>
</Select>
</li>
</ul>

View File

@ -38,7 +38,8 @@ export default {
service_months: 1,
effect_months: 0,
description: '',
status: 0
status: 0,
flowed: 0
}
};
},

View File

@ -9,7 +9,8 @@ export default {
params: {
name: '',
carrier_operator: '',
sn: ''
sn: '',
status: ''
},
type: 0,
trashed: null,
@ -88,12 +89,15 @@ export default {
key: '',
width: 100,
render: (h, { row, column, index }) => {
let type = ['primary', 'warning', 'error'];
let text = ['已启用', '已禁用', '已删除'];
return h('Button', {
props: {
type: row.status ? 'error' : 'primary',
type: type[row.status],
size: 'small'
}
}, row.status ? '已禁用' : '启用中');
}, text[row.status]);
}
},
{
@ -184,7 +188,15 @@ export default {
* @return {[type]} [description]
*/
index(page = 1) {
let params = Object.assign(this.params, { 'type': this.type, 'trashed': this.trashed, 'orderBy': 'id', 'sortedBy': 'asc' });
let params = Object.assign(this.params, { 'type': this.type, 'orderBy': 'id', 'sortedBy': 'asc' });
if (params.status === 2) {
params.status = undefined;
params.trashed = 'only';
} else {
params.trashed = 'without';
}
let data = this.searchDataHandle({}, { page }, params);
this.isShowLoading(true);
API.index(data).then(res => {

View File

@ -63,13 +63,6 @@
</div>
</li>
<li class="ui-list" v-if="[0, 2].indexOf(type) !== -1">
<div class="ui-list-title">后向套餐</div>
<div class="ui-list-content">
<Switch :false-value="0" :true-value="1" v-model="params.flowed"/>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">备注:</div>
<div class="ui-list-content">

View File

@ -0,0 +1,37 @@
<template>
<Drawer
:mask-closable="false"
@on-visible-change="visibleChange"
title="定价历史"
v-model="my_show"
width="500"
>
<Timeline>
<TimelineItem v-for="(item, index) in list" :key="index" :color="!index ? 'green' : 'blue'">
<p
class="time"
>{{moment(item.created_at).format('YYYY-MM-DD')}} - {{moment(item.updated_at).format('YYYY-MM-DD')}}</p>
<p class="content">
<span class="umar-r10">
<b>销售价:</b>
{{item.price}}
</span>
<span v-if="data.type === 0">
<b>续费价:</b>
{{item.renew_price}}
</span>
</p>
</TimelineItem>
</Timeline>
</Drawer>
</template>
<script src="./js/history.js"></script>
<style scoped>
>>> .ivu-table {
font-size: 11px;
}
</style>

View File

@ -75,8 +75,9 @@
<li class="handle-item w-250">
<Select clearable placeholder="状态" v-model="params.status">
<Option :value="0">启用</Option>
<Option :value="1">禁用</Option>
<Option :value="0">已启用</Option>
<Option :value="1">已禁用</Option>
<!-- <Option :value="2">已删除</Option> -->
</Select>
</li>
@ -106,6 +107,8 @@
@add-success="index"
@update-success="index"
></ui-edit>
<ui-history :data="historyObj.data" :show.sync="historyObj.show"></ui-history>
</div>
</template>

View File

@ -0,0 +1,53 @@
import * as API from 'api/virtual/products';
export default {
props: {
show: {
type: Boolean,
default: false
},
data: {
type: Object,
default() {
return null;
}
}
},
watch: {
show(bool) {
this.my_show = bool;
if (bool) {
this.list = [];
this.index();
}
}
},
data() {
return {
loading: false,
my_show: false,
list: []
};
},
methods: {
index() {
let params = {
type: this.data.type,
company_id: this.data.company_id,
package_id: this.data.package_id
};
this.loading = true;
API.history(params).then(res => {
this.loading = false;
if (res.code === 0) {
this.list = res.data;
}
});
},
visibleChange(bool) {
this.$emit('update:show', bool);
}
}
};

View File

@ -3,7 +3,8 @@ import * as API from 'api/virtual/products';
export default {
name: 'Products',
components: {
UiEdit: resolve => require(['views/virtual/products/edit'], resolve)
UiEdit: resolve => require(['views/virtual/products/edit'], resolve),
UiHistory: resolve => require(['views/virtual/products/history'], resolve)
},
data() {
return {
@ -19,6 +20,10 @@ export default {
isUpdate: false,
data: null
},
historyObj: {
show: false,
data: null
},
search: {
show: false
},
@ -27,9 +32,12 @@ export default {
data: [],
columns: [
{
title: 'ID',
key: 'id',
width: 80
title: '序号',
key: '',
width: 80,
render: (h, context) => {
return h('span', context.row._index + 1);
}
},
{
title: '定价名称',
@ -58,21 +66,31 @@ export default {
},
{
title: '备注',
key: 'remark'
key: 'remark',
minWidth: 150,
tooltip: true
},
{
title: '状态',
key: '',
width: 100,
render: (h, { row, column, index }) => {
let type = ['primary', 'warning', 'error'];
let text = ['已启用', '已禁用', '已删除'];
return h('Button', {
props: {
type: row.status ? 'error' : 'primary',
type: type[row.status],
size: 'small'
}
}, row.status ? '已禁用' : '启用中');
}, text[row.status]);
}
},
{
title: '创建时间',
key: 'created_at',
width: 170
},
{
title: '更新时间',
key: 'updated_at',
@ -87,11 +105,30 @@ export default {
column,
index
}) => {
if (row.deleted_at) {
return h('Tag', { props: { color: 'default' } }, '该定价已被删除');
}
row.price = Number(row.price);
row.renew_price = Number(row.renew_price);
let html = [];
html.push(h('Button', {
props: {
type: 'success',
size: 'small',
disabled: false,
icon: 'md-list'
},
class: ['btn'],
on: {
click: (event) => {
this.openHistory(true, row);
}
}
}, '历史'));
if (this.haveJurisdiction('update')) {
html.push(h('Button', {
props: {
@ -109,6 +146,7 @@ export default {
}, '编辑'));
}
/**
if (this.haveJurisdiction('destroy')) {
html.push(h('Button', {
props: {
@ -138,6 +176,7 @@ export default {
}
}, '删除'));
}
*/
if (html.length) {
return h('div', html);
@ -176,6 +215,13 @@ export default {
let params = this.searchDataHandle({}, {}, this.params);
if (params.status === 2) {
params.status = undefined;
params.trashed = 'only';
} else {
params.trashed = 'without';
}
API.index(params).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
@ -205,6 +251,14 @@ export default {
this.editObj = { show, data, isUpdate };
},
/**
* [openHistory 打开历史弹窗]
* @return {[type]} [description]
*/
openHistory(show, row) {
this.historyObj = { show, data: row };
},
/**
* [request 刷新]
* @return {[type]} [description]
@ -214,7 +268,9 @@ export default {
},
resetSearch() {
for (let k in this.params) {
this.params[k] = '';
if (k !== 'company_id') {
this.params[k] = '';
}
}
this.index();
},

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=\favicon.ico><script src=\config.js></script><title></title><link href=/css/chunk-2e798a12.698b0ec2.css rel=prefetch><link href=/css/chunk-996b1e80.5cadf3d0.css rel=prefetch><link href=/js/chunk-00ae0766.3874cd10.js rel=prefetch><link href=/js/chunk-07a274ec.c3ad5dec.js rel=prefetch><link href=/js/chunk-2e798a12.d09459b0.js rel=prefetch><link href=/js/chunk-996b1e80.d3b45e46.js rel=prefetch><link href=/css/app.d71a8195.css rel=preload as=style><link href=/css/chunk-vendors.3c3b2e85.css rel=preload as=style><link href=/js/app.ac2b5fd2.js rel=preload as=script><link href=/js/chunk-vendors.ed6443e8.js rel=preload as=script><link href=/css/chunk-vendors.3c3b2e85.css rel=stylesheet><link href=/css/app.d71a8195.css rel=stylesheet></head><body><noscript><strong>很抱歉如果没有启用JavaScript程序不能正常工作若要继续使用请启用它。</strong></noscript><div id=app></div><script src=/js/chunk-vendors.ed6443e8.js></script><script src=/js/app.ac2b5fd2.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=\favicon.ico><script src=\config.js></script><title></title><link href=/css/chunk-6ac03f5f.b16f7faa.css rel=prefetch><link href=/css/chunk-996b1e80.5cadf3d0.css rel=prefetch><link href=/js/chunk-00ae0766.3874cd10.js rel=prefetch><link href=/js/chunk-07a274ec.c3ad5dec.js rel=prefetch><link href=/js/chunk-6ac03f5f.9af49aa9.js rel=prefetch><link href=/js/chunk-996b1e80.d3b45e46.js rel=prefetch><link href=/css/app.d71a8195.css rel=preload as=style><link href=/css/chunk-vendors.3c3b2e85.css rel=preload as=style><link href=/js/app.8cc6aaf9.js rel=preload as=script><link href=/js/chunk-vendors.ed6443e8.js rel=preload as=script><link href=/css/chunk-vendors.3c3b2e85.css rel=stylesheet><link href=/css/app.d71a8195.css rel=stylesheet></head><body><noscript><strong>很抱歉如果没有启用JavaScript程序不能正常工作若要继续使用请启用它。</strong></noscript><div id=app></div><script src=/js/chunk-vendors.ed6443e8.js></script><script src=/js/app.8cc6aaf9.js></script></body></html>

View File

@ -74,8 +74,8 @@ class Output
*/
public function sql()
{
$fullQuery = vsprintf(str_replace(['%', '?'], ['%%', '%s'], $this->event->sql), $this->event->bindings);
$result = $event->connectionName . ' (' . $event->time . '): ' . $fullQuery;
$fullQuery = vsprintf(str_replace(['%', '?'], ['%%', "'%s'"], $this->event->sql), $this->event->bindings);
$result = $this->event->connectionName . ' (' . $this->event->time . '): ' . $fullQuery;
$this->append($result);
}