From 9c9832245cc3ab8d5a1eae7ecbf8fb94434fd47b Mon Sep 17 00:00:00 2001 From: denghy Date: Mon, 6 Jan 2020 15:02:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=90=86=E5=95=86=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Http/Controllers/AgentController.php | 102 +++++++ .../Virtual/Repositories/AgentRepository.php | 72 +++++ app/Domains/Virtual/Routes/api.php | 7 + app/Domains/Virtual/Services/AgentService.php | 129 +++++++++ app/Models/Virtual/Agent.php | 56 ++++ ..._add_virtual_order_cards_partition_idx.php | 84 ++++++ ..._06_144305_create_virtual_agents_table.php | 44 +++ frontend/src/api/virtual/agents.js | 51 ++++ frontend/src/router/routes.js | 1 + frontend/src/views/virtual/agents/detail.vue | 71 +++++ frontend/src/views/virtual/agents/edit.vue | 111 ++++++++ frontend/src/views/virtual/agents/index.vue | 97 +++++++ .../src/views/virtual/agents/js/detail.js | 29 ++ frontend/src/views/virtual/agents/js/edit.js | 110 ++++++++ frontend/src/views/virtual/agents/js/index.js | 264 ++++++++++++++++++ 15 files changed, 1228 insertions(+) create mode 100644 app/Domains/Virtual/Http/Controllers/AgentController.php create mode 100644 app/Domains/Virtual/Repositories/AgentRepository.php create mode 100644 app/Domains/Virtual/Services/AgentService.php create mode 100644 app/Models/Virtual/Agent.php create mode 100644 database/migrations/2020_01_06_140126_add_virtual_order_cards_partition_idx.php create mode 100644 database/migrations/2020_01_06_144305_create_virtual_agents_table.php create mode 100644 frontend/src/api/virtual/agents.js create mode 100644 frontend/src/views/virtual/agents/detail.vue create mode 100644 frontend/src/views/virtual/agents/edit.vue create mode 100644 frontend/src/views/virtual/agents/index.vue create mode 100644 frontend/src/views/virtual/agents/js/detail.js create mode 100644 frontend/src/views/virtual/agents/js/edit.js create mode 100644 frontend/src/views/virtual/agents/js/index.js diff --git a/app/Domains/Virtual/Http/Controllers/AgentController.php b/app/Domains/Virtual/Http/Controllers/AgentController.php new file mode 100644 index 00000000..f6d5af59 --- /dev/null +++ b/app/Domains/Virtual/Http/Controllers/AgentController.php @@ -0,0 +1,102 @@ +request = $request; + $this->agentService = $agentService; + } + + /** + * 列表. + * + * @return \Illuminate\Http\Response + */ + public function index() + { + $conditions = $this->request->all(); + $conditions['limit'] = $this->request->get('limit', 20); + + $agents = $this->agentService->index($conditions); + + $agents->load(['company:id,name']); + + return res($agents, '代理商列表', 201); + } + + /** + * 代理商详情. + * + * @return \Illuminate\Http\Response + */ + public function show($id) + { + $node = app(AgentRepository::class)->find($id); + + if (!$node) { + throw new NotExistException('代理商不存在或已删除'); + } + + $node->load(['company:id,name']); + + return res($node, '代理商详情', 201); + } + + + /** + * 创建. + * + * @return \Illuminate\Http\Response + */ + public function create() + { + $attributes = $this->request->all(); + + $account = $this->agentService->store($attributes); + + return res($account, '创建成功'); + } + + /** + * 编辑. + * + * @return \Illuminate\Http\Response + */ + public function update($id) + { + $attributes = $this->request->all(); + $attributes['id'] = $id; + + $account = $this->agentService->store($attributes); + + return res($account, '修改成功'); + } + + /** + * 删除. + * + * @return \Illuminate\Http\Response + */ + public function destroy() + { + $ids = $this->request->ids(); + + $this->agentService->destroy($ids); + + return res(true, '删除成功'); + } +} diff --git a/app/Domains/Virtual/Repositories/AgentRepository.php b/app/Domains/Virtual/Repositories/AgentRepository.php new file mode 100644 index 00000000..663c4ce3 --- /dev/null +++ b/app/Domains/Virtual/Repositories/AgentRepository.php @@ -0,0 +1,72 @@ + '=', + // 'created_at' => 'like', + 'name' => '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']); + } + + if (!empty($conditions['name'])) { + $this->model = $this->model->where('name', 'like', "%{$conditions['name']}%"); + } + + if (isset($conditions['status'])) { + $this->model = $this->model->where('status', $conditions['status']); + } + + return $this; + } +} diff --git a/app/Domains/Virtual/Routes/api.php b/app/Domains/Virtual/Routes/api.php index 3fcb9a62..61aeb05b 100644 --- a/app/Domains/Virtual/Routes/api.php +++ b/app/Domains/Virtual/Routes/api.php @@ -36,6 +36,13 @@ $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('/agents/index', ['as' => 'agents.index', 'uses' => 'AgentController@index']); + $router->get('/agents/show/{id}', ['as' => 'agents.show', 'uses' => 'AgentController@show']); + $router->post('/agents/create', ['as' => 'agents.create', 'uses' => 'AgentController@create']); + $router->post('/agents/update/{id}', ['as' => 'agents.update', 'uses' => 'AgentController@update']); + $router->post('/agents/destroy', ['as' => 'agents.destroy', 'uses' => 'AgentController@destroy']); + // 定价管理 $router->get('/products/index', ['as' => 'products.index', 'uses' => 'ProductController@index']); $router->get('/products/history', ['as' => 'products.history', 'uses' => 'ProductController@history']); diff --git a/app/Domains/Virtual/Services/AgentService.php b/app/Domains/Virtual/Services/AgentService.php new file mode 100644 index 00000000..7a251176 --- /dev/null +++ b/app/Domains/Virtual/Services/AgentService.php @@ -0,0 +1,129 @@ +agentServiceRepository = $agentServiceRepository; + } + + /** + * 代理商列表 + * + * @param array $conditions + * @return mixed + */ + public function index(array $conditions = []) + { + $limit = $conditions['limit'] ?? 20; + + $agents = $this->agentServiceRepository->withConditions($conditions) + ->applyConditions()->paginate($limit); + + $agents->map(function ($item) { + $item->status = $item->deleted_at ? 2 : $item->status; + $item->created_at = Carbon::parse($item->created_at)->format('Y-m-d'); + $item->updated_at = Carbon::parse($item->updated_at)->format('Y-m-d'); + }); + + return $agents; + } + + /** + * 存储代理商 + * + * @param array $attributes + * @param Agent $parent + * @return Agent + */ + public function store(array $attributes = []) + { + $attributes = array_only($attributes, array_merge(app(Agent::class)->getFillable())); + + $rule = [ + 'company_id' => ['required', 'exists:virtual_companies,id'], + 'name' => ['required', 'between:2,32', Rule::unique($this->agentServiceRepository->getTable(), 'name')->ignore($attributes['id'])->whereNUll('deleted_at')], + 'contacts' => ['string', 'between:2,32'], + 'mobile' => ['string', 'cn_phone'], + 'address' => ['string'], + 'extends' => ['array'], + ]; + + $message = [ + 'company_id.required' => '请输入企业ID', + 'company_id.exists' => '企业不存在或已删除', + 'name.required' => '请输入代理商名称', + 'name.between' => '代理商名称长度不合法', + 'name.unique' => '代理商名称已经被其他用户所使用', + 'contacts.between' => '联系人长度不合法', + 'mobile.cn_phone' => '手机号不合法', + ]; + + Validator::validate($attributes, $rule, $message); + + if (!$attributes['id']) { + $maxId = Agent::withTrashed()->max('id'); + $attributes['id'] = $maxId ? $maxId + 1 : 1; + + $maxSn = Agent::withTrashed()->max('sn'); + $maxSn = intval(str_replace('No', '', $maxSn)); + $attributes['sn'] = CommonService::stringifyCompanyId($maxSn + 1); + + $node = $this->agentServiceRepository->create($attributes); + } + + if ($attributes['id']) { + $node = $this->agentServiceRepository->find($attributes['id']); + + if (!$node) { + throw new NotExistException('代理商不存在或已删除'); + } + + $this->agentServiceRepository->setModel($node)->update($attributes); + } + + return $node; + } + + /** + * 删除 + * + * @return bool + */ + public function destroy($ids) + { + $ids = is_array($ids) ? $ids : [$ids]; + + $this->agentServiceRepository->destroy($ids); + + return true; + } + + public static function load($id) + { + if (!self::$agents) { + self::$agents = app(AgentRepository::class)->select(['id', 'name', 'status'])->withTrashed()->get()->keyBy('id')->toArray(); + } + + return self::$agents[$id]; + } +} diff --git a/app/Models/Virtual/Agent.php b/app/Models/Virtual/Agent.php new file mode 100644 index 00000000..d28cbe18 --- /dev/null +++ b/app/Models/Virtual/Agent.php @@ -0,0 +1,56 @@ + 'array', + ]; + + public function company() + { + return $this->belongsTo(Company::class, 'company_id', 'id'); + } +} diff --git a/database/migrations/2020_01_06_140126_add_virtual_order_cards_partition_idx.php b/database/migrations/2020_01_06_140126_add_virtual_order_cards_partition_idx.php new file mode 100644 index 00000000..60e72931 --- /dev/null +++ b/database/migrations/2020_01_06_140126_add_virtual_order_cards_partition_idx.php @@ -0,0 +1,84 @@ +index(['sim']); + $table->index(['company_id', 'package_id']); + $table->index(['service_end_at']); + }); + + Schema::table('virtual_order_renewal_cards', function (Blueprint $table) { + $table->index(['sim']); + $table->index(['company_id', 'package_id']); + $table->index(['service_end_at']); + }); + + Schema::table('virtual_order_renewal_package_cards', function (Blueprint $table) { + $table->index(['sim']); + $table->index(['company_id', 'package_id']); + $table->index(['service_end_at']); + }); + + Schema::table('virtual_order_flows_package_cards', function (Blueprint $table) { + $table->index(['sim']); + $table->index(['company_id', 'package_id']); + $table->index(['service_end_at']); + }); + + Schema::table('virtual_order_upgrade_cards', function (Blueprint $table) { + $table->index(['sim']); + $table->index(['company_id', 'package_id']); + $table->index(['service_end_at']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('virtual_order_cards', function (Blueprint $table) { + $table->dropIndex("virtual_order_cards_sim_index"); + $table->dropIndex("virtual_order_cards_company_id_package_id_index"); + $table->dropIndex("virtual_order_cards_service_end_at_index"); + }); + + Schema::table('virtual_order_renewal_cards', function (Blueprint $table) { + $table->dropIndex("virtual_order_renewal_cards_sim_index"); + $table->dropIndex("virtual_order_renewal_cards_company_id_package_id_index"); + $table->dropIndex("virtual_order_renewal_cards_service_end_at_index"); + }); + + Schema::table('virtual_order_renewal_package_cards', function (Blueprint $table) { + $table->dropIndex("virtual_order_renewal_package_cards_sim_index"); + $table->dropIndex("virtual_order_renewal_package_cards_company_id_package_id_index"); + $table->dropIndex("virtual_order_renewal_package_cards_service_end_at_index"); + }); + + Schema::table('virtual_order_flows_package_cards', function (Blueprint $table) { + $table->dropIndex("virtual_order_flows_package_cards_sim_index"); + $table->dropIndex("virtual_order_flows_package_cards_company_id_package_id_index"); + $table->dropIndex("virtual_order_flows_package_cards_service_end_at_index"); + }); + + Schema::table('virtual_order_upgrade_cards', function (Blueprint $table) { + $table->dropIndex("virtual_order_upgrade_cards_sim_index"); + $table->dropIndex("virtual_order_upgrade_cards_company_id_package_id_index"); + $table->dropIndex("virtual_order_upgrade_cards_service_end_at_index"); + }); + } +} diff --git a/database/migrations/2020_01_06_144305_create_virtual_agents_table.php b/database/migrations/2020_01_06_144305_create_virtual_agents_table.php new file mode 100644 index 00000000..434e7e1d --- /dev/null +++ b/database/migrations/2020_01_06_144305_create_virtual_agents_table.php @@ -0,0 +1,44 @@ +increments('id')->comment('代理商ID'); + $table->integer('company_id')->unsigned()->default(0)->comment('企业ID'); + $table->string('sn', 32)->comment('代理商编号'); + $table->string('name', 32)->default('')->comment('代理商名称'); + $table->string('contacts', 20)->default('')->comment('联系人'); + $table->string('mobile', 20)->default('')->comment('手机号'); + $table->string('address')->default('')->comment('地址'); + $table->text('remark')->nullable()->comment('订单备注'); + $table->text('extends')->nullable()->comment('扩展信息'); + $table->tinyInteger('status')->unsigned()->default(0)->comment('状态 0:正常 1:禁用'); + $table->timestamps(); + $table->softDeletes(); + + $table->unique(['sn', 'deleted_at']); + $table->comment("VD代理商"); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('virtual_agents'); + } +} diff --git a/frontend/src/api/virtual/agents.js b/frontend/src/api/virtual/agents.js new file mode 100644 index 00000000..c75c6893 --- /dev/null +++ b/frontend/src/api/virtual/agents.js @@ -0,0 +1,51 @@ +/** + * 代理商管理 + */ + +/** + * [index 代理商列表] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function index(data) { + return service.get('api/virtual/agents/index', { + params: data + }); +} + +/** + * [show 代理商详情] + * @param {[type]} id [description] + * @return {[type]} [description] + */ +export function show(id) { + return service.get(`api/virtual/agents/show/${id}`); +} + +/** + * [create 创建代理商] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function create(data) { + return serviceForm.post('api/virtual/agents/create', data); +} + +/** + * [update 修改代理商] + * @param {[type]} data [description] + * @param {[type]} id [角色id] + * @return {[type]} [description] + */ +export function update(data, id) { + return serviceForm.post(`api/virtual/agents/update/${id}`, data); +} + +/** + * [destroy 删除代理商] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function destroy(data) { + return service.post('api/virtual/agents/destroy', data); +} diff --git a/frontend/src/router/routes.js b/frontend/src/router/routes.js index 89c25ec2..64c6e8e0 100644 --- a/frontend/src/router/routes.js +++ b/frontend/src/router/routes.js @@ -19,6 +19,7 @@ const routes = [ { path: '/iframe', name: 'Iframe', component: load('iframe/index'), meta: { title: 'iframe' } }, { path: '/companies', name: 'Companies', component: load('virtual/companies/index'), meta: { title: '企业管理' } }, { path: '/company/accounts', name: 'CompanyAccounts', component: load('virtual/company_accounts/index'), meta: { title: '账号管理' } }, + { path: '/agents', name: 'Agents', component: load('virtual/agents/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: '属性管理' } }, diff --git a/frontend/src/views/virtual/agents/detail.vue b/frontend/src/views/virtual/agents/detail.vue new file mode 100644 index 00000000..50775a7d --- /dev/null +++ b/frontend/src/views/virtual/agents/detail.vue @@ -0,0 +1,71 @@ + + + + diff --git a/frontend/src/views/virtual/agents/edit.vue b/frontend/src/views/virtual/agents/edit.vue new file mode 100644 index 00000000..cce6765f --- /dev/null +++ b/frontend/src/views/virtual/agents/edit.vue @@ -0,0 +1,111 @@ + + + diff --git a/frontend/src/views/virtual/agents/index.vue b/frontend/src/views/virtual/agents/index.vue new file mode 100644 index 00000000..51c178ed --- /dev/null +++ b/frontend/src/views/virtual/agents/index.vue @@ -0,0 +1,97 @@ + + + diff --git a/frontend/src/views/virtual/agents/js/detail.js b/frontend/src/views/virtual/agents/js/detail.js new file mode 100644 index 00000000..38536f1c --- /dev/null +++ b/frontend/src/views/virtual/agents/js/detail.js @@ -0,0 +1,29 @@ +export default { + props: { + show: { + type: Boolean, + default: false + }, + data: { + type: Object, + default() { + return null; + } + } + }, + watch: { + show(bool) { + this.my_show = bool; + } + }, + data() { + return { + my_show: false + }; + }, + methods: { + visibleChange(bool) { + this.$emit('update:show', bool); + } + } +}; diff --git a/frontend/src/views/virtual/agents/js/edit.js b/frontend/src/views/virtual/agents/js/edit.js new file mode 100644 index 00000000..95ed312c --- /dev/null +++ b/frontend/src/views/virtual/agents/js/edit.js @@ -0,0 +1,110 @@ +import * as API from 'api/virtual/agents'; + +export default { + props: { + show: { + type: Boolean, + default: false + }, + data: { + type: Object, + default() { + return null; + } + } + }, + data() { + return { + my_show: false, + isUpdate: false, + loading: false, + params: { + company_id: '', + name: '', + contacts: '', + mobile: '', + address: '', + remark: '', + status: 0 + } + }; + }, + watch: { + show(bool) { + this.my_show = bool; + if (bool) { + if (this.data) { + for (let k in this.data) { + if (k in this.params) { + this.params[k] = this.data[k]; + } + } + } + } + } + }, + methods: { + ok() { + if (!this.params.company_id) { + this.$Message.info('请选择代理商'); + return; + } + + if (!this.params.name) { + this.$Message.info('请填写代理商名称'); + return; + } + + if (this.params.contacts && !(/[\s\S]{2,32}/.test(this.params.contacts))) { + this.$Message.info('联系人长度在2-32之间'); + return; + } + + if (this.data) { + // 编辑 + API.update(this.params, this.data.id).then(res => { + this.loading = false; + if (res.code == 0) { + this.$emit('update-success'); + this.$Message.success('更新成功'); + this.clear(); + this.completeCompanyInitialized = false; + } + }).catch(err => { + this.loading = false; + }); + } else { + // 添加 + API.create(this.params).then(res => { + this.loading = false; + if (res.code == 0) { + this.$emit('add-success'); + this.$Message.success('添加成功'); + this.clear(); + this.completeCompanyInitialized = false; + } + }).catch(err => { + this.loading = false; + }); + } + }, + + visibleChange(bool) { + if (!bool) { + this.$emit('update:show', false); + } + }, + + clear() { + for (let k in this.params) { + if (k == 'status') { + this.params[k] = 0; + } else { + this.params[k] = ''; + } + } + + this.my_show = false; + } + } +}; diff --git a/frontend/src/views/virtual/agents/js/index.js b/frontend/src/views/virtual/agents/js/index.js new file mode 100644 index 00000000..ad1d793d --- /dev/null +++ b/frontend/src/views/virtual/agents/js/index.js @@ -0,0 +1,264 @@ +import * as API from "api/virtual/agents"; +export default { + name: "Companies", + components: { + UiEdit: resolve => require(["views/virtual/agents/edit"], resolve), + UiDetail: resolve => require(["views/virtual/agents/detail"], resolve) + }, + data() { + return { + params: { + name: "", + status: "" + }, + trashed: null, + list_data: null, + editObj: { + show: false, + data: null + }, + detailObj: { + show: false, + data: null + }, + search: { + show: false + }, + table_titles: [ + { + title: "ID", + key: "id", + width: 80 + }, + { + title: "代理商名称", + key: "name", + width: 300 + }, + { + title: "所属企业", + key: "company.name", + width: 300 + }, + { + title: "联系人", + key: "contacts" + }, + { + title: "电话", + key: "mobile" + }, + { + title: "地址", + key: "address" + }, + { + title: "状态", + key: "", + width: 100, + render: (h, { row, column, index }) => { + let type = ["primary", "warning", "error"]; + let text = ["已启用", "已禁用", "已删除"]; + + return h( + "Button", + { + props: { + type: type[row.status], + size: "small" + } + }, + text[row.status] + ); + } + }, + { + title: "创建时间", + render: (h, { row, column, index }) => { + return h("span", this.moment(row.created_at).format("YYYY-MM-DD")); + }, + width: 170 + }, + { + title: "操作", + key: "action", + render: (h, { row, column, index }) => { + let html = []; + + if (row.deleted_at) { + return h( + "Tag", + { props: { color: "default" } }, + "该代理商已被删除" + ); + } + + if (this.haveJurisdiction("show")) { + html.push( + h( + "Button", + { + props: { + type: "success", + size: "small", + disabled: false, + icon: "md-eye" + }, + class: ["btn"], + on: { + click: event => { + this.detailObj = { + show: true, + data: row + }; + } + } + }, + "查看" + ) + ); + } + + if (this.haveJurisdiction("update")) { + html.push( + h( + "Button", + { + props: { + type: "primary", + size: "small", + disabled: false, + icon: "md-create" + }, + class: ["btn"], + on: { + click: event => { + this.openEdit(true, row); + } + } + }, + "编辑" + ) + ); + } + + if (this.haveJurisdiction("destroy")) { + html.push( + h( + "Button", + { + props: { + type: "error", + size: "small", + disabled: false, + icon: "md-trash" + }, + class: ["btn"], + on: { + click: () => { + this.$Modal.confirm({ + title: "提示", + content: "删除后该代理商不可使用,请谨慎操作", + onOk: () => { + API.destroy({ + ids: row.id + }).then(res => { + if (res.code == 0) { + this.$Message.success("删除成功"); + this.request(); + } + }); + } + }); + } + } + }, + "删除" + ) + ); + } + + if (html.length) { + return h("div", html); + } + } + } + ] + }; + }, + created() { + this.index(1); + }, + methods: { + /** + * [index 列表] + * @param {Number} page [description] + * @return {[type]} [description] + */ + index(page = 1) { + 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); + if (res.code == 0) { + this.list_data = res.data; + } + }) + .catch(() => { + this.isShowLoading(false); + }); + }, + + /** + * [openEdit 打开编辑弹窗] + * @return {[type]} [description] + */ + openEdit(bool, data = null) { + this.editObj = { + show: bool, + data + }; + }, + + /** + * [request 刷新] + * @return {[type]} [description] + */ + request() { + const result = this.list_data; + let page = result.current_page; + + if (this.list_data.data.length == 1) { + page = this.returnPage( + result.total, + result.current_page, + result.per_page + ); + } + + this.index(page); + }, + + resetSearch() { + for (let k in this.params) { + this.params[k] = ""; + } + this.trashed = null; + this.index(1); + } + } +};