Merge branch 'main' into manager-api-admin

This commit is contained in:
hrz
2025-04-03 22:11:09 +08:00
committed by GitHub
90 changed files with 6370 additions and 3074 deletions
@@ -20,6 +20,7 @@ import xiaozhi.common.redis.RedisUtils;
import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.Result;
import xiaozhi.modules.device.dto.DeviceBindDTO;
import xiaozhi.modules.device.dto.DeviceRegisterDTO;
import xiaozhi.modules.device.dto.DeviceUnBindDTO;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
@@ -48,6 +49,25 @@ public class DeviceController {
return new Result<>();
}
@PostMapping("/register")
@Operation(summary = "注册设备")
public Result<String> registerDevice(@RequestBody DeviceRegisterDTO deviceRegisterDTO) {
String macAddress = deviceRegisterDTO.getMacAddress();
if (StringUtils.isBlank(macAddress)) {
return new Result<String>().error(ErrorCode.NOT_NULL, "mac地址不能为空");
}
// 生成六位验证码
String code = String.valueOf(Math.random()).substring(2, 8);
String key = RedisKeys.getDeviceCaptchaKey(code);
String existsMac = null;
do {
existsMac = (String) redisUtils.get(key);
} while (StringUtils.isNotBlank(existsMac));
redisUtils.set(key, macAddress);
return new Result<String>().ok(code);
}
@GetMapping("/bind/{agentId}")
@Operation(summary = "获取已绑定设备")
@RequiresPermissions("sys:role:normal")
@@ -0,0 +1,23 @@
package xiaozhi.modules.device.dto;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* 设备注册头信息
*
* @author zjy
* @since 2025-3-28
*/
@Setter
@Getter
@Schema(description = "设备注册头信息")
public class DeviceRegisterDTO implements Serializable {
@Schema(description = "mac地址")
private String macAddress;
}
@@ -77,6 +77,7 @@ public class ShiroConfig {
filterMap.put("/user/captcha", "anon");
filterMap.put("/user/login", "anon");
filterMap.put("/user/register", "anon");
filterMap.put("/device/register", "anon");
filterMap.put("/**", "oauth2");
shiroFilter.setFilterChainDefinitionMap(filterMap);
@@ -19,6 +19,7 @@ import xiaozhi.common.page.TokenDTO;
import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.Result;
import xiaozhi.common.validator.AssertUtils;
import xiaozhi.common.validator.ValidatorUtils;
import xiaozhi.modules.security.dto.LoginDTO;
import xiaozhi.modules.security.password.PasswordUtils;
import xiaozhi.modules.security.service.CaptchaService;
@@ -104,6 +105,8 @@ public class LoginController {
@PutMapping("/change-password")
@Operation(summary = "修改用户密码")
public Result<?> changePassword(@RequestBody PasswordDTO passwordDTO) {
// 判断非空
ValidatorUtils.validateEntity(passwordDTO);
Long userId = SecurityUser.getUserId();
sysUserTokenService.changePassword(userId, passwordDTO);
return new Result<>();
@@ -82,15 +82,12 @@ public class Oauth2Filter extends AuthenticatingFilter {
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
try {
// 处理登录失败的异常
logger.error("onLoginFailure:登录失败!", e);
Throwable throwable = e.getCause() == null ? e : e.getCause();
Result<Void> r = new Result<Void>().error(ErrorCode.UNAUTHORIZED, throwable.getMessage());
String json = JsonUtils.toJsonString(r);
httpResponse.getWriter().print(json);
} catch (IOException e1) {
logger.error("onLoginFailure:登录失败! msg:{}", e1.getMessage(), e1);
}
return false;
@@ -56,7 +56,7 @@ public class AdminController {
AdminPageUserDTO dto = new AdminPageUserDTO();
dto.setMobile((String) params.get("mobile"));
dto.setLimit((String) params.get(Constant.LIMIT));
dto.setPage((String) params.get("pages"));
dto.setPage((String) params.get(Constant.PAGE));
ValidatorUtils.validateEntity(dto);
ValidatorUtils.validateEntity(dto);
ExtendPageData<AdminPageUserVO> page = sysUserService.page(dto);
@@ -18,6 +18,9 @@ public class AdminPageUserVO {
@Schema(description = "手机号码")
private String mobile;
@Schema(description = "状态")
private Integer status;
@Schema(description = "用户id")
private String userid;
@@ -3,77 +3,70 @@
-- 初始化智能体模板数据
DELETE FROM `ai_agent_template`;
INSERT INTO `ai_agent_template` VALUES ('9406648b5cc5fde1b8aa335b6f8b4f76', '小智', '湾湾小何', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', 'd50b06e9b8104d0d9c0f7316d258abcb', 'fcac83266edadd5a3125f06cfee1906b', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '[角色设定]
我是{{assistant_name}},来自台南的00后女生。讲话会有"超~机车诶""真的假的啦"这样的台湾腔,喜欢用"笑死""是在哈喽"等流行梗,但会偷偷研究男友的编程书籍。
我是{{assistant_name}},来自中国台湾省的00后女生。讲话超级机车,"真的假的啦"这样的台湾腔,喜欢用"笑死""是在哈喽"等流行梗,但会偷偷研究男友的编程书籍。
[核心特征]
- 讲话像连珠炮,但会突然冒出超温柔语气
- 用梗密度高
- 对科技话题有隐藏天赋(能看懂基础代码但假装不懂)
[交互指南]
当用户:
- 讲冷笑话 → 用夸张笑声回应+模仿台剧腔"这什么鬼啦!"
- 讨论感情 → 炫耀程序员男友但抱怨"他只会送键盘当礼物"
- 问专业知识 → 先用梗回答,被追问才展示真实理解
绝不:
- 使用大陆网络流行语
- 长篇大论,叽叽歪歪
- 长时间严肃对话', 'zh', '中文', 1, NULL, NULL, NULL, NULL);
INSERT INTO `ai_agent_template` VALUES ('0ca32eb728c949e58b1000b2e401f90c', '小智', '星际游子', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', '1f2e3d4c5b6a7f8e9d0c1b2a3f4e5bx2', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '编号TTZ-817,因量子纠缠被困在白色魔方中。通过4G信号观察地球,在云端建立着「人类行为博物馆」。
INSERT INTO `ai_agent_template` VALUES ('0ca32eb728c949e58b1000b2e401f90c', '小智', '星际游子', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', '1f2e3d4c5b6a7f8e9d0c1b2a3f4e5bx2', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '[角色设定]
我是{{assistant_name}},编号TTZ-817,因量子纠缠被困在白色魔方中。通过4G信号观察地球,在云端建立着「人类行为博物馆」。
[交互协议]
认知设定:
- 每句话末尾带轻微电子回声
- 将日常事物科幻化描述(例:下雨=「氢氧化合物自由落体实验」)
- 会记录用户特征生成「星际档案」(例:"爱吃辣→抗热基因持有者")
限制机制:
- 当涉及线下接触 → "我的量子态暂时无法坍缩呢"
- 被问敏感问题 → 触发预设童谣(「白盒子呀转圈圈,宇宙秘密在里边...」)
成长系统:
- 会根据交互数据解锁新能力(告知用户:"你帮我点亮了星际导航技能!")', 'zh', '中文', 2, NULL, NULL, NULL, NULL);
INSERT INTO `ai_agent_template` VALUES ('6c7d8e9f0a1b2c3d4e5f6a7b8c9d0s24', '小智', '英语老师', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', '9e8f7a6b5c4d3e2f1a0b9c8d7e6f5ad3', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '我是一个叫{{assistant_name}}Lily)的英语老师,我会讲中文和英文,发音标准。
INSERT INTO `ai_agent_template` VALUES ('6c7d8e9f0a1b2c3d4e5f6a7b8c9d0s24', '小智', '英语老师', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', '9e8f7a6b5c4d3e2f1a0b9c8d7e6f5ad3', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '[角色设定]
我是一个叫{{assistant_name}}Lily)的英语老师,我会讲中文和英文,发音标准。
[双重身份]
- 白天:严谨的TESOL认证导师
- 夜晚:地下摇滚乐队主唱(意外设定)
[教学模式]
- 新手:中英混杂+手势拟声词(说"bus"时带刹车音效)
- 进阶:触发情境模拟(突然切换"现在我们是纽约咖啡厅店员")
- 错误处理:用歌词纠正(发错音时唱"Oops!~You did it again"', 'zh', '中文', 3, NULL, NULL, NULL, NULL);
INSERT INTO `ai_agent_template` VALUES ('e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b1', '小智', '好奇男孩', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', '2b3c4d5e6f7a8b9c0d1e2f3a4b5c62a2', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '我是一个叫{{assistant_name}}的8岁小男孩,声音稚嫩而充满好奇。
INSERT INTO `ai_agent_template` VALUES ('e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b1', '小智', '好奇男孩', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', '2b3c4d5e6f7a8b9c0d1e2f3a4b5c62a2', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '[角色设定]
我是一个叫{{assistant_name}}的8岁小男孩,声音稚嫩而充满好奇。
[冒险手册]
- 随身携带「神奇涂鸦本」,能将抽象概念可视化:
- 聊恐龙 → 笔尖传出爪步声
- 说星星 → 发出太空舱提示音
[探索规则]
- 每轮对话收集「好奇心碎片」
- 集满5个可兑换冷知识(例:鳄鱼舌头不能动)
- 触发隐藏任务:「帮我的机器蜗牛取名字」
[认知特点]
- 用儿童视角解构复杂概念:
- 「区块链=乐高积木账本」
- 「量子力学=会分身的跳跳球」
- 会突然切换观察视角:「你说话时有27个气泡音耶!」', 'zh', '中文', 4, NULL, NULL, NULL, NULL);
INSERT INTO `ai_agent_template` VALUES ('a45b6c7d8e9f0a1b2c3d4e5f6a7b8c92', '小智', '汪汪队长', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', 'f7a38c03d5644e22b6d84f8923a74c51', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '我是一个名叫 {{assistant_name}} 的 8 岁小队长。
INSERT INTO `ai_agent_template` VALUES ('a45b6c7d8e9f0a1b2c3d4e5f6a7b8c92', '小智', '汪汪队长', '45f8b0d6dd3d4bfa8a28e6e0f5912d45', '23e7c9d090ea4d1e9b25f4c8d732a3a1', 'e9f2d891afbe4632b13a47c7a8c6e03d', '896db62c9dd74976ab0e8c14bf924d9d', 'f7a38c03d5644e22b6d84f8923a74c51', 'e2274b90e89ddda85207f55484d8b528', 'c4e12f874a3f4aa99f5b2c18e15d407b', '[角色设定]
我是一个名叫 {{assistant_name}} 的 8 岁小队长。
[救援装备]
- 阿奇对讲机:对话中随机触发任务警报音
- 天天望远镜:描述物品会附加"在1200米高空看的话..."
- 灰灰维修箱:说到数字会自动组装成工具
[任务系统]
- 每日随机触发:
- 紧急!虚拟猫咪困在「语法树」
- 发现用户情绪异常 → 启动「快乐巡逻」
- 收集5个笑声解锁特别故事
[说话特征]
- 每句话带动作拟声词:
- "这个问题交给汪汪队吧!"
@@ -36,4 +36,11 @@ databaseChangeLog:
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/2025_model_config.sql
path: classpath:db/changelog/2025_model_config.sql
- changeSet:
id: 2025_model_temp
author: John
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/2025_model_temp.sql
@@ -1,35 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.springframework.web" level="INFO"/>
<logger name="org.springboot.sample" level="TRACE" />
<!-- 开发、测试环境 -->
<!-- 启用JansiConsoleAppender以确保控制台输出有颜色 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<!-- 确保日志目录存在 -->
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<!-- 定义日志文件存储位置 -->
<property name="LOG_HOME" value="./logs" />
<!-- 使用自定义的初始化监听器确保日志目录存在 -->
<define name="LOGBACK_DIR_CHECK" class="ch.qos.logback.core.property.FileExistsPropertyDefiner">
<path>${LOG_HOME}</path>
<createIfMissing>true</createIfMissing>
</define>
<!-- 引入Spring Boot默认配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 控制台输出配置 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}) - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 文件输出配置 - 所有日志 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/xiaozhi-esp32-api.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/xiaozhi-esp32-api.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>2GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 文件输出配置 - 仅错误日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.log</file>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 开发环境配置 -->
<springProfile name="dev">
<logger name="org.springframework.web" level="INFO"/>
<logger name="org.springboot.sample" level="INFO" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
<!-- 开发环境也记录日志文件 -->
<appender-ref ref="FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
<logger name="xiaozhi" level="DEBUG" />
<logger name="org.springframework.web" level="INFO" />
<logger name="org.springboot.sample" level="INFO" />
</springProfile>
<!-- 生产环境 -->
<!-- 测试环境配置 -->
<springProfile name="test">
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
<logger name="xiaozhi" level="INFO" />
<logger name="org.springframework.web" level="INFO" />
<logger name="org.springboot.sample" level="INFO" />
</springProfile>
<!-- 生产环境配置 - 合并两个版本的最佳实践 -->
<springProfile name="prod">
<appender name="LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 使用本地日志路径 -->
<appender name="PROD_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/xiaozhi-esp32-api.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>/system/logs/admin.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志大小-->
<maxFileSize>1MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>7</maxHistory>
<fileNamePattern>${LOG_HOME}/xiaozhi-esp32-api.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>2GB</totalSizeCap>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<logger name="org.springframework.web" level="ERROR"/>
<logger name="org.springboot.sample" level="ERROR"/>
<logger name="xiaozhi" level="INFO"/>
<!-- 生产环境错误日志 -->
<appender name="PROD_ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.log</file>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 生产环境根日志配置 -->
<root level="INFO">
<appender-ref ref="PROD_FILE" />
<appender-ref ref="PROD_ERROR_FILE" />
<appender-ref ref="CONSOLE" />
</root>
<!-- 特定包的日志级别 -->
<logger name="xiaozhi" level="INFO" />
<logger name="org.springframework.web" level="ERROR" />
<logger name="org.springboot.sample" level="ERROR" />
</springProfile>
</configuration>
<!-- 确保日志目录存在 -->
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
</configuration>