diff --git a/README.md b/README.md index d4287fac3..c7b26ee43 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Release -ContiNew Starter +ContiNew Starter Spring Boot @@ -229,7 +229,7 @@ public class DeptController extends BaseControllerArco Design | 2.56.0 | 字节跳动推出的前端 UI 框架,年轻化的色彩和组件设计。 | | TypeScript | 5.0.4 | TypeScript 是微软开发的一个开源的编程语言,通过在 JavaScript 的基础上添加静态类型定义构建而成。 | | Vite | 5.1.5 | 下一代的前端工具链,为开发提供极速响应。 | -| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.8.0 | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken),可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。 | +| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.8.2 | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken),可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。 | | Spring Boot | 3.2.12 | 简化 Spring 应用的初始搭建和开发过程,基于“约定优于配置”的理念,使开发人员不再需要定义样板化的配置。(Spring Boot 3.0 开始,要求 Java 17 作为最低版本) | | Undertow | 2.3.17.Final | 采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。 | | Sa-Token + JWT | 1.39.0 | 轻量级 Java 权限认证框架,让鉴权变得简单、优雅。 | diff --git a/continew-common/src/main/java/top/continew/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java b/continew-common/src/main/java/top/continew/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java index fc67499ae..30878858b 100644 --- a/continew-common/src/main/java/top/continew/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java +++ b/continew-common/src/main/java/top/continew/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java @@ -20,7 +20,7 @@ import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import top.continew.admin.common.context.UserContextHolder; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; import java.time.LocalDateTime; diff --git a/continew-common/src/main/java/top/continew/admin/common/constant/ContainerConstants.java b/continew-common/src/main/java/top/continew/admin/common/constant/ContainerConstants.java index 0b8030f6f..beb0ba5d8 100644 --- a/continew-common/src/main/java/top/continew/admin/common/constant/ContainerConstants.java +++ b/continew-common/src/main/java/top/continew/admin/common/constant/ContainerConstants.java @@ -16,20 +16,18 @@ package top.continew.admin.common.constant; -import top.continew.starter.extension.crud.constant.ContainerPool; - /** * 数据源容器相关常量(Crane4j 数据填充组件使用) * * @author Charles7c * @since 2024/1/20 12:33 */ -public class ContainerConstants extends ContainerPool { +public class ContainerConstants { /** * 用户昵称 */ - public static final String USER_NICKNAME = ContainerPool.USER_NICKNAME; + public static final String USER_NICKNAME = "UserNickname"; /** * 用户角色 ID 列表 diff --git a/continew-common/src/main/java/top/continew/admin/common/constant/SysConstants.java b/continew-common/src/main/java/top/continew/admin/common/constant/SysConstants.java index a60bad449..7802cee94 100644 --- a/continew-common/src/main/java/top/continew/admin/common/constant/SysConstants.java +++ b/continew-common/src/main/java/top/continew/admin/common/constant/SysConstants.java @@ -65,12 +65,12 @@ public class SysConstants { public static final String ALL_PERMISSION = "*:*:*"; /** - * 账号登录 URI + * 登录 URI */ - public static final String LOGIN_URI = "/auth/account"; + public static final String LOGIN_URI = "/auth/login"; /** - * 退出 URI + * 登出 URI */ public static final String LOGOUT_URI = "/auth/logout"; diff --git a/continew-common/src/main/java/top/continew/admin/common/context/UserContext.java b/continew-common/src/main/java/top/continew/admin/common/context/UserContext.java index 4f0a988f8..31699d51f 100644 --- a/continew-common/src/main/java/top/continew/admin/common/context/UserContext.java +++ b/continew-common/src/main/java/top/continew/admin/common/context/UserContext.java @@ -85,6 +85,16 @@ public class UserContext implements Serializable { */ private Long tenantId; + /* + * 客户端类型 + */ + private String clientType; + + /** + * 客户端 ID + */ + private String clientId; + public UserContext(Set permissions, Set roles, Integer passwordExpirationDays) { this.permissions = permissions; this.setRoles(roles); diff --git a/continew-common/src/main/java/top/continew/admin/common/context/UserContextHolder.java b/continew-common/src/main/java/top/continew/admin/common/context/UserContextHolder.java index 41418298d..d4547ac58 100644 --- a/continew-common/src/main/java/top/continew/admin/common/context/UserContextHolder.java +++ b/continew-common/src/main/java/top/continew/admin/common/context/UserContextHolder.java @@ -20,8 +20,8 @@ import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.convert.Convert; import cn.hutool.extra.spring.SpringUtil; +import top.continew.admin.common.service.CommonUserService; import top.continew.starter.core.util.ExceptionUtils; -import top.continew.starter.extension.crud.service.CommonUserService; /** * 用户上下文 Holder diff --git a/continew-common/src/main/java/top/continew/admin/common/base/BaseController.java b/continew-common/src/main/java/top/continew/admin/common/controller/BaseController.java similarity index 98% rename from continew-common/src/main/java/top/continew/admin/common/base/BaseController.java rename to continew-common/src/main/java/top/continew/admin/common/controller/BaseController.java index 09e25c678..08d27f622 100644 --- a/continew-common/src/main/java/top/continew/admin/common/base/BaseController.java +++ b/continew-common/src/main/java/top/continew/admin/common/controller/BaseController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.continew.admin.common.base; +package top.continew.admin.common.controller; import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.context.SaHolder; diff --git a/continew-common/src/main/java/top/continew/admin/common/model/entity/BaseCreateDO.java b/continew-common/src/main/java/top/continew/admin/common/model/entity/BaseCreateDO.java new file mode 100644 index 000000000..ff74f4861 --- /dev/null +++ b/continew-common/src/main/java/top/continew/admin/common/model/entity/BaseCreateDO.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.common.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 实体类基类 + * + *

+ * 通用字段:创建人、创建时间 + *

+ * + * @author Charles7c + * @since 2025/1/12 23:00 + */ +@Data +public class BaseCreateDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 创建人 + */ + @TableField(fill = FieldFill.INSERT) + private Long createUser; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; +} diff --git a/continew-common/src/main/java/top/continew/admin/common/model/entity/BaseDO.java b/continew-common/src/main/java/top/continew/admin/common/model/entity/BaseDO.java new file mode 100644 index 000000000..7c40af166 --- /dev/null +++ b/continew-common/src/main/java/top/continew/admin/common/model/entity/BaseDO.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.common.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 实体类基类 + * + * @author Charles7c + * @since 2025/1/12 23:00 + */ +@Data +public class BaseDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 创建人 + */ + @TableField(fill = FieldFill.INSERT) + private Long createUser; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 修改人 + */ + @TableField(fill = FieldFill.UPDATE) + private Long updateUser; + + /** + * 修改时间 + */ + @TableField(fill = FieldFill.UPDATE) + private LocalDateTime updateTime; +} diff --git a/continew-common/src/main/java/top/continew/admin/common/model/entity/BaseUpdateDO.java b/continew-common/src/main/java/top/continew/admin/common/model/entity/BaseUpdateDO.java new file mode 100644 index 000000000..3e3b4eb6c --- /dev/null +++ b/continew-common/src/main/java/top/continew/admin/common/model/entity/BaseUpdateDO.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.common.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; + +import java.io.Serial; +import java.time.LocalDateTime; + +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +/** + * 实体类基类 + * + *

+ * 通用字段:创建人、创建时间 + *

+ * + * @author Charles7c + * @since 2025/1/12 23:00 + */ +@Data +public class BaseUpdateDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 修改人 + */ + @TableField(fill = FieldFill.UPDATE) + private Long updateUser; + + /** + * 修改时间 + */ + @TableField(fill = FieldFill.UPDATE) + private LocalDateTime updateTime; +} diff --git a/continew-common/src/main/java/top/continew/admin/common/model/resp/BaseDetailResp.java b/continew-common/src/main/java/top/continew/admin/common/model/resp/BaseDetailResp.java new file mode 100644 index 000000000..4e9aa97b6 --- /dev/null +++ b/continew-common/src/main/java/top/continew/admin/common/model/resp/BaseDetailResp.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.common.model.resp; + +import cn.crane4j.annotation.Assemble; +import cn.crane4j.annotation.Mapping; +import cn.crane4j.annotation.condition.ConditionOnPropertyNotNull; +import com.alibaba.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.constant.ContainerConstants; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 详情响应基类 + * + * @author Charles7c + * @since 2024/12/27 20:32 + */ +@Data +public class BaseDetailResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 修改人 + */ + @JsonIgnore + @ConditionOnPropertyNotNull + @Assemble(container = ContainerConstants.USER_NICKNAME, props = @Mapping(ref = "updateUserString")) + private Long updateUser; + + /** + * 修改人 + */ + @Schema(description = "修改人", example = "李四") + @ExcelProperty(value = "修改人", order = Integer.MAX_VALUE - 2) + private String updateUserString; + + /** + * 修改时间 + */ + @Schema(description = "修改时间", example = "2023-08-08 08:08:08", type = "string") + @ExcelProperty(value = "修改时间", order = Integer.MAX_VALUE - 1) + private LocalDateTime updateTime; +} diff --git a/continew-common/src/main/java/top/continew/admin/common/model/resp/BaseResp.java b/continew-common/src/main/java/top/continew/admin/common/model/resp/BaseResp.java new file mode 100644 index 000000000..801bcbef4 --- /dev/null +++ b/continew-common/src/main/java/top/continew/admin/common/model/resp/BaseResp.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.common.model.resp; + +import cn.crane4j.annotation.Assemble; +import cn.crane4j.annotation.Mapping; +import com.alibaba.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.constant.ContainerConstants; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 响应参数基类 + * + * @author Charles7c + * @since 2024/12/27 20:32 + */ +@Data +public class BaseResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + @ExcelProperty(value = "ID", order = 1) + private Long id; + + /** + * 创建人 + */ + @JsonIgnore + @Assemble(container = ContainerConstants.USER_NICKNAME, props = @Mapping(ref = "createUserString")) + private Long createUser; + + /** + * 创建人 + */ + @Schema(description = "创建人", example = "超级管理员") + @ExcelProperty(value = "创建人", order = Integer.MAX_VALUE - 4) + private String createUserString; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string") + @ExcelProperty(value = "创建时间", order = Integer.MAX_VALUE - 3) + private LocalDateTime createTime; + + /** + * 是否禁用修改 + */ + @Schema(description = "是否禁用修改", example = "true") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean disabled; +} diff --git a/continew-common/src/main/java/top/continew/admin/common/service/CommonUserService.java b/continew-common/src/main/java/top/continew/admin/common/service/CommonUserService.java new file mode 100644 index 000000000..66649a4e4 --- /dev/null +++ b/continew-common/src/main/java/top/continew/admin/common/service/CommonUserService.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.common.service; + +import cn.crane4j.annotation.ContainerMethod; +import cn.crane4j.annotation.MappingType; +import top.continew.admin.common.constant.ContainerConstants; + +/** + * 公共用户业务接口 + * + * @author Charles7c + * @since 2025/1/9 20:17 + */ +public interface CommonUserService { + + /** + * 根据 ID 查询昵称 + * + *

