| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- <?php
- namespace App\Services;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Schema;
- use Illuminate\Support\Facades\Validator;
- use Illuminate\Validation\ValidationException;
- final class MenuBadgeAdminService
- {
- private function ready(): void
- {
- if (!Schema::hasTable('menu_badge_configs')) {
- throw ValidationException::withMessages(['config' => '菜单角标配置未初始化,请先完成系统更新']);
- }
- }
- private function badgeTexts(array $texts): array
- {
- $rules = [];
- $messages = [];
- foreach (['zh' => '中文', 'en' => '英文', 'vi' => '越南文'] as $lang => $label) {
- $rules[$lang] = ['required', 'array:text'];
- $rules[$lang . '.text'] = ['required', 'string', 'max:100'];
- $messages[$lang . '.required'] = $label . '角标不能为空';
- $messages[$lang . '.text.required'] = $label . '角标不能为空';
- }
- $result = Validator::make($texts, $rules, $messages)->validate();
- foreach ($result as $lang => &$text) {
- if (preg_replace('/[\s\p{Z}\x{200B}\x{FEFF}]+/u', '', $text['text']) === '') {
- throw ValidationException::withMessages(['badge_translations.' . $lang . '.text' => '角标不能为空']);
- }
- $text['text'] = trim($text['text']);
- }
- unset($text);
- return $result;
- }
- private function row(object $row): array
- {
- return [
- 'id' => (int) $row->id,
- 'menu_text' => (string) $row->menu_text,
- 'badge_translations' => json_decode((string) $row->badge_translations, true) ?: [
- 'zh' => ['text' => ''], 'en' => ['text' => ''], 'vi' => ['text' => ''],
- ],
- 'sort' => (int) $row->sort,
- 'status' => (int) ($row->status ?? 1),
- 'created_at' => (string) $row->created_at,
- 'updated_at' => (string) $row->updated_at,
- ];
- }
- public function listing(): array
- {
- $this->ready();
- $rows = DB::table('menu_badge_configs')->orderBy('sort')->orderBy('id')->get();
- return ['total' => $rows->count(), 'data' => $rows->map(fn ($row) => $this->row($row))->all()];
- }
- public function save(array $input): array
- {
- $this->ready();
- $data = Validator::make($input, [
- 'id' => ['nullable', 'integer', 'min:1'],
- 'menu_text' => ['required', 'string', 'max:200'],
- 'badge_translations' => ['required', 'array:zh,en,vi'],
- 'sort' => ['nullable', 'integer', 'min:0', 'max:9999'],
- 'status' => ['nullable', 'integer', 'in:0,1'],
- ])->validate();
- $data['menu_text'] = trim($data['menu_text']);
- if ($data['menu_text'] === '') {
- throw ValidationException::withMessages(['menu_text' => '菜单文本不能为空']);
- }
- $data['badge_translations'] = $this->badgeTexts($data['badge_translations']);
- $sort = array_key_exists('sort', $data) && $data['sort'] !== null ? (int) $data['sort'] : null;
- $status = array_key_exists('status', $data) && $data['status'] !== null ? (int) $data['status'] : null;
- return DB::transaction(function () use ($data, $sort, $status) {
- $now = now()->format('Y-m-d H:i:s');
- $payload = [
- 'menu_text' => $data['menu_text'],
- 'badge_translations' => json_encode($data['badge_translations'], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
- 'updated_at' => $now,
- ];
- if (!empty($data['id'])) {
- $before = DB::table('menu_badge_configs')->where('id', $data['id'])->lockForUpdate()->first();
- if (!$before) {
- throw ValidationException::withMessages(['id' => '配置不存在']);
- }
- if ($sort !== null) {
- $payload['sort'] = $sort;
- }
- if ($status !== null) {
- $payload['status'] = $status;
- }
- DB::table('menu_badge_configs')->where('id', $data['id'])->update($payload);
- OperationAuditService::record('menu_badge_config', (int) $data['id'], 'update', $this->row($before), $data);
- $id = (int) $data['id'];
- } else {
- $payload['sort'] = $sort ?? (int) DB::table('menu_badge_configs')->max('sort') + 1;
- $payload['status'] = $status ?? 1;
- $payload['created_at'] = $now;
- $id = (int) DB::table('menu_badge_configs')->insertGetId($payload);
- OperationAuditService::record('menu_badge_config', $id, 'create', [], $data + ['id' => $id]);
- }
- $saved = DB::table('menu_badge_configs')->where('id', $id)->first();
- return $this->row($saved);
- });
- }
- public function delete(array $input): array
- {
- $this->ready();
- $data = Validator::make($input, ['id' => ['required', 'integer', 'min:1']])->validate();
- return DB::transaction(function () use ($data) {
- $before = DB::table('menu_badge_configs')->where('id', $data['id'])->lockForUpdate()->first();
- if (!$before) {
- throw ValidationException::withMessages(['id' => '配置不存在']);
- }
- DB::table('menu_badge_configs')->where('id', $data['id'])->delete();
- OperationAuditService::record('menu_badge_config', (int) $data['id'], 'delete', $this->row($before), []);
- return ['id' => (int) $data['id']];
- });
- }
- public function status(array $input): array
- {
- $this->ready();
- $data = Validator::make($input, [
- 'id' => ['required', 'integer', 'min:1'],
- 'status' => ['required', 'integer', 'in:0,1'],
- ])->validate();
- return DB::transaction(function () use ($data) {
- $before = DB::table('menu_badge_configs')->where('id', $data['id'])->lockForUpdate()->first();
- if (!$before) {
- throw ValidationException::withMessages(['id' => '配置不存在']);
- }
- if ((int) $data['status'] === 1) {
- $this->badgeTexts(json_decode((string) $before->badge_translations, true) ?: []);
- }
- DB::table('menu_badge_configs')->where('id', $data['id'])->update([
- 'status' => (int) $data['status'],
- 'updated_at' => now()->format('Y-m-d H:i:s'),
- ]);
- OperationAuditService::record('menu_badge_config', (int) $data['id'], 'status', $this->row($before), $data);
- $saved = DB::table('menu_badge_configs')->where('id', $data['id'])->first();
- return $this->row($saved);
- });
- }
- }
|