diff --git a/app/Domains/Virtual/Commands/Schedule/AutoActivate.php b/app/Domains/Virtual/Commands/Schedule/AutoActivate.php index e6e3c4e5..16b2d2df 100644 --- a/app/Domains/Virtual/Commands/Schedule/AutoActivate.php +++ b/app/Domains/Virtual/Commands/Schedule/AutoActivate.php @@ -41,7 +41,7 @@ class AutoActivate extends Command // } $sql = "WITH sim_array AS ( - SELECT sim FROM virtual_order_cards WHERE created_at >= '%s' AND created_at <= '%s' AND service_start_at IS NULL + SELECT sim FROM virtual_order_cards WHERE created_at >= '%s' AND created_at <= '%s' AND service_start_at IS NULL ORDER BY created_at DESC ), activate_updates AS ( UPDATE cards SET virtual_activated_at='%s' WHERE sim IN (SELECT * FROM sim_array) AND virtual_activated_at IS NULL ) diff --git a/app/Domains/Virtual/Http/Controllers/PropertyController.php b/app/Domains/Virtual/Http/Controllers/PropertyController.php new file mode 100644 index 00000000..d3601046 --- /dev/null +++ b/app/Domains/Virtual/Http/Controllers/PropertyController.php @@ -0,0 +1,83 @@ +request = $request; + $this->propertyService = $propertyService; + } + + /** + * 配置. + * + * @return \Illuminate\Http\Response + */ + public function settings() + { + if ($this->request->isMethod('POST')) { + $values = $this->request->get('data'); + $res = $this->propertyService->settingsStore($values); + return res($res, '修改成功'); + } else { + $conditions = $this->request->all(); + $res = $this->propertyService->settings($conditions); + return res($res, '配置分类', 201); + } + } + + /** + * 列表. + * + * @return \Illuminate\Http\Response + */ + public function index() + { + $conditions = $this->request->all(); + $list = $this->propertyService->index($conditions); + return res($list, '客户套餐属性配置列表', 201); + } + + /** + * 存储. + * + * @return \Illuminate\Http\Response + */ + public function store() + { + $values = $this->request->get('data'); + $res = $this->propertyService->store($values); + return res($res, '修改成功'); + } + + /** + * 导出. + * + * @return \Illuminate\Http\Response + */ + public function export() + { + // + } + + /** + * 导入. + * + * @return \Illuminate\Http\Response + */ + public function import() + { + // + } +} diff --git a/app/Domains/Virtual/Repositories/PropertyRepository.php b/app/Domains/Virtual/Repositories/PropertyRepository.php new file mode 100644 index 00000000..4d5f4612 --- /dev/null +++ b/app/Domains/Virtual/Repositories/PropertyRepository.php @@ -0,0 +1,62 @@ + '=', + 'created_at' => 'like', + ]; + + public function model() { + return Model::class; + } + + /** + * 数据格式化 + * + * @param mixed $result + * + * @return mixed + */ + public function transform($model) + { + return $model->toArray(); + } + + /** + * 查询条件 + * + * @return void + */ + public function withConditions(array $conditions = []) + { + if (isset($conditions['id'])) { + $conditions['id'] = array_wrap($conditions['id']); + $this->model = $this->model->whereIn('id', $conditions['id']); + } + + return $this; + } +} \ No newline at end of file diff --git a/app/Domains/Virtual/Repositories/PropertySettingRepository.php b/app/Domains/Virtual/Repositories/PropertySettingRepository.php new file mode 100644 index 00000000..c9e073aa --- /dev/null +++ b/app/Domains/Virtual/Repositories/PropertySettingRepository.php @@ -0,0 +1,72 @@ + '=', + 'created_at' => 'like', + ]; + + public function model() + { + return Model::class; + } + + /** + * 数据格式化 + * + * @param mixed $result + * + * @return mixed + */ + public function transform($model) + { + return $model->toArray(); + } + + /** + * 查询条件 + * + * @return void + */ + public function withConditions(array $conditions = []) + { + if (isset($conditions['id'])) { + $conditions['id'] = array_wrap($conditions['id']); + $this->model = $this->model->whereIn('id', $conditions['id']); + } + + return $this; + } + + public function getAll($force = false) + { + if ($force) { + $this->forgetCached(); + } + + return $this->get()->pluck('value', 'name'); + } +} diff --git a/app/Domains/Virtual/Routes/api.php b/app/Domains/Virtual/Routes/api.php index a68cd5db..8a83e641 100644 --- a/app/Domains/Virtual/Routes/api.php +++ b/app/Domains/Virtual/Routes/api.php @@ -36,13 +36,20 @@ $router->group(['prefix' => 'virtual', 'as' => 'virtual', 'middleware' => ['admi // $router->post('/company/addresses/update/{id}', ['as' => 'company.addresses.update', 'uses' => 'CompanyAddressController@update']); // $router->post('/company/addresses/destroy', ['as' => 'company.addresses.destroy', 'uses' => 'CompanyAddressController@destroy']); - // 企业定价管理 + // 定价管理 $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']); + // 属性管理 + $router->addRoute(['GET', 'POST'], '/properties/settings', ['as' => 'properties.settings', 'uses' => 'PropertyController@settings']); + $router->get('/properties/index', ['as' => 'properties.index', 'uses' => 'PropertyController@index']); + $router->post('/properties/store', ['as' => 'properties.store', 'uses' => 'PropertyController@store']); + $router->get('/properties/export', ['as' => 'properties.export', 'uses' => 'PropertyController@export']); + $router->post('/properties/import', ['as' => 'properties.import', 'uses' => 'PropertyController@import']); + // 订单管理 $router->get('/orders/index', ['as' => 'orders.index', 'uses' => 'OrderController@index']); $router->get('/orders/show/{id}', ['as' => 'orders.show', 'uses' => 'OrderController@show']); diff --git a/app/Domains/Virtual/Services/PackageService.php b/app/Domains/Virtual/Services/PackageService.php index 9ac5e562..4bc9f301 100644 --- a/app/Domains/Virtual/Services/PackageService.php +++ b/app/Domains/Virtual/Services/PackageService.php @@ -154,7 +154,7 @@ class PackageService extends Service if (!self::$packages) { self::$packages = app(PackageRepository::class) ->select(['id', 'type', 'sn', 'name', 'carrier_operator', 'flows', 'service_months', 'status']) - ->withTrashed()->get()->keyBy('id'); + ->withTrashed()->get()->keyBy('id')->toArray(); } return self::$packages[$id]; diff --git a/app/Domains/Virtual/Services/PropertyService.php b/app/Domains/Virtual/Services/PropertyService.php new file mode 100644 index 00000000..31474c15 --- /dev/null +++ b/app/Domains/Virtual/Services/PropertyService.php @@ -0,0 +1,304 @@ + '产品类型', + 'vehicle' => '车辆类型', + 'commercial_vehicle' => '商用车分类', + 'company' => '公司类型', + 'platform' => '平台/API类型', + 'customer' => '客户类型', + 'package' => '套餐分类', + 'province' => '省份设置', + 'package_type' => '套餐类型' + ]; + + public static $default = [ + 'product' => '', + 'vehicle' => '', + 'commercial_vehicle' => '', + 'company' => '', + 'platform' => '', + 'customer' => '', + 'package' => '', + 'province' => null, + 'province_status' => 0, + 'created_at' => null, + 'updated_at' => null, + ]; + + /** + * 构造函数 + * + * @return void + */ + public function __construct(ProductRepository $productRepository, PropertyRepository $propertyRepository, PropertySettingRepository $propertySettingRepository) + { + $this->productRepository = $productRepository; + $this->propertyRepository = $propertyRepository; + $this->propertySettingRepository = $propertySettingRepository; + } + + /** + * 配置设置查看 + * + * @param array $conditions + * @return void + */ + public function settings($conditions = []) + { + $settings = $this->propertySettingRepository->getAll(); + + $products = $settings['product']; + + foreach ($settings as $key => $setting) { + if ($setting['name'] === 'package') { + $values = $setting['value']; + foreach ($values as $k => $value) { + foreach ($value as $i => $v) { + if (!in_array($v, $products)) { + unset($settings[$key]['value'][$k][$i]); + $settings[$key]['value'][$k] = array_values($settings[$key]['value'][$k]); + } + } + } + } + } + + if ($conditions['names']) { + $conditions['names'] = array_wrap($conditions['names']); + + $settings = array_where($settings, function ($value) use ($conditions) { + return in_array($value['name'], $conditions['names']); + }); + }; + + return $settings; + } + + /** + * 配置设置写入 + * + * @param array $values + * @return void + */ + public function settingsStore(array $values) + { + $settings = $this->propertySettingRepository->getAll(); + + if (isset($values['product'])) { + } + + + + foreach ($values as $key => $value) { + if (!in_array($key, array_keys(self::$property_types))) { + throw new NotExistException("分类{$key}不存在"); + } + + if ($key !== 'province' && $key !== 'package') { + $value = array_values(array_unique(array_merge($settings[$key] ?: [], $value))); + $value = array_map(function ($item) { + return is_string($item) ? trim($item) : $item; + }, $value); + } + + $values[$key] = $value; + } + + if (isset($values['package'])) { + $packages = $values['package']; + $products = $values['product']; + foreach ($packages as $k => $v) { + if (!is_array($v)) { + throw new InvalidArgumentException('传入的产品参数错误'); + } + + foreach ($v as $m) { + if (!in_array($m, $products)) { + throw new NotExistException("产品({$m})未配置或已删除"); + } + + foreach ($packages as $i => $j) { + if ($i !== $k && in_array($m, $j)) { + throw new NotAllowedException('一个产品仅能归属一个套餐'); + } + } + } + } + } + + $data = []; + + foreach ($values as $key => $value) { + $data[] = [ + 'name' => $key, + 'value' => json_encode($value, 256), + ]; + } + + PropertySetting::upsert($data, 'name'); + + $this->propertySettingRepository->forgetCached(); + + $settings = $this->propertySettingRepository->getAll(); + + return true; + } + + /** + * 客户套餐配置列表 + * + * @return void + */ + public function index($conditions = []) + { + $product = $this->productRepository->whereHas('package', function ($query) { + $query->whereNull('deleted_at'); + })->whereHas('company', function ($query) { + $query->whereNull('deleted_at'); + })->withConditions($conditions)->applyConditions()->get(); + + $properties = $this->propertyRepository->whereHas('package', function ($query) { + $query->whereNull('deleted_at'); + })->whereHas('company', function ($query) { + $query->whereNull('deleted_at'); + })->withConditions($conditions)->applyConditions()->get()->keyBy(function ($item) { + return $item->company_id . '_' . $item->package_id; + })->toArray(); + + $sells = app(OrderCardPartitionRepository::class)->selectRaw('company_id, package_id, count(*) as counts') + ->where('type', 0)->groupBy(['company_id', 'package_id'])->get()->keyBy(function ($item) { + return $item->company_id . '_' . $item->package_id; + })->toArray(); + + $carrierOperators = app(Dicts::class)->get('carrier_operator'); + + $list = $product->map(function ($item) use ($properties, $sells, $carrierOperators) { + $item = $item->toArray(); + $package = PackageService::load($item['package_id']); + $item['company_name'] = CompanyService::load($item['company_id'])['name'] ?? ''; + $item['package_name'] = $package['name'] ?? ''; + $item['flows'] = $package['flows'] ?? 0; + $item['carrier_operator'] = $package['carrier_operator']; + $item['carrier_operator_name'] = $carrierOperators[$item['carrier_operator']] ?? '未知'; + $property = $properties[$item['company_id'] . '_' . $item['package_id']]; + $sell = $sells[$item['company_id'] . '_' . $item['package_id']]; + $item['counts'] = $sell ? $sell['counts'] : 0; + return array_merge($item, $property ?: self::$default); + }); + + return $list; + } + + /** + * 客户套餐配置修改 + * + * @param array $values + * @return void + */ + public function store(array $values) + { + if (! is_array(reset($values))) { + $values = [$values]; + } + + $only = ['company_id', 'package_id', 'product', 'vehicle', 'commercial_vehicle', 'company', 'platform', 'customer', 'province']; + $checks = ['product', 'vehicle', 'company', 'customer']; + + $settings = $this->propertySettingRepository->getAll(); + + $data = []; + + foreach ($values as $key => $value) { + if (!isset($value['company_id']) || !isset($value['package_id'])) { + continue; + } + + $value['platform'] = $value['platform'] ?: ''; + $value['commercial_vehicle'] = $value['commercial_vehicle'] ?: ''; + $value = array_only($value, $only); + + foreach ($value as $k => $v) { + if (in_array($k, $checks) && empty($v)) { + throw new InvalidArgumentException(self::$property_types[$k] . '值不能为空'); + } + + if (in_array($k, $checks) && !in_array($v, $settings[$k])) { + throw new InvalidArgumentException(self::$property_types[$k] . '值不正确'); + } + + if (in_array($k, ['commercial_vehicle', 'platform']) && !in_array($v, $settings[$k]) && ($v !== '')) { + throw new InvalidArgumentException(self::$property_types[$k] . '值不正确'); + } + } + + if (!is_null($value['province'])) { + if (!empty(array_diff(array_keys($value['province']), $settings['province']))) { + throw new InvalidArgumentException('省份配置不正确'); + } + + if (array_sum($value['province']) != 0 && sprintf("%.2f", array_sum($value['province'])) != 100) { + throw new InvalidArgumentException('百分比配置不正确'); + } + + $value['province'] = json_encode($value['province'], 256); + } + + if (!$package = $this->getPackage($value['product'])) { + throw new NotExistException('产品套餐关系未配置'); + } + + $value['package'] = $package; + + $value['created_at'] = date('Y-m-d H:i:s'); + $value['updated_at'] = date('Y-m-d H:i:s'); + + $data[] = $value; + } + + if (empty($data)) { + throw new NotAllowedException('数据未修改'); + } + + $this->propertyRepository->upsert($data, ['company_id', 'package_id']); + + $this->propertyRepository->forgetCached(); + + return true; + } + + protected function getPackage($product) + { + if (!$package = $this->package) { + $settings = $this->propertySettingRepository->getAll(); + $packages = $settings['package']; + $this->package = $package; + } + + foreach ($packages as $key => $products) { + if (in_array($product, $products)) { + return $key; + } + } + + return null; + } +} diff --git a/app/Domains/Virtual/Tests/Services/PropertyServiceTest.php b/app/Domains/Virtual/Tests/Services/PropertyServiceTest.php new file mode 100644 index 00000000..b9fe48b6 --- /dev/null +++ b/app/Domains/Virtual/Tests/Services/PropertyServiceTest.php @@ -0,0 +1,13 @@ +assertTrue(true); + } +} diff --git a/app/Models/Virtual/Property.php b/app/Models/Virtual/Property.php new file mode 100644 index 00000000..b1d7cda6 --- /dev/null +++ b/app/Models/Virtual/Property.php @@ -0,0 +1,24 @@ + 'array', + ]; + + public function company() + { + return $this->belongsTo(Company::class, 'company_id', 'id'); + } + + public function package() + { + return $this->belongsTo(Package::class, 'package_id', 'id'); + } +} diff --git a/app/Models/Virtual/PropertySetting.php b/app/Models/Virtual/PropertySetting.php new file mode 100644 index 00000000..ea552cbc --- /dev/null +++ b/app/Models/Virtual/PropertySetting.php @@ -0,0 +1,17 @@ + 'array', + ]; +} diff --git a/database/migrations/2019_04_02_101740_create_property_tables.php b/database/migrations/2019_04_02_101740_create_property_tables.php new file mode 100644 index 00000000..26643eca --- /dev/null +++ b/database/migrations/2019_04_02_101740_create_property_tables.php @@ -0,0 +1,58 @@ +increments('id')->comment('自增ID'); + $table->integer('company_id')->unsigned()->default(0)->comment('企业ID'); + $table->integer('package_id')->unsigned()->default(0)->comment('套餐ID'); + $table->string('product', 100)->default('')->comment('产品类型'); + $table->string('vehicle', 100)->default('')->comment('车辆类型'); + $table->string('commercial_vehicle', 100)->default('')->comment('商用车分类'); + $table->string('company', 100)->default('')->comment('公司类型'); + $table->string('platform', 100)->default('')->comment('平台/API类型'); + $table->string('customer', 100)->default('')->comment('客户类型'); + $table->string('package', 100)->default('')->comment('套餐分类'); + $table->text('province', 100)->nullable()->comment('省份设置'); + $table->timestamps(); + + $table->unique(['company_id', 'package_id']); + + $table->comment('企业套餐卡属性配置'); + }); + } + + if (!Schema::hasTable('virtual_property_settings')) { + Schema::create('virtual_property_settings', function (Blueprint $table) { + $table->string('name', 20)->default(''); + $table->text('value', 100)->nullable(); + + $table->primary('name'); + $table->comment('卡属性分类配置'); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('virtual_property_settings'); + Schema::dropIfExists('virtual_properties'); + } +} diff --git a/database/seeds/PermissionSeeder.php b/database/seeds/PermissionSeeder.php index df957cb4..b0d3b301 100644 --- a/database/seeds/PermissionSeeder.php +++ b/database/seeds/PermissionSeeder.php @@ -150,6 +150,14 @@ class PermissionSeeder extends Seeder ['name' => 'virtual.products.3.destroy', 'title' => '删除', 'description' => 'destroy', 'type' => 1], ], ], + [ + 'name' => 'virtual.properties.index', 'title' => '属性管理', 'path' => '/properties', 'icon' => 'md-cube', 'type' => 0, 'open' => 3, + 'children' => [ + ['name' => 'virtual.properties.create', 'title' => '设置', 'description' => 'create', 'type' => 1], + ['name' => 'virtual.properties.update', 'title' => '修改', 'description' => 'update', 'type' => 1], + ['name' => 'virtual.properties.output', 'title' => '导出', 'description' => 'output', 'type' => 1], + ], + ], ], ], [ diff --git a/frontend/package.json b/frontend/package.json index e97d0dae..f3a673a3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "pinyin-engine": "^1.1.0", "vue": "^2.5.2", "vue-router": "^3.0.1", + "vuedraggable": "^2.20.0", "vuex": "^3.0.1", "xlsx": "^0.13.5" }, diff --git a/frontend/src/api/virtual/properties.js b/frontend/src/api/virtual/properties.js new file mode 100644 index 00000000..050b445d --- /dev/null +++ b/frontend/src/api/virtual/properties.js @@ -0,0 +1,63 @@ +/** + * 属性管理 + */ + +/** + * [settings 属性设置] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function settings(data) { + return service.get('api/virtual/properties/settings', { + params: data + }); +} + +/** + * [settingsStore 属性设置存储] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function settingsStore(data) { + return service.post('api/virtual/properties/settings', data); +} + +/** + * [index 属性列表] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function index(data) { + return service.get('api/virtual/properties/index', { + params: data + }); +} + +/** + * [store 属性存储] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function store(data) { + return serviceForm.post('api/virtual/properties/store', data); +} + +/** + * [export 属性导出] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function exportExcel(data) { + return service.get('api/virtual/properties/export', { + params: data + }); +} + +/** + * [import 属性导入] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function importExcel(data) { + return service.post('api/virtual/properties/import', data); +} diff --git a/frontend/src/mixins/complete.js b/frontend/src/mixins/complete.js index 1683c622..74fb1964 100644 --- a/frontend/src/mixins/complete.js +++ b/frontend/src/mixins/complete.js @@ -20,8 +20,6 @@ export default { return array; } - console.log(array); - const pinyinEngine = new PinyinEngine(array, [key]); let res = []; diff --git a/frontend/src/router/routes.js b/frontend/src/router/routes.js index 5ae56f88..cd028fc6 100644 --- a/frontend/src/router/routes.js +++ b/frontend/src/router/routes.js @@ -21,6 +21,7 @@ const routes = [ { path: '/company/accounts', name: 'CompanyAccounts', component: load('virtual/company_accounts/index'), meta: { title: '账号管理' } }, { path: '/packages/:type', name: 'Packages', component: load('virtual/packages/index'), meta: { title: '套餐管理' } }, { path: '/products/:type', name: 'Products', component: load('virtual/products/index'), meta: { title: '定价管理' } }, + { path: '/properties', name: 'Properties', component: load('virtual/properties/index'), meta: { title: '属性管理' } }, { path: '/cards', name: 'Cards', component: load('virtual/cards/index'), meta: { title: '客户列表' } }, { path: '/orders/:type', name: 'Orders', component: load('virtual/orders/index'), meta: { title: '订单列表' } }, { path: '/exports', name: 'StatsExports', component: load('exports/index'), meta: { title: '导出记录' } }, diff --git a/frontend/src/views/virtual/orders/cards.vue b/frontend/src/views/virtual/orders/cards.vue index 3e664ed2..505c9d0c 100644 --- a/frontend/src/views/virtual/orders/cards.vue +++ b/frontend/src/views/virtual/orders/cards.vue @@ -51,13 +51,7 @@