diff --git a/app/Domains/Log/config.php b/app/Domains/Log/config.php index 71c27c60..08f6c78b 100644 --- a/app/Domains/Log/config.php +++ b/app/Domains/Log/config.php @@ -4,80 +4,83 @@ return [ 'mmdb_path' => storage_path('app/files/GeoLite2-City.mmdb'), 'actions' => [ - // Account - 'api.accounts.create' => '创建账号', - 'api.accounts.destroy' => '删除账号', - 'api.accounts.update' => '修改账号', + // Account + 'api.accounts.create' => '创建账号', + 'api.accounts.destroy' => '删除账号', + 'api.accounts.update' => '修改账号', - // App - 'api.apps.create' => '创建应用', - 'api.apps.destroy' => '删除应用', - 'api.apps.update' => '修改应用', + // App + 'api.apps.create' => '创建应用', + 'api.apps.destroy' => '删除应用', + 'api.apps.update' => '修改应用', - // Auth - 'api.auth.admin.login' => '登录账号', - 'api.auth.admin.logout' => '登出账号', - 'api.auth.user.login' => '用户登录', - 'api.auth.user.logout' => '用户登出', + // Auth + 'api.auth.admin.login' => '登录账号', + 'api.auth.admin.logout' => '登出账号', + 'api.auth.user.login' => '用户登录', + 'api.auth.user.logout' => '用户登出', - // Category - 'api.categories.create' => '创建分类', - 'api.categories.destroy' => '删除分类', - 'api.categories.update' => '修改分类', + // Category + 'api.categories.create' => '创建分类', + 'api.categories.destroy' => '删除分类', + 'api.categories.update' => '修改分类', - // Config - 'api.configs.set' => '修改配置', + // Config + 'api.configs.set' => '修改配置', - // File - 'api.files.destroy' => '删除文件', - 'api.files.upload' => '上传文件', + // File + 'api.files.destroy' => '删除文件', + 'api.files.upload' => '上传文件', - // Log - 'api.logs.destroy' => '删除日志', + // Log + 'api.logs.destroy' => '删除日志', - // Permission - 'api.permissions.create' => '添加权限', - 'api.permissions.destroy' => '删除权限', - 'api.permissions.update' => '修改权限', + // Permission + 'api.permissions.create' => '添加权限', + 'api.permissions.destroy' => '删除权限', + 'api.permissions.update' => '修改权限', - // Role - 'api.roles.create' => '创建角色', - 'api.roles.destroy' => '删除角色', - 'api.roles.sync_permissions' => '角色分配权限', - 'api.roles.sync_roles' => '分配角色', - 'api.roles.update' => '修改角色', + // Role + 'api.roles.create' => '创建角色', + 'api.roles.destroy' => '删除角色', + 'api.roles.sync_permissions' => '角色分配权限', + 'api.roles.sync_roles' => '分配角色', + 'api.roles.update' => '修改角色', - // SMS - 'api.sms.index' => '发送短信', + // SMS + 'api.sms.index' => '发送短信', - // User - 'api.users.create' => '创建用户', - 'api.users.destroy' => '删除用户', - 'api.users.update' => '修改用户', + // User + 'api.users.create' => '创建用户', + 'api.users.destroy' => '删除用户', + 'api.users.update' => '修改用户', - // 企业管理 - 'api.virtual.companies.create' => '创建企业', - 'api.virtual.companies.destroy' => '删除企业', - 'api.virtual.companies.update' => '修改企业', + // 企业管理 + 'api.virtual.companies.create' => '创建企业', + 'api.virtual.companies.destroy' => '删除企业', + 'api.virtual.companies.update' => '修改企业', // 企业账号 - 'api.virtual.company.accounts.create' => '创建企业账号', - 'api.virtual.company.accounts.destroy' => '删除企业账号', - 'api.virtual.company.accounts.update' => '修改企业账号', + 'api.virtual.company.accounts.create' => '创建企业账号', + 'api.virtual.company.accounts.destroy' => '删除企业账号', + 'api.virtual.company.accounts.update' => '修改企业账号', // 订单管理 - 'api.virtual.orders.create' => '创建订单', - 'api.virtual.orders.destroy' => '删除订单', - 'api.virtual.orders.update' => '修改订单', + 'api.virtual.orders.create' => '创建订单', + 'api.virtual.orders.destroy' => '删除订单', + 'api.virtual.orders.update' => '修改订单', + + // 退货管理 + 'api.virtual.refunds.create' => '添加退货', // 套餐管理 - 'api.virtual.packages.create' => '创建订单', - 'api.virtual.packages.destroy' => '删除订单', - 'api.virtual.packages.update' => '修改订单', + 'api.virtual.packages.create' => '创建订单', + 'api.virtual.packages.destroy' => '删除订单', + 'api.virtual.packages.update' => '修改订单', // 定价管理 - 'api.virtual.products.create' => '创建定价', - 'api.virtual.products.destroy' => '删除定价', - 'api.virtual.products.update' => '修改定价', + 'api.virtual.products.create' => '创建定价', + 'api.virtual.products.destroy' => '删除定价', + 'api.virtual.products.update' => '修改定价', ], ]; diff --git a/app/Domains/Virtual/Http/Controllers/OrderController.php b/app/Domains/Virtual/Http/Controllers/OrderController.php index e33ff0bf..6a62a9e9 100644 --- a/app/Domains/Virtual/Http/Controllers/OrderController.php +++ b/app/Domains/Virtual/Http/Controllers/OrderController.php @@ -1,4 +1,5 @@ $item->transaction_status, 'transaction_status_name' => $transactionStatuses[$item->transaction_status], 'remark' => $item->remark ?? '', - 'order_at' => (string)$item->order_at, - 'deleted_at' => (string)$item->deleted_at, + 'order_at' => (string) $item->order_at, + 'deleted_at' => (string) $item->deleted_at, 'area' => $item->area ?? [], 'address' => $item->address ?? '', 'contacts' => $item->contacts, diff --git a/app/Domains/Virtual/Http/Controllers/RefundController.php b/app/Domains/Virtual/Http/Controllers/RefundController.php new file mode 100644 index 00000000..c69941e9 --- /dev/null +++ b/app/Domains/Virtual/Http/Controllers/RefundController.php @@ -0,0 +1,87 @@ +request = $request; + $this->orderService = $orderService; + } + + /** + * 列表. + * + * @return \Illuminate\Http\Response + */ + public function index() + { + $conditions = $this->request->all(); + $conditions['limit'] = $this->request->get('limit', 20); + + $refunds = $this->orderService->refundedList($conditions); + + return res($refunds, '退货列表', 201); + } + + /** + * 检查退货. + * + * @return \Illuminate\Http\Response + */ + public function check() + { + $sim = $this->request->get('sim'); + + if (empty($sim)) { + throw new InvalidArgumentException('sim卡号不能为空'); + } + + $sim = array_map('intval', array_map('trim', str_to_array($sim, "\n"))); + + $refunded_at = $this->request->get('refunded_at', date('Y-m-d H:i:s')); + + $refunded_at = Carbon::parse($refunded_at); + + $res = $this->orderService->refundedCheck($sim, $refunded_at); + + return res($res, '检查退货', 201); + } + + /** + * 添加退货. + * + * @return \Illuminate\Http\Response + */ + public function create() + { + $sim = $this->request->get('sim'); + + if (empty($sim)) { + throw new InvalidArgumentException('sim卡号不能为空'); + } + + $sim = array_map('intval', array_map('trim', str_to_array($sim, "\n"))); + + $refunded_at = $this->request->get('refunded_at', date('Y-m-d H:i:s')); + + $refunded_at = Carbon::parse($refunded_at); + + $this->orderService->refundedCreate($sim, $refunded_at); + + return res(true, '退货成功'); + } +} diff --git a/app/Domains/Virtual/Repositories/Concerns/OrderCardConcern.php b/app/Domains/Virtual/Repositories/Concerns/OrderCardConcern.php index 3a608f7a..5ca349bd 100644 --- a/app/Domains/Virtual/Repositories/Concerns/OrderCardConcern.php +++ b/app/Domains/Virtual/Repositories/Concerns/OrderCardConcern.php @@ -96,6 +96,14 @@ trait OrderCardConcern $query->where('service_end_at', '<=', Carbon::parse($conditions['service_end_endtime'])); } + if (isset($conditions['refunded_starttime'])) { + $query->where('refunded_at', '>=', Carbon::parse($conditions['refunded_starttime'])); + } + + if (isset($conditions['refunded_endtime'])) { + $query->where('refunded_at', '<=', Carbon::parse($conditions['refunded_endtime'])); + } + if (isset($conditions['month'])) { $time = Carbon::parse($conditions['month'])->format('Y-m-d H:i:s'); $query->where(function ($subQuery) use ($time) { @@ -109,7 +117,7 @@ trait OrderCardConcern 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'])); + ->where('activated_at', '<=', Carbon::parse($conditions['activated_endtime'])); }); } @@ -124,6 +132,10 @@ trait OrderCardConcern $relation->where('transaction_no', 'like', "%{$conditions['transaction_no']}%"); }); } + + if (isset($conditions['is_refunded'])) { + $query->whereNotNull('refunded_at'); + } }); if (isset($conditions['card_status'])) { @@ -144,20 +156,20 @@ trait OrderCardConcern $havingRaw = sprintf("MAX(service_end_at) >= '%s'", date('Y-m-d H:i:s')); $this->model = $this->model->whereNotNull('service_start_at') - ->groupBy('sim')->havingRaw($havingRaw) - ->whereHas('card', function ($relation) { - $relation->whereNull('cancelled_at'); - }); + ->groupBy('sim')->havingRaw($havingRaw) + ->whereHas('card', function ($relation) { + $relation->whereNull('cancelled_at'); + }); break; case 3: $havingRaw = sprintf("MAX(service_end_at) < '%s'", date('Y-m-d H:i:s')); $this->model = $this->model->whereNotNull('service_start_at') - ->groupBy('sim')->havingRaw($havingRaw) - ->whereHas('card', function ($relation) { - $relation->whereNull('cancelled_at'); - }); + ->groupBy('sim')->havingRaw($havingRaw) + ->whereHas('card', function ($relation) { + $relation->whereNull('cancelled_at'); + }); break; case 4: diff --git a/app/Domains/Virtual/Routes/api.php b/app/Domains/Virtual/Routes/api.php index c28b790f..70ea7ec0 100644 --- a/app/Domains/Virtual/Routes/api.php +++ b/app/Domains/Virtual/Routes/api.php @@ -63,6 +63,11 @@ $router->group(['prefix' => 'virtual', 'as' => 'virtual', 'middleware' => ['admi $router->post('/orders/ship', ['as' => 'orders.ship', 'uses' => 'OrderController@ship']); $router->post('/orders/ship-not-base', ['as' => 'orders.shipNotBase', 'uses' => 'OrderController@shipNotBase']); + // 退货管理 + $router->get('/refunds/index', ['as' => 'refunds.index', 'uses' => 'RefundController@index']); + $router->post('/refunds/check', ['as' => 'refunds.check', 'uses' => 'RefundController@check']); + $router->post('/refunds/create', ['as' => 'refunds.create', 'uses' => 'RefundController@create']); + // 客户管理 $router->addRoute(['GET', 'POST'], '/cards/index', ['as' => 'cards.index', 'uses' => 'CardController@index']); $router->get('/cards/export', ['as' => 'cards.export', 'uses' => 'CardController@export']); diff --git a/app/Domains/Virtual/Services/OrderService.php b/app/Domains/Virtual/Services/OrderService.php index d71955aa..c5f35e7f 100644 --- a/app/Domains/Virtual/Services/OrderService.php +++ b/app/Domains/Virtual/Services/OrderService.php @@ -1,4 +1,5 @@ onQueue('sync'); } + + public function refundedList(array $conditions = []) + { + $conditions['type'] = 0; + $conditions['is_refunded'] = true; + $limit = $conditions['limit'] ?? 35; + + $res = $this->orderCardPartitionRepository->withRefunded() + ->withConditions($conditions)->applyConditions()->orderBy('refunded_at', 'desc')->paginate($limit); + + $orders = $this->orderRepository->withConditions(['id' => $res->pluck('order_id')->unique()->toArray()])->get()->keyBy('id'); + $carrierOperators = app(Dicts::class)->get('carrier_operator'); + + + $res->map(function ($item) use ($orders, $carrierOperators) { + $package = PackageService::load($item->package_id); + $item->company_name = CompanyService::load($item->company_id)['name']; + $item->package_name = $package['name']; + $item->carrier_operator = $carrierOperators[$package['carrier_operator']]; + $item->order_sn = $orders[$item['order_id']]['sn']; + }); + + return $res; + } + + public function refundedCheck(array $sim = [], $refunded_at) + { + $errors = []; + + $res = $this->orderCardPartitionRepository->withRefunded()->select('sim', 'type', 'refunded_at', 'service_start_at', 'service_end_at') + ->withConditions(['sim' => $sim, 'endtime' => $refunded_at])->get(); + + // 检查卡是否存在 + $notExists = array_diff($sim, $res->where('type', 0)->pluck('sim')->unique()->toArray()); + foreach ($notExists as $value) { + $errors[$value] = isset($errors[$value]) ? $errors[$value] . ' | ' . '不存在' : '不存在'; + } + + // 检查已退货过 + $refunds = $res->where('type', 0)->where('refunded_at', '<>', '')->pluck('sim')->unique()->toArray(); + foreach ($refunds as $value) { + $errors[$value] = isset($errors[$value]) ? $errors[$value] . ' | ' . '已退货' : '已退货'; + } + + // 检查卡是否已激活 + $activates = $res->where('type', 0)->where('service_start_at', '<>', '')->pluck('sim')->unique()->toArray(); + foreach ($activates as $value) { + $errors[$value] = isset($errors[$value]) ? $errors[$value] . ' | ' . '已激活' : '已激活'; + } + + // 检查是否有其他订单 + $otherOrders = $res->where('type', '<>', 0)->pluck('sim')->unique()->toArray(); + foreach ($otherOrders as $value) { + $errors[$value] = isset($errors[$value]) ? $errors[$value] . ' | ' . '存在其他订单' : '存在其他订单'; + } + + $return = []; + + foreach ($errors as $key => $value) { + array_push($return, ['sim' => $key, 'errors' => $value]); + } + + return $return; + } + + public function refundedCreate(array $sim = [], $refunded_at) + { + $sql = " + UPDATE virtual_order_cards_partition AS v + SET refunded_at = '%s' + FROM cards AS c + WHERE c.sim = v.sim + AND v.sim IN (%s) + AND v.created_at <= '%s' + "; + + $simArrayText = implode(',', $sim); + + DB::update(sprintf($sql, $refunded_at, $simArrayText, $refunded_at)); + + $this->orderCardPartitionRepository->forgetCached(); + + return true; + } } diff --git a/database/seeds/PermissionSeeder.php b/database/seeds/PermissionSeeder.php index 1d8101df..64c60696 100644 --- a/database/seeds/PermissionSeeder.php +++ b/database/seeds/PermissionSeeder.php @@ -51,8 +51,8 @@ class PermissionSeeder extends Seeder [ 'name' => 'logs', 'title' => '日志管理', 'path' => '/logs', 'icon' => 'ios-cube', 'type' => 0, 'open' => 3, 'children' => [ - ['name' => 'logs.show','title' => '查看','description' => 'show','type' => 1], - ['name' => 'logs.destroy','title' => '删除','description' => 'destroy','type' => 1], + ['name' => 'logs.show', 'title' => '查看', 'description' => 'show', 'type' => 1], + ['name' => 'logs.destroy', 'title' => '删除', 'description' => 'destroy', 'type' => 1], ], ], ], @@ -229,6 +229,16 @@ class PermissionSeeder extends Seeder ['name' => 'virtual.orders.3.locked', 'title' => '解锁', 'description' => 'locked', 'type' => 1], ], ], + [ + 'name' => 'virtual.refunds.index', 'title' => '退货管理', 'path' => '/refunds', 'icon' => 'md-undo', 'type' => 0, 'open' => 3, + 'children' => [ + ['name' => 'virtual.refunds.show', 'title' => '查看', 'description' => 'show', 'type' => 1], + ['name' => 'virtual.refunds.create', 'title' => '创建', 'description' => 'create', 'type' => 1], + ['name' => 'virtual.refunds.update', 'title' => '编辑', 'description' => 'update', 'type' => 1], + ['name' => 'virtual.refunds.destroy', 'title' => '删除', 'description' => 'destroy', 'type' => 1], + ['name' => 'virtual.refunds.output', 'title' => '导出', 'description' => 'output', 'type' => 1], + ], + ], ], ], [ diff --git a/frontend/src/api/virtual/refunds.js b/frontend/src/api/virtual/refunds.js new file mode 100644 index 00000000..971e5a4e --- /dev/null +++ b/frontend/src/api/virtual/refunds.js @@ -0,0 +1,32 @@ +/** + * 退货管理 + */ + +/** + * [index 退货列表] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function index(data) { + return service.get("api/virtual/refunds/index", { + params: data + }); +} + +/** + * [check 检查退货] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function check(data) { + return service.post("api/virtual/refunds/check", data); +} + +/** + * [create 添加退货] + * @param {[type]} data [description] + * @return {[type]} [description] + */ +export function create(data) { + return service.post("api/virtual/refunds/create", data); +} diff --git a/frontend/src/router/routes.js b/frontend/src/router/routes.js index a149966b..89c25ec2 100644 --- a/frontend/src/router/routes.js +++ b/frontend/src/router/routes.js @@ -24,6 +24,7 @@ const routes = [ { 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: '/refunds', name: 'Refunds', component: load('virtual/refunds/index'), meta: { title: '退货管理' } }, { path: '/exports', name: 'StatsExports', component: load('exports/index'), meta: { title: '导出记录' } }, { path: '/stats/company-count', name: 'StatsCompanyCount', component: load('stats/company-count/index'), meta: { title: '企业统计' } }, { path: '/stats/order/:type', name: 'StatsOrder', component: load('stats/order/index'), meta: { title: '订单统计' } }, diff --git a/frontend/src/views/virtual/orders/index.vue b/frontend/src/views/virtual/orders/index.vue index cc541471..b59a2370 100644 --- a/frontend/src/views/virtual/orders/index.vue +++ b/frontend/src/views/virtual/orders/index.vue @@ -137,7 +137,7 @@
+
+ +
+