feat: init
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -32,3 +32,4 @@ application-local.properties
|
|||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
*.jar
|
*.jar
|
||||||
|
/mysql
|
||||||
|
|||||||
4
db/2025_11_04_add_templateId.sql
Normal file
4
db/2025_11_04_add_templateId.sql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
-- 增加角色表模板ID字段(若不存在)
|
||||||
|
ALTER TABLE `xiaozhi`.`sys_role`
|
||||||
|
ADD COLUMN `templateId` int unsigned DEFAULT NULL COMMENT '模板ID' AFTER `modelId`;
|
||||||
|
|
||||||
@@ -99,6 +99,7 @@ CREATE TABLE `xiaozhi`.`sys_role` (
|
|||||||
`avatar` varchar(255) DEFAULT NULL COMMENT '角色头像',
|
`avatar` varchar(255) DEFAULT NULL COMMENT '角色头像',
|
||||||
`ttsId` int DEFAULT NULL COMMENT 'TTS服务ID',
|
`ttsId` int DEFAULT NULL COMMENT 'TTS服务ID',
|
||||||
`modelId` int unsigned DEFAULT NULL COMMENT '模型ID',
|
`modelId` int unsigned DEFAULT NULL COMMENT '模型ID',
|
||||||
|
`templateId` int unsigned DEFAULT NULL COMMENT '模板ID',
|
||||||
`sttId` int unsigned DEFAULT NULL COMMENT 'STT服务ID',
|
`sttId` int unsigned DEFAULT NULL COMMENT 'STT服务ID',
|
||||||
`vadSpeechTh` FLOAT DEFAULT 0.5 COMMENT '语音检测阈值',
|
`vadSpeechTh` FLOAT DEFAULT 0.5 COMMENT '语音检测阈值',
|
||||||
`vadSilenceTh` FLOAT DEFAULT 0.3 COMMENT '静音检测阈值',
|
`vadSilenceTh` FLOAT DEFAULT 0.3 COMMENT '静音检测阈值',
|
||||||
|
|||||||
26
docker-compose-mysql.yml
Normal file
26
docker-compose-mysql.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
container_name: my-mysql
|
||||||
|
image: mysql:8.0
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: root123 # root 用户密码
|
||||||
|
MYSQL_DATABASE: xiaozhi # 初始化数据库
|
||||||
|
MYSQL_USER: xiaozhi # 初始化用户
|
||||||
|
MYSQL_PASSWORD: 123456 # 用户密码
|
||||||
|
TZ: Asia/Shanghai # 时区
|
||||||
|
volumes:
|
||||||
|
- ./mysql/data:/var/lib/mysql # 持久化数据
|
||||||
|
- ./mysql/init:/docker-entrypoint-initdb.d # 初始化脚本
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
"--character-set-server=utf8mb4",
|
||||||
|
"--collation-server=utf8mb4_general_ci",
|
||||||
|
"--lower_case_table_names=1",
|
||||||
|
"--default-authentication-plugin=mysql_native_password"
|
||||||
|
]
|
||||||
11
docker-compose-redis.yml
Normal file
11
docker-compose-redis.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:7.2
|
||||||
|
container_name: local-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:6379:6379"
|
||||||
|
command: >
|
||||||
|
redis-server --appendonly no --timeout 10 --tcp-keepalive 300
|
||||||
@@ -61,6 +61,11 @@ public class SysRole extends Base<SysRole> {
|
|||||||
*/
|
*/
|
||||||
private String modelName;
|
private String modelName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板ID(提示词模板)
|
||||||
|
*/
|
||||||
|
private Integer templateId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* STT服务ID
|
* STT服务ID
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<sql id="roleSql">
|
<sql id="roleSql">
|
||||||
sys_role.roleId, sys_role.avatar, sys_role.roleName, sys_role.roleDesc, sys_role.voiceName,
|
sys_role.roleId, sys_role.avatar, sys_role.roleName, sys_role.roleDesc, sys_role.voiceName,
|
||||||
sys_role.modelId, sys_role.sttId, sys_role.ttsId,
|
sys_role.modelId, sys_role.templateId, sys_role.sttId, sys_role.ttsId,
|
||||||
sys_role.vadSpeechTh, sys_role.vadSilenceTh, sys_role.vadEnergyTh, sys_role.vadSilenceMs,
|
sys_role.vadSpeechTh, sys_role.vadSilenceTh, sys_role.vadEnergyTh, sys_role.vadSilenceMs,
|
||||||
sys_role.userId, sys_role.state, sys_role.isDefault, sys_role.createTime
|
sys_role.userId, sys_role.state, sys_role.isDefault, sys_role.createTime
|
||||||
</sql>
|
</sql>
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
<if test="voiceName != null and voiceName != ''">voiceName = #{voiceName},</if>
|
<if test="voiceName != null and voiceName != ''">voiceName = #{voiceName},</if>
|
||||||
<if test="isDefault != null and isDefault != ''">isDefault = #{isDefault},</if>
|
<if test="isDefault != null and isDefault != ''">isDefault = #{isDefault},</if>
|
||||||
<if test="modelId != null and modelId != ''">modelId = #{modelId},</if>
|
<if test="modelId != null and modelId != ''">modelId = #{modelId},</if>
|
||||||
|
<if test="templateId != null">templateId = #{templateId},</if>
|
||||||
<if test="ttsId != null and ttsId != ''">
|
<if test="ttsId != null and ttsId != ''">
|
||||||
<choose>
|
<choose>
|
||||||
<when test="ttsId == -1">ttsId = null,</when>
|
<when test="ttsId == -1">ttsId = null,</when>
|
||||||
@@ -79,12 +80,13 @@
|
|||||||
</update>
|
</update>
|
||||||
|
|
||||||
<insert id="add" useGeneratedKeys="true" keyProperty="roleName" parameterType="com.xiaozhi.entity.SysRole">
|
<insert id="add" useGeneratedKeys="true" keyProperty="roleName" parameterType="com.xiaozhi.entity.SysRole">
|
||||||
INSERT INTO sys_role ( avatar, roleName, roleDesc, voiceName, modelId, ttsId, sttId, userId, isDefault ) VALUES (
|
INSERT INTO sys_role ( avatar, roleName, roleDesc, voiceName, modelId, templateId, ttsId, sttId, userId, isDefault ) VALUES (
|
||||||
#{avatar},
|
#{avatar},
|
||||||
#{roleName},
|
#{roleName},
|
||||||
#{roleDesc},
|
#{roleDesc},
|
||||||
#{voiceName},
|
#{voiceName},
|
||||||
#{modelId},
|
#{modelId},
|
||||||
|
#{templateId},
|
||||||
<choose>
|
<choose>
|
||||||
<when test="ttsId == -1">null</when>
|
<when test="ttsId == -1">null</when>
|
||||||
<otherwise>#{ttsId}</otherwise>
|
<otherwise>#{ttsId}</otherwise>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class AppRoleController extends BaseController {
|
|||||||
public AjaxResult query(SysRole role, HttpServletRequest request) {
|
public AjaxResult query(SysRole role, HttpServletRequest request) {
|
||||||
try {
|
try {
|
||||||
PageFilter pageFilter = initPageFilter(request);
|
PageFilter pageFilter = initPageFilter(request);
|
||||||
role.setUserId(Math.toIntExact(AppLoginCache.get().getUserId()));
|
// role.setUserId(Math.toIntExact(AppLoginCache.get().getUserId()));
|
||||||
List<SysRole> roleList = roleService.query(role, pageFilter);
|
List<SysRole> roleList = roleService.query(role, pageFilter);
|
||||||
AjaxResult result = AjaxResult.success();
|
AjaxResult result = AjaxResult.success();
|
||||||
result.put("data", new PageInfo<>(roleList));
|
result.put("data", new PageInfo<>(roleList));
|
||||||
|
|||||||
@@ -436,24 +436,18 @@
|
|||||||
style="margin-bottom: 16px" />
|
style="margin-bottom: 16px" />
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- 提示词编辑模式选择 -->
|
<!-- 仅允许选择模板(禁用自定义) -->
|
||||||
<div style="margin-bottom: 16px; display: flex; justify-content: space-between; align-items: center">
|
<div style="margin-bottom: 16px; display: flex; justify-content: space-between; align-items: center">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-radio-group v-model="promptEditorMode" button-style="solid" @change="handlePromptModeChange">
|
<a-tag color="blue">仅支持从模板选择</a-tag>
|
||||||
<a-radio-button value="template">使用模板</a-radio-button>
|
<a-select style="width: 240px" placeholder="请选择模板" v-model="selectedTemplateId"
|
||||||
<a-radio-button value="custom">自定义</a-radio-button>
|
@change="handleTemplateChange" :loading="templatesLoading" :allowClear="false">
|
||||||
</a-radio-group>
|
<a-select-option v-for="template in promptTemplates" :key="template.templateId"
|
||||||
|
:value="template.templateId">
|
||||||
<template v-if="promptEditorMode === 'template'">
|
{{ template.templateName }}
|
||||||
<a-select style="width: 200px" placeholder="选择模板" v-model="selectedTemplateId"
|
<a-tag v-if="template.isDefault == 1" color="green" size="small">默认</a-tag>
|
||||||
@change="handleTemplateChange" :loading="templatesLoading">
|
</a-select-option>
|
||||||
<a-select-option v-for="template in promptTemplates" :key="template.templateId"
|
</a-select>
|
||||||
:value="template.templateId">
|
|
||||||
{{ template.templateName }}
|
|
||||||
<a-tag v-if="template.isDefault == 1" color="green" size="small">默认</a-tag>
|
|
||||||
</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
|
|
||||||
<!-- 模板管理按钮 -->
|
<!-- 模板管理按钮 -->
|
||||||
@@ -470,7 +464,7 @@
|
|||||||
{
|
{
|
||||||
rules: [],
|
rules: [],
|
||||||
},
|
},
|
||||||
]" :disabled="selectedModelType === 'agent'" :rows="10" placeholder="请输入角色提示词,描述角色的特点、知识背景和行为方式等" />
|
]" :disabled="true" :rows="10" placeholder="请选择模板,提示词将随模板自动填充" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 表单操作按钮 -->
|
<!-- 表单操作按钮 -->
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
@@ -600,7 +594,8 @@ export default {
|
|||||||
|
|
||||||
// 表单相关
|
// 表单相关
|
||||||
roleForm: this.$form.createForm(this),
|
roleForm: this.$form.createForm(this),
|
||||||
promptEditorMode: 'custom',
|
// 仅模板模式
|
||||||
|
promptEditorMode: 'template',
|
||||||
selectedTemplateId: null,
|
selectedTemplateId: null,
|
||||||
promptTemplates: [],
|
promptTemplates: [],
|
||||||
templatesLoading: false,
|
templatesLoading: false,
|
||||||
@@ -1284,12 +1279,19 @@ export default {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.roleForm.validateFields((err, values) => {
|
this.roleForm.validateFields((err, values) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
|
// 校验必须选择模板
|
||||||
|
if (!this.selectedTemplateId) {
|
||||||
|
this.$message.warning('请选择模板');
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.submitLoading = true;
|
this.submitLoading = true;
|
||||||
|
|
||||||
// 添加语音提供商信息
|
// 添加语音提供商信息
|
||||||
const formData = {
|
const formData = {
|
||||||
...values,
|
...values,
|
||||||
avatar: this.avatarUrl,
|
avatar: this.avatarUrl,
|
||||||
|
// 记录模板ID
|
||||||
|
templateId: this.selectedTemplateId,
|
||||||
// 将开关的布尔值转换为数字(0或1)
|
// 将开关的布尔值转换为数字(0或1)
|
||||||
isDefault: values.isDefault ? 1 : 0
|
isDefault: values.isDefault ? 1 : 0
|
||||||
};
|
};
|
||||||
@@ -1372,6 +1374,9 @@ export default {
|
|||||||
this.selectedModelType = record.modelType || MODEL_TYPE.LLM;
|
this.selectedModelType = record.modelType || MODEL_TYPE.LLM;
|
||||||
this.selectedModelProvider = record.modelProvider || '';
|
this.selectedModelProvider = record.modelProvider || '';
|
||||||
|
|
||||||
|
// 同步模板选中项
|
||||||
|
this.selectedTemplateId = record.templateId || this.selectedTemplateId || null;
|
||||||
|
|
||||||
// 设置表单值,将isDefault从数字转为布尔值
|
// 设置表单值,将isDefault从数字转为布尔值
|
||||||
roleForm.setFieldsValue({
|
roleForm.setFieldsValue({
|
||||||
roleName: record.roleName,
|
roleName: record.roleName,
|
||||||
@@ -1406,6 +1411,13 @@ export default {
|
|||||||
ttsId: this.selectedTtsId,
|
ttsId: this.selectedTtsId,
|
||||||
voiceName: record.voiceName
|
voiceName: record.voiceName
|
||||||
});
|
});
|
||||||
|
// 如果存在模板ID,填充模板内容
|
||||||
|
if (this.selectedTemplateId && this.promptTemplates && this.promptTemplates.length > 0) {
|
||||||
|
const t = this.promptTemplates.find(t => t.templateId === this.selectedTemplateId);
|
||||||
|
if (t) {
|
||||||
|
roleForm.setFieldsValue({ roleDesc: t.templateContent });
|
||||||
|
}
|
||||||
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -1468,7 +1480,7 @@ export default {
|
|||||||
resetForm() {
|
resetForm() {
|
||||||
this.roleForm.resetFields();
|
this.roleForm.resetFields();
|
||||||
this.editingRoleId = null;
|
this.editingRoleId = null;
|
||||||
this.promptEditorMode = 'custom';
|
this.promptEditorMode = 'template';
|
||||||
this.audioUrl = '';
|
this.audioUrl = '';
|
||||||
this.avatarUrl = ''; // 重置头像
|
this.avatarUrl = ''; // 重置头像
|
||||||
this.avatarFile = null; // 清空文件对象
|
this.avatarFile = null; // 清空文件对象
|
||||||
@@ -1568,11 +1580,9 @@ export default {
|
|||||||
|
|
||||||
// 如果有默认模板,应用默认模板
|
// 如果有默认模板,应用默认模板
|
||||||
if (this.promptTemplates && this.promptTemplates.length > 0) {
|
if (this.promptTemplates && this.promptTemplates.length > 0) {
|
||||||
const defaultTemplate = this.promptTemplates.find(t => t.isDefault == 1);
|
const defaultTemplate = this.promptTemplates.find(t => t.isDefault == 1) || this.promptTemplates[0];
|
||||||
if (defaultTemplate) {
|
if (defaultTemplate) {
|
||||||
this.selectedTemplateId = defaultTemplate.templateId;
|
this.selectedTemplateId = defaultTemplate.templateId;
|
||||||
this.promptEditorMode = 'template';
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.roleForm.setFieldsValue({
|
this.roleForm.setFieldsValue({
|
||||||
roleDesc: defaultTemplate.templateContent
|
roleDesc: defaultTemplate.templateContent
|
||||||
@@ -1593,15 +1603,13 @@ export default {
|
|||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.promptTemplates = res.data.list;
|
this.promptTemplates = res.data.list;
|
||||||
// 如果有默认模板,自动选择
|
// 自动选择默认模板或第一个
|
||||||
const defaultTemplate = this.promptTemplates.find(t => t.isDefault == 1);
|
const defaultTemplate = this.promptTemplates.find(t => t.isDefault == 1) || this.promptTemplates[0];
|
||||||
if (defaultTemplate) {
|
if (!this.selectedTemplateId && defaultTemplate) {
|
||||||
this.selectedTemplateId = defaultTemplate.templateId;
|
this.selectedTemplateId = defaultTemplate.templateId;
|
||||||
if (this.promptEditorMode === 'template') {
|
this.roleForm.setFieldsValue({
|
||||||
this.roleForm.setFieldsValue({
|
roleDesc: defaultTemplate.templateContent
|
||||||
roleDesc: defaultTemplate.templateContent
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.showError(res.message);
|
this.showError(res.message);
|
||||||
@@ -1744,17 +1752,8 @@ export default {
|
|||||||
this.selectedTtsId = value;
|
this.selectedTtsId = value;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 处理提示词模式变更
|
// 自定义模式已禁用(保留空实现,避免引用报错)
|
||||||
handlePromptModeChange(e) {
|
handlePromptModeChange() {},
|
||||||
if (e.target.value === 'template' && this.selectedTemplateId) {
|
|
||||||
const template = this.promptTemplates.find(t => t.templateId === this.selectedTemplateId);
|
|
||||||
if (template) {
|
|
||||||
this.roleForm.setFieldsValue({
|
|
||||||
roleDesc: template.templateContent
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 处理模板选择变更
|
// 处理模板选择变更
|
||||||
handleTemplateChange(templateId) {
|
handleTemplateChange(templateId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user