+ * 数据填充容器 {@link ContainerConstants#USER_NICKNAME} + *

+ * + * @param id ID + * @return 昵称 + */ + @ContainerMethod(namespace = ContainerConstants.USER_NICKNAME, type = MappingType.ORDER_OF_KEYS) + String getNicknameById(Long id); +} diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java b/continew-module-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java new file mode 100644 index 000000000..c0d2cbb1c --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth; + +import cn.dev33.satoken.stp.SaLoginModel; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.extra.spring.SpringUtil; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +import top.continew.admin.auth.model.req.LoginReq; +import top.continew.admin.common.config.properties.TenantProperties; +import top.continew.admin.common.context.RoleContext; +import top.continew.admin.common.context.UserContext; +import top.continew.admin.common.context.UserContextHolder; +import top.continew.admin.common.context.UserExtraContext; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.model.entity.DeptDO; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.model.resp.ClientResp; +import top.continew.admin.system.service.DeptService; +import top.continew.admin.system.service.OptionService; +import top.continew.admin.system.service.RoleService; +import top.continew.admin.system.service.UserService; +import top.continew.starter.core.validation.CheckUtils; +import top.continew.starter.core.validation.Validator; +import top.continew.starter.extension.tenant.TenantHandler; +import top.continew.starter.extension.tenant.context.TenantContextHolder; +import top.continew.starter.web.util.SpringWebUtils; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import static top.continew.admin.system.enums.PasswordPolicyEnum.PASSWORD_EXPIRATION_DAYS; + +/** + * 登录处理器基类 + * + * @author KAI + * @author Charles7c + * @since 2024/12/22 14:52 + */ +@Component +public abstract class AbstractLoginHandler implements LoginHandler { + + @Resource + protected OptionService optionService; + @Resource + protected UserService userService; + @Resource + protected RoleService roleService; + @Resource + private DeptService deptService; + @Resource + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Resource + protected TenantProperties tenantProperties; + + protected static final String CAPTCHA_EXPIRED = "验证码已失效"; + protected static final String CAPTCHA_ERROR = "验证码错误"; + protected static final String CLIENT_ID = "clientId"; + + @Override + public void preLogin(T req, ClientResp client, HttpServletRequest request) { + // 参数校验 + Validator.validate(req); + } + + @Override + public void postLogin(T req, ClientResp client, HttpServletRequest request) { + } + + /** + * 认证 + * + * @param user 用户信息 + * @param client 客户端信息 + * @return token 令牌信息 + */ + protected String authenticate(UserDO user, ClientResp client) { + // 获取权限、角色、密码过期天数 + Long userId = user.getId(); + Long tenantId = TenantContextHolder.getTenantId(); + CompletableFuture> permissionFuture = CompletableFuture.supplyAsync(() -> { + Set permissionSet = new HashSet<>(); + if (tenantProperties.isEnabled()) { + SpringUtil.getBean(TenantHandler.class) + .execute(tenantId, () -> permissionSet.addAll(roleService.listPermissionByUserId(userId))); + } else { + permissionSet.addAll(roleService.listPermissionByUserId(userId)); + } + return permissionSet; + }, threadPoolTaskExecutor); + CompletableFuture> roleFuture = CompletableFuture.supplyAsync(() -> { + Set roleSet = new HashSet<>(); + if (tenantProperties.isEnabled()) { + SpringUtil.getBean(TenantHandler.class) + .execute(tenantId, () -> roleSet.addAll(roleService.listByUserId(userId))); + } else { + roleSet.addAll(roleService.listByUserId(userId)); + } + return roleSet; + }, threadPoolTaskExecutor); + CompletableFuture passwordExpirationDaysFuture = CompletableFuture.supplyAsync(() -> optionService + .getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name())); + CompletableFuture.allOf(permissionFuture, roleFuture, passwordExpirationDaysFuture); + UserContext userContext = new UserContext(permissionFuture.join(), roleFuture + .join(), passwordExpirationDaysFuture.join()); + BeanUtil.copyProperties(user, userContext); + // 设置登录配置参数 + SaLoginModel model = new SaLoginModel(); + model.setActiveTimeout(client.getActiveTimeout()); + model.setTimeout(client.getTimeout()); + model.setDevice(client.getClientType()); + userContext.setClientType(client.getClientType()); + model.setExtra(CLIENT_ID, client.getClientId()); + userContext.setClientId(client.getClientId()); + userContext.setTenantId(tenantId); + // 登录并缓存用户信息 + StpUtil.login(userContext.getId(), model.setExtraData(BeanUtil.beanToMap(new UserExtraContext(SpringWebUtils + .getRequest())))); + UserContextHolder.setContext(userContext); + return StpUtil.getTokenValue(); + } + + /** + * 检查用户状态 + * + * @param user 用户信息 + */ + protected void checkUserStatus(UserDO user) { + CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, user.getStatus(), "此账号已被禁用,如有疑问,请联系管理员"); + DeptDO dept = deptService.getById(user.getDeptId()); + CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, dept.getStatus(), "此账号所属部门已被禁用,如有疑问,请联系管理员"); + } +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/LoginHandler.java b/continew-module-system/src/main/java/top/continew/admin/auth/LoginHandler.java new file mode 100644 index 000000000..f5a74936b --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/LoginHandler.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth; + +import jakarta.servlet.http.HttpServletRequest; +import top.continew.admin.auth.enums.AuthTypeEnum; +import top.continew.admin.auth.model.req.LoginReq; +import top.continew.admin.auth.model.resp.LoginResp; +import top.continew.admin.system.model.resp.ClientResp; + +/** + * 登录处理器 + * + * @author KAI + * @author Charles7c + * @since 2024/12/22 14:52 + */ +public interface LoginHandler { + + /** + * 登录 + * + * @param req 登录请求参数 + * @param client 客户端信息 + * @param request 请求对象 + * @return 登录响应参数 + */ + LoginResp login(T req, ClientResp client, HttpServletRequest request); + + /** + * 登录前置处理 + * + * @param req 登录请求参数 + * @param client 客户端信息 + * @param request 请求对象 + */ + void preLogin(T req, ClientResp client, HttpServletRequest request); + + /** + * 登录后置处理 + * + * @param req 登录请求参数 + * @param client 客户端信息 + * @param request 请求对象 + */ + void postLogin(T req, ClientResp client, HttpServletRequest request); + + /** + * 获取认证类型 + * + * @return 认证类型 + */ + AuthTypeEnum getAuthType(); +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/LoginHandlerFactory.java b/continew-module-system/src/main/java/top/continew/admin/auth/LoginHandlerFactory.java new file mode 100644 index 000000000..0d2699643 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/LoginHandlerFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import top.continew.admin.auth.enums.AuthTypeEnum; +import top.continew.admin.auth.model.req.LoginReq; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +/** + * 登录处理器工厂 + * + * @author KAI + * @author Charles7c + * @since 2024/12/20 15:16 + */ +@Component +public class LoginHandlerFactory { + + private final Map> handlerMap = new EnumMap<>(AuthTypeEnum.class); + + @Autowired + public LoginHandlerFactory(List> handlers) { + for (LoginHandler handler : handlers) { + handlerMap.put(handler.getAuthType(), handler); + } + } + + /** + * 根据认证类型获取 + * + * @param authType 认证类型 + * @return 认证处理器 + */ + public LoginHandler getHandler(AuthTypeEnum authType) { + return (LoginHandler)handlerMap.get(authType); + } +} diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/enums/AuthTypeEnum.java b/continew-module-system/src/main/java/top/continew/admin/auth/enums/AuthTypeEnum.java new file mode 100644 index 000000000..63846293b --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/enums/AuthTypeEnum.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.UiConstants; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 认证类型枚举 + * + * @author KAI + * @author Charles7c + * @since 2024/12/22 14:52 + */ +@Getter +@RequiredArgsConstructor +public enum AuthTypeEnum implements BaseEnum { + + /** + * 账号 + */ + ACCOUNT("ACCOUNT", "账号", UiConstants.COLOR_SUCCESS), + + /** + * 邮箱 + */ + EMAIL("EMAIL", "邮箱", UiConstants.COLOR_PRIMARY), + + /** + * 手机号 + */ + PHONE("PHONE", "手机号", UiConstants.COLOR_PRIMARY), + + /** + * 第三方账号 + */ + SOCIAL("SOCIAL", "第三方账号", UiConstants.COLOR_ERROR); + + private final String value; + private final String description; + private final String color; +} diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/handler/AccountLoginHandler.java b/continew-module-system/src/main/java/top/continew/admin/auth/handler/AccountLoginHandler.java new file mode 100644 index 000000000..0a677d7c2 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/handler/AccountLoginHandler.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth.handler; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.servlet.JakartaServletUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; +import top.continew.admin.auth.AbstractLoginHandler; +import top.continew.admin.auth.enums.AuthTypeEnum; +import top.continew.admin.auth.model.req.AccountLoginReq; +import top.continew.admin.auth.model.resp.LoginResp; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.common.constant.SysConstants; +import top.continew.admin.common.util.SecureUtils; +import top.continew.admin.system.enums.PasswordPolicyEnum; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.model.resp.ClientResp; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.util.ExceptionUtils; +import top.continew.starter.core.validation.CheckUtils; +import top.continew.starter.core.validation.ValidationUtils; + +import java.time.Duration; + +/** + * 账号登录处理器 + * + * @author KAI + * @author Charles7c + * @since 2024/12/22 14:58 + */ +@Component +@RequiredArgsConstructor +public class AccountLoginHandler extends AbstractLoginHandler { + + private final PasswordEncoder passwordEncoder; + + @Override + public LoginResp login(AccountLoginReq req, ClientResp client, HttpServletRequest request) { + // 解密密码 + String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getPassword())); + ValidationUtils.throwIfBlank(rawPassword, "密码解密失败"); + // 验证用户名密码 + String username = req.getUsername(); + UserDO user = userService.getByUsername(username); + boolean isError = ObjectUtil.isNull(user) || !passwordEncoder.matches(rawPassword, user.getPassword()); + // 检查账号锁定状态 + this.checkUserLocked(req.getUsername(), request, isError); + ValidationUtils.throwIf(isError, "用户名或密码错误"); + // 检查用户状态 + super.checkUserStatus(user); + // 执行认证 + String token = this.authenticate(user, client); + return LoginResp.builder().token(token).build(); + } + + @Override + public void preLogin(AccountLoginReq req, ClientResp client, HttpServletRequest request) { + super.preLogin(req, client, request); + // 校验验证码 + int loginCaptchaEnabled = optionService.getValueByCode2Int("LOGIN_CAPTCHA_ENABLED"); + if (SysConstants.YES.equals(loginCaptchaEnabled)) { + ValidationUtils.throwIfBlank(req.getCaptcha(), "验证码不能为空"); + ValidationUtils.throwIfBlank(req.getUuid(), "验证码标识不能为空"); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + req.getUuid(); + String captcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); + RedisUtils.delete(captchaKey); + ValidationUtils.throwIfNotEqualIgnoreCase(req.getCaptcha(), captcha, CAPTCHA_ERROR); + } + } + + @Override + public AuthTypeEnum getAuthType() { + return AuthTypeEnum.ACCOUNT; + } + + /** + * 检测用户是否已被锁定 + * + * @param username 用户名 + * @param request 请求对象 + * @param isError 是否登录错误 + */ + private void checkUserLocked(String username, HttpServletRequest request, boolean isError) { + // 不锁定 + int maxErrorCount = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_COUNT.name()); + if (maxErrorCount <= SysConstants.NO) { + return; + } + // 检测是否已被锁定 + String key = CacheConstants.USER_PASSWORD_ERROR_KEY_PREFIX + RedisUtils.formatKey(username, JakartaServletUtil + .getClientIP(request)); + int lockMinutes = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_MINUTES.name()); + Integer currentErrorCount = ObjectUtil.defaultIfNull(RedisUtils.get(key), 0); + CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "账号锁定 {} 分钟,请稍后再试", lockMinutes); + // 登录成功清除计数 + if (!isError) { + RedisUtils.delete(key); + return; + } + // 登录失败递增计数 + currentErrorCount++; + RedisUtils.set(key, currentErrorCount, Duration.ofMinutes(lockMinutes)); + CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "密码错误已达 {} 次,账号锁定 {} 分钟", maxErrorCount, lockMinutes); + } +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/handler/EmailLoginHandler.java b/continew-module-system/src/main/java/top/continew/admin/auth/handler/EmailLoginHandler.java new file mode 100644 index 000000000..3537982fa --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/handler/EmailLoginHandler.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth.handler; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import top.continew.admin.auth.AbstractLoginHandler; +import top.continew.admin.auth.enums.AuthTypeEnum; +import top.continew.admin.auth.model.req.EmailLoginReq; +import top.continew.admin.auth.model.resp.LoginResp; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.model.resp.ClientResp; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.validation.ValidationUtils; + +/** + * 邮箱登录处理器 + * + * @author KAI + * @author Charles7c + * @since 2024/12/22 14:58 + */ +@Component +public class EmailLoginHandler extends AbstractLoginHandler { + + @Override + public LoginResp login(EmailLoginReq req, ClientResp client, HttpServletRequest request) { + // 验证邮箱 + UserDO user = userService.getByEmail(req.getEmail()); + ValidationUtils.throwIfNull(user, "此邮箱未绑定本系统账号"); + // 检查用户状态 + super.checkUserStatus(user); + // 执行认证 + String token = super.authenticate(user, client); + return LoginResp.builder().token(token).build(); + } + + @Override + public void preLogin(EmailLoginReq req, ClientResp client, HttpServletRequest request) { + String email = req.getEmail(); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email; + String captcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); + ValidationUtils.throwIfNotEqualIgnoreCase(req.getCaptcha(), captcha, CAPTCHA_ERROR); + RedisUtils.delete(captchaKey); + } + + @Override + public AuthTypeEnum getAuthType() { + return AuthTypeEnum.EMAIL; + } +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/handler/PhoneLoginHandler.java b/continew-module-system/src/main/java/top/continew/admin/auth/handler/PhoneLoginHandler.java new file mode 100644 index 000000000..772a40d57 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/handler/PhoneLoginHandler.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth.handler; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import top.continew.admin.auth.AbstractLoginHandler; +import top.continew.admin.auth.enums.AuthTypeEnum; +import top.continew.admin.auth.model.req.PhoneLoginReq; +import top.continew.admin.auth.model.resp.LoginResp; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.model.resp.ClientResp; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.validation.ValidationUtils; + +/** + * 手机号登录处理器 + * + * @author KAI + * @author Charles7c + * @since 2024/12/22 14:59 + */ +@Component +public class PhoneLoginHandler extends AbstractLoginHandler { + + @Override + public LoginResp login(PhoneLoginReq req, ClientResp client, HttpServletRequest request) { + // 验证手机号 + UserDO user = userService.getByPhone(req.getPhone()); + ValidationUtils.throwIfNull(user, "此手机号未绑定本系统账号"); + // 检查用户状态 + super.checkUserStatus(user); + // 执行认证 + String token = super.authenticate(user, client); + return LoginResp.builder().token(token).build(); + } + + @Override + public void preLogin(PhoneLoginReq req, ClientResp client, HttpServletRequest request) { + String phone = req.getPhone(); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone; + String captcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); + ValidationUtils.throwIfNotEqualIgnoreCase(req.getCaptcha(), captcha, CAPTCHA_ERROR); + RedisUtils.delete(captchaKey); + } + + @Override + public AuthTypeEnum getAuthType() { + return AuthTypeEnum.PHONE; + } +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/handler/SocialLoginHandler.java b/continew-module-system/src/main/java/top/continew/admin/auth/handler/SocialLoginHandler.java new file mode 100644 index 000000000..4fd70052b --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/handler/SocialLoginHandler.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth.handler; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.json.JSONUtil; +import com.xkcoding.justauth.AuthRequestFactory; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthRequest; +import org.springframework.stereotype.Component; +import top.continew.admin.auth.AbstractLoginHandler; +import top.continew.admin.auth.enums.AuthTypeEnum; +import top.continew.admin.auth.model.req.SocialLoginReq; +import top.continew.admin.auth.model.resp.LoginResp; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.constant.SysConstants; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.admin.system.enums.MessageTemplateEnum; +import top.continew.admin.system.enums.MessageTypeEnum; +import top.continew.admin.system.model.entity.RoleDO; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.model.entity.UserSocialDO; +import top.continew.admin.system.model.req.MessageReq; +import top.continew.admin.system.model.resp.ClientResp; +import top.continew.admin.system.service.MessageService; +import top.continew.admin.system.service.UserRoleService; +import top.continew.admin.system.service.UserSocialService; +import top.continew.starter.core.autoconfigure.project.ProjectProperties; +import top.continew.starter.core.exception.BadRequestException; +import top.continew.starter.core.validation.ValidationUtils; +import top.continew.starter.messaging.websocket.util.WebSocketUtils; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; + +/** + * 第三方账号登录处理器 + * + * @author KAI + * @author Charles7c + * @since 2024/12/25 14:21 + */ +@Component +@RequiredArgsConstructor +public class SocialLoginHandler extends AbstractLoginHandler { + + private final AuthRequestFactory authRequestFactory; + private final UserSocialService userSocialService; + private final UserRoleService userRoleService; + private final MessageService messageService; + private final ProjectProperties projectProperties; + + @Override + public LoginResp login(SocialLoginReq req, ClientResp client, HttpServletRequest request) { + // 获取第三方登录信息 + AuthRequest authRequest = this.getAuthRequest(req.getSource()); + AuthCallback callback = new AuthCallback(); + callback.setCode(req.getCode()); + callback.setState(req.getState()); + AuthResponse response = authRequest.login(callback); + ValidationUtils.throwIf(!response.ok(), response.getMsg()); + AuthUser authUser = response.getData(); + // 如未绑定则自动注册新用户,保存或更新关联信息 + String source = authUser.getSource(); + String openId = authUser.getUuid(); + UserSocialDO userSocial = userSocialService.getBySourceAndOpenId(source, openId); + UserDO user; + if (null == userSocial) { + String username = authUser.getUsername(); + String nickname = authUser.getNickname(); + UserDO existsUser = userService.getByUsername(username); + String randomStr = RandomUtil.randomString(RandomUtil.BASE_CHAR, 5); + if (null != existsUser || !ReUtil.isMatch(RegexConstants.USERNAME, username)) { + username = randomStr + IdUtil.fastSimpleUUID(); + } + if (!ReUtil.isMatch(RegexConstants.GENERAL_NAME, nickname)) { + nickname = source.toLowerCase() + randomStr; + } + user = new UserDO(); + user.setUsername(username); + user.setNickname(nickname); + user.setGender(GenderEnum.valueOf(authUser.getGender().name())); + user.setAvatar(authUser.getAvatar()); + user.setDeptId(SysConstants.SUPER_DEPT_ID); + user.setStatus(DisEnableStatusEnum.ENABLE); + userService.save(user); + Long userId = user.getId(); + RoleDO role = roleService.getByCode(SysConstants.SUPER_ROLE_CODE); + userRoleService.assignRolesToUser(Collections.singletonList(role.getId()), userId); + userSocial = new UserSocialDO(); + userSocial.setUserId(userId); + userSocial.setSource(source); + userSocial.setOpenId(openId); + this.sendSecurityMsg(user); + } else { + user = BeanUtil.copyProperties(userService.getById(userSocial.getUserId()), UserDO.class); + } + // 检查用户状态 + super.checkUserStatus(user); + userSocial.setMetaJson(JSONUtil.toJsonStr(authUser)); + userSocial.setLastLoginTime(LocalDateTime.now()); + userSocialService.saveOrUpdate(userSocial); + // 执行认证 + String token = super.authenticate(user, client); + return LoginResp.builder().token(token).build(); + } + + @Override + public void preLogin(SocialLoginReq req, ClientResp client, HttpServletRequest request) { + super.preLogin(req, client, request); + if (StpUtil.isLogin()) { + StpUtil.logout(); + } + } + + @Override + public AuthTypeEnum getAuthType() { + return AuthTypeEnum.SOCIAL; + } + + /** + * 获取 AuthRequest + * + * @param source 平台名称 + * @return AuthRequest + */ + private AuthRequest getAuthRequest(String source) { + try { + return authRequestFactory.get(source); + } catch (Exception e) { + throw new BadRequestException("暂不支持 [%s] 平台账号登录".formatted(source)); + } + } + + /** + * 发送安全消息 + * + * @param user 用户信息 + */ + private void sendSecurityMsg(UserDO user) { + MessageReq req = new MessageReq(); + MessageTemplateEnum socialRegister = MessageTemplateEnum.SOCIAL_REGISTER; + req.setTitle(socialRegister.getTitle().formatted(projectProperties.getName())); + req.setContent(socialRegister.getContent().formatted(user.getNickname())); + req.setType(MessageTypeEnum.SECURITY); + messageService.add(req, CollUtil.toList(user.getId())); + List tokenList = StpUtil.getTokenValueListByLoginId(user.getId()); + for (String token : tokenList) { + WebSocketUtils.sendMessage(token, "1"); + } + } +} diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/model/query/OnlineUserQuery.java b/continew-module-system/src/main/java/top/continew/admin/auth/model/query/OnlineUserQuery.java index 26a7ee7f3..e5e7b42bc 100644 --- a/continew-module-system/src/main/java/top/continew/admin/auth/model/query/OnlineUserQuery.java +++ b/continew-module-system/src/main/java/top/continew/admin/auth/model/query/OnlineUserQuery.java @@ -45,6 +45,12 @@ public class OnlineUserQuery implements Serializable { @Schema(description = "用户昵称", example = "张三") private String nickname; + /** + * 客户端 ID + */ + @Schema(description = "客户端 ID", example = "ef51c9a3e9046c4f2ea45142c8a8344a") + private String clientId; + /** * 登录时间 */ diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/model/req/AccountLoginReq.java b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/AccountLoginReq.java index 0ed4148c6..c1baac052 100644 --- a/continew-module-system/src/main/java/top/continew/admin/auth/model/req/AccountLoginReq.java +++ b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/AccountLoginReq.java @@ -21,7 +21,6 @@ import lombok.Data; import java.io.Serial; -import java.io.Serializable; /** * 账号登录参数 @@ -31,7 +30,7 @@ */ @Data @Schema(description = "账号登录参数") -public class AccountLoginReq implements Serializable { +public class AccountLoginReq extends LoginReq { @Serial private static final long serialVersionUID = 1L; diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/model/req/EmailLoginReq.java b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/EmailLoginReq.java index 5959a898b..62c8a3ec2 100644 --- a/continew-module-system/src/main/java/top/continew/admin/auth/model/req/EmailLoginReq.java +++ b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/EmailLoginReq.java @@ -24,7 +24,6 @@ import org.hibernate.validator.constraints.Length; import java.io.Serial; -import java.io.Serializable; /** * 邮箱登录参数 @@ -34,7 +33,7 @@ */ @Data @Schema(description = "邮箱登录参数") -public class EmailLoginReq implements Serializable { +public class EmailLoginReq extends LoginReq { @Serial private static final long serialVersionUID = 1L; diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/model/req/LoginReq.java b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/LoginReq.java new file mode 100644 index 000000000..9cdd8c0d3 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/LoginReq.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth.model.req; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import top.continew.admin.auth.enums.AuthTypeEnum; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 登录参数基类 + * + * @author KAI + * @author Charles7c + * @since 2024/12/22 15:16 + */ +@Data +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "authType", visible = true) +@JsonSubTypes({@JsonSubTypes.Type(value = AccountLoginReq.class, name = "ACCOUNT"), + @JsonSubTypes.Type(value = EmailLoginReq.class, name = "EMAIL"), + @JsonSubTypes.Type(value = PhoneLoginReq.class, name = "PHONE"), + @JsonSubTypes.Type(value = SocialLoginReq.class, name = "SOCIAL")}) +@Schema(description = "基础登录参数") +public class LoginReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 客户端 ID + */ + @Schema(description = "客户端 ID", example = "ef51c9a3e9046c4f2ea45142c8a8344a") + @NotBlank(message = "客户端ID不能为空") + private String clientId; + + /** + * 认证类型 + */ + @Schema(description = "认证类型", example = "ACCOUNT") + @NotNull(message = "认证类型非法") + private AuthTypeEnum authType; +} diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/model/req/PhoneLoginReq.java b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/PhoneLoginReq.java index f81cc74f5..260cec225 100644 --- a/continew-module-system/src/main/java/top/continew/admin/auth/model/req/PhoneLoginReq.java +++ b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/PhoneLoginReq.java @@ -24,7 +24,6 @@ import org.hibernate.validator.constraints.Length; import java.io.Serial; -import java.io.Serializable; /** * 手机号登录参数 @@ -34,7 +33,7 @@ */ @Data @Schema(description = "手机号登录参数") -public class PhoneLoginReq implements Serializable { +public class PhoneLoginReq extends LoginReq { @Serial private static final long serialVersionUID = 1L; diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/model/req/SocialLoginReq.java b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/SocialLoginReq.java new file mode 100644 index 000000000..a7f746eae --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/model/req/SocialLoginReq.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; + +/** + * 第三方账号登录参数 + * + * @author KAI + * @author Charles7c + * @since 2024/12/25 15:43 + */ +@Data +@Schema(description = "第三方账号登录参数") +public class SocialLoginReq extends LoginReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 第三方登录平台 + */ + @Schema(description = "第三方登录平台", example = "gitee") + @NotBlank(message = "第三方登录平台不能为空") + private String source; + + /** + * 授权码 + */ + @Schema(description = "授权码", example = "a08d33e9e577fb339de027499784ed4e871d6f62ae65b459153e906ab546bd56") + @NotBlank(message = "授权码不能为空") + private String code; + + /** + * 状态码 + */ + @Schema(description = "状态码", example = "2ca8d8baf437eb374efaa1191a3d") + @NotBlank(message = "状态码不能为空") + private String state; +} diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/model/resp/LoginResp.java b/continew-module-system/src/main/java/top/continew/admin/auth/model/resp/LoginResp.java index e890258eb..c0c166aa9 100644 --- a/continew-module-system/src/main/java/top/continew/admin/auth/model/resp/LoginResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/auth/model/resp/LoginResp.java @@ -24,14 +24,14 @@ import java.io.Serializable; /** - * 令牌信息 + * 登录响应参数 * * @author Charles7c * @since 2022/12/21 20:42 */ @Data @Builder -@Schema(description = "令牌信息") +@Schema(description = "登录响应参数") public class LoginResp implements Serializable { @Serial diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/model/resp/OnlineUserResp.java b/continew-module-system/src/main/java/top/continew/admin/auth/model/resp/OnlineUserResp.java index ee74a30f1..a5e0d7d36 100644 --- a/continew-module-system/src/main/java/top/continew/admin/auth/model/resp/OnlineUserResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/auth/model/resp/OnlineUserResp.java @@ -16,7 +16,10 @@ package top.continew.admin.auth.model.resp; -import cn.crane4j.annotation.*; +import cn.crane4j.annotation.Assemble; +import cn.crane4j.annotation.AssembleMethod; +import cn.crane4j.annotation.ContainerMethod; +import cn.crane4j.annotation.MappingType; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import top.continew.admin.auth.service.OnlineUserService; @@ -65,6 +68,18 @@ public class OnlineUserResp implements Serializable { @Schema(description = "昵称", example = "张三") private String nickname; + /** + * 客户端类型 + */ + @Schema(description = "客户端类型", example = "PC") + private String clientType; + + /** + * 客户端 ID + */ + @Schema(description = "客户端 ID", example = "ef51c9a3e9046c4f2ea45142c8a8344a") + private String clientId; + /** * 登录 IP */ diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/service/LoginService.java b/continew-module-system/src/main/java/top/continew/admin/auth/service/AuthService.java similarity index 58% rename from continew-module-system/src/main/java/top/continew/admin/auth/service/LoginService.java rename to continew-module-system/src/main/java/top/continew/admin/auth/service/AuthService.java index 34a813594..3289ebdf7 100644 --- a/continew-module-system/src/main/java/top/continew/admin/auth/service/LoginService.java +++ b/continew-module-system/src/main/java/top/continew/admin/auth/service/AuthService.java @@ -17,52 +17,28 @@ package top.continew.admin.auth.service; import jakarta.servlet.http.HttpServletRequest; -import me.zhyd.oauth.model.AuthUser; +import top.continew.admin.auth.model.req.LoginReq; +import top.continew.admin.auth.model.resp.LoginResp; import top.continew.admin.auth.model.resp.RouteResp; import java.util.List; /** - * 登录业务接口 + * 认证业务接口 * * @author Charles7c * @since 2022/12/21 21:48 */ -public interface LoginService { +public interface AuthService { /** - * 账号登录 + * 登录 * - * @param username 用户名 - * @param password 密码 - * @param request 请求对象 - * @return 令牌 + * @param req 登录请求参数 + * @param request 请求对象 + * @return 登录响应参数 */ - String accountLogin(String username, String password, HttpServletRequest request); - - /** - * 手机号登录 - * - * @param phone 手机号 - * @return 令牌 - */ - String phoneLogin(String phone); - - /** - * 邮箱登录 - * - * @param email 邮箱 - * @return 令牌 - */ - String emailLogin(String email); - - /** - * 三方账号登录 - * - * @param authUser 三方账号信息 - * @return 令牌 - */ - String socialLogin(AuthUser authUser); + LoginResp login(LoginReq req, HttpServletRequest request); /** * 构建路由树 diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/AuthServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/AuthServiceImpl.java new file mode 100644 index 000000000..3e1699943 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/AuthServiceImpl.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.auth.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.lang.tree.TreeUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.auth.LoginHandler; +import top.continew.admin.auth.LoginHandlerFactory; +import top.continew.admin.auth.enums.AuthTypeEnum; +import top.continew.admin.auth.model.req.LoginReq; +import top.continew.admin.auth.model.resp.LoginResp; +import top.continew.admin.auth.model.resp.RouteResp; +import top.continew.admin.auth.service.AuthService; +import top.continew.admin.common.constant.SysConstants; +import top.continew.admin.common.context.UserContextHolder; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.enums.MenuTypeEnum; +import top.continew.admin.system.model.resp.ClientResp; +import top.continew.admin.system.model.resp.MenuResp; +import top.continew.admin.system.service.ClientService; +import top.continew.admin.system.service.MenuService; +import top.continew.admin.system.service.RoleService; +import top.continew.starter.core.validation.ValidationUtils; +import top.continew.starter.extension.crud.annotation.TreeField; +import top.continew.starter.extension.crud.autoconfigure.CrudProperties; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 认证业务实现 + * + * @author Charles7c + * @since 2022/12/21 21:49 + */ +@Service +@RequiredArgsConstructor +public class AuthServiceImpl implements AuthService { + + private final LoginHandlerFactory loginHandlerFactory; + private final ClientService clientService; + private final RoleService roleService; + private final MenuService menuService; + private final CrudProperties crudProperties; + + @Override + public LoginResp login(LoginReq req, HttpServletRequest request) { + AuthTypeEnum authType = req.getAuthType(); + // 校验客户端 + ClientResp client = clientService.getByClientId(req.getClientId()); + ValidationUtils.throwIfNull(client, "客户端不存在"); + ValidationUtils.throwIf(DisEnableStatusEnum.DISABLE.equals(client.getStatus()), "客户端已禁用"); + ValidationUtils.throwIf(!client.getAuthType().contains(authType.getValue()), "该客户端暂未授权 [{}] 认证", authType + .getDescription()); + // 获取处理器 + LoginHandler loginHandler = loginHandlerFactory.getHandler(authType); + // 登录前置处理 + loginHandler.preLogin(req, client, request); + // 登录 + LoginResp loginResp = loginHandler.login(req, client, request); + // 登录后置处理 + loginHandler.postLogin(req, client, request); + return loginResp; + } + + @Override + public List buildRouteTree(Long userId) { + Set roleCodeSet = roleService.listCodeByUserId(userId); + if (CollUtil.isEmpty(roleCodeSet)) { + return new ArrayList<>(0); + } + // 查询菜单列表 + Set menuSet = new LinkedHashSet<>(); + if (roleCodeSet.contains(SysConstants.SUPER_ROLE_CODE)) { + menuSet.addAll(menuService.listAll(UserContextHolder.getTenantId())); + } else { + roleCodeSet.forEach(roleCode -> menuSet.addAll(menuService.listByRoleCode(roleCode, UserContextHolder + .getTenantId()))); + } + List menuList = menuSet.stream().filter(m -> !MenuTypeEnum.BUTTON.equals(m.getType())).toList(); + if (CollUtil.isEmpty(menuList)) { + return new ArrayList<>(0); + } + // 构建路由树 + TreeField treeField = MenuResp.class.getDeclaredAnnotation(TreeField.class); + TreeNodeConfig treeNodeConfig = crudProperties.getTree().genTreeNodeConfig(treeField); + List> treeList = TreeUtil.build(menuList, treeField.rootId(), treeNodeConfig, (m, tree) -> { + tree.setId(m.getId()); + tree.setParentId(m.getParentId()); + tree.setName(m.getTitle()); + tree.setWeight(m.getSort()); + tree.putExtra("type", m.getType().getValue()); + tree.putExtra("path", m.getPath()); + tree.putExtra("name", m.getName()); + tree.putExtra("component", m.getComponent()); + tree.putExtra("redirect", m.getRedirect()); + tree.putExtra("icon", m.getIcon()); + tree.putExtra("isExternal", m.getIsExternal()); + tree.putExtra("isCache", m.getIsCache()); + tree.putExtra("isHidden", m.getIsHidden()); + tree.putExtra("permission", m.getPermission()); + }); + return BeanUtil.copyToList(treeList, RouteResp.class); + } +} diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java deleted file mode 100644 index 50e78652d..000000000 --- a/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package top.continew.admin.auth.service.impl; - -import cn.dev33.satoken.stp.SaLoginConfig; -import cn.dev33.satoken.stp.StpUtil; -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.tree.Tree; -import cn.hutool.core.lang.tree.TreeNodeConfig; -import cn.hutool.core.lang.tree.TreeUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.RandomUtil; -import cn.hutool.core.util.ReUtil; -import cn.hutool.extra.servlet.JakartaServletUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.hutool.json.JSONUtil; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import me.zhyd.oauth.model.AuthUser; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import top.continew.admin.auth.model.resp.RouteResp; -import top.continew.admin.auth.service.LoginService; -import top.continew.admin.common.config.properties.TenantProperties; -import top.continew.admin.common.constant.CacheConstants; -import top.continew.admin.common.constant.RegexConstants; -import top.continew.admin.common.constant.SysConstants; -import top.continew.admin.common.context.RoleContext; -import top.continew.admin.common.context.UserContext; -import top.continew.admin.common.context.UserContextHolder; -import top.continew.admin.common.context.UserExtraContext; -import top.continew.admin.common.enums.DisEnableStatusEnum; -import top.continew.admin.common.enums.GenderEnum; -import top.continew.admin.system.enums.MenuTypeEnum; -import top.continew.admin.system.enums.MessageTemplateEnum; -import top.continew.admin.system.enums.MessageTypeEnum; -import top.continew.admin.system.enums.PasswordPolicyEnum; -import top.continew.admin.system.model.entity.DeptDO; -import top.continew.admin.system.model.entity.RoleDO; -import top.continew.admin.system.model.entity.UserDO; -import top.continew.admin.system.model.entity.UserSocialDO; -import top.continew.admin.system.model.req.MessageReq; -import top.continew.admin.system.model.resp.MenuResp; -import top.continew.admin.system.service.*; -import top.continew.starter.cache.redisson.util.RedisUtils; -import top.continew.starter.core.autoconfigure.project.ProjectProperties; -import top.continew.starter.core.validation.CheckUtils; -import top.continew.starter.extension.crud.annotation.TreeField; -import top.continew.starter.extension.crud.autoconfigure.CrudProperties; -import top.continew.starter.extension.tenant.TenantHandler; -import top.continew.starter.extension.tenant.context.TenantContextHolder; - -import top.continew.starter.messaging.websocket.util.WebSocketUtils; -import top.continew.starter.web.util.SpringWebUtils; - -import java.time.Duration; -import java.time.LocalDateTime; -import java.util.*; -import java.util.concurrent.CompletableFuture; - -import static top.continew.admin.system.enums.PasswordPolicyEnum.PASSWORD_EXPIRATION_DAYS; - -/** - * 登录业务实现 - * - * @author Charles7c - * @since 2022/12/21 21:49 - */ -@Service -@RequiredArgsConstructor -public class LoginServiceImpl implements LoginService { - - private final ProjectProperties projectProperties; - private final CrudProperties crudProperties; - private final PasswordEncoder passwordEncoder; - private final ThreadPoolTaskExecutor threadPoolTaskExecutor; - private final UserService userService; - private final DeptService deptService; - private final RoleService roleService; - private final MenuService menuService; - private final UserRoleService userRoleService; - private final UserSocialService userSocialService; - private final OptionService optionService; - private final MessageService messageService; - private final TenantProperties tenantProperties; - - @Override - public String accountLogin(String username, String password, HttpServletRequest request) { - UserDO user = userService.getByUsername(username); - boolean isError = ObjectUtil.isNull(user) || !passwordEncoder.matches(password, user.getPassword()); - this.checkUserLocked(username, request, isError); - CheckUtils.throwIf(isError, "用户名或密码错误"); - this.checkUserStatus(user); - return this.login(user); - } - - @Override - public String phoneLogin(String phone) { - UserDO user = userService.getByPhone(phone); - CheckUtils.throwIfNull(user, "此手机号未绑定本系统账号"); - this.checkUserStatus(user); - return this.login(user); - } - - @Override - public String emailLogin(String email) { - UserDO user = userService.getByEmail(email); - CheckUtils.throwIfNull(user, "此邮箱未绑定本系统账号"); - this.checkUserStatus(user); - return this.login(user); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public String socialLogin(AuthUser authUser) { - String source = authUser.getSource(); - String openId = authUser.getUuid(); - UserSocialDO userSocial = userSocialService.getBySourceAndOpenId(source, openId); - UserDO user; - if (null == userSocial) { - String username = authUser.getUsername(); - String nickname = authUser.getNickname(); - UserDO existsUser = userService.getByUsername(username); - String randomStr = RandomUtil.randomString(RandomUtil.BASE_CHAR, 5); - if (null != existsUser || !ReUtil.isMatch(RegexConstants.USERNAME, username)) { - username = randomStr + IdUtil.fastSimpleUUID(); - } - if (!ReUtil.isMatch(RegexConstants.GENERAL_NAME, nickname)) { - nickname = source.toLowerCase() + randomStr; - } - user = new UserDO(); - user.setUsername(username); - user.setNickname(nickname); - user.setGender(GenderEnum.valueOf(authUser.getGender().name())); - user.setAvatar(authUser.getAvatar()); - user.setDeptId(SysConstants.SUPER_DEPT_ID); - Long userId = userService.add(user); - RoleDO role = roleService.getByCode(SysConstants.SUPER_ROLE_CODE); - userRoleService.assignRolesToUser(Collections.singletonList(role.getId()), userId); - userSocial = new UserSocialDO(); - userSocial.setUserId(userId); - userSocial.setSource(source); - userSocial.setOpenId(openId); - this.sendSecurityMsg(user); - } else { - user = BeanUtil.copyProperties(userService.getById(userSocial.getUserId()), UserDO.class); - } - this.checkUserStatus(user); - userSocial.setMetaJson(JSONUtil.toJsonStr(authUser)); - userSocial.setLastLoginTime(LocalDateTime.now()); - userSocialService.saveOrUpdate(userSocial); - return this.login(user); - } - - @Override - public List buildRouteTree(Long userId) { - Set roleCodeSet = roleService.listCodeByUserId(userId); - if (CollUtil.isEmpty(roleCodeSet)) { - return new ArrayList<>(0); - } - // 查询菜单列表 - Set menuSet = new LinkedHashSet<>(); - if (roleCodeSet.contains(SysConstants.SUPER_ROLE_CODE)) { - menuSet.addAll(menuService.listAll(UserContextHolder.getTenantId())); - } else { - roleCodeSet.forEach(roleCode -> menuSet.addAll(menuService.listByRoleCode(roleCode, UserContextHolder - .getTenantId()))); - } - List menuList = menuSet.stream().filter(m -> !MenuTypeEnum.BUTTON.equals(m.getType())).toList(); - if (CollUtil.isEmpty(menuList)) { - return new ArrayList<>(0); - } - // 构建路由树 - TreeField treeField = MenuResp.class.getDeclaredAnnotation(TreeField.class); - TreeNodeConfig treeNodeConfig = crudProperties.getTree().genTreeNodeConfig(treeField); - List> treeList = TreeUtil.build(menuList, treeField.rootId(), treeNodeConfig, (m, tree) -> { - tree.setId(m.getId()); - tree.setParentId(m.getParentId()); - tree.setName(m.getTitle()); - tree.setWeight(m.getSort()); - tree.putExtra("type", m.getType().getValue()); - tree.putExtra("path", m.getPath()); - tree.putExtra("name", m.getName()); - tree.putExtra("component", m.getComponent()); - tree.putExtra("redirect", m.getRedirect()); - tree.putExtra("icon", m.getIcon()); - tree.putExtra("isExternal", m.getIsExternal()); - tree.putExtra("isCache", m.getIsCache()); - tree.putExtra("isHidden", m.getIsHidden()); - tree.putExtra("permission", m.getPermission()); - }); - return BeanUtil.copyToList(treeList, RouteResp.class); - } - - /** - * 登录并缓存用户信息 - * - * @param user 用户信息 - * @return 令牌 - */ - private String login(UserDO user) { - Long userId = user.getId(); - Long tenantId = TenantContextHolder.getTenantId(); - CompletableFuture> permissionFuture = CompletableFuture.supplyAsync(() -> { - Set permissionSet = new HashSet<>(); - if (tenantProperties.isEnabled()) { - SpringUtil.getBean(TenantHandler.class) - .execute(tenantId, () -> permissionSet.addAll(roleService.listPermissionByUserId(userId))); - } else { - permissionSet.addAll(roleService.listPermissionByUserId(userId)); - } - return permissionSet; - }, threadPoolTaskExecutor); - CompletableFuture> roleFuture = CompletableFuture.supplyAsync(() -> { - Set roleSet = new HashSet<>(); - if (tenantProperties.isEnabled()) { - SpringUtil.getBean(TenantHandler.class) - .execute(tenantId, () -> roleSet.addAll(roleService.listByUserId(userId))); - } else { - roleSet.addAll(roleService.listByUserId(userId)); - } - return roleSet; - }, threadPoolTaskExecutor); - CompletableFuture passwordExpirationDaysFuture = CompletableFuture.supplyAsync(() -> optionService - .getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name())); - CompletableFuture.allOf(permissionFuture, roleFuture, passwordExpirationDaysFuture); - UserContext userContext = new UserContext(permissionFuture.join(), roleFuture - .join(), passwordExpirationDaysFuture.join()); - BeanUtil.copyProperties(user, userContext); - // 设置租户ID - userContext.setTenantId(tenantId); - // 登录并缓存用户信息 - StpUtil.login(userContext.getId(), SaLoginConfig.setExtraData(BeanUtil - .beanToMap(new UserExtraContext(SpringWebUtils.getRequest())))); - UserContextHolder.setContext(userContext); - return StpUtil.getTokenValue(); - } - - /** - * 检查用户状态 - * - * @param user 用户信息 - */ - private void checkUserStatus(UserDO user) { - CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, user.getStatus(), "此账号已被禁用,如有疑问,请联系管理员"); - DeptDO dept = deptService.getById(user.getDeptId()); - CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, dept.getStatus(), "此账号所属部门已被禁用,如有疑问,请联系管理员"); - } - - /** - * 检测用户是否已被锁定 - * - * @param username 用户名 - * @param request 请求对象 - * @param isError 是否登录错误 - */ - private void checkUserLocked(String username, HttpServletRequest request, boolean isError) { - // 不锁定 - int maxErrorCount = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_COUNT.name()); - if (maxErrorCount <= SysConstants.NO) { - return; - } - // 检测是否已被锁定 - String key = CacheConstants.USER_PASSWORD_ERROR_KEY_PREFIX + RedisUtils.formatKey(username, JakartaServletUtil - .getClientIP(request)); - int lockMinutes = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_MINUTES.name()); - Integer currentErrorCount = ObjectUtil.defaultIfNull(RedisUtils.get(key), 0); - CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "账号锁定 {} 分钟,请稍后再试", lockMinutes); - // 登录成功清除计数 - if (!isError) { - RedisUtils.delete(key); - return; - } - // 登录失败递增计数 - currentErrorCount++; - RedisUtils.set(key, currentErrorCount, Duration.ofMinutes(lockMinutes)); - CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "密码错误已达 {} 次,账号锁定 {} 分钟", maxErrorCount, lockMinutes); - } - - /** - * 发送安全消息 - * - * @param user 用户信息 - */ - private void sendSecurityMsg(UserDO user) { - MessageReq req = new MessageReq(); - MessageTemplateEnum socialRegister = MessageTemplateEnum.SOCIAL_REGISTER; - req.setTitle(socialRegister.getTitle().formatted(projectProperties.getName())); - req.setContent(socialRegister.getContent().formatted(user.getNickname())); - req.setType(MessageTypeEnum.SECURITY); - messageService.add(req, CollUtil.toList(user.getId())); - List tokenList = StpUtil.getTokenValueListByLoginId(user.getId()); - for (String token : tokenList) { - WebSocketUtils.sendMessage(token, "1"); - } - } -} diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java index 12c2fbbdd..1b9c22893 100644 --- a/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java +++ b/continew-module-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java @@ -77,7 +77,8 @@ public List list(OnlineUserQuery query) { for (Map.Entry> entry : tokenMap.entrySet()) { Long userId = entry.getKey(); UserContext userContext = UserContextHolder.getContext(userId); - if (null == userContext || !this.isMatchNickname(query.getNickname(), userContext)) { + if (null == userContext || !this.isMatchNickname(query.getNickname(), userContext) || !this + .isMatchClientId(query.getClientId(), userContext)) { continue; } //租户数据过滤 @@ -89,12 +90,13 @@ public List list(OnlineUserQuery query) { List loginTimeList = query.getLoginTime(); entry.getValue().parallelStream().forEach(token -> { UserExtraContext extraContext = UserContextHolder.getExtraContext(token); - if (this.isMatchLoginTime(loginTimeList, extraContext.getLoginTime())) { - OnlineUserResp resp = BeanUtil.copyProperties(userContext, OnlineUserResp.class); - BeanUtil.copyProperties(extraContext, resp); - resp.setToken(token); - list.add(resp); + if (!this.isMatchLoginTime(loginTimeList, extraContext.getLoginTime())) { + return; } + OnlineUserResp resp = BeanUtil.copyProperties(userContext, OnlineUserResp.class); + BeanUtil.copyProperties(extraContext, resp); + resp.setToken(token); + list.add(resp); }); } // 设置排序 @@ -131,6 +133,20 @@ private boolean isMatchNickname(String nickname, UserContext userContext) { .getNickname(userContext.getId()), nickname); } + /** + * 是否匹配客户端 ID + * + * @param clientId 客户端 ID + * @param userContext 用户上下文信息 + * @return 是否匹配客户端 ID + */ + private boolean isMatchClientId(String clientId, UserContext userContext) { + if (StrUtil.isBlank(clientId)) { + return true; + } + return Objects.equals(userContext.getClientId(), clientId); + } + /** * 是否匹配登录时间 * diff --git a/continew-module-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java b/continew-module-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java index 0ca4be9fa..cde757e56 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java @@ -18,7 +18,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; -import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; @@ -69,8 +69,9 @@ public void validateRange(int value, Map policyMap) { super.validateRange(value, policyMap); return; } - Integer passwordExpirationDays = ObjUtil.defaultIfNull(Convert.toInt(policyMap.get(PASSWORD_EXPIRATION_DAYS - .name())), SpringUtil.getBean(OptionService.class).getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name())); + Integer passwordExpirationDays = ObjectUtil.defaultIfNull(Convert.toInt(policyMap + .get(PASSWORD_EXPIRATION_DAYS.name())), SpringUtil.getBean(OptionService.class) + .getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name())); if (passwordExpirationDays > SysConstants.NO) { ValidationUtils.throwIf(value >= passwordExpirationDays, "密码到期前的提示时间应小于密码有效期"); return; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/mapper/ClientMapper.java b/continew-module-system/src/main/java/top/continew/admin/system/mapper/ClientMapper.java new file mode 100644 index 000000000..8f328cee7 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/mapper/ClientMapper.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.mapper; + +import top.continew.admin.system.model.entity.ClientDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 客户端 Mapper + * + * @author KAI + * @since 2024/12/03 16:04 + */ +public interface ClientMapper extends BaseMapper { +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/mapper/DictItemMapper.java b/continew-module-system/src/main/java/top/continew/admin/system/mapper/DictItemMapper.java index 08ee5ee7a..1d88d3b38 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/mapper/DictItemMapper.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/mapper/DictItemMapper.java @@ -16,9 +16,7 @@ package top.continew.admin.system.mapper; -import com.baomidou.dynamic.datasource.annotation.DS; import org.apache.ibatis.annotations.Param; -import top.continew.admin.common.constant.SysConstants; import top.continew.admin.system.model.entity.DictItemDO; import top.continew.starter.data.mp.base.BaseMapper; import top.continew.starter.extension.crud.model.resp.LabelValueResp; @@ -31,7 +29,6 @@ * @author Charles7c * @since 2023/9/11 21:29 */ -@DS(SysConstants.DEFAULT_DATASOURCE) public interface DictItemMapper extends BaseMapper { /** diff --git a/continew-module-system/src/main/java/top/continew/admin/system/mapper/DictMapper.java b/continew-module-system/src/main/java/top/continew/admin/system/mapper/DictMapper.java index dba156b7d..783526513 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/mapper/DictMapper.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/mapper/DictMapper.java @@ -16,8 +16,6 @@ package top.continew.admin.system.mapper; -import com.baomidou.dynamic.datasource.annotation.DS; -import top.continew.admin.common.constant.SysConstants; import top.continew.admin.system.model.entity.DictDO; import top.continew.starter.data.mp.base.BaseMapper; @@ -27,6 +25,5 @@ * @author Charles7c * @since 2023/9/11 21:29 */ -@DS(SysConstants.DEFAULT_DATASOURCE) public interface DictMapper extends BaseMapper { } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/mapper/OptionMapper.java b/continew-module-system/src/main/java/top/continew/admin/system/mapper/OptionMapper.java index db9f1473a..8ea34c37f 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/mapper/OptionMapper.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/mapper/OptionMapper.java @@ -16,10 +16,8 @@ package top.continew.admin.system.mapper; -import com.baomidou.dynamic.datasource.annotation.DS; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; -import top.continew.admin.common.constant.SysConstants; import top.continew.admin.system.model.entity.OptionDO; import top.continew.starter.data.mp.base.BaseMapper; @@ -31,7 +29,6 @@ * @author Bull-BCLS * @since 2023/8/26 19:38 */ -@DS(SysConstants.DEFAULT_DATASOURCE) public interface OptionMapper extends BaseMapper { /** diff --git a/continew-module-system/src/main/java/top/continew/admin/system/mapper/UserPasswordHistoryMapper.java b/continew-module-system/src/main/java/top/continew/admin/system/mapper/UserPasswordHistoryMapper.java index a7b850999..b9ac571bf 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/mapper/UserPasswordHistoryMapper.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/mapper/UserPasswordHistoryMapper.java @@ -17,8 +17,8 @@ package top.continew.admin.system.mapper; import org.apache.ibatis.annotations.Param; -import top.continew.starter.data.mp.base.BaseMapper; import top.continew.admin.system.model.entity.UserPasswordHistoryDO; +import top.continew.starter.data.mp.base.BaseMapper; /** * 用户历史密码 Mapper diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/ClientDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/ClientDO.java new file mode 100644 index 000000000..f10ba6104 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/ClientDO.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.model.entity.BaseDO; + +import java.io.Serial; +import java.util.List; + +/** + * 客户端实体 + * + * @author KAI + * @author Charles7c + * @since 2024/12/03 16:04 + */ +@Data +@TableName(value = "sys_client", autoResultMap = true) +public class ClientDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 客户端 ID + */ + private String clientId; + + /** + * 客户端 Key + */ + private String clientKey; + + /** + * 客户端秘钥 + */ + private String clientSecret; + + /** + * 登录类型 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List authType; + + /** + * 客户端类型 + */ + private String clientType; + + /** + * Token 最低活跃频率(单位:秒,-1:不限制,永不冻结) + */ + private Long activeTimeout; + + /** + * Token 有效期(单位:秒,-1:永不过期) + */ + private Long timeout; + + /** + * 状态 + */ + private DisEnableStatusEnum status; +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DeptDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DeptDO.java index 5a8c87afc..0f6755642 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DeptDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DeptDO.java @@ -19,7 +19,7 @@ import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import top.continew.admin.common.enums.DisEnableStatusEnum; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DictDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DictDO.java index 2909901fe..b514c6353 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DictDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DictDO.java @@ -19,7 +19,7 @@ import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import top.continew.starter.extension.crud.annotation.DictField; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DictItemDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DictItemDO.java index 4a4af79d5..190ccff93 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DictItemDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/DictItemDO.java @@ -19,7 +19,7 @@ import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import top.continew.admin.common.enums.DisEnableStatusEnum; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java index d5e9611f8..95ce396ae 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java @@ -25,7 +25,7 @@ import top.continew.admin.system.enums.StorageTypeEnum; import top.continew.starter.core.constant.StringConstants; import top.continew.starter.core.util.StrUtils; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; import java.io.Serial; import java.net.URL; @@ -117,7 +117,7 @@ public FileInfo toFileInfo(StorageDO storageDO) { * 例如: * http://domain.cn/bucketName/2024/11/27/6746ec3b2907f0de80afdd70.png => 2024/11/27/ * http://bucketName.domain.cn/2024/11/27/6746ec3b2907f0de80afdd70.png => 2024/11/27/ - * + * * @param url 文件路径 * @param storageDO 存储桶信息 * @return diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MenuDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MenuDO.java index 9a0ded735..ef5afff12 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MenuDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MenuDO.java @@ -20,7 +20,7 @@ import lombok.Data; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.system.enums.MenuTypeEnum; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java index 0e997611e..98842c749 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java @@ -20,8 +20,8 @@ import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.Data; +import top.continew.admin.common.model.entity.BaseDO; import top.continew.admin.system.enums.NoticeScopeEnum; -import top.continew.starter.extension.crud.model.entity.BaseDO; import java.io.Serial; import java.time.LocalDateTime; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/OptionDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/OptionDO.java index 40944636a..a6fd33abf 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/OptionDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/OptionDO.java @@ -18,7 +18,7 @@ import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; -import top.continew.starter.extension.crud.model.entity.BaseUpdateDO; +import top.continew.admin.common.model.entity.BaseUpdateDO; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/RoleDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/RoleDO.java index bf7be5532..69ecff3f8 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/RoleDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/RoleDO.java @@ -20,7 +20,7 @@ import lombok.Data; import top.continew.admin.common.enums.DataScopeEnum; import top.continew.starter.extension.crud.annotation.DictField; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/StorageDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/StorageDO.java index 9a5f8fe08..e6109c437 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/StorageDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/StorageDO.java @@ -20,7 +20,7 @@ import lombok.Data; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.system.enums.StorageTypeEnum; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; import top.continew.starter.security.crypto.annotation.FieldEncrypt; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/UserDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/UserDO.java index 5eabf272a..cafc4349d 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/UserDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/UserDO.java @@ -24,7 +24,7 @@ import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.common.enums.GenderEnum; import top.continew.starter.extension.crud.annotation.DictField; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; import top.continew.starter.security.crypto.annotation.FieldEncrypt; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/query/ClientQuery.java b/continew-module-system/src/main/java/top/continew/admin/system/model/query/ClientQuery.java new file mode 100644 index 000000000..0a5314282 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/query/ClientQuery.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 客户端查询条件 + * + * @author KAI + * @author Charles7c + * @since 2024/12/03 16:04 + */ +@Data +@Schema(description = "客户端查询条件") +public class ClientQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 客户端 Key + */ + @Schema(description = "客户端 Key", example = "PC") + private String clientKey; + + /** + * 客户端秘钥 + */ + @Schema(description = "客户端秘钥", example = "dd77ab1e353a027e0d60ce3b151e8642") + private String clientSecret; + + /** + * 认证类型 + */ + @Schema(description = "认证类型", example = "ACCOUNT") + @Query(type = QueryType.IN) + private List authType; + + /** + * 客户端类型 + */ + @Schema(description = "客户端类型", example = "PC") + private String clientType; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/req/ClientReq.java b/continew-module-system/src/main/java/top/continew/admin/system/model/req/ClientReq.java new file mode 100644 index 000000000..1e2849c37 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/req/ClientReq.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; +import java.util.List; + +/** + * 创建或修改客户端参数 + * + * @author KAI + * @author Charles7c + * @since 2024/12/03 16:04 + */ +@Data +@Schema(description = "创建或修改客户端参数") +public class ClientReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 客户端 Key + */ + @Schema(description = "客户端 Key", example = "PC") + @NotBlank(message = "客户端Key不能为空") + @Length(max = 32, message = "客户端Key长度不能超过 {max} 个字符") + private String clientKey; + + /** + * 客户端秘钥 + */ + @Schema(description = "客户端秘钥", example = "dd77ab1e353a027e0d60ce3b151e8642") + @NotBlank(message = "客户端秘钥不能为空") + @Length(max = 255, message = "客户端秘钥长度不能超过 {max} 个字符") + private String clientSecret; + + /** + * 认证类型 + */ + @Schema(description = "认证类型", example = "ACCOUNT") + @NotEmpty(message = "认证类型不能为空") + private List authType; + + /** + * 客户端类型 + */ + @Schema(description = "客户端类型", example = "PC") + @NotBlank(message = "客户端类型不能为空") + @Length(max = 32, message = "客户端类型长度不能超过 {max} 个字符") + private String clientType; + + /** + * Token 最低活跃频率(单位:秒,-1:不限制,永不冻结) + */ + @Schema(description = "Token 最低活跃频率(单位:秒,-1:不限制,永不冻结)", example = "1800") + private Long activeTimeout; + + /** + * Token 有效期(单位:秒,-1:永不过期) + */ + @Schema(description = "Token 有效期(单位:秒,-1:永不过期)", example = "86400") + private Long timeout; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 客户端 ID + */ + @Schema(hidden = true) + private String clientId; +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/req/user/UserImportRowReq.java b/continew-module-system/src/main/java/top/continew/admin/system/model/req/user/UserImportRowReq.java index b8fdf324b..793bb8941 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/req/user/UserImportRowReq.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/req/user/UserImportRowReq.java @@ -19,7 +19,6 @@ import cn.hutool.core.lang.RegexPool; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import lombok.Data; import org.hibernate.validator.constraints.Length; @@ -65,7 +64,7 @@ public class UserImportRowReq extends BaseReq { /** * 部门名称 */ - @NotNull(message = "所属部门不能为空") + @NotBlank(message = "所属部门不能为空") private String deptName; /** diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/ClientResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/ClientResp.java new file mode 100644 index 000000000..b59be0b08 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/ClientResp.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; +import top.continew.admin.common.enums.DisEnableStatusEnum; + +import java.io.Serial; +import java.util.List; + +/** + * 客户端信息 + * + * @author KAI + * @author Charles7c + * @since 2024/12/03 16:04 + */ +@Data +@Schema(description = "客户端信息") +public class ClientResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 客户端 ID + */ + @Schema(description = "客户端 ID", example = "ef51c9a3e9046c4f2ea45142c8a8344a") + private String clientId; + + /** + * 客户端 Key + */ + @Schema(description = "客户端 Key", example = "PC") + private String clientKey; + + /** + * 客户端秘钥 + */ + @Schema(description = "客户端秘钥", example = "dd77ab1e353a027e0d60ce3b151e8642") + private String clientSecret; + + /** + * 认证类型 + */ + @Schema(description = "认证类型", example = "ACCOUNT") + private List authType; + + /** + * 客户端类型 + */ + @Schema(description = "客户端类型", example = "PC") + private String clientType; + + /** + * Token 最低活跃频率(单位:秒,-1:不限制,永不冻结) + */ + @Schema(description = "Token 最低活跃频率(单位:秒,-1:不限制,永不冻结)", example = "1800") + private Long activeTimeout; + + /** + * Token 有效期(单位:秒,-1:永不过期) + */ + @Schema(description = "Token 有效期(单位:秒,-1:永不过期)", example = "86400") + private Long timeout; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DeptResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DeptResp.java index 5218465f0..73d0e8dbb 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DeptResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DeptResp.java @@ -20,10 +20,10 @@ import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.starter.extension.crud.annotation.TreeField; import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DictItemResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DictItemResp.java index f5308edb4..958e06775 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DictItemResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DictItemResp.java @@ -19,9 +19,9 @@ import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DictResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DictResp.java index 02db65c2b..301738c79 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DictResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/DictResp.java @@ -18,7 +18,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; +import top.continew.admin.common.model.resp.BaseDetailResp; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/FileResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/FileResp.java index 21fc025db..381f729dc 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/FileResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/FileResp.java @@ -19,8 +19,8 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; import top.continew.admin.system.enums.FileTypeEnum; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/MenuResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/MenuResp.java index c5b7b0e97..e2ac18561 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/MenuResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/MenuResp.java @@ -18,10 +18,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseResp; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.system.enums.MenuTypeEnum; import top.continew.starter.extension.crud.annotation.TreeField; -import top.continew.starter.extension.crud.model.resp.BaseResp; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java index c01d35cb0..82edd45c6 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java @@ -20,8 +20,8 @@ import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; import top.continew.admin.system.enums.NoticeScopeEnum; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; import java.io.Serial; import java.time.LocalDateTime; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java index 27e022b1b..8931a0c68 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java @@ -18,9 +18,9 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseResp; import top.continew.admin.system.enums.NoticeScopeEnum; import top.continew.admin.system.enums.NoticeStatusEnum; -import top.continew.starter.extension.crud.model.resp.BaseResp; import java.io.Serial; import java.time.LocalDateTime; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/RoleDetailResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/RoleDetailResp.java index 566fa5711..07f092429 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/RoleDetailResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/RoleDetailResp.java @@ -23,10 +23,10 @@ import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; import top.continew.admin.common.enums.DataScopeEnum; import top.continew.admin.system.service.RoleDeptService; import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; import java.io.Serial; import java.util.List; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/RoleResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/RoleResp.java index b5b0f6f15..ed624c02b 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/RoleResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/RoleResp.java @@ -18,8 +18,8 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; import top.continew.admin.common.enums.DataScopeEnum; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; import java.io.Serial; @@ -71,9 +71,4 @@ public class RoleResp extends BaseDetailResp { */ @Schema(description = "描述", example = "测试人员描述信息") private String description; - - @Override - public Boolean getDisabled() { - return this.getIsSystem(); - } } diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/StorageResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/StorageResp.java index 2c93a14da..439b15bf4 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/StorageResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/StorageResp.java @@ -18,9 +18,9 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.system.enums.StorageTypeEnum; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; import top.continew.starter.security.mask.annotation.JsonMask; import java.io.Serial; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/user/UserDetailResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/user/UserDetailResp.java index afe3b7fcc..e983cb788 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/user/UserDetailResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/user/UserDetailResp.java @@ -26,13 +26,13 @@ import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; import top.continew.admin.common.constant.ContainerConstants; import top.continew.admin.common.context.UserContextHolder; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.common.enums.GenderEnum; import top.continew.admin.system.model.resp.DeptResp; import top.continew.admin.system.service.DeptService; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; import top.continew.starter.file.excel.converter.ExcelListConverter; import top.continew.starter.security.crypto.annotation.FieldEncrypt; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/user/UserResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/user/UserResp.java index dc3a19d9a..c160dc356 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/user/UserResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/user/UserResp.java @@ -20,11 +20,11 @@ import cn.crane4j.core.executor.handler.ManyToManyAssembleOperationHandler; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import top.continew.admin.common.model.resp.BaseDetailResp; import top.continew.admin.common.constant.ContainerConstants; import top.continew.admin.common.context.UserContextHolder; import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.common.enums.GenderEnum; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; import top.continew.starter.security.mask.annotation.JsonMask; import top.continew.starter.security.mask.enums.MaskType; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/ClientService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/ClientService.java new file mode 100644 index 000000000..ac2cf2d6c --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/ClientService.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.service; + +import top.continew.admin.system.model.query.ClientQuery; +import top.continew.admin.system.model.req.ClientReq; +import top.continew.admin.system.model.resp.ClientResp; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 客户端业务接口 + * + * @author KAI + * @author Charles7c + * @since 2024/12/03 16:04 + */ +public interface ClientService extends BaseService { + + /** + * 根据客户端 ID 查詢 + * + * @param clientId 客戶端 ID + * @return 客户端信息 + */ + ClientResp getByClientId(String clientId); +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/MessageService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/MessageService.java index 67fe7f749..235c6baa8 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/MessageService.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/MessageService.java @@ -16,14 +16,14 @@ package top.continew.admin.system.service; -import java.util.List; - import top.continew.admin.system.model.query.MessageQuery; import top.continew.admin.system.model.req.MessageReq; import top.continew.admin.system.model.resp.MessageResp; import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.resp.PageResp; +import java.util.List; + /** * 消息业务接口 * diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/MessageUserService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/MessageUserService.java index f53f3140b..84e3221f8 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/MessageUserService.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/MessageUserService.java @@ -16,10 +16,10 @@ package top.continew.admin.system.service; -import java.util.List; - import top.continew.admin.system.model.resp.MessageUnreadResp; +import java.util.List; + /** * 消息和用户关联业务接口 * diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeService.java index c7e2f9ea8..795d9a2ea 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeService.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeService.java @@ -22,8 +22,8 @@ import top.continew.admin.system.model.resp.NoticeDetailResp; import top.continew.admin.system.model.resp.NoticeResp; import top.continew.admin.system.model.resp.dashboard.DashboardNoticeResp; -import top.continew.starter.extension.crud.service.BaseService; import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.service.BaseService; import java.util.List; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/UserService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/UserService.java index 5b5a20313..6cfeba280 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/UserService.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/UserService.java @@ -124,14 +124,6 @@ public interface UserService extends BaseService implements ClientService { + + private final OnlineUserService onlineUserService; + + @Override + public void beforeAdd(ClientReq req) { + String clientId = DigestUtil.md5Hex(req.getClientKey() + StringConstants.COLON + req.getClientSecret()); + req.setClientId(clientId); + } + + @Override + public void beforeDelete(List ids) { + // 如果还存在在线用户,则不能删除 + OnlineUserQuery query = new OnlineUserQuery(); + for (Long id : ids) { + ClientDO client = this.getById(id); + query.setClientId(client.getClientId()); + CheckUtils.throwIfNotEmpty(onlineUserService.list(query), "客户端 [{}] 还存在在线用户,不能删除", client.getClientKey()); + } + } + + @Override + public ClientResp getByClientId(String clientId) { + return baseMapper.lambdaQuery() + .eq(ClientDO::getClientId, clientId) + .oneOpt() + .map(client -> BeanUtil.copyProperties(client, ClientResp.class)) + .orElse(null); + } +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java index b71df4b49..371e70f3b 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java @@ -23,9 +23,9 @@ import top.continew.admin.system.model.entity.NoticeDO; import top.continew.admin.system.model.query.NoticeQuery; import top.continew.admin.system.model.req.NoticeReq; -import top.continew.admin.system.model.resp.dashboard.DashboardNoticeResp; import top.continew.admin.system.model.resp.NoticeDetailResp; import top.continew.admin.system.model.resp.NoticeResp; +import top.continew.admin.system.model.resp.dashboard.DashboardNoticeResp; import top.continew.admin.system.service.NoticeService; import top.continew.starter.extension.crud.service.BaseServiceImpl; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java index d8cad6178..13fa434a0 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java @@ -51,6 +51,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import top.continew.admin.auth.service.OnlineUserService; +import top.continew.admin.common.service.CommonUserService; import top.continew.admin.common.constant.CacheConstants; import top.continew.admin.common.constant.RegexConstants; import top.continew.admin.common.constant.SysConstants; @@ -81,9 +82,9 @@ import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.query.SortQuery; import top.continew.starter.extension.crud.model.resp.PageResp; -import top.continew.starter.extension.crud.service.CommonUserService; import top.continew.starter.extension.crud.service.BaseServiceImpl; import top.continew.starter.web.util.FileUploadUtils; +import top.continew.starter.core.util.SpringUtils; import java.io.IOException; import java.time.Duration; @@ -209,6 +210,12 @@ public void delete(List ids) { ids.forEach(onlineUserService::kickOut); } + @Override + @Cached(key = "#id", name = CacheConstants.USER_KEY_PREFIX, cacheType = CacheType.BOTH, syncLocal = true) + public String getNicknameById(Long id) { + return baseMapper.selectNicknameById(id); + } + @Override public void downloadImportTemplate(HttpServletResponse response) throws IOException { try { @@ -439,13 +446,6 @@ public void updateEmail(String newEmail, String oldPassword, Long id) { baseMapper.lambdaUpdate().set(UserDO::getEmail, newEmail).eq(UserDO::getId, id).update(); } - @Override - public Long add(UserDO user) { - user.setStatus(DisEnableStatusEnum.ENABLE); - baseMapper.insert(user); - return user.getId(); - } - @Override public UserDO getByUsername(String username) { return baseMapper.selectByUsername(username); @@ -469,12 +469,6 @@ public Long countByDeptIds(List deptIds) { return baseMapper.lambdaQuery().in(UserDO::getDeptId, deptIds).count(); } - @Override - @Cached(key = "#id", name = CacheConstants.USER_KEY_PREFIX, cacheType = CacheType.BOTH, syncLocal = true) - public String getNicknameById(Long id) { - return baseMapper.selectNicknameById(id); - } - @Override protected List list(UserQuery query, SortQuery sortQuery, Class targetClass) { QueryWrapper queryWrapper = this.buildQueryWrapper(query); @@ -525,7 +519,7 @@ private void doImportUser(List insertList, List updateList, List baseMapper.insert(insertList); } if (CollUtil.isNotEmpty(updateList)) { - this.updateBatchById(updateList); + SpringUtils.getProxy(this).updateBatchById(updateList); userRoleService.deleteByUserIds(updateList.stream().map(UserDO::getId).toList()); } if (CollUtil.isNotEmpty(userRoleDOList)) { diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/UserSocialServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/UserSocialServiceImpl.java index 2b4e3ce9b..c508aac85 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/UserSocialServiceImpl.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/UserSocialServiceImpl.java @@ -16,24 +16,20 @@ package top.continew.admin.system.service.impl; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - +import cn.hutool.json.JSONUtil; import lombok.RequiredArgsConstructor; - +import me.zhyd.oauth.model.AuthUser; import org.springframework.stereotype.Service; - -import cn.hutool.json.JSONUtil; - import top.continew.admin.system.enums.SocialSourceEnum; import top.continew.admin.system.mapper.UserSocialMapper; import top.continew.admin.system.model.entity.UserSocialDO; import top.continew.admin.system.service.UserSocialService; import top.continew.starter.core.validation.CheckUtils; -import me.zhyd.oauth.model.AuthUser; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * 用户社会化关联业务实现 diff --git a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/enums/QueryTypeEnum.java b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/enums/QueryTypeEnum.java index e7b99c696..c610975ee 100644 --- a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/enums/QueryTypeEnum.java +++ b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/enums/QueryTypeEnum.java @@ -18,7 +18,6 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; - import top.continew.starter.core.enums.BaseEnum; /** diff --git a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/model/entity/GenConfigDO.java b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/model/entity/GenConfigDO.java index 068338432..937d8bb01 100644 --- a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/model/entity/GenConfigDO.java +++ b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/model/entity/GenConfigDO.java @@ -141,9 +141,8 @@ public void setTableName(String tableName) { */ @Schema(description = "类名前缀", example = "User") public String getClassNamePrefix() { - String tableName = this.getTableName(); - String rawClassName = StrUtils.blankToDefault(this.getTablePrefix(), tableName, prefix -> StrUtil - .removePrefix(tableName, prefix)); + String rawClassName = StrUtils.blankToDefault(this.getTablePrefix(), this.getTableName(), prefix -> StrUtil + .removePrefix(this.getTableName(), prefix)); return StrUtil.upperFirst(StrUtil.toCamelCase(rawClassName)); } } diff --git a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/model/req/GenConfigReq.java b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/model/req/GenConfigReq.java index dc0a258f2..6ae357e5d 100644 --- a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/model/req/GenConfigReq.java +++ b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/model/req/GenConfigReq.java @@ -16,22 +16,19 @@ package top.continew.admin.generator.model.req; -import java.io.Serial; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; - import lombok.Data; - -import io.swagger.v3.oas.annotations.media.Schema; - import top.continew.admin.generator.model.entity.FieldConfigDO; import top.continew.admin.generator.model.entity.GenConfigDO; +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + /** * 代码生成配置信息 * diff --git a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java index 44467b909..883a42c05 100644 --- a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java +++ b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java @@ -80,10 +80,17 @@ public interface GeneratorService { List preview(List tableNames); /** - * 生成代码 + * 生成下载代码 * * @param tableNames 表名称列表 * @param response 响应对象 */ - void generate(List tableNames, HttpServletResponse response); + void downloadCode(List tableNames, HttpServletResponse response); + + /** + * 生成下载代码 + * + * @param tableNames 表名称列表 + */ + void generateCode(List tableNames); } diff --git a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java index 8b72e6d2e..6d3fe62be 100644 --- a/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java +++ b/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java @@ -31,7 +31,9 @@ import cn.hutool.extra.template.engine.freemarker.FreemarkerEngine; import cn.hutool.system.SystemUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import freemarker.ext.beans.BeansWrapper; +import freemarker.template.Configuration; +import freemarker.template.DefaultObjectWrapper; +import freemarker.template.DefaultObjectWrapperBuilder; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -250,7 +252,7 @@ private void setPreviewPath(GeneratePreviewResp generatePreview, } @Override - public void generate(List tableNames, HttpServletResponse response) { + public void downloadCode(List tableNames, HttpServletResponse response) { try { String tempDir = SystemUtil.getUserInfo().getTempDir(); // 删除旧代码 @@ -272,6 +274,32 @@ public void generate(List tableNames, HttpServletResponse response) { } } + @Override + public void generateCode(List tableNames) { + try { + String projectPath = System.getProperty("user.dir"); + tableNames.forEach(tableName -> { + // 初始化配置及数据 + List generatePreviewList = this.preview(tableName); + // 生成代码 + for (GeneratePreviewResp generatePreview : generatePreviewList) { + // 后端:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl/XxxServiceImpl.java + // 前端:continew-admin/continew-admin-ui/src/views/system/user/index.vue + File file = new File(projectPath + generatePreview.getPath() + .replace("continew-admin\\continew-admin", ""), generatePreview.getFileName()); + // 如果已经存在,且不允许覆盖,则跳过 + if (!file.exists() || Boolean.TRUE.equals(genConfigMapper.selectById(tableName).getIsOverride())) { + FileUtil.writeUtf8String(generatePreview.getContent(), file); + } + } + }); + + } catch (Exception e) { + log.error("Generate code of table '{}' occurred an error. {}", tableNames, e.getMessage(), e); + throw new BusinessException("代码生成失败,请手动清理生成文件"); + } + } + /** * 生成预览 * @@ -291,9 +319,10 @@ private List preview(String tableName) { Map templateConfigMap = generatorProperties.getTemplateConfigs(); TemplateEngine engine = TemplateUtil .createEngine(new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH)); + // 在模板中允许使用静态方法 if (engine instanceof FreemarkerEngine freemarkerEngine) { - freemarkerEngine.getConfiguration() - .setSharedVariable("statics", BeansWrapper.getDefaultInstance().getStaticModels()); + DefaultObjectWrapper wrapper = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_33).build(); + freemarkerEngine.getConfiguration().setSharedVariable("statics", wrapper.getStaticModels()); } for (Map.Entry templateConfigEntry : templateConfigMap.entrySet()) { GeneratorProperties.TemplateConfig templateConfig = templateConfigEntry.getValue(); diff --git a/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Controller.ftl b/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Controller.ftl index c60098ee7..36555cc0f 100644 --- a/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Controller.ftl +++ b/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Controller.ftl @@ -7,7 +7,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.*; import top.continew.starter.extension.crud.annotation.CrudRequestMapping; -import top.continew.admin.common.base.BaseController; +import top.continew.admin.common.controller.BaseController; import ${packageName}.model.query.${classNamePrefix}Query; import ${packageName}.model.req.${classNamePrefix}Req; import ${packageName}.model.resp.${classNamePrefix}DetailResp; diff --git a/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/DetailResp.ftl b/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/DetailResp.ftl index 5f8dfc7de..2eacd0a5a 100644 --- a/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/DetailResp.ftl +++ b/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/DetailResp.ftl @@ -15,7 +15,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; -import top.continew.starter.extension.crud.model.resp.BaseDetailResp; +import top.continew.admin.common.model.resp.BaseDetailResp; /** * ${businessName}详情信息 diff --git a/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Entity.ftl b/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Entity.ftl index f5707e972..2c6a49a65 100644 --- a/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Entity.ftl +++ b/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Entity.ftl @@ -12,7 +12,7 @@ import lombok.Data; import com.baomidou.mybatisplus.annotation.TableName; -import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.admin.common.model.entity.BaseDO; /** * ${businessName}实体 diff --git a/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Resp.ftl b/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Resp.ftl index 1c12bd969..feb7d0530 100644 --- a/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Resp.ftl +++ b/continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Resp.ftl @@ -12,7 +12,7 @@ import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; -import top.continew.starter.extension.crud.model.resp.BaseResp; +import top.continew.admin.common.model.resp.BaseResp; /** * ${businessName}信息 diff --git a/continew-plugin/continew-plugin-generator/src/main/resources/templates/frontend/AddModal.ftl b/continew-plugin/continew-plugin-generator/src/main/resources/templates/frontend/AddModal.ftl index de1744ffa..8db65a6da 100644 --- a/continew-plugin/continew-plugin-generator/src/main/resources/templates/frontend/AddModal.ftl +++ b/continew-plugin/continew-plugin-generator/src/main/resources/templates/frontend/AddModal.ftl @@ -4,8 +4,8 @@ :title="title" :mask-closable="false" :esc-to-close="false" - draggable :width="width >= 600 ? 600 : '100%'" + draggable @before-ok="save" @close="reset" > diff --git a/continew-plugin/continew-plugin-generator/src/main/resources/templates/frontend/index.ftl b/continew-plugin/continew-plugin-generator/src/main/resources/templates/frontend/index.ftl index 0b9f71286..f5b5b9f02 100644 --- a/continew-plugin/continew-plugin-generator/src/main/resources/templates/frontend/index.ftl +++ b/continew-plugin/continew-plugin-generator/src/main/resources/templates/frontend/index.ftl @@ -1,5 +1,5 @@