Bladeren bron

集計機能追加

develop
sosuke.iwabuchi 2 jaren geleden
bovenliggende
commit
2fe646a629
11 gewijzigde bestanden met toevoegingen van 549 en 5 verwijderingen
  1. +343
    -0
      app/Console/Commands/SummaryUse.php
  2. +1
    -1
      app/Models/BaseModel.php
  3. +3
    -3
      app/Models/Ex/LoginUser.php
  4. +3
    -0
      app/Models/SMSSendOrder.php
  5. +25
    -0
      app/Models/UseByKeySummary.php
  6. +12
    -0
      app/Models/UseByKeySummaryHistory.php
  7. +28
    -0
      app/Models/UseSummary.php
  8. +12
    -0
      app/Models/UseSummaryHistory.php
  9. +17
    -1
      app/Util/MigrationHelper.php
  10. +50
    -0
      database/migrations/2023_06_07_144200_create_use_summaries_table.php
  11. +55
    -0
      database/migrations/2023_06_08_092700_create_use_by_key_summaries_table.php

+ 343
- 0
app/Console/Commands/SummaryUse.php Bestand weergeven

@@ -0,0 +1,343 @@
<?php

namespace App\Console\Commands;

use App\Logic\SMS\SMSManager;
use App\Models\Contract;
use App\Models\ReceiptIssuingOrder;
use App\Models\SMSSendOrder;
use App\Models\UseByKeySummary;
use App\Models\UseSummary;
use App\Util\DateUtil;
use App\Util\DBUtil;
use Exception;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use LogicException;

class SummaryUse extends BaseCommand
{

const COMMAND = "summary:use {--contractId=} {--yyyymm=}";

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = self::COMMAND;

/**
* The console command description.
*
* @var string
*/
protected $description = '利用実績を集計する';


static public function getCommand()
{
return self::COMMAND;
}


private string $targetYYYYMM = "";
private ?Carbon $targetDate = null;


/**
* Create a new command instance.
*
* @return void
*/
public function __construct(private SMSManager $manager)
{
parent::__construct();
}

/**
* Execute the console command.
*
* @return int
*/
public function service(): int
{
$this->setTargetYYYYMM();
$this->outputInfo(sprintf("対象集計年月: %s", $this->targetYYYYMM));

$db = DBUtil::instance();
try {
$db->beginTransaction();
foreach ($this->getTargetContractIds() as $contractId) {
$this->handleTarget($contractId);
}

$db->commit();
} catch (Exception $e) {
$db->rollBack();
throw $e;
}



return self::RESULTCODE_SUCCESS;
}

private function handleTarget(string $contractId)
{
// 集計対象のモデルを取得
$model = $this->getModel($contractId);

// 集計
$summaryByKeys = $this->summary($model);

// 保存
$this->save($model, $summaryByKeys);
}

private function getTargetContractIds(): array
{
if ($this->option('contractId')) {

return [$this->option('contractId')];
}
return Contract::pluck(Contract::COL_NAME_ID)->toArray();
}

private function setTargetYYYYMM()
{
$yyyyMM = $this->option('yyyymm');

// デフォルト判定
if ($yyyyMM === null) {
// デフォルトは先月とする
$this->targetDate = DateUtil::now()->addMonth(-1)->setDay(1)->setTime(0, 0);
$this->targetYYYYMM = $this->targetDate->format('Ym');
return;
}

// フォーマットチェック
if (!preg_match("/^\d{6}$/", $yyyyMM)) {
throw new Exception('YYYYMM不正');
}
// 日付チェック
$tmpDate = Carbon::createFromFormat('Ym', $yyyyMM);
if (!$tmpDate->isValid()) {
throw new Exception('YYYYMM 日付不正');
}

$this->targetDate = $tmpDate->setDay(1)->setTime(0, 0);;
$this->targetYYYYMM = $this->targetDate->format('Ym');;
}

private function getModel(string $contractId): UseSummary
{
$model = UseSummary::whereContractId($contractId)
->whereSummaryYyyymm($this->targetYYYYMM)
->firstOrNew([
UseSummary::COL_NAME_CONTRACT_ID => $contractId,
UseSummary::COL_NAME_SUMMARY_YYYYMM => $this->targetYYYYMM,
]);

$currentYYYYMM = intval(DateUtil::now()->format('Ym'));
$targetYYYYMM = intval($this->targetYYYYMM);
if ($targetYYYYMM < $currentYYYYMM) {
$model->is_fixed = true;
}

return $model;
}

/**
* Undocumented function
*
* @param UseSummary $model
* @return Collection<string,UseByKeySummary>
*/
private function summary(UseSummary $model): Collection
{

$from = $this->targetDate->clone()->setTime(0, 0);
$to = $this->targetDate->clone()->addMonth()->setTime(0, 0);

/**
* @var Collection<string,UseByKeySummary>
*/
$summaryByKeys = collect();

$makeKey = function (?string $key1, ?string $key2) {
return sprintf("%s-%s", $key1 ?? "NULL", $key2 ?? "NULL");
};

$targetOrders = ReceiptIssuingOrder::getBuilder()
->where(ReceiptIssuingOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, ">=", $from)
->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, "<", $to);

// 領収証発行依頼件数の取得
$receiptIssuingOrders = DB::table($targetOrders, 'target_orders')
->groupBy(
ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
)
->select([
ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
DB::raw(sprintf("count(*) as count")),
])
->get();
$model->receipt_order_count = 0;
foreach ($receiptIssuingOrders as $ele) {

$key1 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1);
$key2 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2);
$count = data_get($ele, 'count');

