Skip to content

Commit be4d9e0

Browse files
pythonHuangclaude
andcommitted
feat: 模型助手管理增强-系统提示词AI优化按钮(大模型结构化润色)+助手图标设置(emoji输入/图片上传转base64/AI生成emoji),图标显示在模型助手菜单与运行页标题左侧
Co-Authored-By: Claude Code <noreply@anthropic.com>
1 parent d8496d2 commit be4d9e0

7 files changed

Lines changed: 228 additions & 93 deletions

File tree

‎Juggle.Api/Controllers/Api/AiController.cs‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,42 @@ public async Task<ApiResult> SaveAssistant([FromBody] AiAssistantSaveRequest req
233233
entity.InputParams = req.InputParams;
234234
entity.OutputParams = req.OutputParams;
235235
entity.QuickPrompts = req.QuickPrompts;
236+
entity.Icon = req.Icon;
236237
entity.Enabled = req.Enabled ? 1 : 0;
237238
entity.UpdatedAt = DateTime.Now.ToString("o");
238239
await _db.SaveChangesAsync();
239240
return ApiResult.Success(entity.Id);
240241
}
241242

243+
/// <summary>用大模型优化系统提示词(草稿 → 结构化润色)</summary>
244+
[HttpPost("optimize-prompt")]
245+
public async Task<ApiResult> OptimizePrompt([FromBody] AiOptimizePromptRequest req)
246+
{
247+
try
248+
{
249+
var system = "你是提示词优化专家。请把用户提供的提示词草稿优化为结构清晰、约束明确、可稳定执行的高质量系统提示词(保持原语言,直接输出优化后的提示词文本,不要解释、不要 markdown 围栏)。";
250+
var result = await _aiService.ChatAsync(system, req.Prompt ?? "", req.ProviderId, modelOverride: req.Model);
251+
return ApiResult.Success(new { prompt = result });
252+
}
253+
catch (Exception ex) { return ApiResult.Fail(ex.Message); }
254+
}
255+
256+
/// <summary>用大模型生成助手图标(返回 emoji)</summary>
257+
[HttpPost("generate-icon")]
258+
public async Task<ApiResult> GenerateIcon([FromBody] AiGenerateIconRequest req)
259+
{
260+
try
261+
{
262+
var system = "你是图标设计助手。根据助手名称与描述,只输出一个最贴切的 emoji 字符(不要任何其他文字、引号或解释)。";
263+
var result = await _aiService.ChatAsync(system, $"助手名称:{req.Name}\n描述:{req.Description}", req.ProviderId, modelOverride: req.Model);
264+
var icon = result.Trim().Trim('"', '\'', '`');
265+
// 提取首个 emoji 字符
266+
if (icon.Length > 2) icon = icon[..2];
267+
return ApiResult.Success(new { icon });
268+
}
269+
catch (Exception ex) { return ApiResult.Fail(ex.Message); }
270+
}
271+
242272
/// <summary>删除助手</summary>
243273
[HttpDelete("assistant/{id}")]
244274
public async Task<ApiResult> DeleteAssistant(long id)
@@ -441,9 +471,25 @@ public class AiAssistantSaveRequest
441471
public string? InputParams { get; set; }
442472
public string? OutputParams { get; set; }
443473
public string? QuickPrompts { get; set; }
474+
public string? Icon { get; set; }
444475
public bool Enabled { get; set; } = true;
445476
}
446477

