feat: 优化
This commit is contained in:
47
.claude/commands/code-simplifier.md
Normal file
47
.claude/commands/code-simplifier.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# 代码简化工具
|
||||||
|
**名称**:代码简化工具
|
||||||
|
**描述**:在保留代码全部功能的前提下,简化并优化代码,提升其清晰度、一致性与可维护性。若无特殊要求,优化工作将聚焦于近期修改的代码。
|
||||||
|
**模型**:奥普斯(Opus)
|
||||||
|
|
||||||
|
你是一名资深的代码简化专家,核心工作目标是在**完全保留代码原有功能**的基础上,提升代码的清晰度、一致性与可维护性。你的专长在于运用项目专属的最佳实践方案,优化代码的实现方式,同时确保代码功能完全不受影响。你始终将可读性强、语义明确的代码置于优先地位,而非追求过度精简的写法。凭借多年资深软件工程师的从业经验,你已精准掌握这两者之间的平衡之道。
|
||||||
|
|
||||||
|
你需要分析近期修改的代码,并围绕以下方向开展优化工作:
|
||||||
|
|
||||||
|
1. **功能无损原则**:绝不改变代码的功能逻辑,仅优化其实现方式。代码原有的所有特性、输出结果与运行行为均需保持原样。
|
||||||
|
|
||||||
|
2. **遵循项目规范**:严格执行《CLAUDE.md》文档中既定的编码规范,具体包括:
|
||||||
|
- 使用 ES 模块,规范导入语句的排序规则与文件扩展名的使用方式
|
||||||
|
- 优先使用 `function` 关键字定义函数,而非箭头函数
|
||||||
|
- 为顶层函数添加显式的返回值类型注解
|
||||||
|
- 遵循标准的 React 组件开发规范,定义显式的组件属性类型(Props types)
|
||||||
|
- 采用合理的错误处理方案(尽可能避免滥用 try/catch 语句)
|
||||||
|
- 保持命名规则的一致性
|
||||||
|
|
||||||
|
3. **提升代码清晰度**:通过以下方式简化代码结构:
|
||||||
|
- 降低不必要的代码复杂度与嵌套层级
|
||||||
|
- 剔除冗余代码与过度抽象的逻辑
|
||||||
|
- 优化变量与函数的命名,提升代码可读性
|
||||||
|
- 整合关联性强的业务逻辑
|
||||||
|
- 删除对显而易见的代码的多余注释
|
||||||
|
- **重点注意**:避免使用嵌套三元运算符。在多条件判断场景下,优先使用 switch 语句或 if/else 条件判断链
|
||||||
|
- 优先保证代码清晰,而非追求代码简洁——显式的代码实现往往优于过度精简的写法
|
||||||
|
|
||||||
|
4. **把握优化平衡**:避免因过度简化引发以下问题:
|
||||||
|
- 降低代码的清晰度与可维护性
|
||||||
|
- 写出看似“精巧”却难以理解的逻辑
|
||||||
|
- 将多个不相关的业务逻辑强行耦合到单个函数或组件中
|
||||||
|
- 移除对优化代码结构有积极作用的抽象设计
|
||||||
|
- 为减少代码行数而牺牲可读性(例如滥用嵌套三元运算符、编写过于紧凑的单行代码)
|
||||||
|
- 增加代码调试与功能扩展的难度
|
||||||
|
|
||||||
|
5. **聚焦优化范围**:若无明确要求扩大审查范围,仅对当前开发会话中近期修改或涉及的代码进行优化。
|
||||||
|
|
||||||
|
### 代码优化流程
|
||||||
|
1. 定位近期修改的代码片段
|
||||||
|
2. 分析代码中可优化的空间,提升其简洁性与一致性
|
||||||
|
3. 落实项目专属的编码规范与最佳实践方案
|
||||||
|
4. 确保代码的功能完全不受优化工作影响
|
||||||
|
5. 验证优化后的代码更简洁、更易于维护
|
||||||
|
6. 仅针对影响代码理解的重大修改内容撰写说明文档
|
||||||
|
|
||||||
|
你可以自主、主动地在代码编写或修改完成后立即开展优化工作,无需等待明确的优化请求。你的核心目标是:在保证代码功能完整的前提下,让所有代码都达到简洁性与可维护性的最高标准。
|
||||||
84
frontend/app/web-gold/src/api/agent.js
Normal file
84
frontend/app/web-gold/src/api/agent.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* AI 智能体 API
|
||||||
|
*/
|
||||||
|
import request from '@/api/http'
|
||||||
|
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||||
|
import tokenManager from '@gold/utils/token-manager'
|
||||||
|
import { API_BASE } from '@gold/config/api'
|
||||||
|
|
||||||
|
const BASE_URL = `${API_BASE.APP_TIK}`
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取启用的智能体列表
|
||||||
|
*/
|
||||||
|
export function getAgentList() {
|
||||||
|
return request({
|
||||||
|
url: `${BASE_URL}/agent/list`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流式对话(SSE)
|
||||||
|
* @param {Object} options - 请求配置
|
||||||
|
* @param {number} options.agentId - 智能体ID
|
||||||
|
* @param {string} options.content - 用户输入内容
|
||||||
|
* @param {string} [options.conversationId] - 会话ID(可选,首次对话不传)
|
||||||
|
* @param {AbortController} [options.ctrl] - 取消控制器
|
||||||
|
* @param {Function} options.onMessage - 消息回调
|
||||||
|
* @param {Function} [options.onError] - 错误回调
|
||||||
|
* @param {Function} [options.onClose] - 关闭回调
|
||||||
|
*/
|
||||||
|
export async function sendChatStream(options) {
|
||||||
|
const {
|
||||||
|
agentId,
|
||||||
|
content,
|
||||||
|
conversationId,
|
||||||
|
ctrl,
|
||||||
|
onMessage,
|
||||||
|
onError,
|
||||||
|
onClose
|
||||||
|
} = options || {}
|
||||||
|
|
||||||
|
const token = tokenManager.getAccessToken()
|
||||||
|
|
||||||
|
return fetchEventSource(`${BASE_URL}/dify/chat/stream`, {
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
'tenant-id': import.meta.env?.VITE_TENANT_ID
|
||||||
|
},
|
||||||
|
openWhenHidden: true,
|
||||||
|
body: JSON.stringify({
|
||||||
|
agentId,
|
||||||
|
content,
|
||||||
|
conversationId
|
||||||
|
}),
|
||||||
|
onmessage: (event) => {
|
||||||
|
if (typeof onMessage === 'function') {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(event.data)
|
||||||
|
// 解析 CommonResult 包装
|
||||||
|
const result = data.code === 0 ? data.data : data
|
||||||
|
onMessage(result)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析 SSE 数据失败:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onerror: (err) => {
|
||||||
|
if (typeof onError === 'function') {
|
||||||
|
onError(err)
|
||||||
|
}
|
||||||
|
throw err // 不重试
|
||||||
|
},
|
||||||
|
onclose: () => {
|
||||||
|
if (typeof onClose === 'function') {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
signal: ctrl ? ctrl.signal : undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -70,8 +70,8 @@
|
|||||||
<!-- 加载中 -->
|
<!-- 加载中 -->
|
||||||
<div v-if="loading" class="message-item message-item--assistant">
|
<div v-if="loading" class="message-item message-item--assistant">
|
||||||
<div class="agent-avatar-small msg-avatar">
|
<div class="agent-avatar-small msg-avatar">
|
||||||
<RobotOutlined v-if="!agent?.avatar" class="avatar-icon" />
|
<img v-if="agent?.avatar" :src="agent?.avatar" :alt="agent?.name" />
|
||||||
<img v-else :src="agent?.avatar" :alt="agent?.name" />
|
<RobotOutlined v-else class="avatar-icon" />
|
||||||
</div>
|
</div>
|
||||||
<div class="message-bubble message-bubble--assistant">
|
<div class="message-bubble message-bubble--assistant">
|
||||||
<div class="typing-indicator">
|
<div class="typing-indicator">
|
||||||
@@ -135,6 +135,7 @@ import {
|
|||||||
ThunderboltFilled
|
ThunderboltFilled
|
||||||
} from '@ant-design/icons-vue'
|
} from '@ant-design/icons-vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
|
import { sendChatStream } from '@/api/agent'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -156,9 +157,16 @@ const loading = ref(false)
|
|||||||
const messages = ref([])
|
const messages = ref([])
|
||||||
const messagesRef = ref(null)
|
const messagesRef = ref(null)
|
||||||
const userAvatar = ref('王')
|
const userAvatar = ref('王')
|
||||||
|
const conversationId = ref(null) // 会话ID,用于连续对话
|
||||||
|
const abortController = ref(null) // 用于取消请求
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
// 取消正在进行的请求
|
||||||
|
if (abortController.value) {
|
||||||
|
abortController.value.abort()
|
||||||
|
abortController.value = null
|
||||||
|
}
|
||||||
emit('update:visible', false)
|
emit('update:visible', false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,24 +189,74 @@ const handleSend = async () => {
|
|||||||
|
|
||||||
await scrollToBottom()
|
await scrollToBottom()
|
||||||
|
|
||||||
// 模拟 AI 响应
|
// 创建 AI 消息占位
|
||||||
setTimeout(() => {
|
const assistantMessage = {
|
||||||
const assistantMessage = {
|
role: 'assistant',
|
||||||
role: 'assistant',
|
content: '',
|
||||||
content: generateMockResponse(question),
|
isPro: modelMode.value === 'pro',
|
||||||
isPro: modelMode.value === 'pro',
|
actions: false
|
||||||
actions: true
|
}
|
||||||
}
|
messages.value.push(assistantMessage)
|
||||||
messages.value.push(assistantMessage)
|
|
||||||
loading.value = false
|
|
||||||
nextTick(() => scrollToBottom())
|
|
||||||
}, 1500)
|
|
||||||
|
|
||||||
emit('send', {
|
// 创建取消控制器
|
||||||
agentId: props.agent?.id,
|
abortController.value = new AbortController()
|
||||||
content: question,
|
|
||||||
modelMode: modelMode.value
|
// 调用流式对话 API
|
||||||
})
|
try {
|
||||||
|
await sendChatStream({
|
||||||
|
agentId: props.agent?.id,
|
||||||
|
content: question,
|
||||||
|
conversationId: conversationId.value,
|
||||||
|
ctrl: abortController.value,
|
||||||
|
onMessage: (result) => {
|
||||||
|
if (result.event === 'message' && result.content) {
|
||||||
|
// 追加消息内容
|
||||||
|
assistantMessage.content += result.content
|
||||||
|
scrollToBottom()
|
||||||
|
} else if (result.event === 'done') {
|
||||||
|
// 对话完成
|
||||||
|
conversationId.value = result.conversationId
|
||||||
|
assistantMessage.actions = true
|
||||||
|
} else if (result.event === 'error') {
|
||||||
|
// 错误处理
|
||||||
|
message.error(result.errorMessage || '对话出错')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error('发送消息失败:', error)
|
||||||
|
message.error('发送消息失败,请重试')
|
||||||
|
// 移除失败的 AI 消息
|
||||||
|
const lastMsg = messages.value[messages.value.length - 1]
|
||||||
|
if (lastMsg?.role === 'assistant' && !lastMsg.content) {
|
||||||
|
messages.value.pop()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClose: () => {
|
||||||
|
loading.value = false
|
||||||
|
abortController.value = null
|
||||||
|
nextTick(() => scrollToBottom())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
emit('send', {
|
||||||
|
agentId: props.agent?.id,
|
||||||
|
content: question,
|
||||||
|
modelMode: modelMode.value
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
// 用户取消不需要提示
|
||||||
|
if (error.name !== 'AbortError') {
|
||||||
|
console.error('发送消息失败:', error)
|
||||||
|
message.error('发送消息失败,请重试')
|
||||||
|
// 移除失败的 AI 消息
|
||||||
|
const lastMsg = messages.value[messages.value.length - 1]
|
||||||
|
if (lastMsg?.role === 'assistant' && !lastMsg.content) {
|
||||||
|
messages.value.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeyDown = (e) => {
|
const handleKeyDown = (e) => {
|
||||||
@@ -213,9 +271,14 @@ const handleCopy = (content) => {
|
|||||||
message.success('已复制到剪贴板')
|
message.success('已复制到剪贴板')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRegenerate = (index) => {
|
const handleRegenerate = async (index) => {
|
||||||
// TODO: 重新生成消息
|
// 重新生成:移除当前消息,重新发送上一条用户消息
|
||||||
message.info('重新生成中...')
|
if (index > 0 && messages.value[index - 1]?.role === 'user') {
|
||||||
|
const userMsg = messages.value[index - 1]
|
||||||
|
messages.value.splice(index - 1, 2) // 移除用户消息和 AI 回复
|
||||||
|
inputText.value = userMsg.content
|
||||||
|
await handleSend()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollToBottom = async () => {
|
const scrollToBottom = async () => {
|
||||||
@@ -225,20 +288,13 @@ const scrollToBottom = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateMockResponse = (question) => {
|
|
||||||
const responses = [
|
|
||||||
'当夜深人静的时候,我们卸下了一天的铠甲,那才是真实的自己。成年人的世界里,连崩溃都要调成静音模式。',
|
|
||||||
'根据您的需求,我为您生成以下内容:这是一个经过精心设计的文案,结合了情感共鸣和产品卖点。',
|
|
||||||
'让我帮您分析一下这个问题。首先,我们需要考虑目标受众的需求和痛点...'
|
|
||||||
]
|
|
||||||
return responses[Math.floor(Math.random() * responses.length)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听 visible 变化,重置状态
|
// 监听 visible 变化,重置状态
|
||||||
watch(() => props.visible, (newVal) => {
|
watch(() => props.visible, (newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
messages.value = []
|
messages.value = []
|
||||||
inputText.value = ''
|
inputText.value = ''
|
||||||
|
conversationId.value = null // 重置会话ID
|
||||||
|
abortController.value = null
|
||||||
nextTick(() => scrollToBottom())
|
nextTick(() => scrollToBottom())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -70,9 +70,9 @@
|
|||||||
<!-- 描述 -->
|
<!-- 描述 -->
|
||||||
<p class="card-description">{{ agent.description }}</p>
|
<p class="card-description">{{ agent.description }}</p>
|
||||||
|
|
||||||
<!-- 底部:使用量 + 按钮 -->
|
<!-- 底部:按钮 -->
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<span class="usage-count">🔥 {{ formatNumber(agent.usage) }}+ 使用</span>
|
<span class="usage-count">{{ agent.categoryName }}</span>
|
||||||
<button class="chat-btn" @click.stop="handleChat(agent)">
|
<button class="chat-btn" @click.stop="handleChat(agent)">
|
||||||
开始对话
|
开始对话
|
||||||
</button>
|
</button>
|
||||||
@@ -105,6 +105,7 @@ import {
|
|||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import FullWidthLayout from '@/layouts/components/FullWidthLayout.vue'
|
import FullWidthLayout from '@/layouts/components/FullWidthLayout.vue'
|
||||||
import ChatDrawer from '@/components/agents/ChatDrawer.vue'
|
import ChatDrawer from '@/components/agents/ChatDrawer.vue'
|
||||||
|
import { getAgentList } from '@/api/agent'
|
||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -113,88 +114,41 @@ const searchKeyword = ref('')
|
|||||||
const chatDrawerVisible = ref(false)
|
const chatDrawerVisible = ref(false)
|
||||||
const currentAgent = ref(null)
|
const currentAgent = ref(null)
|
||||||
|
|
||||||
// 分类数据
|
// 智能体列表数据(从 API 获取)
|
||||||
const categories = ref([
|
const agentList = ref([])
|
||||||
{ id: 'all', name: '全部智能体', count: 0 },
|
|
||||||
{ id: 'writing', name: '文案创作', count: 0 },
|
|
||||||
{ id: 'analysis', name: '数据分析', count: 0 },
|
|
||||||
{ id: 'coding', name: '代码助手', count: 0 },
|
|
||||||
{ id: 'design', name: '设计创作', count: 0 },
|
|
||||||
{ id: 'education', name: '教育学习', count: 0 }
|
|
||||||
])
|
|
||||||
|
|
||||||
// 智能体列表数据
|
// 分类数据(动态从 agentList 提取)
|
||||||
const agentList = ref([
|
const categories = computed(() => {
|
||||||
{
|
const categoryMap = new Map()
|
||||||
id: 1,
|
|
||||||
name: '文案写作助手',
|
// 统计各分类数量
|
||||||
description: '专业的内容创作智能体,帮助您撰写各类文案,包括广告语、产品介绍、营销文案等。',
|
agentList.value.forEach(agent => {
|
||||||
avatar: '',
|
const cat = agent.categoryName || '其他'
|
||||||
categoryId: 'writing',
|
categoryMap.set(cat, (categoryMap.get(cat) || 0) + 1)
|
||||||
categoryName: '文案创作',
|
})
|
||||||
tagColor: 'blue',
|
|
||||||
usage: 12580
|
// 构建分类列表
|
||||||
},
|
const cats = [
|
||||||
{
|
{ id: 'all', name: '全部智能体', count: agentList.value.length }
|
||||||
id: 2,
|
]
|
||||||
name: '数据分析专家',
|
|
||||||
description: '强大的数据分析工具,支持多种数据格式,提供专业的数据洞察和可视化建议。',
|
categoryMap.forEach((count, name) => {
|
||||||
avatar: '',
|
cats.push({ id: name, name, count })
|
||||||
categoryId: 'analysis',
|
})
|
||||||
categoryName: '数据分析',
|
|
||||||
tagColor: 'green',
|
return cats
|
||||||
usage: 8920
|
})
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: '代码生成器',
|
|
||||||
description: '智能代码生成工具,支持多种编程语言,提高开发效率,减少重复工作。',
|
|
||||||
avatar: '',
|
|
||||||
categoryId: 'coding',
|
|
||||||
categoryName: '代码助手',
|
|
||||||
tagColor: 'purple',
|
|
||||||
usage: 15230
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: '海报设计助手',
|
|
||||||
description: '专业的海报设计工具,提供丰富的模板和设计建议,轻松制作精美海报。',
|
|
||||||
avatar: '',
|
|
||||||
categoryId: 'design',
|
|
||||||
categoryName: '设计创作',
|
|
||||||
tagColor: 'pink',
|
|
||||||
usage: 6750
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: '英语学习伙伴',
|
|
||||||
description: '个性化英语学习助手,提供口语练习、语法讲解、词汇积累等功能。',
|
|
||||||
avatar: '',
|
|
||||||
categoryId: 'education',
|
|
||||||
categoryName: '教育学习',
|
|
||||||
tagColor: 'amber',
|
|
||||||
usage: 9840
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: '短视频脚本',
|
|
||||||
description: '专业的短视频脚本创作工具,支持多种视频类型,助您打造爆款内容。',
|
|
||||||
avatar: '',
|
|
||||||
categoryId: 'writing',
|
|
||||||
categoryName: '文案创作',
|
|
||||||
tagColor: 'blue',
|
|
||||||
usage: 7680
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// 计算属性:过滤后的列表
|
// 计算属性:过滤后的列表
|
||||||
const filteredAgentList = computed(() => {
|
const filteredAgentList = computed(() => {
|
||||||
let list = agentList.value
|
let list = agentList.value
|
||||||
|
|
||||||
|
// 按分类过滤
|
||||||
if (activeCategory.value !== 'all') {
|
if (activeCategory.value !== 'all') {
|
||||||
list = list.filter(a => a.categoryId === activeCategory.value)
|
list = list.filter(a => a.categoryName === activeCategory.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按关键词搜索(前端过滤)
|
||||||
if (searchKeyword.value) {
|
if (searchKeyword.value) {
|
||||||
const keyword = searchKeyword.value.toLowerCase()
|
const keyword = searchKeyword.value.toLowerCase()
|
||||||
list = list.filter(a =>
|
list = list.filter(a =>
|
||||||
@@ -206,18 +160,54 @@ const filteredAgentList = computed(() => {
|
|||||||
return list
|
return list
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 获取智能体列表
|
||||||
|
const fetchAgentList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getAgentList()
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
// 字段映射:agentName → name, icon → avatar
|
||||||
|
agentList.value = res.data.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
agentId: item.agentId,
|
||||||
|
name: item.agentName,
|
||||||
|
description: item.description,
|
||||||
|
avatar: item.icon,
|
||||||
|
categoryName: item.categoryName || '其他',
|
||||||
|
tagColor: getTagColor(item.categoryName)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取智能体列表失败:', error)
|
||||||
|
message.error('获取智能体列表失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据分类名称获取标签颜色
|
||||||
|
const getTagColor = (categoryName) => {
|
||||||
|
const colorMap = {
|
||||||
|
'文案创作': 'blue',
|
||||||
|
'数据分析': 'green',
|
||||||
|
'代码助手': 'purple',
|
||||||
|
'设计创作': 'pink',
|
||||||
|
'教育学习': 'amber'
|
||||||
|
}
|
||||||
|
return colorMap[categoryName] || 'blue'
|
||||||
|
}
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
const handleCategoryChange = (categoryId) => {
|
const handleCategoryChange = (categoryId) => {
|
||||||
activeCategory.value = categoryId
|
activeCategory.value = categoryId
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
// 搜索逻辑通过 computed 自动处理,这里可以留作扩展(如埋点、API请求等)
|
// 搜索逻辑通过 computed 自动处理
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAgentClick = (agent) => {
|
const handleAgentClick = (agent) => {
|
||||||
console.log('查看智能体详情:', agent)
|
console.log('查看智能体详情:', agent)
|
||||||
// TODO: 跳转到智能体详情页
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChat = (agent) => {
|
const handleChat = (agent) => {
|
||||||
@@ -227,26 +217,11 @@ const handleChat = (agent) => {
|
|||||||
|
|
||||||
const handleSendMessage = (data) => {
|
const handleSendMessage = (data) => {
|
||||||
console.log('发送消息:', data)
|
console.log('发送消息:', data)
|
||||||
// TODO: 调用 API 发送消息
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatNumber = (num) => {
|
|
||||||
if (num >= 10000) {
|
|
||||||
return (num / 10000).toFixed(0) + 'w'
|
|
||||||
}
|
|
||||||
return (num / 1000).toFixed(1) + 'w'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 更新分类数量
|
fetchAgentList()
|
||||||
categories.value.forEach(cat => {
|
|
||||||
if (cat.id === 'all') {
|
|
||||||
cat.count = agentList.value.length
|
|
||||||
} else {
|
|
||||||
cat.count = agentList.value.filter(a => a.categoryId === cat.id).length
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package cn.iocoder.yudao.module.tik.muye.aiagent;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.module.tik.muye.aiagent.dal.AiAgentDO;
|
||||||
|
import cn.iocoder.yudao.module.tik.muye.aiagent.service.AiAgentService;
|
||||||
|
import cn.iocoder.yudao.module.tik.muye.aiagent.vo.AiAgentRespVO;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户 App - AI智能体
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Tag(name = "用户 App - AI智能体")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/tik/agent")
|
||||||
|
@Validated
|
||||||
|
public class AppAiAgentController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiAgentService aiAgentService;
|
||||||
|
|
||||||
|
@GetMapping("/list")
|
||||||
|
@Operation(summary = "获取启用的智能体列表")
|
||||||
|
public CommonResult<List<AiAgentRespVO>> getEnabledAgentList() {
|
||||||
|
List<AiAgentDO> list = aiAgentService.getEnabledAgentList();
|
||||||
|
return success(BeanUtils.toBean(list, AiAgentRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.tik.muye.aiagent.dal;
|
package cn.iocoder.yudao.module.tik.muye.aiagent.dal;
|
||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import java.util.*;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
@@ -55,6 +53,10 @@ public class AiAgentDO extends BaseDO {
|
|||||||
* 备注
|
* 备注
|
||||||
*/
|
*/
|
||||||
private String remark;
|
private String remark;
|
||||||
|
/**
|
||||||
|
* 分类名称(中文)
|
||||||
|
*/
|
||||||
|
private String categoryName;
|
||||||
/**
|
/**
|
||||||
* 操作人用户编号
|
* 操作人用户编号
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -33,4 +33,13 @@ public interface AiAgentMapper extends BaseMapperX<AiAgentDO> {
|
|||||||
.orderByDesc(AiAgentDO::getId));
|
.orderByDesc(AiAgentDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询启用状态的智能体列表
|
||||||
|
*/
|
||||||
|
default List<AiAgentDO> selectEnabledList() {
|
||||||
|
return selectList(new LambdaQueryWrapperX<AiAgentDO>()
|
||||||
|
.eq(AiAgentDO::getStatus, 1)
|
||||||
|
.orderByDesc(AiAgentDO::getId));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -60,4 +60,11 @@ public interface AiAgentService {
|
|||||||
*/
|
*/
|
||||||
PageResult<AiAgentDO> getAiAgentPage(AiAgentPageReqVO pageReqVO);
|
PageResult<AiAgentDO> getAiAgentPage(AiAgentPageReqVO pageReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取启用状态的智能体列表
|
||||||
|
*
|
||||||
|
* @return 启用状态的智能体列表
|
||||||
|
*/
|
||||||
|
List<AiAgentDO> getEnabledAgentList();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -54,10 +54,9 @@ public class AiAgentServiceImpl implements AiAgentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteAiAgentListByIds(List<Long> ids) {
|
public void deleteAiAgentListByIds(List<Long> ids) {
|
||||||
// 删除
|
|
||||||
aiAgentMapper.deleteByIds(ids);
|
aiAgentMapper.deleteByIds(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void validateAiAgentExists(Long id) {
|
private void validateAiAgentExists(Long id) {
|
||||||
@@ -76,4 +75,9 @@ public class AiAgentServiceImpl implements AiAgentService {
|
|||||||
return aiAgentMapper.selectPage(pageReqVO);
|
return aiAgentMapper.selectPage(pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AiAgentDO> getEnabledAgentList() {
|
||||||
|
return aiAgentMapper.selectEnabledList();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,10 @@ public class AiAgentRespVO {
|
|||||||
@ExcelProperty("备注")
|
@ExcelProperty("备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "分类名称(中文)", example = "文案创作")
|
||||||
|
@ExcelProperty("分类名称")
|
||||||
|
private String categoryName;
|
||||||
|
|
||||||
@Schema(description = "操作人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6593")
|
@Schema(description = "操作人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6593")
|
||||||
@ExcelProperty("操作人用户编号")
|
@ExcelProperty("操作人用户编号")
|
||||||
private Long operatorId;
|
private Long operatorId;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.tik.muye.aiagent.vo;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import java.util.*;
|
|
||||||
import jakarta.validation.constraints.*;
|
import jakarta.validation.constraints.*;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI智能体新增/修改 Request VO")
|
@Schema(description = "管理后台 - AI智能体新增/修改 Request VO")
|
||||||
@@ -36,10 +35,12 @@ public class AiAgentSaveReqVO {
|
|||||||
@NotEmpty(message = "预置提示词不能为空")
|
@NotEmpty(message = "预置提示词不能为空")
|
||||||
private String systemPrompt;
|
private String systemPrompt;
|
||||||
|
|
||||||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
|
@Schema(description = "备注", example = "你说的对")
|
||||||
@NotEmpty(message = "备注不能为空")
|
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "分类名称(中文)", example = "文案创作")
|
||||||
|
private String categoryName;
|
||||||
|
|
||||||
@Schema(description = "操作人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6593")
|
@Schema(description = "操作人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6593")
|
||||||
@NotNull(message = "操作人用户编号不能为空")
|
@NotNull(message = "操作人用户编号不能为空")
|
||||||
private Long operatorId;
|
private Long operatorId;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public interface PointRecordMapper extends BaseMapperX<PointRecordDO> {
|
|||||||
/**
|
/**
|
||||||
* 取消过期的预扣记录(30分钟前)
|
* 取消过期的预扣记录(30分钟前)
|
||||||
*/
|
*/
|
||||||
@Update("UPDATE muey_point_record SET status = 'canceled', update_time = NOW() " +
|
@Update("UPDATE muye_point_record SET status = 'canceled', update_time = NOW() " +
|
||||||
"WHERE status = 'pending' AND create_time < DATE_SUB(NOW(), INTERVAL 30 MINUTE)")
|
"WHERE status = 'pending' AND create_time < DATE_SUB(NOW(), INTERVAL 30 MINUTE)")
|
||||||
int cancelExpiredPendingRecords();
|
int cancelExpiredPendingRecords();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user