$model->receipt_order_count += $count;

$key = $makeKey($key1, $key2);

$summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);

$summaryByKeys->put($key, $summaryByKey);
}

// SMS送信実績の取得
$SMSs = SMSSendOrder::getBuilder()
->where(SMSSendOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
->whereNotNull(SMSSendOrder::COL_NAME_SEND_DATETIME)
->where(SMSSendOrder::COL_NAME_SEND_DATETIME, ">=", $from)
->where(SMSSendOrder::COL_NAME_SEND_DATETIME, "<", $to)
->where(SMSSendOrder::COL_NAME_DONE, true)
->groupBy(
ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2
)
->select([
ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
DB::raw(sprintf("count(*) as count")),
DB::raw(sprintf("sum(%s) as cost", SMSSendOrder::COL_NAME_COST)),
])
->get();

$model->sms_send_count = 0;
$model->sms_send_cost = 0;
foreach ($SMSs as $ele) {
$key1 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY1);
$key2 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY2);
$count = data_get($ele, 'count');
$cost = data_get($ele, 'cost');

$model->sms_send_count += $count;
$model->sms_send_cost += $cost;

$key = $makeKey($key1, $key2);


$summaryByKey = $summaryByKeys->get($key);
if (!$summaryByKey instanceof UseByKeySummary) {
$summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
$summaryByKeys->put($key, $summaryByKey);
}

$summaryByKey->fill([
UseByKeySummary::COL_NAME_SMS_SEND_COUNT => $count,
UseByKeySummary::COL_NAME_SMS_SEND_COST => $cost,
]);
}

return $summaryByKeys;
}


/**
* @param UseSummary $model
* @param Collection<UseByKeySummary> $summaryByKeys
* @return void
*/
private function save(UseSummary $model, Collection $summaryByKeys)
{


$contract = $model->contract;

if (!$contract) {
throw new Exception("契約不正");
}

$this->outputInfo(
sprintf(
"集計完了:%s 領収証発行依頼:%d件 SMS送信件数:%d件 SMSコスト:%d円",
$contract->name,
$model->receipt_order_count,
$model->sms_send_count,
$model->sms_send_cost,
)
);


$model->save();

$deleteTargets = UseByKeySummary::whereContractId($model->contract_id)
->whereSummaryYyyymm($this->targetYYYYMM)
->get();


foreach ($summaryByKeys as $summary) {

if (!($summary instanceof UseByKeySummary)) {
throw new LogicException("モデル不正");
}

$summary->save();

$deleteTargets = $deleteTargets->reject(function ($ele) use ($summary) {
return $ele->summary_key1 === $summary->summary_key1 && $ele->summary_key2 === $summary->summary_key2;
});
}

foreach ($deleteTargets as $ele) {
$ele->delete();
}
}

private function getUseByKeyModel(UseSummary $model, ?string $key1, ?string $key2): UseByKeySummary
{

$query = UseByKeySummary::whereContractId($model->contract_id)
->whereSummaryYyyymm($this->targetYYYYMM);
if ($key1 === null) {
$query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY1);
} else {
$query->whereSummaryKey1($key1);
}

if ($key2 === null) {
$query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY2);
} else {
$query->whereSummaryKey2($key2);
}


