Pārlūkot izejas kodu

1.新增画风管理功能2.画风相关接口改为从表读取

lh 3 nedēļas atpakaļ
vecāks
revīzija
39130f63c2

+ 14 - 66
app/Http/Controllers/Anime/AnimeController.php

@@ -195,72 +195,20 @@ class AnimeController extends BaseController
 
     public function artStyleList()
     {
-        $result = [
-            [
-                'art_style' => '唯美真人风格',
-                'pic_url' => "http://zw-ai.oss-cn-hangzhou.aliyuncs.com/mp_audio/art_styles/%E5%94%AF%E7%BE%8E%E7%9C%9F%E4%BA%BA.jpg"
-            ],
-            [
-                'art_style' => '真人古风风格',
-                'pic_url' => "http://zw-ai.oss-cn-hangzhou.aliyuncs.com/mp_audio/art_styles/%E7%9C%9F%E4%BA%BA%E5%8F%A4%E9%A3%8E.jpg"
-            ],
-            [
-                'art_style' => '3D真人风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/3D真人风格.png"
-            ],
-            [
-                'art_style' => '日系动漫风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/日系动漫风格.jpg"
-            ],
-            [
-                'art_style' => '国漫风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/国漫风格.jpg"
-            ],
-            [
-                'art_style' => 'Q版卡通风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/Q版卡通风格.jpg"
-            ],
-            [
-                'art_style' => '简约扁平风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/简约扁平风格.jpg"
-            ],
-            [
-                'art_style' => '古风仙侠风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/古风仙侠风格.jpg"
-            ],
-            [
-                'art_style' => '武侠风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/武侠风格.jpg"
-            ],
-            [
-                'art_style' => '新中式水墨风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/新中式水墨风格.jpg"
-            ],
-            [
-                'art_style' => '写实插画风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/写实插画风格.jpg"
-            ],
-            [
-                'art_style' => '3D卡通风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/3D卡通风格.jpg"
-            ],
-            [
-                'art_style' => '条漫风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/条漫风格.jpg"
-            ],
-            [
-                'art_style' => '赛博朋克风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/赛博朋克风格.jpg"
-            ],
-            [
-                'art_style' => '暗黑悬疑风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/暗黑悬疑风格.jpg"
-            ],
-            [
-                'art_style' => '治愈清新风格',
-                'pic_url' => "https://cdn-zwai.ycsd.cn/mp_audio/art_styles/治愈清新风格.jpg"
-            ],
-        ];
+        // 从画风表读取(仅启用且未删除),返回字段 art_style + pic_url 保持不变
+        $result = DB::table('mp_art_styles')
+            ->where('is_enabled', 1)
+            ->where('is_deleted', 0)
+            ->orderBy('id')
+            ->get(['art_style', 'pic_url'])
+            ->map(function ($item) {
+                return [
+                    'art_style' => (string)$item->art_style,
+                    'pic_url'   => (string)$item->pic_url,
+                ];
+            })
+            ->values()
+            ->toArray();
 
         return $this->success($result);
     }

+ 166 - 0
app/Http/Controllers/Manage/ArtStyleController.php

