model = WxCardSchemeModel::class; $this->selectField = [ 'id', 'name', 'code', 'preview', 'is_preset', 'app_code', 'sort', 'status', 'created_at', 'updated_at', ]; $this->queryField = [ 'name' => 'like', 'code' => 'like', 'app_code' => '=', 'is_preset' => '=', 'status' => '=', ]; $this->orderBy = ['name' => 'sort', 'sort' => 'asc']; } public function list(): array { $result = $this->getPageList(); $ids = array_values(array_filter(array_map( static fn ($row) => (int) ($row['id'] ?? 0), $result['items'] ?? [] ))); if ($ids === []) { return $result; } $extras = WxCardSchemeModel::whereIn('id', $ids)->get(['id', 'layers'])->keyBy('id'); foreach ($result['items'] as &$item) { $extra = $extras[(int) $item['id']] ?? null; $item['layers'] = is_array($extra?->layers) ? $extra->layers : []; } unset($item); return $result; } public function option(): mixed { $this->optionField = ['id', 'name', 'code', 'is_preset']; return $this->getOption(); } public function detail($id): mixed { $saved = $this->selectField; $this->selectField = ['*']; try { return $this->getDetail($id); } finally { $this->selectField = $saved; } } public function create($params): mixed { return $this->insert($this->normalize($params)); } public function update($id, $params): mixed { $row = WxCardSchemeModel::where('id', $id)->where('deleted_at', 0)->first(); if (empty($row)) { $this->utils->errorThrow('方案不存在'); } // 预设只允许改 layers / name / preview,不许改 code 以免小程序引用断裂 if ((int) $row['is_preset'] === 1) { $params['code'] = $row['code']; $params['is_preset'] = 1; } return $this->save($id, $this->normalize($params, (int) $id)); } public function delete($ids): mixed { $hasPreset = WxCardSchemeModel::whereIn('id', (array) $ids)->where('is_preset', 1)->exists(); if ($hasPreset) { $this->utils->errorThrow('内置预设不能删除,请另存为方案后再改'); } return $this->del($ids); } /** * 把当前图层另存为新方案(运营自由设计后的落点) */ public function saveAs(array $params): array { $name = trim((string) ($params['name'] ?? '')); if ($name === '') { $this->utils->errorThrow('请填写方案名称'); } $code = trim((string) ($params['code'] ?? '')); if ($code === '') { $code = 'custom-' . substr(md5($name . microtime(true)), 0, 8); } $id = $this->insert($this->normalize([ 'name' => $name, 'code' => $code, 'preview' => (string) ($params['preview'] ?? ''), 'layers' => $params['layers'] ?? [], 'is_preset' => 0, 'app_code' => (string) ($params['app_code'] ?? ''), 'sort' => (int) ($params['sort'] ?? 100), 'status' => 0, ])); return ['id' => $id, 'code' => $code, 'name' => $name]; } /** * 用 config/wx_card_schemes.php 灌 30 套预设 */ public function initPresets(bool $overwrite = false): array { $schema = WxTemplateSchemaService::getInstance(); $inserted = 0; $updated = 0; $skipped = 0; $now = time(); DB::connection('business')->transaction(function () use ($schema, $overwrite, $now, &$inserted, &$updated, &$skipped) { foreach ((array) config('wx_card_schemes', []) as $index => $preset) { $code = (string) ($preset['code'] ?? ''); $name = (string) ($preset['name'] ?? ''); if ($code === '' || $name === '') { continue; } $layers = $schema->sanitizeLayers($preset['layers'] ?? [], true); $row = [ 'name' => $name, 'code' => $code, 'preview' => (string) ($preset['preview'] ?? ''), 'layers' => json_encode($layers, JSON_UNESCAPED_UNICODE), 'is_preset' => 1, 'app_code' => '', 'sort' => $index, 'status' => 0, 'updated_at' => $now, ]; $exists = WxCardSchemeModel::where('code', $code)->where('app_code', '')->first(); if (!empty($exists)) { if (!$overwrite) { $skipped++; continue; } WxCardSchemeModel::where('id', $exists['id'])->update($row); $updated++; continue; } $row['created_at'] = $now; WxCardSchemeModel::insert($row); $inserted++; } }); return ['inserted' => $inserted, 'updated' => $updated, 'skipped' => $skipped]; } /** * 按 code 列表取方案(主题下发用);库没有时回退到配置预设 * * 必须是 static:小程序 GET wx/theme 会调这里,不能走 getInstance()。 * 本类继承 BaseService,构造函数会按后台 JWT 验 token,小程序 token 会被直接拒掉。 * * @param array $codes * @return array */ public static function mapByCodes(array $codes): array { $codes = array_values(array_unique(array_filter(array_map('strval', $codes)))); if ($codes === []) { return []; } $map = []; try { $rows = WxCardSchemeModel::whereIn('code', $codes) ->where('deleted_at', 0) ->where('status', 0) ->get(['code', 'name', 'layers', 'is_preset']); foreach ($rows as $row) { $map[(string) $row['code']] = [ 'code' => (string) $row['code'], 'name' => (string) $row['name'], 'layers' => is_array($row['layers']) ? $row['layers'] : [], 'is_preset' => (int) $row['is_preset'], ]; } } catch (\Throwable) { // 表尚未建时走配置兜底,避免主题接口 500 把小程序打白 } foreach ((array) config('wx_card_schemes', []) as $preset) { $code = (string) ($preset['code'] ?? ''); if ($code === '' || isset($map[$code]) || !in_array($code, $codes, true)) { continue; } $map[$code] = [ 'code' => $code, 'name' => (string) ($preset['name'] ?? $code), 'layers' => (array) ($preset['layers'] ?? []), 'is_preset' => 1, ]; } return $map; } private function normalize(array $params, int $excludeId = 0): array { $schema = WxTemplateSchemaService::getInstance(); if (array_key_exists('layers', $params)) { $params['layers'] = json_encode($schema->sanitizeLayers($params['layers'], true), JSON_UNESCAPED_UNICODE); } if (!empty($params['code'])) { $duplicate = WxCardSchemeModel::where('code', $params['code']) ->where('app_code', (string) ($params['app_code'] ?? '')) ->when($excludeId > 0, fn ($query) => $query->where('id', '!=', $excludeId)) ->exists(); if ($duplicate) { $this->utils->errorThrow('方案标识已存在'); } } return $params; } }