$summary = $query->firstOrNew([
UseByKeySummary::COL_NAME_CONTRACT_ID => $model->contract_id,
UseByKeySummary::COL_NAME_SUMMARY_YYYYMM => $this->targetYYYYMM,
UseByKeySummary::COL_NAME_SUMMARY_KEY1 => $key1,
UseByKeySummary::COL_NAME_SUMMARY_KEY2 => $key2,
]);

$currentYYYYMM = intval(DateUtil::now()->format('Ym'));
$targetYYYYMM = intval($this->targetYYYYMM);
if ($targetYYYYMM < $currentYYYYMM) {
$summary->is_fixed = true;
}

return $summary;
}
}

+ 1
- 1
app/Models/BaseModel.php Bestand weergeven

@@ -35,7 +35,7 @@ abstract class BaseModel extends Model implements IModelFeature

public static function getBuilder(string $name = 'main'): Builder
{
return DB::table(static::getTableName(), $name);
return DB::table(static::getTableName(), $name)->whereNull(static::COL_NAME_DELETED_AT);
}

public static function getTableName(): string


+ 3
- 3
app/Models/Ex/LoginUser.php Bestand weergeven

@@ -58,13 +58,13 @@ class LoginUser
Session::put(self::SESSION_KEY_CURERNT_CONTRACT_ID, $contractId);
}

public function getCurrentContractId(): string
public function getCurrentContractId(): ?string
{
$user = $this->user();
if ($user && $user->role === UserRole::SUPER_ADMIN) {
return Session::get(self::SESSION_KEY_CURERNT_CONTRACT_ID, "");
return Session::get(self::SESSION_KEY_CURERNT_CONTRACT_ID);
}
return data_get($user, User::COL_NAME_CONTRACT_ID, "");
return data_get($user, User::COL_NAME_CONTRACT_ID);
}

public function getCurrentContract(): ?Contract


+ 3
- 0
app/Models/SMSSendOrder.php Bestand weergeven

@@ -20,6 +20,9 @@ class SMSSendOrder extends AppModel
const COL_NAME_CONTENT = "content";
const COL_NAME_PHONE_NUMBER = "phone_number";

const COL_NAME_SUMMARY_KEY1 = "summary_key1";
const COL_NAME_SUMMARY_KEY2 = "summary_key2";

protected $casts = [
self::COL_NAME_PORPOSE => SMSSendPurpose::class,
self::COL_NAME_SEND_DATETIME => 'datetime',


+ 25
- 0
app/Models/UseByKeySummary.php Bestand weergeven

@@ -0,0 +1,25 @@
<?php

namespace App\Models;

use App\Models\Feature\ContractFeature;

class UseByKeySummary extends AppModel
{
use ContractFeature;

const COL_NAME_SUMMARY_YYYYMM = 'summary_yyyymm';
const COL_NAME_SUMMARY_KEY1 = "summary_key1";
const COL_NAME_SUMMARY_KEY2 = "summary_key2";
const COL_NAME_RECEIPT_ORDER_COUNT = 'receipt_order_count';
const COL_NAME_SMS_SEND_COUNT = 'sms_send_count';
const COL_NAME_SMS_SEND_COST = 'sms_send_cost';
const COL_NAME_IS_FIXED = 'is_fixed';

protected $casts = [];

public function getModelName(): string
{
return "利用実績キー毎集計";
}
}

+ 12
- 0
app/Models/UseByKeySummaryHistory.php Bestand weergeven

@@ -0,0 +1,12 @@
<?php

namespace App\Models;


class UseByKeySummaryHistory extends HistoryModel
{
public function getModelName(): string
{
return "利用実績キー毎集計履歴";
}
}

+ 28
- 0
app/Models/UseSummary.php Bestand weergeven

@@ -0,0 +1,28 @@
<?php

namespace App\Models;

use App\Models\Feature\ContractFeature;

class UseSummary extends AppModel
{
use ContractFeature;

const COL_NAME_SUMMARY_YYYYMM = 'summary_yyyymm';
const COL_NAME_SUMMARY_DATE_START = 'summary_date_start';
const COL_NAME_SUMMARY_DATE_END = 'summary_date_end';
const COL_NAME_RECEIPT_ORDER_COUNT = 'receipt_order_count';
const COL_NAME_SMS_SEND_COUNT = 'sms_send_count';
const COL_NAME_SMS_SEND_COST = 'sms_send_cost';
const COL_NAME_IS_FIXED = 'is_fixed';

protected $casts = [
self::COL_NAME_SUMMARY_DATE_START => 'datetime',
self::COL_NAME_SUMMARY_DATE_END => 'datetime',
];

public function getModelName(): string
{
return "利用実績集計";
}
}

+ 12
- 0
app/Models/UseSummaryHistory.php Bestand weergeven

@@ -0,0 +1,12 @@
<?php

namespace App\Models;


class UseSummaryHistory extends HistoryModel
{
public function getModelName(): string
{
return "利用実績集計履歴";
}
}

+ 17
- 1
app/Util/MigrationHelper.php Bestand weergeven

@@ -87,7 +87,18 @@ class MigrationHelper
if ($this->forHistory) {
$this->table->index($columns, $indexName);
} else {
$this->table->index([ColumnName::DELETED_AT, ...$columns], $indexName);
$this->table->index([...$columns, ColumnName::DELETED_AT], $indexName);
}
return $this;
}

