From ae758da449a4fad5f61052fcdd2d4f9c624d2a95 Mon Sep 17 00:00:00 2001 From: lucky_feng Date: Thu, 13 Mar 2025 18:03:35 +0800 Subject: [PATCH] Manage api device bind (#311) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 增加绑定设备,解绑设备,获取用户设备列表,获取已绑定设备,admin获取所有设备 api相关请求 * delete头部 * delete错误 * 解绑修改为post * 绑定解绑完善请求参数 * 解绑备注 * update:删除用户表无用属性 * update:绑定设备从redis中获取 * update:优化用户名查询代码 --------- Co-authored-by: hrz <1710360675@qq.com> --- .../xiaozhi/common/exception/ErrorCode.java | 1 + .../java/xiaozhi/common/redis/RedisKeys.java | 57 +----- .../device/controller/DeviceController.java | 83 +++++++++ .../xiaozhi/modules/device/dao/DeviceDao.java | 9 + .../modules/device/dto/DeviceHeaderDTO.java | 19 ++ .../modules/device/dto/DeviceUnBindDTO.java | 19 ++ .../modules/device/entity/DeviceEntity.java | 57 ++++++ .../modules/device/service/DeviceService.java | 18 ++ .../service/impl/DeviceServiceImpl.java | 57 ++++++ .../modules/security/config/ShiroConfig.java | 5 - .../modules/security/oauth2/Oauth2Realm.java | 23 ++- .../sys/controller/SysUserController.java | 142 +-------------- .../xiaozhi/modules/sys/dao/SysUserDao.java | 23 +-- .../modules/sys/entity/SysUserEntity.java | 29 +-- .../modules/sys/service/SysUserService.java | 30 ---- .../sys/service/impl/SysUserServiceImpl.java | 90 ++-------- .../resources/db/changelog/202503131429.sql | 167 ++++++++++++++++++ .../db/changelog/db.changelog-master.yaml | 7 + .../main/resources/i18n/messages.properties | 1 + .../resources/i18n/messages_en_US.properties | 1 + .../resources/i18n/messages_zh_CN.properties | 3 +- .../resources/i18n/messages_zh_TW.properties | 3 +- .../main/resources/mapper/sys/SysUserDao.xml | 33 ---- 23 files changed, 479 insertions(+), 398 deletions(-) create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/device/controller/DeviceController.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceDao.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceHeaderDTO.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUnBindDTO.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceEntity.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceService.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java create mode 100644 main/manager-api/src/main/resources/db/changelog/202503131429.sql diff --git a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java index e2e5de5d..e5365b3c 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java +++ b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java @@ -42,4 +42,5 @@ public interface ErrorCode { int PASSWORD_LENGTH_ERROR = 10030; int PASSWORD_WEAK_ERROR = 10031; int DEL_MYSELF_ERROR = 10032; + int DEVICE_CAPTCHA_ERROR = 10033; } diff --git a/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java b/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java index 0c594059..21121fd7 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java +++ b/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java @@ -21,60 +21,9 @@ public class RedisKeys { } /** - * 登录用户Key + * 未注册设备验证码Key */ - public static String getSecurityUserKey(Long id) { - return "sys:security:user:" + id; + public static String getDeviceCaptchaKey(String captcha) { + return "sys:device:captcha:" + captcha; } - - /** - * 系统日志Key - */ - public static String getSysLogKey() { - return "sys:log"; - } - - /** - * 系统资源Key - */ - public static String getSysResourceKey() { - return "sys:resource"; - } - - /** - * 用户菜单导航Key - */ - public static String getUserMenuNavKey(Long userId) { - return "sys:user:nav:" + userId; - } - - /** - * 用户权限标识Key - */ - public static String getUserPermissionsKey(Long userId) { - return "sys:user:permissions:" + userId; - } - - /** - * 用户登陆错误次数 - * - * @param username - * @return - */ - public static String getUserLoginErrorCountKey(String username) { - return "sys:user:login:error:" + username; - } - - public static String getUserInfoKey(Long userId) { - return "sys:user:" + userId; - } - - public static String getDataScopeListKey(Long userId) { - return "sys:user:data:scope:" + userId; - } - - public static String getSysUserName(Long id) { - return "sys:user:name" + id; - } - } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/controller/DeviceController.java b/main/manager-api/src/main/java/xiaozhi/modules/device/controller/DeviceController.java new file mode 100644 index 00000000..1974689c --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/controller/DeviceController.java @@ -0,0 +1,83 @@ +package xiaozhi.modules.device.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; +import xiaozhi.common.constant.Constant; +import xiaozhi.common.exception.ErrorCode; +import xiaozhi.common.page.PageData; +import xiaozhi.common.redis.RedisKeys; +import xiaozhi.common.redis.RedisUtils; +import xiaozhi.common.user.UserDetail; +import xiaozhi.common.utils.JsonUtils; +import xiaozhi.common.utils.Result; +import xiaozhi.modules.device.dto.DeviceHeaderDTO; +import xiaozhi.modules.device.dto.DeviceUnBindDTO; +import xiaozhi.modules.device.entity.DeviceEntity; +import xiaozhi.modules.device.service.DeviceService; +import xiaozhi.modules.security.user.SecurityUser; + +import java.util.List; +import java.util.Map; + +@AllArgsConstructor +@RestController +@RequestMapping("/device") +@Tag(name = "设备管理") + +public class DeviceController { + private final DeviceService deviceService; + private final RedisUtils redisUtils; + + + @PostMapping("/bind/{deviceCode}") + @Operation(summary = "绑定设备") + @RequiresPermissions("sys:role:normal") + public Result bindDevice(@PathVariable String deviceCode) { + UserDetail user = SecurityUser.getUser(); + + String deviceHeaders = (String) redisUtils.get(RedisKeys.getDeviceCaptchaKey(deviceCode)); + if (StringUtils.isBlank(deviceHeaders)) { + return new Result().error(ErrorCode.DEVICE_CAPTCHA_ERROR); + } + DeviceHeaderDTO deviceHeader = JsonUtils.parseObject(deviceHeaders.getBytes(), DeviceHeaderDTO.class); + DeviceEntity device = deviceService.bindDevice(user.getId(), deviceHeader); + return new Result().ok(device); + } + + @GetMapping("/bind") + @Operation(summary = "获取已绑定设备") + @RequiresPermissions("sys:role:normal") + public Result> getUserDevices() { + UserDetail user = SecurityUser.getUser(); + List devices = deviceService.getUserDevices(user.getId()); + return new Result>().ok(devices); + } + + @PostMapping("/unbind") + @Operation(summary = "解绑设备") + @RequiresPermissions("sys:role:normal") + public Result unbindDevice(@RequestBody DeviceUnBindDTO unDeviveBind) { + UserDetail user = SecurityUser.getUser(); + deviceService.unbindDevice(user.getId(), unDeviveBind.getDeviceId()); + return new Result(); + } + + @GetMapping("/all") + @Operation(summary = "设备列表(管理员)") + @RequiresPermissions("sys:role:superAdmin") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true), + }) + public Result> adminDeviceList( + @Parameter(hidden = true) @RequestParam Map params) { + PageData page = deviceService.adminDeviceList(params); + return new Result>().ok(page); + } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceDao.java b/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceDao.java new file mode 100644 index 00000000..9e4a8cd4 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/dao/DeviceDao.java @@ -0,0 +1,9 @@ +package xiaozhi.modules.device.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import xiaozhi.modules.device.entity.DeviceEntity; + +@Mapper +public interface DeviceDao extends BaseMapper { +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceHeaderDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceHeaderDTO.java new file mode 100644 index 00000000..83327fef --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceHeaderDTO.java @@ -0,0 +1,19 @@ +package xiaozhi.modules.device.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "设备连接头信息") +public class DeviceHeaderDTO { + + @Schema(description = "设备ID") + private String deviceId; + + @Schema(description = "协议版本号") + private Long protocolVersion; + + @Schema(description = "认证信息") + private String authorization; + +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUnBindDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUnBindDTO.java new file mode 100644 index 00000000..830a51ae --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/dto/DeviceUnBindDTO.java @@ -0,0 +1,19 @@ +package xiaozhi.modules.device.dto; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serializable; + +/** + * 设备解绑表单 + */ +@Data +@Schema(description = "设备解绑表单") +public class DeviceUnBindDTO implements Serializable { + + @Schema(description = "设备ID") + @NotBlank(message = "设备ID不能为空") + private Long deviceId; + +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceEntity.java new file mode 100644 index 00000000..bc1a7e88 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/entity/DeviceEntity.java @@ -0,0 +1,57 @@ +package xiaozhi.modules.device.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +@Data +@TableName("ai_device") +@Schema(description = "设备信息") +public class DeviceEntity { + @Schema(description = "设备ID") + private Long id; + + @Schema(description = "关联用户ID") + private Long userId; + + @Schema(description = "MAC地址") + private String macAddress; + + @Schema(description = "最后连接时间") + private Date lastConnectedAt; + + @Schema(description = "自动更新开关(0关闭/1开启)") + private Integer autoUpdate; + + @Schema(description = "设备硬件型号") + private String board; + + @Schema(description = "设备别名") + private String alias; + + @Schema(description = "智能体编码") + private String agentCode; + + @Schema(description = "智能体ID") + private Long agentId; + + @Schema(description = "固件版本号") + private String appVersion; + + @Schema(description = "排序") + private Integer sort; + + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建时间") + private Date createDate; + + @Schema(description = "更新者") + private Long updater; + + @Schema(description = "更新时间") + private Date updateDate; +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceService.java b/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceService.java new file mode 100644 index 00000000..16ad44fe --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/service/DeviceService.java @@ -0,0 +1,18 @@ +package xiaozhi.modules.device.service; + +import xiaozhi.common.page.PageData; +import xiaozhi.modules.device.dto.DeviceHeaderDTO; +import xiaozhi.modules.device.entity.DeviceEntity; + +import java.util.List; +import java.util.Map; + +public interface DeviceService { + DeviceEntity bindDevice(Long userId, DeviceHeaderDTO deviceHeader); + + List getUserDevices(Long userId); + + void unbindDevice(Long userId, Long deviceId); + + PageData adminDeviceList(Map params); +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java new file mode 100644 index 00000000..279a9f2e --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java @@ -0,0 +1,57 @@ +package xiaozhi.modules.device.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.springframework.stereotype.Service; +import xiaozhi.common.page.PageData; +import xiaozhi.common.service.impl.BaseServiceImpl; +import xiaozhi.modules.device.dao.DeviceDao; +import xiaozhi.modules.device.dto.DeviceHeaderDTO; +import xiaozhi.modules.device.entity.DeviceEntity; +import xiaozhi.modules.device.service.DeviceService; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Service +public class DeviceServiceImpl extends BaseServiceImpl implements DeviceService { + private final DeviceDao deviceDao; + + // 添加构造函数来初始化 deviceMapper + public DeviceServiceImpl(DeviceDao deviceDao) { + this.deviceDao = deviceDao; + } + + @Override + public DeviceEntity bindDevice(Long userId, DeviceHeaderDTO deviceHeader) { + DeviceEntity device = new DeviceEntity(); + device.setUserId(userId); + device.setMacAddress(deviceHeader.getDeviceId()); + device.setCreateDate(new Date()); + deviceDao.insert(device); + return device; + } + + @Override + public List getUserDevices(Long userId) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("user_id", userId); + return deviceDao.selectList(wrapper); + } + + @Override + public void unbindDevice(Long userId, Long deviceId) { + deviceDao.deleteById(deviceId); + } + + @Override + public PageData adminDeviceList(Map params) { + IPage page = deviceDao.selectPage( + getPage(params, "sort", true), + new QueryWrapper<>() + ); + return new PageData<>(page.getRecords(), page.getTotal()); + } + +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java index 29f6b706..f770c79f 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java @@ -61,14 +61,9 @@ public class ShiroConfig { Map filterMap = new LinkedHashMap<>(); filterMap.put("/webjars/**", "anon"); filterMap.put("/druid/**", "anon"); - filterMap.put("/login", "anon"); - filterMap.put("/publicKey", "anon"); filterMap.put("/v3/api-docs/**", "anon"); filterMap.put("/doc.html", "anon"); - filterMap.put("/sys/oss/download/**", "anon"); - filterMap.put("/captcha", "anon"); filterMap.put("/favicon.ico", "anon"); - filterMap.put("/mobile/**", "anon"); filterMap.put("/user/**", "anon"); filterMap.put("/**", "oauth2"); shiroFilter.setFilterChainDefinitionMap(filterMap); diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Realm.java b/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Realm.java index 933724a1..3f8a3eeb 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Realm.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/oauth2/Oauth2Realm.java @@ -1,12 +1,5 @@ package xiaozhi.modules.security.oauth2; -import xiaozhi.common.exception.ErrorCode; -import xiaozhi.common.user.UserDetail; -import xiaozhi.common.utils.ConvertUtils; -import xiaozhi.common.utils.MessageUtils; -import xiaozhi.modules.security.entity.SysUserTokenEntity; -import xiaozhi.modules.security.service.ShiroService; -import xiaozhi.modules.sys.entity.SysUserEntity; import jakarta.annotation.Resource; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; @@ -15,8 +8,15 @@ import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; +import xiaozhi.common.exception.ErrorCode; +import xiaozhi.common.user.UserDetail; +import xiaozhi.common.utils.ConvertUtils; +import xiaozhi.common.utils.MessageUtils; +import xiaozhi.modules.security.entity.SysUserTokenEntity; +import xiaozhi.modules.security.service.ShiroService; +import xiaozhi.modules.sys.entity.SysUserEntity; +import xiaozhi.modules.sys.enums.SuperAdminEnum; -import java.util.List; import java.util.Set; /** @@ -45,6 +45,13 @@ public class Oauth2Realm extends AuthorizingRealm { //用户权限列表 Set permsSet = shiroService.getUserPermissions(user); + if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) { + permsSet.add("sys:role:superAdmin"); + permsSet.add("sys:role:normal"); + } else { + permsSet.add("sys:role:normal"); + } + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(permsSet); return info; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysUserController.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysUserController.java index ea3e6f82..9da7c62b 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysUserController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/controller/SysUserController.java @@ -1,34 +1,9 @@ package xiaozhi.modules.sys.controller; -import xiaozhi.common.annotation.LogOperation; -import xiaozhi.common.constant.Constant; -import xiaozhi.common.exception.ErrorCode; -import xiaozhi.common.exception.RenException; -import xiaozhi.common.page.PageData; -import xiaozhi.common.user.UserDetail; -import xiaozhi.common.utils.ConvertUtils; -import xiaozhi.common.utils.Result; -import xiaozhi.common.validator.AssertUtils; -import xiaozhi.common.validator.ValidatorUtils; -import xiaozhi.common.validator.group.AddGroup; -import xiaozhi.common.validator.group.DefaultGroup; -import xiaozhi.common.validator.group.UpdateGroup; -import xiaozhi.modules.security.password.PasswordUtils; -import xiaozhi.modules.security.user.SecurityUser; -import xiaozhi.modules.sys.dto.PasswordDTO; -import xiaozhi.modules.sys.dto.SysUserDTO; -import xiaozhi.modules.sys.service.SysUserService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; -import org.apache.shiro.authz.annotation.RequiresPermissions; -import org.springframework.web.bind.annotation.*; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; /** * 用户管理 @@ -38,118 +13,5 @@ import java.util.Map; @RequestMapping("/sys/user") @Tag(name = "用户管理") public class SysUserController { - private final SysUserService sysUserService; - @GetMapping("page") - @Operation(summary = "分页") - @Parameters({ - @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true), - @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true), - @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), - @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), - @Parameter(name = "username", description = "用户名"), - @Parameter(name = "gender", description = "性别"), - @Parameter(name = "deptId", description = "部门ID") - }) - @RequiresPermissions("sys:user:page") - public Result> page(@Parameter(hidden = true) @RequestParam Map params) { - PageData page = sysUserService.page(params); - - return new Result>().ok(page); - } - - @GetMapping("{id}") - @Operation(summary = "信息") - @RequiresPermissions("sys:user:info") - public Result get(@PathVariable("id") Long id) { - SysUserDTO data = sysUserService.get(id); - return new Result().ok(data); - } - - @GetMapping("info") - @Operation(summary = "登录用户信息") - public Result info() { - SysUserDTO data = ConvertUtils.sourceToTarget(SecurityUser.getUser(), SysUserDTO.class); - return new Result().ok(data); - } - - @PutMapping("password") - @Operation(summary = "修改密码") - @LogOperation("修改密码") - public Result password(@RequestBody PasswordDTO dto) { - //效验数据 - ValidatorUtils.validateEntity(dto); - String newPassword = dto.getNewPassword(); - - //密码的强度 - if (newPassword == null || newPassword.length() < 8) { - return new Result().error(ErrorCode.PASSWORD_LENGTH_ERROR); - } - if (!sysUserService.isStrongPassword(newPassword)) { - return new Result().error(ErrorCode.PASSWORD_WEAK_ERROR); - } - UserDetail user = SecurityUser.getUser(); - //原密码不正确 - if (!PasswordUtils.matches(dto.getPassword(), user.getPassword())) { - return new Result().error(ErrorCode.PASSWORD_ERROR); - } - - sysUserService.updatePassword(user.getId(), dto.getNewPassword()); - - return new Result(); - } - - @PostMapping - @Operation(summary = "保存") - @LogOperation("保存") - @RequiresPermissions("sys:user:save") - public Result save(@RequestBody SysUserDTO dto) { - //效验数据 - ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); - - sysUserService.save(dto); - - return new Result(); - } - - @PutMapping - @Operation(summary = "修改") - @LogOperation("修改") - @RequiresPermissions("sys:user:update") - public Result update(@RequestBody SysUserDTO dto) { - //效验数据 - ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); - - sysUserService.update(dto); - - return new Result(); - } - - @PutMapping("app") - @Operation(summary = "修改") - @LogOperation("修改") - @RequiresPermissions("sys:user:update") - public Result updateUserInfo(@RequestBody SysUserDTO dto) { - sysUserService.updateUserInfo(dto); - - return new Result(); - } - - @DeleteMapping - @Operation(summary = "删除") - @LogOperation("删除") - @RequiresPermissions("sys:user:delete") - public Result delete(@RequestBody Long[] ids) { - //效验数据 - AssertUtils.isArrayEmpty(ids, "id"); - - List idList = Arrays.asList(ids); - if (idList.contains(SecurityUser.getUserId())) { - throw new RenException(ErrorCode.DEL_MYSELF_ERROR); - } - - sysUserService.deleteBatchIds(idList); - - return new Result(); - } } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysUserDao.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysUserDao.java index 08c48a4d..339e522a 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysUserDao.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/dao/SysUserDao.java @@ -1,12 +1,8 @@ package xiaozhi.modules.sys.dao; +import org.apache.ibatis.annotations.Mapper; import xiaozhi.common.dao.BaseDao; import xiaozhi.modules.sys.entity.SysUserEntity; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -import java.util.List; -import java.util.Map; /** * 系统用户 @@ -14,21 +10,4 @@ import java.util.Map; @Mapper public interface SysUserDao extends BaseDao { - List getList(Map params); - - SysUserEntity getById(Long id); - - SysUserEntity getByUsername(String username); - - int updatePassword(@Param("id") Long id, @Param("newPassword") String newPassword); - - /** - * 根据部门ID,查询用户数 - */ - int getCountByDeptId(Long deptId); - - /** - * 根据部门ID,查询用户ID列表 - */ - List getUserIdListByDeptId(List deptIdList); } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysUserEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysUserEntity.java index a5d14a71..764ee199 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysUserEntity.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/entity/SysUserEntity.java @@ -24,30 +24,6 @@ public class SysUserEntity extends BaseEntity { * 密码 */ private String password; - /** - * 姓名 - */ - private String realName; - /** - * 头像 - */ - private String headUrl; - /** - * 性别 0:男 1:女 2:保密 - */ - private Integer gender; - /** - * 邮箱 - */ - private String email; - /** - * 手机号 - */ - private String mobile; - /** - * 部门ID - */ - private Long deptId; /** * 超级管理员 0:否 1:是 */ @@ -56,6 +32,11 @@ public class SysUserEntity extends BaseEntity { * 状态 0:停用 1:正常 */ private Integer status; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT) + private Long create_date; /** * 更新者 */ diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java index 8427fd09..f92de360 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/SysUserService.java @@ -1,49 +1,19 @@ package xiaozhi.modules.sys.service; -import xiaozhi.common.page.PageData; import xiaozhi.common.service.BaseService; import xiaozhi.modules.sys.dto.SysUserDTO; import xiaozhi.modules.sys.entity.SysUserEntity; -import java.util.List; -import java.util.Map; - /** * 系统用户 */ public interface SysUserService extends BaseService { - PageData page(Map params); - - List list(Map params); - - SysUserDTO get(Long id); - SysUserDTO getByUsername(String username); void save(SysUserDTO dto); - void update(SysUserDTO dto); - - void updateUserInfo(SysUserDTO dto); - void delete(Long[] ids); - /** - * 修改密码 - * - * @param id 用户ID - * @param newPassword 新密码 - */ - void updatePassword(Long id, String newPassword); - - /** - * 验证密码强度 - * - * @param newPassword - * @return - */ - boolean isStrongPassword(String newPassword); - } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java index 1828039b..5c3a10c5 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysUserServiceImpl.java @@ -1,15 +1,11 @@ package xiaozhi.modules.sys.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.metadata.IPage; import lombok.AllArgsConstructor; -import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import xiaozhi.common.exception.ErrorCode; import xiaozhi.common.exception.RenException; -import xiaozhi.common.page.PageData; -import xiaozhi.common.redis.RedisUtils; import xiaozhi.common.service.impl.BaseServiceImpl; import xiaozhi.common.utils.ConvertUtils; import xiaozhi.modules.security.password.PasswordUtils; @@ -21,7 +17,6 @@ import xiaozhi.modules.sys.service.SysUserService; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -32,40 +27,17 @@ import java.util.regex.Pattern; @AllArgsConstructor @Service public class SysUserServiceImpl extends BaseServiceImpl implements SysUserService { - private final RedisUtils redisUtils; - - @Override - public PageData page(Map params) { - //转换成like - paramsToLike(params, "username"); - - //分页 - IPage page = getPage(params, "t1.create_date", false); - - //查询 - List list = baseDao.getList(params); - - return getPageData(list, page.getTotal(), SysUserDTO.class); - } - - @Override - public List list(Map params) { - - List entityList = baseDao.getList(params); - - return ConvertUtils.sourceToTarget(entityList, SysUserDTO.class); - } - - @Override - public SysUserDTO get(Long id) { - SysUserEntity entity = baseDao.getById(id); - - return ConvertUtils.sourceToTarget(entity, SysUserDTO.class); - } + private final SysUserDao sysUserDao; @Override public SysUserDTO getByUsername(String username) { - SysUserEntity entity = baseDao.getByUsername(username); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username", username); + List users = sysUserDao.selectList(queryWrapper); + if (users == null || users.isEmpty()) { + return null; + } + SysUserEntity entity = users.getFirst(); return ConvertUtils.sourceToTarget(entity, SysUserDTO.class); } @@ -90,44 +62,11 @@ public class SysUserServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); return baseDao.selectCount(queryWrapper); } - @Override - public boolean isStrongPassword(String password) { + private boolean isStrongPassword(String password) { // 弱密码的正则表达式 String weakPasswordRegex = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).+$"; Pattern pattern = Pattern.compile(weakPasswordRegex); diff --git a/main/manager-api/src/main/resources/db/changelog/202503131429.sql b/main/manager-api/src/main/resources/db/changelog/202503131429.sql new file mode 100644 index 00000000..e939b7fe --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202503131429.sql @@ -0,0 +1,167 @@ +-- 模型配置表 +DROP TABLE IF EXISTS `ai_model_config`; +CREATE TABLE `ai_model_config` ( + `id` BIGINT NOT NULL COMMENT '主键', + `model_type` VARCHAR(20) COMMENT '模型类型(Memory/ASR/VAD/LLM/TTS)', + `model_code` VARCHAR(50) COMMENT '模型编码(如AliLLM、DoubaoTTS)', + `model_name` VARCHAR(50) COMMENT '模型名称', + `is_default` TINYINT(1) DEFAULT 0 COMMENT '是否默认配置(0否 1是)', + `is_enabled` TINYINT(1) DEFAULT 0 COMMENT '是否启用(原注释有误,应为是否启用而非是否默认配置)', + `config_json` JSON COMMENT '模型配置(JSON格式)', + `doc_link` VARCHAR(200) COMMENT '官方文档链接', + `remark` VARCHAR(255) COMMENT '备注', + `sort` INT UNSIGNED DEFAULT 0 COMMENT '排序', + `creator` BIGINT COMMENT '创建者', + `create_date` DATETIME COMMENT '创建时间', + `updater` BIGINT COMMENT '更新者', + `update_date` DATETIME COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='模型配置表'; + +-- TTS 音色表 +DROP TABLE IF EXISTS `ai_tts_voice`; +CREATE TABLE `ai_tts_voice` ( + `id` BIGINT NOT NULL COMMENT '主键', + `tts_model_id` BIGINT COMMENT '对应 TTS 模型主键', + `name` VARCHAR(20) COMMENT '音色名称', + `tts_voice` VARCHAR(50) COMMENT '音色编码', + `languages` VARCHAR(50) COMMENT '语言', + `voice_demo` VARCHAR(500) DEFAULT NULL COMMENT '音色 Demo', + `remark` VARCHAR(255) COMMENT '备注', + `sort` INT UNSIGNED DEFAULT 0 COMMENT '排序', + `creator` BIGINT COMMENT '创建者', + `create_date` DATETIME COMMENT '创建时间', + `updater` BIGINT COMMENT '更新者', + `update_date` DATETIME COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='TTS 音色表'; + +-- 对话历史表 +DROP TABLE IF EXISTS `ai_chat_history`; +CREATE TABLE `ai_chat_history` ( + `id` BIGINT NOT NULL COMMENT '对话编号', + `user_id` BIGINT COMMENT '用户编号', + `agent_id` BIGINT DEFAULT NULL COMMENT '聊天角色', + `device_id` BIGINT DEFAULT NULL COMMENT '设备编号(原注释有误,应为设备编号)', + `message_count` INT COMMENT '信息汇总', + `sort` INT UNSIGNED DEFAULT 0 COMMENT '排序', + `creator` BIGINT COMMENT '创建者', + `create_date` DATETIME COMMENT '创建时间', + `updater` BIGINT COMMENT '更新者', + `update_date` DATETIME COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对话历史表'; + +-- 对话信息表 +DROP TABLE IF EXISTS `ai_chat_message`; +CREATE TABLE `ai_chat_message` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '对话记录唯一标识', + `user_id` BIGINT COMMENT '用户唯一标识', + `chat_id` VARCHAR(64) COMMENT '对话历史 ID', + `agent_name` VARCHAR(64) COMMENT '智能体名称', + `role` ENUM('user', 'assistant') COMMENT '角色(用户或助理)', + `content` TEXT COMMENT '对话内容', + `embedding` TEXT COMMENT '对话内容的嵌入向量(可选)', + `url` VARCHAR(255) COMMENT '相关音频文件的 URL(可选)', + `prompt_tokens` INT UNSIGNED DEFAULT 0 COMMENT '提示令牌数', + `total_tokens` INT UNSIGNED DEFAULT 0 COMMENT '总令牌数', + `completion_tokens` INT UNSIGNED DEFAULT 0 COMMENT '完成令牌数', + `prompt_ms` INT UNSIGNED DEFAULT 0 COMMENT '提示耗时(毫秒)', + `total_ms` INT UNSIGNED DEFAULT 0 COMMENT '总耗时(毫秒)', + `completion_ms` INT UNSIGNED DEFAULT 0 COMMENT '完成耗时(毫秒)', + `sort` INT UNSIGNED DEFAULT 0 COMMENT '排序', + `creator` BIGINT COMMENT '创建者', + `create_date` DATETIME COMMENT '创建时间', + `updater` BIGINT COMMENT '更新者', + `update_date` DATETIME COMMENT '更新时间', + PRIMARY KEY (`id`), + INDEX `idx_user_id_chat_id_role` (`user_id`, `chat_id`) COMMENT '用户 ID、聊天会话 ID 和角色的联合索引,用于快速检索对话记录', + INDEX `idx_created_at` (`create_date`) COMMENT '创建时间的索引,用于按时间排序或检索对话记录' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='对话信息表'; + +-- 设备信息表 +DROP TABLE IF EXISTS `ai_device`; +CREATE TABLE `ai_device` ( + `id` BIGINT NOT NULL COMMENT '设备唯一标识', + `user_id` BIGINT COMMENT '关联用户 ID', + `mac_address` VARCHAR(50) COMMENT 'MAC 地址', + `last_connected_at` DATETIME COMMENT '最后连接时间', + `auto_update` TINYINT UNSIGNED DEFAULT 0 COMMENT '自动更新开关(0 关闭/1 开启)', + `board` VARCHAR(50) COMMENT '设备硬件型号', + `alias` VARCHAR(64) DEFAULT NULL COMMENT '设备别名', + `agent_code` VARCHAR(36) COMMENT '智能体编码', + `agent_id` BIGINT COMMENT '智能体 ID', + `app_version` VARCHAR(20) COMMENT '固件版本号', + `sort` INT UNSIGNED DEFAULT 0 COMMENT '排序', + `creator` BIGINT COMMENT '创建者', + `create_date` DATETIME COMMENT '创建时间', + `updater` BIGINT COMMENT '更新者', + `update_date` DATETIME COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备信息表'; + +-- 智能体配置表 +DROP TABLE IF EXISTS `ai_agent`; +CREATE TABLE `ai_agent` ( + `id` BIGINT NOT NULL COMMENT '智能体唯一标识', + `user_id` BIGINT COMMENT '所属用户 ID', + `agent_code` VARCHAR(36) COMMENT '智能体唯一凭证', + `agent_name` VARCHAR(64) COMMENT '智能体名称', + `tts_voice` VARCHAR(64) COMMENT '语音合成标识', + `llm_model` VARCHAR(32) COMMENT '大语言模型标识', + `memory` TEXT COMMENT '历史记忆数据', + `character` TEXT COMMENT '角色设定参数', + `long_memory_switch` TINYINT UNSIGNED DEFAULT 0 COMMENT '长期记忆开关', + `lang_code` VARCHAR(10) COMMENT '语言编码', + `language` VARCHAR(10) COMMENT '交互语种', + `sort` INT UNSIGNED DEFAULT 0 COMMENT '排序权重', + `creator` BIGINT COMMENT '创建者 ID', + `created_at` DATETIME COMMENT '创建时间', + `updater` BIGINT COMMENT '更新者 ID', + `updated_at` DATETIME COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体配置表'; + +-- 智能体配置模板表 +DROP TABLE IF EXISTS `ai_agent_template`; +CREATE TABLE `ai_agent_template` ( + `id` BIGINT NOT NULL COMMENT '智能体唯一标识', + `agent_code` VARCHAR(36) COMMENT '智能体编码', + `agent_name` VARCHAR(64) COMMENT '智能体名称', + `asr_model_id` VARCHAR(32) COMMENT '语音识别模型标识', + `vad_model_id` VARCHAR(64) COMMENT '语音活动检测标识', + `llm_model_id` VARCHAR(32) COMMENT '大语言模型标识', + `tts_model_id` VARCHAR(32) COMMENT '语音合成模型标识', + `tts_voice_id` VARCHAR(32) COMMENT '音色标识', + `memory` TEXT COMMENT '历史记忆数据', + `character` TEXT COMMENT '角色设定参数', + `long_memory_switch` TINYINT UNSIGNED DEFAULT 0 COMMENT '长期记忆开关', + `lang_code` VARCHAR(10) COMMENT '语言编码', + `language` VARCHAR(10) COMMENT '交互语种', + `sort` INT UNSIGNED DEFAULT 0 COMMENT '排序权重', + `creator` BIGINT COMMENT '创建者 ID', + `created_at` DATETIME COMMENT '创建时间', + `updater` BIGINT COMMENT '更新者 ID', + `updated_at` DATETIME COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体配置模板表'; + +-- 声纹识别表 +DROP TABLE IF EXISTS `ai_voiceprint`; +CREATE TABLE `ai_voiceprint` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '声纹唯一标识', + `name` VARCHAR(64) COMMENT '声纹名称', + `user_id` BIGINT COMMENT '用户 ID(关联用户表)', + `agent_id` BIGINT COMMENT '关联智能体 ID', + `agent_code` VARCHAR(36) COMMENT '关联智能体编码', + `agent_name` VARCHAR(36) COMMENT '关联智能体名称', + `description` VARCHAR(255) COMMENT '声纹描述', + `embedding` LONGTEXT COMMENT '声纹特征向量(JSON 数组格式)', + `memory` TEXT COMMENT '关联记忆数据', + `sort` INT UNSIGNED DEFAULT 0 COMMENT '排序权重', + `creator` BIGINT COMMENT '创建者 ID', + `created_at` DATETIME COMMENT '创建时间', + `updater` BIGINT COMMENT '更新者 ID', + `updated_at` DATETIME COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='声纹识别表'; \ No newline at end of file diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml index 563858cc..d1d7a557 100755 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -16,3 +16,10 @@ databaseChangeLog: - sqlFile: encoding: utf8 path: classpath:db/changelog/202503101631.sql + - changeSet: + id: 202503131429 + author: czc + changes: + - sqlFile: + encoding: utf8 + path: classpath:db/changelog/202503131429.sql \ No newline at end of file diff --git a/main/manager-api/src/main/resources/i18n/messages.properties b/main/manager-api/src/main/resources/i18n/messages.properties index 53851b2d..91e6a285 100644 --- a/main/manager-api/src/main/resources/i18n/messages.properties +++ b/main/manager-api/src/main/resources/i18n/messages.properties @@ -33,3 +33,4 @@ 10030=\u5BC6\u7801\u957F\u5EA6\u4E0D\u8DB3{0}\u4F4D 10031=\u5F31\u5BC6\u7801 10032=\u5220\u9664\u672C\u6570\u636E\u5F02\u5E38 +10033=\u8BBE\u5907\u9A8C\u8BC1\u7801\u9519\u8BEF diff --git a/main/manager-api/src/main/resources/i18n/messages_en_US.properties b/main/manager-api/src/main/resources/i18n/messages_en_US.properties index a1dd7f77..515de18a 100644 --- a/main/manager-api/src/main/resources/i18n/messages_en_US.properties +++ b/main/manager-api/src/main/resources/i18n/messages_en_US.properties @@ -32,3 +32,4 @@ 10030=The password is less than {0} digits. 10031=The password must consist of numbers, uppercase and lowercase letters, and special characters at the same time 10032=Exception in deleting this data +10033=Device verification code error \ No newline at end of file diff --git a/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties b/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties index 7828c357..23b9abfa 100644 --- a/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties +++ b/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties @@ -31,4 +31,5 @@ 10029=\u4E0D\u80FD\u5305\u542B\u975E\u6CD5\u5B57\u7B26 10030=\u5BC6\u7801\u957F\u5EA6\u4E0D\u8DB3{0}\u4F4D 10031=\u5BC6\u7801\u5FC5\u987B\u540C\u65F6\u5305\u542B\u6570\u5B57\u3001\u5927\u5C0F\u5B57\u6BCD\u548C\u7279\u6B8A\u5B57\u7B26\u7EC4\u6210 -10032=\u5220\u9664\u672C\u6570\u636E\u5F02\u5E38 \ No newline at end of file +10032=\u5220\u9664\u672C\u6570\u636E\u5F02\u5E38 +10033=\u8BBE\u5907\u9A8C\u8BC1\u7801\u9519\u8BEF \ No newline at end of file diff --git a/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties b/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties index 5331e8a0..2a6bd8d0 100644 --- a/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties +++ b/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties @@ -31,4 +31,5 @@ 10029=\u4E0D\u80FD\u5305\u542B\u975E\u6CD5\u5B57\u7B26 10030=\u5BC6\u78BC\u9577\u5EA6\u4E0D\u8DB3{0}\u4F4D 10031=\u5BC6\u78BC\u5FC5\u9808\u540C\u6642\u5305\u542B\u6578\u5B57\u3001\u5927\u5C0F\u5B57\u6BCD\u548C\u7279\u6B8A\u5B57\u7B26\u7D44\u6210 -10032=\u522A\u9664\u6B64\u6578\u64DA\u7570\u5E38 \ No newline at end of file +10032=\u522A\u9664\u6B64\u6578\u64DA\u7570\u5E38 +10033=\u8A2D\u5099\u9A57\u8B49\u78BC\u932F\u8AA4 \ No newline at end of file diff --git a/main/manager-api/src/main/resources/mapper/sys/SysUserDao.xml b/main/manager-api/src/main/resources/mapper/sys/SysUserDao.xml index c34d3549..007e91c4 100644 --- a/main/manager-api/src/main/resources/mapper/sys/SysUserDao.xml +++ b/main/manager-api/src/main/resources/mapper/sys/SysUserDao.xml @@ -3,37 +3,4 @@ - - - - - - - - update sys_user set password = #{newPassword} where id = #{id} - - \ No newline at end of file