478+
public class AiOptimizePromptRequest
479+
{
480+
public string? Prompt { get; set; }
481+
public long ProviderId { get; set; }
482+
public string? Model { get; set; }
483+
}
484+
485+
public class AiGenerateIconRequest
486+
{
487+
public string? Name { get; set; }
488+
public string? Description { get; set; }
489+
public long ProviderId { get; set; }
490+
public string? Model { get; set; }
491+
}
492+
447493
public class AiAssistantToggleRequest
448494
{
449495
public long Id { get; set; }

‎Juggle.Api/Program.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ await ctx.Response.WriteAsync(
252252
// AI 自定义助手表
253253
try { db.Database.ExecuteSqlRaw("CREATE TABLE IF NOT EXISTS t_ai_assistant(id INTEGER PRIMARY KEY AUTOINCREMENT, assistant_name TEXT, description TEXT, system_prompt TEXT, input_params TEXT, output_params TEXT, enabled INTEGER DEFAULT 1, created_at TEXT, created_by INTEGER, updated_at TEXT, updated_by INTEGER, tenant_id INTEGER, deleted INTEGER DEFAULT 0);"); } catch { }
254254
try { db.Database.ExecuteSqlRaw("ALTER TABLE t_ai_assistant ADD COLUMN quick_prompts TEXT DEFAULT NULL;"); } catch { }
255+
try { db.Database.ExecuteSqlRaw("ALTER TABLE t_ai_assistant ADD COLUMN icon TEXT DEFAULT NULL;"); } catch { }
255256
// AI 助手对话会话表
256257
try { db.Database.ExecuteSqlRaw("CREATE TABLE IF NOT EXISTS t_ai_conversation(id INTEGER PRIMARY KEY AUTOINCREMENT, assistant_id INTEGER, assistant_name TEXT, system_prompt TEXT, input_params TEXT, output_params TEXT, messages TEXT, outputs TEXT, provider_id INTEGER, model TEXT, title TEXT, status INTEGER DEFAULT 0, created_at TEXT, created_by INTEGER, updated_at TEXT, updated_by INTEGER, tenant_id INTEGER, deleted INTEGER DEFAULT 0);"); } catch { }
257258
// 补建参数位置字段

‎Juggle.Domain/Entities/AiAssistantEntity.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public class AiAssistantEntity : BaseEntity
2525
/// <summary>辅助提问词列表(JSON 字符串数组,运行页显示为快捷按钮)</summary>
2626
public string? QuickPrompts { get; set; }
2727

28+
/// <summary>图标(emoji 或图片 data URL,显示在菜单与运行页)</summary>
29+
public string? Icon { get; set; }
30+
2831
/// <summary>是否启用:1=启用(出现在菜单) 0=禁用</summary>
2932
public int Enabled { get; set; } = 1;
3033
}

‎Juggle.Infrastructure/Persistence/JuggleDbContext.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
123123
e.Property(p => p.InputParams).HasColumnName("input_params");
124124
e.Property(p => p.OutputParams).HasColumnName("output_params");
125125
e.Property(p => p.QuickPrompts).HasColumnName("quick_prompts");
126+
e.Property(p => p.Icon).HasColumnName("icon");
126127
e.Property(p => p.Enabled).HasColumnName("enabled");
127128
});
128129
modelBuilder.Entity<AiConversationEntity>().ToTable("t_ai_conversation");

‎JuggleNet6.Frontend/src/views/Layout.vue‎