@@ -0,0 +1,166 @@
+<?php
+
+namespace App\Http\Controllers\Manage;
+
+use App\Libs\ApiResponse;
+use App\Libs\Utils;
+use App\Services\Manage\ArtStyleService;
+use Illuminate\Http\Request;
+use Illuminate\Routing\Controller as BaseController;
+use Illuminate\Support\Facades\Validator;
+
+class ArtStyleController extends BaseController
+{
+    use ApiResponse;
+
+    protected $artStyleService;
+
+    public function __construct(ArtStyleService $artStyleService)
+    {
+        $this->artStyleService = $artStyleService;
+    }
+
+    /**
+     * 画风管理列表(分页)
+     *
+     * @param Request $request
+     * @return mixed
+     */
+    public function adminList(Request $request)
+    {
+        $data = $request->all();
+
+        $validator = Validator::make($data, [
+            'page'       => 'nullable|integer|min:1',
+            'per_page'   => 'nullable|integer|min:1|max:100',
+            'is_enabled' => 'nullable|integer|in:0,1',
+            'keyword'    => 'nullable|string|max:100',
+        ], [
+            'page.integer'     => 'page必须为整数',
+            'page.min'         => 'page不能小于1',
+            'per_page.integer' => 'per_page必须为整数',
+            'per_page.min'     => 'per_page不能小于1',
+            'per_page.max'     => 'per_page不能超过100',
+            'is_enabled.in'    => 'is_enabled仅支持0或1',
+            'keyword.max'      => 'keyword不能超过100个字符',
+        ]);
+
+        if ($validator->fails()) {
+            Utils::throwError('1002:' . $validator->errors()->first());
+        }
+
+        $result = $this->artStyleService->getList($data);
+
+        $list = collect($result->items())->map(function ($item) {
+            // $item->aliases = is_string($item->aliases) ? json_decode($item->aliases, true) : [];
+            return $item;
+        });
+
+        return $this->success([
+            'meta' => getMeta($result),
+            'list' => $list,
+        ]);
+    }
+
+    /**
+     * 新增画风
+     *
+     * @param Request $request
+     * @return mixed
+     */
+    public function add(Request $request)
+    {
+        $data = $request->all();
+
+        $validator = Validator::make($data, [
+            'art_style'        => 'required|string|max:100',
+            'pic_url'          => 'required|string|max:500',
+            'aliases'          => 'nullable',
+            'prompt'           => 'required|string',
+            'character_prefix' => 'nullable|string',
+            'scene_prefix'     => 'nullable|string',
+            'is_enabled'       => 'nullable|integer|in:0,1',
+        ], [
+            'art_style.required' => '请传入画风名称',
+            'art_style.max'      => '画风名称不能超过100个字符',
+            'pic_url.required'   => '请传入画风图片URL',
+            'pic_url.max'        => '画风图片URL不能超过500个字符',
+            'prompt.required'    => '请传入画风提示词prompt',
+            'is_enabled.in'      => 'is_enabled仅支持0或1',
+        ]);
+
+        if ($validator->fails()) {
+            Utils::throwError('1002:' . $validator->errors()->first());
+        }
+
+        $result = $this->artStyleService->addArtStyle($data);
+
+        return $this->success($result);
+    }
+
+    /**
+     * 修改画风
+     *
+     * @param Request $request
+     * @return mixed
+     */
+    public function edit(Request $request)
+    {
+        $data = $request->all();
+
+        $validator = Validator::make($data, [
+            'id'               => 'required|integer|min:1',
+            'pic_url'          => 'required|string|max:500',
+            'art_style'        => 'nullable|string|max:100',
+            'aliases'          => 'nullable',
+            'prompt'           => 'required|string',
+            'character_prefix' => 'nullable|string',
+            'scene_prefix'     => 'nullable|string',
+            'is_enabled'       => 'nullable|integer|in:0,1',
+        ], [
+            'id.required'    => '请传入画风ID',
+            'id.integer'     => '画风ID必须为整数',
+            'id.min'         => '画风ID不能小于1',
+            'pic_url.required' => '请传入画风图片URL',
+            'pic_url.max'      => '画风图片URL不能超过500个字符',
+            'art_style.max'    => '画风名称不能超过100个字符',
+            'prompt.required'  => '请传入画风提示词prompt',
+            'is_enabled.in'    => 'is_enabled仅支持0或1',
+        ]);
+
+        if ($validator->fails()) {
+            Utils::throwError('1002:' . $validator->errors()->first());
+        }
+
+        $result = $this->artStyleService->editArtStyle($data);
+
+        return $this->success($result);
+    }
+
+    /**
+     * 删除画风(软删除)
+     *
+     * @param Request $request
+     * @return mixed
+     */
+    public function delete(Request $request)
+    {
+        $data = $request->all();
+
+        $validator = Validator::make($data, [
+            'id' => 'required|integer|min:1',
+        ], [
+            'id.required' => '请传入画风ID',
+            'id.integer'  => '画风ID必须为整数',
+            'id.min'      => '画风ID不能小于1',
+        ]);
+
+        if ($validator->fails()) {
+            Utils::throwError('1002:' . $validator->errors()->first());
+        }
+
+        $result = $this->artStyleService->deleteArtStyle($data);
+
+        return $this->success(['success' => $result ? 1 : 0]);
+    }
+}

+ 85 - 16
app/Services/DeepSeek/DeepSeekService.php

