diff --git a/app/Domains/Real/Commands/Sync/MongoSync.php b/app/Domains/Real/Commands/Sync/MongoSync.php index a377c7cc..fb5fda45 100644 --- a/app/Domains/Real/Commands/Sync/MongoSync.php +++ b/app/Domains/Real/Commands/Sync/MongoSync.php @@ -4,6 +4,7 @@ namespace App\Domains\Real\Commands\Sync; use Carbon\Carbon; use App\Models\Card\Card; +use Illuminate\Support\Arr; use MongoDB\BSON\UTCDateTime; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Artisan; @@ -25,7 +26,8 @@ class MongoSync extends Command public function handle() { - $nextMicrotime = $microtime = app(ConfigService::class)->get(self::CURSOR_KEY) ?: 946656000000; + $microtime = app(ConfigService::class)->get(self::CURSOR_KEY) ?: 946656000000; + $nextMicrotime = intval(microtime(true) * 1000); $utcDateTime = new UTCDateTime($microtime); @@ -33,9 +35,8 @@ class MongoSync extends Command $blocs = app(BlocRepository::class)->get()->pluck('id', 'sn')->toArray(); $query = DB::connection('mongo')->table('tblCard') - ->select(['cNo', 'iccid', 'imsi', 'comId', 'oType', 'saDate', 'sDate']) - ->where('isDel', '<>', 1) - ->where('sDate', '>', $utcDateTime) + ->select(['cNo', 'bNo', 'iccid', 'imsi', 'comId', 'oType', 'saDate', 'sDate']) + ->where('oRDate', '>', $utcDateTime) ->orderBy('sDate'); $total = $query->count(); @@ -66,18 +67,22 @@ class MongoSync extends Command 'carrier_operator' => self::$carrierOperators[$value['oType']] ?? 255, 'activated_at' => $activated_at, 'virtual_activated_at' => $activated_at, + 'order_status' => intval(!empty($value['bNo'])), 'created_at' => $value['sDate']->toDateTime()->format('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]; - - $nextMicrotime = (string) $value['sDate']; - - $nextMicrotime = ($nextMicrotime > $microtime) ? $nextMicrotime : $microtime; } - Card::upsert($values, 'sim', true); + $builder = Card::query()->toBase(); - app(ConfigService::class)->set(self::CURSOR_KEY, intval($nextMicrotime)); + $sql = $builder->getGrammar()->compileInsert($builder, $values); + + $sql .= 'on conflict (sim) do update set + activated_at=excluded.activated_at, + virtual_activated_at=COALESCE(cards.virtual_activated_at, excluded.activated_at), + order_status=excluded.order_status'; + + $builder->connection->insert($sql, Arr::flatten($array, 1)); if ($page * $this->limit >= $total) { break; @@ -86,6 +91,8 @@ class MongoSync extends Command $page++; } + app(ConfigService::class)->set(self::CURSOR_KEY, intval($nextMicrotime)); + app(CardRepository::class)->forgetCached(); } } diff --git a/app/Domains/Stats/Http/Controllers/OrderController.php b/app/Domains/Stats/Http/Controllers/OrderController.php index 0be1fe2c..de801944 100644 --- a/app/Domains/Stats/Http/Controllers/OrderController.php +++ b/app/Domains/Stats/Http/Controllers/OrderController.php @@ -24,44 +24,27 @@ class OrderController extends Controller * * @return \Illuminate\Http\Response */ - public function index($type) + public function index() { $conditions = $this->request->all(); - $conditions['type'] = $type; - $res = $this->OrderService->index($conditions); + $res = $this->orderService->index($conditions); return res($res, '订单统计', 201); } /** - * 创建. + * 统计明细. * * @return \Illuminate\Http\Response */ - public function create() + public function detail() { - // - } + $conditions = $this->request->all(); - /** - * 编辑. - * - * @return \Illuminate\Http\Response - */ - public function update($id) - { - // - } - - /** - * 删除. - * - * @return \Illuminate\Http\Response - */ - public function destroy() - { - // + $res = $this->orderService->detail($conditions); + + return res($res, '统计明细', 201); } } diff --git a/app/Domains/Stats/Routes/api.php b/app/Domains/Stats/Routes/api.php index 3d2e2dff..3cc90eba 100644 --- a/app/Domains/Stats/Routes/api.php +++ b/app/Domains/Stats/Routes/api.php @@ -7,6 +7,6 @@ $router->group(['prefix' => 'stats', 'as' => 'stats', 'middleware' => ['adminAut $router->get('/company-count/export', ['as' => 'company-count.export', 'uses' => 'CompanyCountController@export']); // 订单统计 - $router->get('/order/{type}', ['as' => 'order.index', 'uses' => 'OrderController@index']); + $router->get('/order', ['as' => 'order.index', 'uses' => 'OrderController@index']); }); diff --git a/app/Domains/Stats/Services/OrderService.php b/app/Domains/Stats/Services/OrderService.php index 0283d318..c30d0feb 100644 --- a/app/Domains/Stats/Services/OrderService.php +++ b/app/Domains/Stats/Services/OrderService.php @@ -43,16 +43,15 @@ class OrderService extends Service * * @return void */ - public function index($type, array $conditions = []) + public function index(array $conditions = []) { - $conditions['type'] = $type; $conditions['source'] = 1; $companies = $this->companyRepository->withTrashed()->get()->pluck('name', 'id')->toArray(); $packages = $this->packageRepository->withTrashed()->get()->pluck('name', 'id')->toArray(); $select = [ - DB::raw("string_agg(DISTINCT id, ',') as id"), + DB::raw("array_to_string(array_agg(id), ',') as order_id"), 'company_id', 'package_id', 'product_id', @@ -63,10 +62,12 @@ class OrderService extends Service ]; $orders = $this->orderRepository->select($select)->withConditions($conditions)->applyConditions() - ->groupBy(['company_id', 'product_id', 'pay_channel'])->paginate($conditions['limit']); - + ->groupBy(['company_id', 'package_id', 'product_id', 'unit_price', 'pay_channel'])->paginate($conditions['limit']); + $orders->map(function ($item) use ($companies, $packages) { - $item->company_name = $packages[$item->company_id]; + $item->unit_price = floatval(sprintf('%.02f', $item->unit_price/100)); + $item->custom_price = floatval(sprintf('%.02f', $item->custom_price/100)); + $item->company_name = $companies[$item->company_id]; $item->package_name = $packages[$item->package_id]; $item->pay_channel_name = CommonService::namePayChannel($item->pay_channel); }); @@ -88,29 +89,19 @@ class OrderService extends Service $repository = app($class); $companies = $this->companyRepository->withTrashed()->get()->pluck('name', 'id')->toArray(); - $packages = $this->packageRepository->withTrashed()->get()->pluck('name', 'id')->toArray(); + $packages = $this->packageRepository->withTrashed()->get()->keyBy('id')->toArray(); - $repository->withConditions($conditions)->applyConditions()->paginate($conditions['limit']); + $cards = $repository->with('order:id,unit_price,pay_channel,order_at')->withConditions($conditions)->applyConditions()->paginate($conditions['limit']); - $select = [ - 'id', - 'company_id', - 'package_id', - 'unit_price', - 'pay_channel', - DB::raw('SUM(counts) as counts'), - DB::raw('SUM(custom_price) as custom_price'), - ]; - - $orders = $this->orderRepository->select($select)->withConditions($conditions)->applyConditions() - ->groupBy(['company_id', 'product_id', 'pay_channel'])->paginate($conditions['limit']); - - $orders->map(function ($item) use ($companies, $packages) { + $cards->map(function ($item) use ($companies, $packages) { $item->company_name = $packages[$item->company_id]; - $item->package_name = $packages[$item->package_id]; - $item->pay_channel_name = CommonService::namePayChannel($item->pay_channel); + $item->package_name = $packages[$item->package_id]['name']; + $item->service_months = $packages[$item->package_id]['service_months']; + $item->unit_price = $item->order['unit_price']; + $item->pay_channel_name = CommonService::namePayChannel($item->order['pay_channel']); + $item->order_at = $item->order['order_at']; }); - return $orders; + return $cards; } } diff --git a/app/Domains/Virtual/Repositories/Concerns/OrderCardConcern.php b/app/Domains/Virtual/Repositories/Concerns/OrderCardConcern.php index f7f2c323..402041f7 100644 --- a/app/Domains/Virtual/Repositories/Concerns/OrderCardConcern.php +++ b/app/Domains/Virtual/Repositories/Concerns/OrderCardConcern.php @@ -18,6 +18,11 @@ trait OrderCardConcern $this->model = $this->model->whereIn('id', $conditions['id']); } + if (isset($conditions['order_id'])) { + $conditions['order_id'] = array_wrap($conditions['order_id']); + $this->model = $this->model->whereIn('order_id', $conditions['order_id']); + } + if (isset($conditions['sim'])) { $conditions['sim'] = array_wrap($conditions['sim']); $this->model = $this->model->whereIn('sim', $conditions['sim']); diff --git a/database/migrations/2018_12_24_164218_create_cards_table.php b/database/migrations/2018_12_24_164218_create_cards_table.php index 739b59e5..783b4698 100644 --- a/database/migrations/2018_12_24_164218_create_cards_table.php +++ b/database/migrations/2018_12_24_164218_create_cards_table.php @@ -26,6 +26,7 @@ class CreateCardsTable extends Migration $table->timestamp('activated_at')->nullable()->comment('激活时间'); $table->timestamp('virtual_activated_at')->nullable()->comment('虚拟激活时间'); $table->tinyInteger('type')->unsigned()->default(0)->comment('类型(0:真实卡 1:虚拟卡 2:未知卡)'); + $table->tinyInteger('order_status')->unsigned()->default(0)->comment('订单状态 0:正常 1:退货'); $table->timestamp('cancelled_at')->nullable()->comment('注销时间'); $table->timestamps(); diff --git a/database/seeds/PermissionSeeder.php b/database/seeds/PermissionSeeder.php index d36cd255..b0baacb3 100644 --- a/database/seeds/PermissionSeeder.php +++ b/database/seeds/PermissionSeeder.php @@ -153,9 +153,11 @@ class PermissionSeeder extends Seeder 'type' => 0, 'open' => 3, 'children' => [ - [ - 'name' => 'stats.company-index', 'title' => '企业统计', 'path' => '/stats/company-count', 'icon' => 'md-pulse', 'type' => 0, 'open' => 3 - ], + ['name' => 'stats.company-index', 'title' => '企业统计', 'path' => '/stats/company-count', 'icon' => 'md-pulse', 'type' => 0, 'open' => 3], + ['name' => 'stats.order.0', 'title' => '销售订单统计', 'path' => '/stats/order/0', 'icon' => 'md-pulse', 'type' => 0, 'open' => 3], + ['name' => 'stats.order.1', 'title' => '续费订单统计', 'path' => '/stats/order/1', 'icon' => 'md-pulse', 'type' => 0, 'open' => 3], + ['name' => 'stats.order.2', 'title' => '续费包订单统计', 'path' => '/stats/order/2', 'icon' => 'md-pulse', 'type' => 0, 'open' => 3], + ['name' => 'stats.order.3', 'title' => '加油包订单统计', 'path' => '/stats/order/3', 'icon' => 'md-pulse', 'type' => 0, 'open' => 3], ], ], [ diff --git a/frontend/src/router/routes.js b/frontend/src/router/routes.js index 648dee6a..473e4060 100644 --- a/frontend/src/router/routes.js +++ b/frontend/src/router/routes.js @@ -24,7 +24,8 @@ const routes = [ { path: '/packages/:type', name: 'Packages', component: load('virtual/packages/index'), meta: { title: '套餐管理' } }, { path: '/cards', name: 'Cards', component: load('virtual/cards/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/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: '订单统计' } } ] }, { path: '*', redirect: { path: '/home' } } diff --git a/frontend/src/views/stats/company-count/js/index.js b/frontend/src/views/stats/company-count/js/index.js index b87a7973..3644d59a 100644 --- a/frontend/src/views/stats/company-count/js/index.js +++ b/frontend/src/views/stats/company-count/js/index.js @@ -52,9 +52,7 @@ export default { }; }, created() { - window.t = this; this.index(); - console.log(this.$refs.table); }, methods: { /** @@ -129,7 +127,6 @@ export default { this.$nextTick(() => { setTimeout(() => { let html = $('.ivu-table-header colgroup').html(); - console.log(html); $('.table-footer-colgroup').html(html); }, 10); }); diff --git a/frontend/src/views/stats/order/index.vue b/frontend/src/views/stats/order/index.vue new file mode 100644 index 00000000..134b55a0 --- /dev/null +++ b/frontend/src/views/stats/order/index.vue @@ -0,0 +1,114 @@ + + + diff --git a/frontend/src/views/stats/order/js/index.js b/frontend/src/views/stats/order/js/index.js new file mode 100644 index 00000000..d4e67a29 --- /dev/null +++ b/frontend/src/views/stats/order/js/index.js @@ -0,0 +1,162 @@ +import { sumBy } from 'service/util'; +export default { + name: 'StatsOrder', + data() { + return { + search: { + show: true + }, + options: { + company_name: null, + package_name: null, + pay_channel: null, + time: null + }, + data: [], + list: [], + stats: {}, + page: { + total: 0, + limit: 10, + page: 1 + }, + columns: [ + { + title: '企业名称', + key: 'company_name' + }, + { + title: '套餐名称', + key: 'package_name', + width: 150 + }, + { + title: '支付方式', + key: 'pay_channel_name', + width: 120 + }, + { + title: '销售单价', + key: 'unit_price', + width: 120 + }, + { + title: '销售数量', + key: 'counts', + width: 120 + }, + { + title: '销售总金额', + key: 'custom_price', + width: 120 + } + ] + }; + }, + created() { + this.type = Number(this.$route.params.type); + this.index(); + }, + methods: { + /** + * [index 列表] + * @param {Number} page [description] + * @return {[type]} [description] + */ + index() { + let options = Object.assign({ + orderBy: 'company_id', + sortedBy: 'asc', + type: this.type + }, + this.options); + + let params = this.searchDataHandle({}, { + limit: 0 + }, options); + + this.isShowLoading(true); + + service.get('api/stats/order', { params }).then(res => { + this.isShowLoading(false); + if (res.code == 0) { + this.list = res.data; + this.page.total = this.list.length; + this.changePage(1); + } + }).catch(() => { + this.isShowLoading(false); + }); + }, + + /** + * [request 刷新] + * @return {[type]} [description] + */ + request() { + let page = this.page.page; + + if (this.data.length == 1) { + page = this.returnPage(this.page.total, this.page.page, this.page.limit); + } + + this.index(); + this.changePage(page); + }, + + resetSearch() { + for (let k in this.options) { + this.options[k] = null; + } + + this.index(); + }, + changeLimit(limit) { + this.page.limit = limit; + this.changePage(1); + }, + changePage(page) { + this.page.page = page; + this.data = this.list.slice((page - 1) * this.page.limit, page * this.page.limit); + + this.stats = { + counts: sumBy(this.list, 'counts'), + custom_price: sumBy(this.list, 'custom_price') + }; + + this.$nextTick(() => { + setTimeout(() => { + let html = $('.ivu-table-header colgroup').html(); + $('.table-footer-colgroup').html(html); + }, 10); + }); + }, + + exportExcel() { + this.isShowLoading(true); + + let options = Object.assign({ + orderBy: 'id', + sortedBy: 'asc' + }, + + this.options); + + let params = this.searchDataHandle({}, {}, options); + + this.isShowLoading(true); + + service.get('api/stats/company-count/export', { + params + }).then((res) => { + if (res.code === 0) { + this.downloadFile(res.data); + } + + this.isShowLoading(false); + }).catch(() => { + this.isShowLoading(false); + }); + } + } +}; diff --git a/tests/MongoTest.php b/tests/MongoTest.php index c0b069be..73f1e9f7 100644 --- a/tests/MongoTest.php +++ b/tests/MongoTest.php @@ -1,25 +1,10 @@ modify('-1 month'); -echo $date->format('Y-m-d'); -$res = Carbon::parse('2018-10')->endOfMonth()->subMonth(); +$count = DB::connection('mongo')->table('tblCard') +->count(); -dd($res); - -$conditions = [ - 'starttime' => Carbon::parse('2018-10-01')->startOfDay(), - 'endtime' => Carbon::parse('2018-10-31')->startOfDay(), -]; - -$res = \DB::connection('mongo')->table('tblCard')->where(function ($query) use ($conditions) { - $query->where('exPCodes.cDate', '>=', $conditions['starttime'])->where('exPCodes.cDate', '<=', $conditions['endtime'])->where('oDate', 'exists', false); -})->orWhere(function ($query) use ($conditions) { - $query->where('exPCodes.oDate', '>=', $conditions['starttime'])->where('exPCodes.oDate', '<=', $conditions['endtime'])->where('pType', 0); -})->first(); - -dd($res['exPCodes']); +dd($count);