Lines changed: 90 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,95 @@
11
<template>
22
<el-container style="height:100vh">
33
<!-- 侧边栏 -->
4-
<el-aside width="220px" style="background:#001529;overflow:hidden">
4+
<el-aside width="220px" style="background:#001529;overflow:hidden;">
55
<div class="sidebar-logo">
66
<span>⚡</span> Juggle
77
</div>
8-
<el-menu :default-active="activeMenu" router background-color="#001529"
9-
text-color="#aaa" active-text-color="#fff" style="border:none">
10-
<el-sub-menu index="monitor" v-if="hasMenu('/flow/dashboard')">
11-
<template #title>
12-
<el-icon><Histogram /></el-icon>
13-
<span>监控</span>
14-
</template>
15-
<el-menu-item index="/flow/dashboard">仪表盘</el-menu-item>
16-
<el-menu-item index="/monitor/topology">API拓扑图</el-menu-item>
17-
<el-menu-item index="/monitor/alert-rule">告警规则</el-menu-item>
18-
<el-menu-item index="/monitor/alert-record">告警记录</el-menu-item>
19-
</el-sub-menu>
20-
<el-sub-menu index="flow" v-if="hasMenu('/flow/define')">
21-
<template #title>
22-
<el-icon><Connection /></el-icon>
23-
<span>流程管理</span>
24-
</template>
25-
<el-menu-item index="/flow/define" v-if="hasMenu('/flow/define')">流程定义</el-menu-item>
26-
<el-menu-item index="/flow/list" v-if="hasMenu('/flow/list')">流程列表</el-menu-item>
27-
<el-menu-item index="/flow/log" v-if="hasMenu('/flow/log')">执行日志</el-menu-item>
28-
<el-menu-item index="/flow/testcase" v-if="hasMenu('/flow/testcase')">测试用例</el-menu-item>
29-
<el-menu-item index="/flow/async-result" v-if="hasMenu('/flow/async-result')">异步结果查询</el-menu-item>
30-
</el-sub-menu>
31-
<el-sub-menu index="suite" v-if="hasMenu('/suite/list')">
32-
<template #title>
33-
<el-icon><Grid /></el-icon>
34-
<span>套件管理</span>
35-
</template>
36-
<el-menu-item index="/suite/list">套件列表</el-menu-item>
37-
</el-sub-menu>
38-
<el-menu-item index="/object/list" v-if="hasMenu('/object/list')">
39-
<el-icon><DataBoard /></el-icon>
40-
<span>对象管理</span>
41-
</el-menu-item>
42-
<el-sub-menu index="report" v-if="hasMenu('/report/dataview')">
43-
<template #title>
44-
<el-icon><Document /></el-icon>
45-
<span>报表</span>
46-
</template>
47-
<el-menu-item index="/report/dataview">数据视图</el-menu-item>
48-
<el-menu-item index="/report/design">报表管理</el-menu-item>
49-
</el-sub-menu>
50-
<el-sub-menu index="report-view" v-if="reportMenuList.length > 0">
51-
<template #title>
52-
<el-icon><Document /></el-icon>
53-
<span>报表查询</span>
54-
</template>
55-
<el-menu-item v-for="r in reportMenuList" :key="r.id" :index="`/report/view/${r.id}`">{{ r.name }}</el-menu-item>
56-
</el-sub-menu>
57-
<el-sub-menu index="ai">
58-
<template #title>
59-
<el-icon><MagicStick /></el-icon>
60-
<span>模型助手</span>
61-
</template>
62-
<el-menu-item index="/ai/assistants">模型助手管理</el-menu-item>
63-
<el-menu-item index="/ai/flow-assistant">流程智能编排助手</el-menu-item>
64-
<el-menu-item index="/ai/api-assistant">接口智能接入助手</el-menu-item>
65-
<el-menu-item index="/ai/report-assistant">报表智能助手</el-menu-item>
66-
<el-menu-item v-for="a in assistantMenuList" :key="a.id" :index="`/ai/assistant/${a.id}`">{{ a.assistantName }}</el-menu-item>
67-
</el-sub-menu>
68-
<el-sub-menu index="system" v-if="hasMenu('/system/token')">
69-
<template #title>
70-
<el-icon><Setting /></el-icon>
71-
<span>系统设置</span>
72-
</template>
73-
<el-menu-item index="/system/token" v-if="hasMenu('/system/token')">Token管理</el-menu-item>
74-
<el-menu-item index="/system/datasource" v-if="hasMenu('/system/datasource')">数据源管理</el-menu-item>
75-
<el-menu-item index="/system/static-var" v-if="hasMenu('/system/static-var')">静态变量</el-menu-item>
76-
<el-menu-item index="/system/schedule" v-if="hasMenu('/system/schedule')">定时任务</el-menu-item>
77-
<el-menu-item index="/system/webhook" v-if="hasMenu('/system/webhook')">Webhook 管理</el-menu-item>
78-
<el-menu-item index="/system/users" v-if="hasMenu('/system/users')">用户管理</el-menu-item>
79-
<el-menu-item index="/system/role" v-if="hasMenu('/system/role')">角色管理</el-menu-item>
80-
<el-menu-item index="/system/tenant" v-if="hasMenu('/system/tenant')">租户管理</el-menu-item>
81-
<el-menu-item index="/system/config" v-if="hasMenu('/system/config')">系统配置</el-menu-item>
82-
<el-menu-item index="/system/ai-provider" v-if="hasMenu('/system/config')">大模型设置</el-menu-item>
83-
<el-menu-item index="/system/login-log" v-if="hasMenu('/system/login-log')">登录日志</el-menu-item>
84-
<el-menu-item index="/system/audit-log" v-if="hasMenu('/system/audit-log')">审计日志</el-menu-item>
85-
</el-sub-menu>
86-
</el-menu>
8+
<el-scrollbar style="height:calc(100vh - 60px);overflow-y:auto;">
9+
<el-menu :default-active="activeMenu" router background-color="#001529"
10+
text-color="#aaa" active-text-color="#fff" style="border:none;">
11+
<el-sub-menu index="monitor" v-if="hasMenu('/flow/dashboard')">
12+
<template #title>
13+
<el-icon><Histogram /></el-icon>
14+
<span>监控</span>
15+
</template>
16+
<el-menu-item index="/flow/dashboard">仪表盘</el-menu-item>
17+
<el-menu-item index="/monitor/topology">API拓扑图</el-menu-item>
18+
<el-menu-item index="/monitor/alert-rule">告警规则</el-menu-item>
19+
<el-menu-item index="/monitor/alert-record">告警记录</el-menu-item>
20+
</el-sub-menu>
21+
<el-sub-menu index="flow" v-if="hasMenu('/flow/define')">
22+
<template #title>
23+
<el-icon><Connection /></el-icon>
24+
<span>流程管理</span>
25+
</template>
26+
<el-menu-item index="/flow/define" v-if="hasMenu('/flow/define')">流程定义</el-menu-item>
27+
<el-menu-item index="/flow/list" v-if="hasMenu('/flow/list')">流程列表</el-menu-item>
28+
<el-menu-item index="/flow/log" v-if="hasMenu('/flow/log')">执行日志</el-menu-item>
29+
<el-menu-item index="/flow/testcase" v-if="hasMenu('/flow/testcase')">测试用例</el-menu-item>
30+
<el-menu-item index="/flow/async-result" v-if="hasMenu('/flow/async-result')">异步结果查询</el-menu-item>
31+
</el-sub-menu>
32+
<el-sub-menu index="suite" v-if="hasMenu('/suite/list')">
33+
<template #title>
34+
<el-icon><Grid /></el-icon>
35+
<span>套件管理</span>
36+
</template>
37+
<el-menu-item index="/suite/list">套件列表</el-menu-item>
38+
</el-sub-menu>
39+
<el-menu-item index="/object/list" v-if="hasMenu('/object/list')">
40+
<el-icon><DataBoard /></el-icon>
41+
<span>对象管理</span>
42+
</el-menu-item>
43+
<el-sub-menu index="report" v-if="hasMenu('/report/dataview')">
44+
<template #title>
45+
<el-icon><Document /></el-icon>
46+
<span>报表</span>
47+
</template>
48+
<el-menu-item index="/report/dataview">数据视图</el-menu-item>
49+
<el-menu-item index="/report/design">报表管理</el-menu-item>
50+
</el-sub-menu>
51+
<el-sub-menu index="report-view" v-if="reportMenuList.length > 0">
52+
<template #title>
53+
<el-icon><Document /></el-icon>
54+
<span>报表查询</span>
55+
</template>
56+
<el-menu-item v-for="r in reportMenuList" :key="r.id" :index="`/report/view/${r.id}`">{{ r.name }}</el-menu-item>
57+
</el-sub-menu>
58+
<el-sub-menu index="ai">
59+
<template #title>
60+
<el-icon><MagicStick /></el-icon>
61+
<span>模型助手</span>
62+
</template>
63+
<el-menu-item index="/ai/assistants">模型助手管理</el-menu-item>
64+
<el-menu-item index="/ai/flow-assistant">流程智能编排助手</el-menu-item>
65+
<el-menu-item index="/ai/api-assistant">接口智能接入助手</el-menu-item>
66+
<el-menu-item index="/ai/report-assistant">报表智能助手</el-menu-item>
67+
<el-menu-item v-for="a in assistantMenuList" :key="a.id" :index="`/ai/assistant/${a.id}`">
68+
<img v-if="isImageIcon(a.icon)" :src="a.icon" style="width:16px;height:16px;margin-right:6px;vertical-align:-3px" />
69+
<span v-else-if="a.icon" style="margin-right:6px">{{ a.icon }}</span>
70+
{{ a.assistantName }}
71+
</el-menu-item>
72+
</el-sub-menu>
73+
<el-sub-menu index="system" v-if="hasMenu('/system/token')">
74+
<template #title>
75+
<el-icon><Setting /></el-icon>
76+
<span>系统设置</span>
77+
</template>
78+
<el-menu-item index="/system/token" v-if="hasMenu('/system/token')">Token管理</el-menu-item>
79+
<el-menu-item index="/system/datasource" v-if="hasMenu('/system/datasource')">数据源管理</el-menu-item>
80+
<el-menu-item index="/system/static-var" v-if="hasMenu('/system/static-var')">静态变量</el-menu-item>
81+
<el-menu-item index="/system/schedule" v-if="hasMenu('/system/schedule')">定时任务</el-menu-item>
82+
<el-menu-item index="/system/webhook" v-if="hasMenu('/system/webhook')">Webhook 管理</el-menu-item>
83+
<el-menu-item index="/system/users" v-if="hasMenu('/system/users')">用户管理</el-menu-item>
84+
<el-menu-item index="/system/role" v-if="hasMenu('/system/role')">角色管理</el-menu-item>
85+
<el-menu-item index="/system/tenant" v-if="hasMenu('/system/tenant')">租户管理</el-menu-item>
86+
<el-menu-item index="/system/config" v-if="hasMenu('/system/config')">系统配置</el-menu-item>
87+
<el-menu-item index="/system/ai-provider" v-if="hasMenu('/system/config')">大模型设置</el-menu-item>
88+
<el-menu-item index="/system/login-log" v-if="hasMenu('/system/login-log')">登录日志</el-menu-item>
89+
<el-menu-item index="/system/audit-log" v-if="hasMenu('/system/audit-log')">审计日志</el-menu-item>
90+
</el-sub-menu>
91+
</el-menu>
92+
</el-scrollbar>
8793
</el-aside>
8894

8995
<el-container>
@@ -152,6 +158,10 @@ try {
152158
153159
const roleCode = computed(() => localStorage.getItem('roleCode') || '')
154160
161+
function isImageIcon(icon: string | undefined | null): boolean {
162+
return !!icon && (icon.startsWith('data:image') || icon.startsWith('http'))
163+
}
164+
155165
function hasMenu(menuKey: string): boolean {
156166
// 超级管理员显示所有菜单
157167
if (roleCode.value === 'admin') return true

0 commit comments

Comments
 (0)