234 lines
8.2 KiB
PHP
234 lines
8.2 KiB
PHP
<?php
|
||
|
||
namespace App\Service\business;
|
||
|
||
use App\BaseApp\BaseService;
|
||
use App\Models\business\WxCardSchemeModel;
|
||
use Illuminate\Support\Facades\DB;
|
||
|
||
/**
|
||
* 卡片画布方案:15 套预设 + 运营另存为的自定义方案
|
||
*/
|
||
class WxCardSchemeService extends BaseService
|
||
{
|
||
public function __construct()
|
||
{
|
||
parent::__construct();
|
||
$this->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<int, string> $codes
|
||
* @return array<string, 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;
|
||
}
|
||
}
|