public function unique(int $number, array $columns)
{
$uniqueName = $this->getUniqueName($number);
if ($this->forHistory) {
$this->table->unique($columns, $uniqueName);
} else {
$this->table->unique([...$columns, ColumnName::DELETED_AT,], $uniqueName);
}
return $this;
}
@@ -104,6 +115,11 @@ class MigrationHelper
return sprintf("%s_idx_%02d", $this->table->getTable(), $number);
}

private function getUniqueName(int $number)
{
return sprintf("%s_uq_%02d", $this->table->getTable(), $number);
}


public function contractId(bool $nullable = false)
{


+ 50
- 0
database/migrations/2023_06_07_144200_create_use_summaries_table.php Bestand weergeven

@@ -0,0 +1,50 @@
<?php

use App\Models\ColumnName;
use App\Util\MigrationHelper;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{

MigrationHelper::createTable('use_summaries', $this->schema());
MigrationHelper::createTable('use_summary_histories', $this->schema(true));
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('use_summaries');
Schema::dropIfExists('use_summary_histories');
}

private function schema(bool $forHistory = false)
{

return function (Blueprint $table, MigrationHelper $helper) use ($forHistory) {
$helper->baseColumn()
->contractId();

$table->string("summary_yyyymm")->comment("集計年月");
$table->unsignedInteger("receipt_order_count")->default(0)->comment("領収証発行依頼件数");
$table->unsignedInteger("sms_send_count")->default(0)->comment("SMS送信件数");
$table->unsignedInteger("sms_send_cost")->default(0)->comment("SMS送信コスト");
$table->boolean('is_fixed')->default(false)->comment('確定済み');

if ($forHistory) {
$helper->index(1, [ColumnName::CONTRACT_ID, 'summary_yyyymm']);
} else {
$helper->unique(1, [ColumnName::CONTRACT_ID, 'summary_yyyymm']);
}
};
}
};

+ 55
- 0
database/migrations/2023_06_08_092700_create_use_by_key_summaries_table.php Bestand weergeven

@@ -0,0 +1,55 @@
<?php

use App\Models\ColumnName;
use App\Util\MigrationHelper;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{

MigrationHelper::createTable('use_by_key_summaries', $this->schema());
MigrationHelper::createTable('use_by_key_summary_histories', $this->schema(true));
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('use_by_key_summaries');
Schema::dropIfExists('use_by_key_summary_histories');
}

private function schema(bool $forHistory = false)
{

return function (Blueprint $table, MigrationHelper $helper) use ($forHistory) {
$helper->baseColumn()
->contractId();

$table->string("summary_yyyymm")->comment("集計年月");
$table->string("summary_key1")->comment("集計キー1")->nullable();
$table->string("summary_key2")->comment("集計キー2")->nullable();

$table->unsignedInteger("receipt_order_count")->default(0)->comment("領収証発行依頼件数");
$table->unsignedInteger("sms_send_count")->default(0)->comment("SMS送信件数");
$table->unsignedInteger("sms_send_cost")->default(0)->comment("SMS送信コスト");
$table->boolean('is_fixed')->default(false)->comment('確定済み');



if ($forHistory) {
$helper->index(1, [ColumnName::CONTRACT_ID, 'summary_yyyymm', 'summary_key1', 'summary_key2']);
} else {
$helper->unique(1, [ColumnName::CONTRACT_ID, 'summary_yyyymm', 'summary_key1', 'summary_key2']);
}
};
}
};

Laden…
Annuleren
Opslaan