} */ public static function current(): array { if (! self::seeded()) { return self::fromConfig(); } $shops = []; foreach (self::storedShopNames() as $name) { $rule = self::storedRule($name); if ($rule !== null) { $shops[$name] = $rule; } } return [ 'initial_effective_at' => self::cleanDate(setting(self::EFFECTIVE_AT_KEY, null)) ?? self::DEFAULT_EFFECTIVE_AT, 'shops' => self::normalizeShops($shops), ]; } public static function effectiveAt(): string { return self::current()['initial_effective_at']; } /** @return array */ public static function shops(): array { return self::current()['shops']; } public static function seeded(): bool { return setting(self::signatureKey(), null) !== null; } /** 把代码文件里的期初数据固化成可编辑的设置值。 */ public static function seedFromConfig(): void { if (self::seeded()) { return; } $config = self::fromConfig(); $payload = [self::EFFECTIVE_AT_KEY => $config['initial_effective_at']]; foreach ($config['shops'] as $name => $rule) { $payload += self::rulePayload($name, $rule); } $payload[self::shopListKey()] = implode("\n", array_keys($config['shops'])); $payload[self::signatureKey()] = (string) count($config['shops']); setting($payload)->save(); } /** @param array $shops */ public static function saveShops(array $shops, ?string $effectiveAt = null): void { $current = self::current(); $normalized = self::normalizeShops($shops); $payload = [ self::EFFECTIVE_AT_KEY => self::cleanDate($effectiveAt) ?? $current['initial_effective_at'], self::shopListKey() => implode("\n", array_keys($normalized)), self::signatureKey() => (string) count($normalized), ]; foreach ($normalized as $name => $rule) { $payload += self::rulePayload($name, $rule); } setting($payload)->save(); // 删除本次保存后不再存在的店铺键。 foreach ($current['shops'] as $name => $rule) { if (isset($normalized[$name])) { continue; } self::forgetKeys($name); } } /** @return array{previous_company:string,company:string,changed_at:string}|null */ public static function shop(string $storeName): ?array { return self::shops()[trim($storeName)] ?? null; } /** @param array{previous_company:string,company:string,changed_at:string} $rule */ public static function putShop(string $storeName, array $rule): void { $shops = self::shops(); $shops[trim($storeName)] = $rule; self::saveShops($shops); } public static function forgetShop(string $storeName): void { $shops = self::shops(); $name = trim($storeName); unset($shops[$name]); self::saveShops($shops); self::forgetKeys($name); } /** * @return array{initial_effective_at:string,shops:array} */ private static function fromConfig(): array { return [ 'initial_effective_at' => self::cleanDate(config('store_report_initial_periods.initial_effective_at')) ?? self::DEFAULT_EFFECTIVE_AT, 'shops' => self::normalizeShops((array) config('store_report_initial_periods.shops', [])), ]; } /** @param array $shops */ private static function normalizeShops(array $shops): array { $clean = []; foreach ($shops as $storeName => $rule) { $name = trim((string) $storeName); if ($name === '' || ! is_array($rule)) { continue; } $changedAt = self::cleanDate($rule['changed_at'] ?? null); if ($changedAt === null) { continue; } $clean[$name] = [ 'previous_company' => trim((string) ($rule['previous_company'] ?? '')), 'company' => trim((string) ($rule['company'] ?? '')), 'changed_at' => $changedAt, ]; } uasort($clean, static fn (array $left, array $right): int => strcmp($left['changed_at'], $right['changed_at'])); return $clean; } private static function cleanDate(mixed $value): ?string { $value = trim((string) ($value ?? '')); if (preg_match('/^\d{4}-\d{2}-\d{2}$/D', $value) !== 1) { return null; } return $value; } private static function shopKey(string $storeName): string { return sha1(mb_strtolower(trim($storeName))); } private static function shopListKey(): string { return self::PREFIX . '.shop_list'; } private static function signatureKey(): string { return self::PREFIX . '.seeded'; } private static function ruleKey(string $storeName, string $field): string { return self::PREFIX . '.shop.' . self::shopKey($storeName) . '.' . $field; } /** @return list */ private static function storedShopNames(): array { $list = setting(self::shopListKey(), ''); $list = is_array($list) ? implode("\n", $list) : (string) $list; return array_values(array_filter(array_map('trim', explode("\n", $list)))); } /** @return array{previous_company:string,company:string,changed_at:string}|null */ private static function storedRule(string $storeName): ?array { $changedAt = self::cleanDate(setting(self::ruleKey($storeName, 'changed_at'), null)); if ($changedAt === null) { return null; } return [ 'previous_company' => trim((string) setting(self::ruleKey($storeName, 'previous_company'), '')), 'company' => trim((string) setting(self::ruleKey($storeName, 'company'), '')), 'changed_at' => $changedAt, ]; } /** * @param array{previous_company:string,company:string,changed_at:string} $rule * @return array */ private static function rulePayload(string $storeName, array $rule): array { return [ self::ruleKey($storeName, 'previous_company') => $rule['previous_company'], self::ruleKey($storeName, 'company') => $rule['company'], self::ruleKey($storeName, 'changed_at') => $rule['changed_at'], self::ruleKey($storeName, 'name') => trim($storeName), ]; } private static function forgetKeys(string $storeName): void { foreach (['previous_company', 'company', 'changed_at', 'name'] as $field) { setting()->forget(self::ruleKey($storeName, $field)); } setting()->save(); } }