사이트·관리자 봉투 물류 기능(수불·통계·레포트·재고·발주)과 DB·메뉴·E2E를 운영 반영한다.

통계 분석(전년대비·월별·계절별), 수급계획·LOT 수불, 지정판매소·실사·메뉴 링크 등을 포함한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
taekyoungc
2026-06-01 16:15:15 +09:00
parent 21e7b91871
commit 0f1d414f37
129 changed files with 18068 additions and 1585 deletions

View File

@@ -0,0 +1,106 @@
<?php
namespace App\Libraries\Blockchain;
use App\Models\BlockchainLedgerModel;
class SqlLedger
{
private BlockchainLedgerModel $ledgerModel;
public function __construct()
{
$this->ledgerModel = model(BlockchainLedgerModel::class);
}
/**
* @param array<string,mixed> $payload
* @return array{index:int,hash:string,previous_hash:string}
*/
public function appendBlock(string $txType, array $payload, ?string $entityUuid, int $entityVersion, ?int $actorIdx, ?int $lgIdx): array
{
$latest = $this->ledgerModel->orderBy('bl_idx', 'DESC')->first();
// 원장이 비어 있으면 $latest 가 null — $latest->bl_hash 는 PHP 8에서 치명 오류(? 는 ?? 와 달리 속성 접근 자체가 먼저 평가됨)
$previousHash = ($latest === null || ! isset($latest->bl_hash) || (string) $latest->bl_hash === '')
? str_repeat('0', 64)
: (string) $latest->bl_hash;
$now = date('Y-m-d H:i:s');
$normalizedPayload = $this->normalizeArray($payload);
$payloadJson = json_encode($normalizedPayload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}';
$hashInput = implode('|', [
$now,
$txType,
$entityUuid ?? '',
(string) $entityVersion,
$payloadJson,
$previousHash,
'0',
]);
$currentHash = hash('sha256', $hashInput);
$this->ledgerModel->insert([
'bl_created_at' => $now,
'bl_tx_type' => $txType,
'bl_entity_uuid' => $entityUuid,
'bl_entity_version' => $entityVersion,
'bl_payload' => $payloadJson,
'bl_previous_hash' => $previousHash,
'bl_hash' => $currentHash,
'bl_nonce' => 0,
'bl_actor_idx' => $actorIdx,
'bl_lg_idx' => $lgIdx,
]);
return [
'index' => (int) $this->ledgerModel->getInsertID(),
'hash' => $currentHash,
'previous_hash' => $previousHash,
];
}
/**
* @param array<string,mixed> $data
* @return array<string,mixed>
*/
private function normalizeArray(array $data): array
{
ksort($data);
foreach ($data as $key => $value) {
if (is_array($value)) {
if ($this->isAssoc($value)) {
/** @var array<string,mixed> $assoc */
$assoc = $value;
$data[$key] = $this->normalizeArray($assoc);
} else {
$normalizedList = [];
foreach ($value as $item) {
if (is_array($item) && $this->isAssoc($item)) {
/** @var array<string,mixed> $assoc */
$assoc = $item;
$normalizedList[] = $this->normalizeArray($assoc);
} else {
$normalizedList[] = $item;
}
}
$data[$key] = $normalizedList;
}
}
}
return $data;
}
/**
* @param array<mixed> $array
*/
private function isAssoc(array $array): bool
{
if ($array === []) {
return false;
}
return array_keys($array) !== range(0, count($array) - 1);
}
}