@@ -2575,6 +2575,27 @@ class DeepSeekService
 
     private function getArtStylePromptMappings($art_style='')
     {
+        // 优先从画风表读取(入库后以表数据为准,保持原返回结构:['画风名' => ['aliases' => [...], 'prompt' => "..."]]
+        $dbRows = DB::table('mp_art_styles')
+            ->where('is_deleted', 0)
+            ->orderBy('id')
+            ->get(['art_style', 'aliases', 'prompt']);
+        if ($dbRows->isNotEmpty()) {
+            $arr = [];
+            foreach ($dbRows as $row) {
+                $arr[$row->art_style] = [
+                    'aliases' => is_string($row->aliases) ? json_decode($row->aliases, true) : [],
+                    'prompt'  => (string)$row->prompt,
+                ];
+            }
+
+            if ($art_style) {
+                return isset($arr[$art_style]) ? $arr[$art_style]['prompt'] : '';
+            }
+
+            return $arr;
+        }
+
         $arr = [
             '唯美真人风格' => [
                 'aliases'   => ['唯美真人风格', '唯美真人', '唯美', '真人'],
@@ -2868,70 +2889,118 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
     }
 
     public function getArtStyleShortPromptByInput($inputArtStyle) {
+        // 优先从画风表读取;character_prefix/scene_prefix 缺失时用通用 prompt 兜底,均缺失则返回空数组
+        $row = DB::table('mp_art_styles')
+            ->where('art_style', $inputArtStyle)
+            ->where('is_deleted', 0)
+            ->first(['character_prefix', 'scene_prefix', 'prompt']);
+        if ($row) {
+            $prompt = (string)$row->prompt;
+            $result = [
+                'character_prefix' => (string)$row->character_prefix !== '' ? (string)$row->character_prefix : $prompt,
+                'scene_prefix' => (string)$row->scene_prefix !== '' ? (string)$row->scene_prefix : $prompt,
+            ];
+            // 前缀与通用提示词均不存在时返回空数组
+            if ($result['character_prefix'] === '' && $result['scene_prefix'] === '') {
+                return [];
+            }
+            return $result;
+        }
+
         $stylePrefixes = [
             '唯美真人风格' => [
                 'character_prefix' => '唯美真人风格,电影级摄影,真实人物,高级质感,自然光影,肤质细腻,景深虚化,超高清,8K画质,情绪表达丰富',
-                'scene_prefix' => '唯美真人风格,电影感场景,真实环境,自然光影,高级色彩,细节丰富,超高清摄影,氛围感强'
+                'scene_prefix' => '唯美真人风格,电影感场景,真实环境,自然光影,高级色彩,细节丰富,超高清摄影,氛围感强',
+                'prompt' => '唯美真人风格,电影级摄影,真实光影,高级质感,自然色彩,细节丰富,氛围感强,超高清,8K画质'
             ],
             '真人古风风格' => [
                 'character_prefix' => '真人古风风格,东方古典美学,真实人物,古代服饰,精致妆容,电影级摄影,国风质感,超高清',
-                'scene_prefix' => '真人古风风格,东方建筑,古典园林,电影级光影,国风美学,真实场景,高细节,超高清'
+                'scene_prefix' => '真人古风风格,东方建筑,古典园林,电影级光影,国风美学,真实场景,高细节,超高清',
+                'prompt' => '真人古风风格,东方古典美学,国风质感,古韵氛围,电影级摄影,精致细节,高级光影,超高清'
             ],
             '日系动漫风格' => [
                 'character_prefix' => '日系动漫风格,二次元角色,精致线稿,高质量赛璐璐上色,细腻表情,动画电影质感,高细节',
-                'scene_prefix' => '日系动漫风格,动画电影场景,唯美背景,细腻光影,高质量绘制,色彩丰富,氛围感强'
+                'scene_prefix' => '日系动漫风格,动画电影场景,唯美背景,细腻光影,高质量绘制,色彩丰富,氛围感强',
+                'prompt' => '日系动漫风格,高质量二次元画风,精致线稿,细腻上色,动画电影质感,高细节,唯美光影'
             ],
             '国漫风格' => [
                 'character_prefix' => '国漫风格,高品质角色设计,东方审美,精致建模感,细腻刻画,动态表现强,电影级渲染',
-                'scene_prefix' => '国漫风格,东方幻想场景,精细绘制,大气构图,电影级光影,高品质渲染'
+                'scene_prefix' => '国漫风格,东方幻想场景,精细绘制,大气构图,电影级光影,高品质渲染',
+                'prompt' => '国漫风格,东方幻想美学,高品质角色设计,精致渲染,细腻刻画,电影级光影,高细节'
             ],
             'Q版卡通风格' => [
                 'character_prefix' => 'Q版卡通风格,大头小身比例,可爱造型,圆润线条,丰富表情,萌系设计,高质量插画',
-                'scene_prefix' => 'Q版卡通风格,可爱场景,色彩鲜艳,童趣氛围,简洁细腻,绘本质感'
+                'scene_prefix' => 'Q版卡通风格,可爱场景,色彩鲜艳,童趣氛围,简洁细腻,绘本质感',
+                'prompt' => 'Q版卡通风格,可爱萌系画风,大头小身比例,圆润造型,鲜艳色彩,童趣氛围,高品质插画'
             ],
             '简约扁平风格' => [
                 'character_prefix' => '简约扁平风格,矢量插画,几何造型,简洁线条,干净配色,现代设计感',
-                'scene_prefix' => '简约扁平风格,矢量场景,几何构图,色彩简洁,现代感设计,视觉清爽'
+                'scene_prefix' => '简约扁平风格,矢量场景,几何构图,色彩简洁,现代感设计,视觉清爽',
+                'prompt' => '简约扁平风格,矢量插画,现代设计感,几何构图,简洁线条,干净配色,视觉清爽'
             ],
             '武侠风格' => [
                 'character_prefix' => '武侠风格,东方侠客气质,江湖人物,飘逸服饰,凌厉气场,电影级光影,高细节绘制',
-                'scene_prefix' => '武侠风格,江湖场景,古道山林,客栈酒馆,刀光剑影,大气构图,东方美学'
+                'scene_prefix' => '武侠风格,江湖场景,古道山林,客栈酒馆,刀光剑影,大气构图,东方美学',
+                'prompt' => '武侠风格,东方江湖美学,侠义氛围,飘逸意境,大气构图,电影级光影,高细节绘制'
             ],
             '古风仙侠风格' => [
                 'character_prefix' => '古风仙侠风格,仙气飘逸,东方幻想角色,精致古装,灵气环绕,电影级特效,高品质渲染',
-                'scene_prefix' => '古风仙侠风格,仙境场景,云海仙山,灵气缭绕,梦幻光效,东方幻想美学'
+                'scene_prefix' => '古风仙侠风格,仙境场景,云海仙山,灵气缭绕,梦幻光效,东方幻想美学',
+                'prompt' => '古风仙侠风格,东方幻想美学,仙气缭绕,灵气氛围,梦幻光效,电影级渲染,高品质画面'
             ],
             '新中式水墨风格' => [
                 'character_prefix' => '新中式水墨风格,国风写意,水墨晕染,留白艺术,东方韵味,诗意氛围,高级插画',
-                'scene_prefix' => '新中式水墨风格,山水意境,墨韵渲染,留白构图,东方美学,诗意场景'
+                'scene_prefix' => '新中式水墨风格,山水意境,墨韵渲染,留白构图,东方美学,诗意场景',
+                'prompt' => '新中式水墨风格,东方水墨美学,写意表达,墨韵渲染,留白构图,诗意氛围,高级艺术感'
             ],
             '写实插画风格' => [
                 'character_prefix' => '写实插画风格,半写实人物,细节丰富,真实比例,数字绘画,高质量渲染,电影级质感',
-                'scene_prefix' => '写实插画风格,高细节环境,数字绘景,真实光影,层次丰富,电影感构图'
+                'scene_prefix' => '写实插画风格,高细节环境,数字绘景,真实光影,层次丰富,电影感构图',
+                'prompt' => '写实插画风格,数字绘画,真实质感,细节丰富,光影自然,电影感构图,高品质渲染'
             ],
             '3D卡通风格' => [
                 'character_prefix' => '3D卡通风格,高品质三维建模,皮克斯动画质感,表情生动,角色可爱,细腻渲染',
-                'scene_prefix' => '3D卡通风格,动画电影场景,三维建模,色彩明快,光影柔和,高品质渲染'
+                'scene_prefix' => '3D卡通风格,动画电影场景,三维建模,色彩明快,光影柔和,高品质渲染',
+                'prompt' => '3D卡通风格,高品质三维渲染,动画电影质感,角色生动,色彩明快,细节丰富'
             ],
             '条漫风格' => [
                 'character_prefix' => '条漫风格,韩漫质感,精致线稿,人物颜值高,细腻上色,剧情漫画风,高质量绘制',
-                'scene_prefix' => '条漫风格,漫画背景,剧情分镜感,细腻光影,现代漫画质感'
+                'scene_prefix' => '条漫风格,漫画背景,剧情分镜感,细腻光影,现代漫画质感',
+                'prompt' => '条漫风格,韩漫质感,精致线稿,细腻上色,剧情感强,视觉表现突出,高质量绘制'
             ],
             '赛博朋克风格' => [
                 'character_prefix' => '赛博朋克风格,未来科技感,机械义体,霓虹光效,高对比色彩,科幻都市氛围,电影级渲染',
-                'scene_prefix' => '赛博朋克风格,未来都市,霓虹街道,全息广告,机械科技元素,夜景氛围,电影级光影'
+                'scene_prefix' => '赛博朋克风格,未来都市,霓虹街道,全息广告,机械科技元素,夜景氛围,电影级光影',
+                'prompt' => '赛博朋克风格,未来科技感,霓虹光效,高对比色彩,机械元素,科幻都市氛围,电影级渲染'
             ],
             '暗黑悬疑风格' => [
                 'character_prefix' => '暗黑悬疑风格,神秘压抑氛围,强烈明暗对比,电影级灯光,细腻刻画,悬疑感十足',
-                'scene_prefix'  => '暗黑悬疑风格,阴郁环境,低饱和色调,迷雾光影,神秘气氛,电影感构图'
+                'scene_prefix'  => '暗黑悬疑风格,阴郁环境,低饱和色调,迷雾光影,神秘气氛,电影感构图',
+                'prompt' => '暗黑悬疑风格,低饱和色调,神秘氛围,压抑光影,电影级灯光,悬念感强,高细节表现'
             ],
             '治愈清新风格' => [
                 'character_prefix' => '治愈清新风格,温暖柔和色调,干净画面,自然光线,亲和气质,轻松愉悦氛围,高品质插画',
-                'scene_prefix'  => '治愈清新风格,阳光明亮,自然环境,温暖色彩,舒适氛围,细腻绘制,疗愈感强'
+                'scene_prefix'  => '治愈清新风格,阳光明亮,自然环境,温暖色彩,舒适氛围,细腻绘制,疗愈感强',
+                'prompt' => '治愈清新风格,温暖柔和色调,自然光线,清新配色,舒适氛围,细腻绘制,疗愈感十足'
             ]
         ];
 
-        return isset($stylePrefixes[$inputArtStyle]) ? $stylePrefixes[$inputArtStyle] : [];
+        if (isset($stylePrefixes[$inputArtStyle])) {
+            $item = $stylePrefixes[$inputArtStyle];
+            $prompt = (string)($item['prompt'] ?? '');
+            // 前缀缺失时用通用 prompt 兜底
+            $result = [
+                'character_prefix' => !empty($item['character_prefix']) ? $item['character_prefix'] : $prompt,
+                'scene_prefix' => !empty($item['scene_prefix']) ? $item['scene_prefix'] : $prompt,
+            ];
+            // 与表分支一致:前缀与通用提示词均不存在时返回空数组
+            if ($result['character_prefix'] === '' && $result['scene_prefix'] === '') {
+                return [];
+            }
+            return $result;
+        }
+
+        return [];
     }
 
     private function normalizeArtStyleInput($value): string

+ 241 - 0
app/Services/Manage/ArtStyleService.php

@@ -0,0 +1,241 @@
+<?php
+
+namespace App\Services\Manage;
+
+use App\Facade\Site;
+use App\Libs\Utils;
+use Illuminate\Support\Facades\DB;
+
+class ArtStyleService
+{
+    /**
+     * 画风管理列表(分页)
+     *
+     * @param array $params
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     */
+    public function getList(array $params)
+    {
+        $this->assertSuperadmin();
+
+        $perPage = (int)getProp($params, 'per_page', 15);
+        if ($perPage < 1) {
+            $perPage = 15;
+        }
+
+        $query = DB::table('mp_art_styles')->where('is_deleted', 0)->select('id', 'art_style', 'prompt', 'character_prefix', 'scene_prefix', 'pic_url', 'is_enabled', 'created_at');
+
+        // 是否启用筛选
+        if (array_key_exists('is_enabled', $params) && $params['is_enabled'] !== '' && $params['is_enabled'] !== null) {
+            $query->where('is_enabled', (int)$params['is_enabled']);
+        }
+
+        // 画风名称模糊筛选
+        $keyword = trim((string)getProp($params, 'art_style', ''));
+        if ($keyword !== '') {
+            $query->where('art_style', 'like', "%{$keyword}%");
+        }
+
+        return $query->orderByDesc('id')->paginate($perPage);
+    }
+
+    /**
+     * 新增画风
+     *
+     * @param array $params
+     * @return array
+     */
+    public function addArtStyle(array $params): array
+    {
+        $this->assertSuperadmin();
+
+        $artStyle = trim((string)getProp($params, 'art_style', ''));
+        $picUrl   = trim((string)getProp($params, 'pic_url', ''));
+        $isEnabled = (int)getProp($params, 'is_enabled', 1);
+
+        if ($artStyle === '') {
+            Utils::throwError('1002:请传入画风名称');
+        }
+        if ($picUrl === '') {
+            Utils::throwError('1002:请传入画风图片URL');
+        }
+        if (trim((string)getProp($params, 'prompt', '')) === '') {
+            Utils::throwError('1002:请传入画风提示词prompt');
+        }
+        if (DB::table('mp_art_styles')->where('art_style', $artStyle)->where('is_deleted', 0)->exists()) {
+            Utils::throwError('1002:画风名称已存在');
+        }
+
+        $id = DB::table('mp_art_styles')->insertGetId([
+            'art_style'       => $artStyle,
+            'aliases'         => $this->normalizeAliases($params, $artStyle),
+            'prompt'          => trim((string)getProp($params, 'prompt', '')),
+            'character_prefix'=> (string)getProp($params, 'character_prefix', ''),
+            'scene_prefix'    => (string)getProp($params, 'scene_prefix', ''),
+            'pic_url'         => $picUrl,
+            'is_enabled'      => $isEnabled,
+            'is_deleted'      => 0,
+            'created_at'      => date('Y-m-d H:i:s'),
+            'updated_at'      => date('Y-m-d H:i:s'),
+        ]);
+
+        $row = DB::table('mp_art_styles')->where('id', $id)->first();
+        $result = $this->buildStyleResult($row);
+        $result['id'] = $id;
+
+        return $result;
+    }
+
+    /**
+     * 修改画风
+     *
+     * @param array $params
+     * @return array
+     */
+    public function editArtStyle(array $params): array
+    {
+        $this->assertSuperadmin();
+
+        $id = (int)getProp($params, 'id', 0);
+        if ($id < 1) {
+            Utils::throwError('1002:请传入画风ID');
+        }
+
+        $row = DB::table('mp_art_styles')->where('id', $id)->where('is_deleted', 0)->first();
+        if (!$row) {
+            Utils::throwError('20003:画风不存在');
+        }
+
+        $update = [];
+
+        $artStyle = trim((string)getProp($params, 'art_style', ''));
+        if ($artStyle !== '') {
+            if (DB::table('mp_art_styles')->where('art_style', $artStyle)->where('id', '<>', $id)->where('is_deleted', 0)->exists()) {
+                Utils::throwError('1002:画风名称已存在');
+            }
+            $update['art_style'] = $artStyle;
+        }
+
+        // 图片URL必填
+        $picUrl = trim((string)getProp($params, 'pic_url', ''));
+        if ($picUrl === '') {
+            Utils::throwError('1002:请传入画风图片URL');
+        }
+        $update['pic_url'] = $picUrl;
+
+        // prompt 必填(非空)
+        $prompt = trim((string)getProp($params, 'prompt', ''));
+        if ($prompt === '') {
+            Utils::throwError('1002:请传入画风提示词prompt');
+        }
+        $update['prompt'] = $prompt;
+
+        // aliases 不传时默认使用画风名(新画风名优先,未改则沿用原画风名)
+        $defaultAliasStyle = $artStyle !== '' ? $artStyle : (string)$row->art_style;
+        $update['aliases'] = $this->normalizeAliases($params, $defaultAliasStyle);
+
+        if (array_key_exists('character_prefix', $params)) {
+            $update['character_prefix'] = (string)$params['character_prefix'];
+        }
+        if (array_key_exists('scene_prefix', $params)) {
+            $update['scene_prefix'] = (string)$params['scene_prefix'];
+        }
+        if (array_key_exists('is_enabled', $params) && $params['is_enabled'] !== '' && $params['is_enabled'] !== null) {
+            $update['is_enabled'] = (int)$params['is_enabled'];
+        }
+
+        $update['updated_at'] = date('Y-m-d H:i:s');
+        DB::table('mp_art_styles')->where('id', $id)->update($update);
+
+        $row = DB::table('mp_art_styles')->where('id', $id)->first();
+
+        return $this->buildStyleResult($row);
+    }
+
+    /**
+     * 删除画风(软删除 is_deleted=1)
+     *
+     * @param array $params
+     * @return array
+     */
+    public function deleteArtStyle(array $params)
+    {
+        $this->assertSuperadmin();
+
+        $id = (int)getProp($params, 'id', 0);
+        if ($id < 1) {
+            Utils::throwError('1002:请传入画风ID');
+        }
+
+        $row = DB::table('mp_art_styles')->where('id', $id)->where('is_deleted', 0)->first();
+        if (!$row) {
+            Utils::throwError('20003:画风不存在');
+        }
+
+        return DB::table('mp_art_styles')->where('id', $id)->update([
+            'is_deleted' => 1,
+            'updated_at' => date('Y-m-d H:i:s'),
+        ]);
+        
+    }
+
+    /**
+     * 规范化别名(支持数组或JSON字符串)
+     *
+     * @param array $params
+     * @return string
+     */
+    private function normalizeAliases(array $params, string $defaultArtStyle = ''): string
+    {
+        // 未传 aliases(或为空)时,默认使用画风名本身作为别名
+        if (!array_key_exists('aliases', $params) || $params['aliases'] === null || $params['aliases'] === '') {
+            return json_encode([$defaultArtStyle], JSON_UNESCAPED_UNICODE);
+        }
+
+        $aliases = getProp($params, 'aliases', []);
+        if (is_string($aliases)) {
+            $aliases = json_decode($aliases, true) ?: [];
+        }
+        if (!is_array($aliases)) {
+            $aliases = [];
+        }
+
+        $aliases = array_values(array_filter(array_map('trim', $aliases), function ($item) {
+            return $item !== '';
+        }));
+
+        return json_encode($aliases, JSON_UNESCAPED_UNICODE);
+    }
+
+    /**
+     * 仅超管可管理画风
+     *
+     * @return void
+     */
+    private function assertSuperadmin(): void
+    {
+        if (Site::getRole() !== 'superadmin') {
+            Utils::throwError('1005:权限不足');
+        }
+    }
+
+    /**
+     * 构建画风返回信息(add/edit 成功后返回)
+     *
+     * @param object $row
+     * @return array
+     */
+    private function buildStyleResult($row): array
+    {
+        return [
+            'id'               => (int)$row->id,
+            'art_style'        => (string)$row->art_style,
+            'prompt'           => (string)$row->prompt,
+            'character_prefix' => (string)$row->character_prefix,
+            'scene_prefix'     => (string)$row->scene_prefix,
+            'pic_url'          => (string)$row->pic_url,
+            'is_enabled'       => (int)$row->is_enabled,
+            'created_at'       => transDate($row->created_at),
+        ];
+    }
+}

+ 8 - 0
routes/api.php

@@ -12,6 +12,7 @@ use App\Http\Controllers\PromptTemplate\PromptTemplateController;
 use App\Http\Controllers\Points\PointsController;
 use App\Http\Controllers\Points\PointsStatsController;
 use App\Http\Controllers\Manage\ManageUserController;
+use App\Http\Controllers\Manage\ArtStyleController;
 use App\Http\Controllers\TaskCenter\TaskCenterController;
 use Illuminate\Support\Facades\Route;
 
@@ -114,6 +115,13 @@ Route::group(['middleware' => ['bindToken', 'bindExportToken', 'checkLogin']], f
         Route::get('list', [ManageUserController::class, 'companyList']); // 公司列表(不带分页)
     });
 
+    Route::group(['prefix' => 'artStyle'], function () {
+        Route::get('adminList', [ArtStyleController::class, 'adminList']); // 画风管理列表(分页)
+        Route::post('add', [ArtStyleController::class, 'add']);            // 新增画风(图片URL必填)
+        Route::post('edit', [ArtStyleController::class, 'edit']);          // 修改画风(图片URL必填)
+        Route::get('delete', [ArtStyleController::class, 'delete']);      // 删除画风(软删除)
+    });
+
 
     Route::group(['prefix' => 'deepseek'], function () {
         Route::post('chatWithReasoner', [DeepSeekController::class, 'chatWithReasoner']);