mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-11-04 10:57:10 +08:00 
			
		
		
		
	chore: continew-starter 2.4.0 => 2.5.0
1.continew-starter-log-httptrace-pro => continew-starter-log-interceptor 2.移除 WebMvcConfiguration 配置(已迁移到 Starter 项目) 3.Starter 全局响应(新)适配,自定义异常拦截调整到 Admin 项目 4.部分 API 调整
This commit is contained in:
		@@ -13,7 +13,7 @@
 | 
			
		||||
<img src="https://sonarcloud.io/api/project_badges/measure?project=Charles7c_continew-admin&metric=alert_status" alt="Sonar Status" />
 | 
			
		||||
</a>
 | 
			
		||||
<a href="https://github.com/continew-org/continew-starter" target="_blank">
 | 
			
		||||
<img src="https://img.shields.io/badge/ContiNew Starter-2.4.0-%236CB52D.svg" alt="ContiNew Starter" />
 | 
			
		||||
<img src="https://img.shields.io/badge/ContiNew Starter-2.5.0-%236CB52D.svg" alt="ContiNew Starter" />
 | 
			
		||||
</a>
 | 
			
		||||
<a href="https://spring.io/projects/spring-boot" target="_blank">
 | 
			
		||||
<img src="https://img.shields.io/badge/Spring Boot-3.2.7-%236CB52D.svg?logo=Spring-Boot" alt="Spring Boot" />
 | 
			
		||||
@@ -211,12 +211,12 @@ public class DeptController extends BaseController<DeptService, DeptResp, DeptDe
 | 
			
		||||
## 核心技术栈
 | 
			
		||||
 | 
			
		||||
| 名称                                                         | 版本           | 简介                                                         |
 | 
			
		||||
| :----------------------------------------------------------- | :----------- | :----------------------------------------------------------- |
 | 
			
		||||
| :----------------------------------------------------------- |:-------------| :----------------------------------------------------------- |
 | 
			
		||||
| <a href="https://cn.vuejs.org/" target="_blank">Vue</a>      | 3.4.21       | 渐进式 JavaScript 框架,易学易用,性能出色,适用场景丰富的 Web 前端框架。 |
 | 
			
		||||
| <a href="https://arco.design/vue/docs/start" target="_blank">Arco Design</a> | 2.55.0       | 字节跳动推出的前端 UI 框架,年轻化的色彩和组件设计。         |
 | 
			
		||||
| <a href="https://www.typescriptlang.org/zh/" target="_blank">TypeScript</a> | 5.0.4        | TypeScript 是微软开发的一个开源的编程语言,通过在 JavaScript 的基础上添加静态类型定义构建而成。 |
 | 
			
		||||
| <a href="https://cn.vitejs.dev/" target="_blank">Vite</a>    | 5.1.5        | 下一代的前端工具链,为开发提供极速响应。                     |
 | 
			
		||||
| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.4.0        | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken),可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。 |
 | 
			
		||||
| [ContiNew Starter](https://github.com/continew-org/continew-starter) | 2.5.0        | ContiNew Starter 包含了一系列经过企业实践优化的依赖包(如 MyBatis-Plus、SaToken),可轻松集成到应用中,为开发人员减少手动引入依赖及配置的麻烦,为 Spring Boot Web 项目的灵活快速构建提供支持。 |
 | 
			
		||||
| <a href="https://spring.io/projects/spring-boot" target="_blank">Spring Boot</a> | 3.2.7        | 简化 Spring 应用的初始搭建和开发过程,基于“约定优于配置”的理念,使开发人员不再需要定义样板化的配置。(Spring Boot 3.0 开始,要求 Java 17 作为最低版本) |
 | 
			
		||||
| <a href="https://undertow.io/" target="_blank">Undertow</a>  | 2.3.13.Final | 采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。 |
 | 
			
		||||
| <a href="https://sa-token.dev33.cn/" target="_blank">Sa-Token + JWT</a> | 1.38.0       | 轻量级 Java 权限认证框架,让鉴权变得简单、优雅。             |
 | 
			
		||||
 
 | 
			
		||||
@@ -1,62 +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.common.config;
 | 
			
		||||
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
 | 
			
		||||
import org.springframework.http.converter.HttpMessageConverter;
 | 
			
		||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 | 
			
		||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 | 
			
		||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Web MVC 配置
 | 
			
		||||
 *
 | 
			
		||||
 * @author Charles7c
 | 
			
		||||
 * @since 2022/12/11 19:40
 | 
			
		||||
 */
 | 
			
		||||
@EnableWebMvc
 | 
			
		||||
@Configuration
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
public class WebMvcConfiguration implements WebMvcConfigurer {
 | 
			
		||||
 | 
			
		||||
    private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 解决 Jackson2ObjectMapperBuilderCustomizer 配置不生效的问题
 | 
			
		||||
     * <p>
 | 
			
		||||
     * MappingJackson2HttpMessageConverter 对象在程序启动时创建了多个,移除多余的,保证只有一个
 | 
			
		||||
     * </p>
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
 | 
			
		||||
        converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance);
 | 
			
		||||
        if (Objects.isNull(mappingJackson2HttpMessageConverter)) {
 | 
			
		||||
            converters.add(0, new MappingJackson2HttpMessageConverter());
 | 
			
		||||
        } else {
 | 
			
		||||
            converters.add(0, mappingJackson2HttpMessageConverter);
 | 
			
		||||
        }
 | 
			
		||||
        // 自定义 converters 时,需要手动在最前面添加 ByteArrayHttpMessageConverter
 | 
			
		||||
        // 否则 Spring Doc OpenAPI 的 /*/api-docs/**(例如:/v3/api-docs/default)接口响应内容会变为 Base64 编码后的内容,最终导致接口文档解析失败
 | 
			
		||||
        // 详情请参阅:https://github.com/springdoc/springdoc-openapi/issues/2143
 | 
			
		||||
        converters.add(0, new ByteArrayHttpMessageConverter());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,87 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.config.exception;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.text.CharSequenceUtil;
 | 
			
		||||
import cn.hutool.core.util.NumberUtil;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.core.annotation.Order;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.web.bind.annotation.ExceptionHandler;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
 | 
			
		||||
import org.springframework.web.multipart.MultipartException;
 | 
			
		||||
import top.continew.starter.core.exception.BadRequestException;
 | 
			
		||||
import top.continew.starter.core.exception.BusinessException;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 全局异常处理器
 | 
			
		||||
 *
 | 
			
		||||
 * @author Charles7c
 | 
			
		||||
 * @since 2024/8/7 20:21
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@Order(99)
 | 
			
		||||
@RestControllerAdvice
 | 
			
		||||
public class GlobalExceptionHandler {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 拦截业务异常
 | 
			
		||||
     */
 | 
			
		||||
    @ExceptionHandler(BusinessException.class)
 | 
			
		||||
    public R handleBusinessException(BusinessException e, HttpServletRequest request) {
 | 
			
		||||
        log.error("请求地址 [{}],发生业务异常。", request.getRequestURI(), e);
 | 
			
		||||
        return R.fail(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), e.getMessage());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 拦截自定义验证异常-错误请求
 | 
			
		||||
     */
 | 
			
		||||
    @ExceptionHandler(BadRequestException.class)
 | 
			
		||||
    public R handleBadRequestException(BadRequestException e, HttpServletRequest request) {
 | 
			
		||||
        log.warn("请求地址 [{}],自定义验证失败。", request.getRequestURI(), e);
 | 
			
		||||
        return R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), e.getMessage());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 拦截文件上传异常-超过上传大小限制
 | 
			
		||||
     */
 | 
			
		||||
    @ExceptionHandler(MultipartException.class)
 | 
			
		||||
    public R handleRequestTooBigException(MultipartException e, HttpServletRequest request) {
 | 
			
		||||
        String msg = e.getMessage();
 | 
			
		||||
        R defaultFail = R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), msg);
 | 
			
		||||
        if (CharSequenceUtil.isBlank(msg)) {
 | 
			
		||||
            return defaultFail;
 | 
			
		||||
        }
 | 
			
		||||
        String sizeLimit;
 | 
			
		||||
        Throwable cause = e.getCause();
 | 
			
		||||
        if (null != cause) {
 | 
			
		||||
            msg = msg.concat(cause.getMessage().toLowerCase());
 | 
			
		||||
        }
 | 
			
		||||
        if (msg.contains("size") && msg.contains("exceed")) {
 | 
			
		||||
            sizeLimit = CharSequenceUtil.subBetween(msg, "the maximum size ", " for");
 | 
			
		||||
        } else if (msg.contains("larger than")) {
 | 
			
		||||
            sizeLimit = CharSequenceUtil.subAfter(msg, "larger than ", true);
 | 
			
		||||
        } else {
 | 
			
		||||
            return defaultFail;
 | 
			
		||||
        }
 | 
			
		||||
        String errorMsg = "请上传小于 %sKB 的文件".formatted(NumberUtil.parseLong(sizeLimit) / 1024);
 | 
			
		||||
        log.warn("请求地址 [{}],上传文件失败,文件大小超过限制。", request.getRequestURI(), e);
 | 
			
		||||
        return R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), errorMsg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,72 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.config.exception;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.exception.NotLoginException;
 | 
			
		||||
import cn.dev33.satoken.exception.NotPermissionException;
 | 
			
		||||
import cn.dev33.satoken.exception.NotRoleException;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.core.annotation.Order;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.web.bind.annotation.ExceptionHandler;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 全局 SaToken 异常处理器
 | 
			
		||||
 *
 | 
			
		||||
 * @author Charles7c
 | 
			
		||||
 * @since 2024/8/7 20:21
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@Order(99)
 | 
			
		||||
@RestControllerAdvice
 | 
			
		||||
public class GlobalSaTokenExceptionHandler {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 认证异常-登录认证
 | 
			
		||||
     */
 | 
			
		||||
    @ExceptionHandler(NotLoginException.class)
 | 
			
		||||
    public R handleNotLoginException(NotLoginException e, HttpServletRequest request) {
 | 
			
		||||
        log.error("请求地址 [{}],认证失败,无法访问系统资源。", request.getRequestURI(), e);
 | 
			
		||||
        String errorMsg = switch (e.getType()) {
 | 
			
		||||
            case NotLoginException.KICK_OUT -> "您已被踢下线。";
 | 
			
		||||
            case NotLoginException.BE_REPLACED_MESSAGE -> "您已被顶下线。";
 | 
			
		||||
            default -> "您的登录状态已过期,请重新登录。";
 | 
			
		||||
        };
 | 
			
		||||
        return R.fail(String.valueOf(HttpStatus.UNAUTHORIZED.value()), errorMsg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 认证异常-权限认证
 | 
			
		||||
     */
 | 
			
		||||
    @ExceptionHandler(NotPermissionException.class)
 | 
			
		||||
    public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
 | 
			
		||||
        log.error("请求地址 [{}],权限码校验失败。", request.getRequestURI(), e);
 | 
			
		||||
        return R.fail(String.valueOf(HttpStatus.FORBIDDEN.value()), "没有访问权限,请联系管理员授权");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 认证异常-角色认证
 | 
			
		||||
     */
 | 
			
		||||
    @ExceptionHandler(NotRoleException.class)
 | 
			
		||||
    public R handleNotRoleException(NotRoleException e, HttpServletRequest request) {
 | 
			
		||||
        log.error("请求地址 [{}],角色权限校验失败。", request.getRequestURI(), e);
 | 
			
		||||
        return R.fail(String.valueOf(HttpStatus.FORBIDDEN.value()), "没有访问权限,请联系管理员授权");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -16,7 +16,6 @@
 | 
			
		||||
 | 
			
		||||
package top.continew.admin.generator.service;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import top.continew.admin.generator.model.entity.FieldConfigDO;
 | 
			
		||||
import top.continew.admin.generator.model.entity.GenConfigDO;
 | 
			
		||||
@@ -86,8 +85,7 @@ public interface GeneratorService {
 | 
			
		||||
     * 生成代码
 | 
			
		||||
     *
 | 
			
		||||
     * @param tableNames 表明层
 | 
			
		||||
     * @param request    请求对象
 | 
			
		||||
     * @param response   响应对象
 | 
			
		||||
     */
 | 
			
		||||
    void generate(List<String> tableNames, HttpServletRequest request, HttpServletResponse response);
 | 
			
		||||
    void generate(List<String> tableNames, HttpServletResponse response);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,6 @@ import cn.hutool.core.util.ZipUtil;
 | 
			
		||||
import cn.hutool.db.meta.Column;
 | 
			
		||||
import cn.hutool.system.SystemUtil;
 | 
			
		||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
@@ -281,7 +280,7 @@ public class GeneratorServiceImpl implements GeneratorService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void generate(List<String> tableNames, HttpServletRequest request, HttpServletResponse response) {
 | 
			
		||||
    public void generate(List<String> tableNames, HttpServletResponse response) {
 | 
			
		||||
        try {
 | 
			
		||||
            String tempDir = SystemUtil.getUserInfo().getTempDir();
 | 
			
		||||
            // 删除旧代码
 | 
			
		||||
@@ -296,7 +295,7 @@ public class GeneratorServiceImpl implements GeneratorService {
 | 
			
		||||
            File tempDirFile = new File(tempDir, projectProperties.getAppName());
 | 
			
		||||
            String zipFilePath = tempDirFile.getPath() + jodd.io.ZipUtil.ZIP_EXT;
 | 
			
		||||
            ZipUtil.zip(tempDirFile.getPath(), zipFilePath);
 | 
			
		||||
            FileUploadUtils.download(request, response, new File(zipFilePath), true);
 | 
			
		||||
            FileUploadUtils.download(response, new File(zipFilePath));
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.error("Generate code of table '{}' occurred an error. {}", tableNames, e.getMessage(), e);
 | 
			
		||||
            throw new BusinessException("代码生成失败,请手动清理生成文件");
 | 
			
		||||
 
 | 
			
		||||
@@ -20,12 +20,14 @@ import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.hutool.core.bean.BeanUtil;
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.img.ImgUtil;
 | 
			
		||||
import cn.hutool.core.io.IoUtil;
 | 
			
		||||
import cn.hutool.core.io.file.FileNameUtil;
 | 
			
		||||
import cn.hutool.core.io.resource.ResourceUtil;
 | 
			
		||||
import cn.hutool.core.lang.UUID;
 | 
			
		||||
import cn.hutool.core.map.MapUtil;
 | 
			
		||||
import cn.hutool.core.util.*;
 | 
			
		||||
import cn.hutool.core.util.CharsetUtil;
 | 
			
		||||
import cn.hutool.core.util.EnumUtil;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.hutool.extra.validation.ValidationUtil;
 | 
			
		||||
import cn.hutool.http.ContentType;
 | 
			
		||||
import cn.hutool.json.JSONUtil;
 | 
			
		||||
@@ -77,6 +79,7 @@ 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.impl.BaseServiceImpl;
 | 
			
		||||
import top.continew.starter.web.util.FileUploadUtils;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
@@ -129,13 +132,8 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
 | 
			
		||||
    @Override
 | 
			
		||||
    public void downloadImportUserTemplate(HttpServletResponse response) throws IOException {
 | 
			
		||||
        try {
 | 
			
		||||
            byte[] bytes = ResourceUtil.readBytes("templates/import/userImportTemplate.xlsx");
 | 
			
		||||
            response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode("用户导入模板.xlsx"));
 | 
			
		||||
            response.addHeader("Content-Length", String.valueOf(bytes.length));
 | 
			
		||||
            response.setHeader("Access-Control-Allow-Origin", "*");
 | 
			
		||||
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
 | 
			
		||||
            response.setContentType("application/octet-stream;charset=UTF-8");
 | 
			
		||||
            IoUtil.write(response.getOutputStream(), true, bytes);
 | 
			
		||||
            FileUploadUtils.download(response, ResourceUtil
 | 
			
		||||
                .getStream("templates/import/userImportTemplate.xlsx"), "用户导入模板.xlsx");
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.error("下载用户导入模板失败:", e);
 | 
			
		||||
            response.setCharacterEncoding(CharsetUtil.UTF_8);
 | 
			
		||||
 
 | 
			
		||||
@@ -25,10 +25,10 @@
 | 
			
		||||
    </properties>
 | 
			
		||||
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <!-- ContiNew Starter 日志模块 - HttpTracePro(Spring Boot Actuator HttpTrace 定制增强版) -->
 | 
			
		||||
        <!-- ContiNew Starter 日志模块 - 拦截器版(Spring Boot Actuator HttpTrace 增强版) -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>top.continew</groupId>
 | 
			
		||||
            <artifactId>continew-starter-log-httptrace-pro</artifactId>
 | 
			
		||||
            <artifactId>continew-starter-log-interceptor</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- 系统管理模块(存放系统管理模块相关功能,例如:部门管理、角色管理、用户管理等) -->
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
 | 
			
		||||
import top.continew.starter.extension.crud.annotation.EnableCrudRestController;
 | 
			
		||||
import top.continew.starter.web.annotation.EnableGlobalExceptionHandler;
 | 
			
		||||
import top.continew.starter.web.annotation.EnableGlobalResponse;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 启动程序
 | 
			
		||||
@@ -44,13 +44,13 @@ import top.continew.starter.web.annotation.EnableGlobalExceptionHandler;
 | 
			
		||||
 * @since 2022/12/8 23:15
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@RestController
 | 
			
		||||
@EnableFileStorage
 | 
			
		||||
@EnableMethodCache(basePackages = "top.continew.admin")
 | 
			
		||||
@EnableGlobalResponse
 | 
			
		||||
@EnableCrudRestController
 | 
			
		||||
@RestController
 | 
			
		||||
@SpringBootApplication
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@EnableCrudRestController
 | 
			
		||||
@EnableGlobalExceptionHandler
 | 
			
		||||
@EnableMethodCache(basePackages = "top.continew.admin")
 | 
			
		||||
public class ContiNewAdminApplication implements ApplicationRunner {
 | 
			
		||||
 | 
			
		||||
    private final ProjectProperties projectProperties;
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import top.continew.admin.system.mapper.LogMapper;
 | 
			
		||||
import top.continew.admin.system.service.UserService;
 | 
			
		||||
import top.continew.starter.log.core.dao.LogDao;
 | 
			
		||||
import top.continew.starter.log.httptracepro.autoconfigure.ConditionalOnEnabledLog;
 | 
			
		||||
import top.continew.starter.log.interceptor.autoconfigure.ConditionalOnEnabledLog;
 | 
			
		||||
import top.continew.starter.web.autoconfigure.trace.TraceProperties;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,6 @@ import top.continew.admin.system.service.UserService;
 | 
			
		||||
import top.continew.starter.cache.redisson.util.RedisUtils;
 | 
			
		||||
import top.continew.starter.core.util.ExceptionUtils;
 | 
			
		||||
import top.continew.starter.core.util.validate.ValidationUtils;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
import top.continew.starter.log.core.annotation.Log;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -69,7 +68,7 @@ public class AuthController {
 | 
			
		||||
    @SaIgnore
 | 
			
		||||
    @Operation(summary = "账号登录", description = "根据账号和密码进行登录认证")
 | 
			
		||||
    @PostMapping("/account")
 | 
			
		||||
    public R<LoginResp> accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) {
 | 
			
		||||
    public LoginResp accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) {
 | 
			
		||||
        String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + loginReq.getUuid();
 | 
			
		||||
        String captcha = RedisUtils.get(captchaKey);
 | 
			
		||||
        ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED);
 | 
			
		||||
@@ -79,13 +78,13 @@ public class AuthController {
 | 
			
		||||
        String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(loginReq.getPassword()));
 | 
			
		||||
        ValidationUtils.throwIfBlank(rawPassword, "密码解密失败");
 | 
			
		||||
        String token = loginService.accountLogin(loginReq.getUsername(), rawPassword, request);
 | 
			
		||||
        return R.ok(LoginResp.builder().token(token).build());
 | 
			
		||||
        return LoginResp.builder().token(token).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SaIgnore
 | 
			
		||||
    @Operation(summary = "手机号登录", description = "根据手机号和验证码进行登录认证")
 | 
			
		||||
    @PostMapping("/phone")
 | 
			
		||||
    public R<LoginResp> phoneLogin(@Validated @RequestBody PhoneLoginReq loginReq) {
 | 
			
		||||
    public LoginResp phoneLogin(@Validated @RequestBody PhoneLoginReq loginReq) {
 | 
			
		||||
        String phone = loginReq.getPhone();
 | 
			
		||||
        String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone;
 | 
			
		||||
        String captcha = RedisUtils.get(captchaKey);
 | 
			
		||||
@@ -93,13 +92,13 @@ public class AuthController {
 | 
			
		||||
        ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR);
 | 
			
		||||
        RedisUtils.delete(captchaKey);
 | 
			
		||||
        String token = loginService.phoneLogin(phone);
 | 
			
		||||
        return R.ok(LoginResp.builder().token(token).build());
 | 
			
		||||
        return LoginResp.builder().token(token).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SaIgnore
 | 
			
		||||
    @Operation(summary = "邮箱登录", description = "根据邮箱和验证码进行登录认证")
 | 
			
		||||
    @PostMapping("/email")
 | 
			
		||||
    public R<LoginResp> emailLogin(@Validated @RequestBody EmailLoginReq loginReq) {
 | 
			
		||||
    public LoginResp emailLogin(@Validated @RequestBody EmailLoginReq loginReq) {
 | 
			
		||||
        String email = loginReq.getEmail();
 | 
			
		||||
        String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email;
 | 
			
		||||
        String captcha = RedisUtils.get(captchaKey);
 | 
			
		||||
@@ -107,35 +106,35 @@ public class AuthController {
 | 
			
		||||
        ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR);
 | 
			
		||||
        RedisUtils.delete(captchaKey);
 | 
			
		||||
        String token = loginService.emailLogin(email);
 | 
			
		||||
        return R.ok(LoginResp.builder().token(token).build());
 | 
			
		||||
        return LoginResp.builder().token(token).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "用户退出", description = "注销用户的当前登录")
 | 
			
		||||
    @Parameter(name = "Authorization", description = "令牌", required = true, example = "Bearer xxxx-xxxx-xxxx-xxxx", in = ParameterIn.HEADER)
 | 
			
		||||
    @PostMapping("/logout")
 | 
			
		||||
    public R<Object> logout() {
 | 
			
		||||
    public Object logout() {
 | 
			
		||||
        Object loginId = StpUtil.getLoginId(-1L);
 | 
			
		||||
        StpUtil.logout();
 | 
			
		||||
        return R.ok(loginId);
 | 
			
		||||
        return loginId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Log(ignore = true)
 | 
			
		||||
    @Operation(summary = "获取用户信息", description = "获取登录用户信息")
 | 
			
		||||
    @GetMapping("/user/info")
 | 
			
		||||
    public R<UserInfoResp> getUserInfo() {
 | 
			
		||||
    public UserInfoResp getUserInfo() {
 | 
			
		||||
        LoginUser loginUser = LoginHelper.getLoginUser();
 | 
			
		||||
        UserDetailResp userDetailResp = userService.get(loginUser.getId());
 | 
			
		||||
        UserInfoResp userInfoResp = BeanUtil.copyProperties(userDetailResp, UserInfoResp.class);
 | 
			
		||||
        userInfoResp.setPermissions(loginUser.getPermissions());
 | 
			
		||||
        userInfoResp.setRoles(loginUser.getRoleCodes());
 | 
			
		||||
        userInfoResp.setPwdExpired(loginUser.isPasswordExpired());
 | 
			
		||||
        return R.ok(userInfoResp);
 | 
			
		||||
        return userInfoResp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Log(ignore = true)
 | 
			
		||||
    @Operation(summary = "获取路由信息", description = "获取登录用户的路由信息")
 | 
			
		||||
    @GetMapping("/route")
 | 
			
		||||
    public R<List<RouteResp>> listRoute() {
 | 
			
		||||
        return R.ok(loginService.buildRouteTree(LoginHelper.getUserId()));
 | 
			
		||||
    public List<RouteResp> listRoute() {
 | 
			
		||||
        return loginService.buildRouteTree(LoginHelper.getUserId());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -36,7 +36,6 @@ import top.continew.admin.auth.service.LoginService;
 | 
			
		||||
import top.continew.starter.core.exception.BadRequestException;
 | 
			
		||||
import top.continew.starter.core.util.validate.ValidationUtils;
 | 
			
		||||
import top.continew.starter.log.core.annotation.Log;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 三方账号认证 API
 | 
			
		||||
@@ -58,17 +57,17 @@ public class SocialAuthController {
 | 
			
		||||
    @Operation(summary = "三方账号登录授权", description = "三方账号登录授权")
 | 
			
		||||
    @Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
 | 
			
		||||
    @GetMapping("/{source}")
 | 
			
		||||
    public R<SocialAuthAuthorizeResp> authorize(@PathVariable String source) {
 | 
			
		||||
    public SocialAuthAuthorizeResp authorize(@PathVariable String source) {
 | 
			
		||||
        AuthRequest authRequest = this.getAuthRequest(source);
 | 
			
		||||
        return R.ok(SocialAuthAuthorizeResp.builder()
 | 
			
		||||
        return SocialAuthAuthorizeResp.builder()
 | 
			
		||||
            .authorizeUrl(authRequest.authorize(AuthStateUtils.createState()))
 | 
			
		||||
            .build());
 | 
			
		||||
            .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "三方账号登录", description = "三方账号登录")
 | 
			
		||||
    @Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
 | 
			
		||||
    @PostMapping("/{source}")
 | 
			
		||||
    public R<LoginResp> login(@PathVariable String source, @RequestBody AuthCallback callback) {
 | 
			
		||||
    public LoginResp login(@PathVariable String source, @RequestBody AuthCallback callback) {
 | 
			
		||||
        if (StpUtil.isLogin()) {
 | 
			
		||||
            StpUtil.logout();
 | 
			
		||||
        }
 | 
			
		||||
@@ -77,7 +76,7 @@ public class SocialAuthController {
 | 
			
		||||
        ValidationUtils.throwIf(!response.ok(), response.getMsg());
 | 
			
		||||
        AuthUser authUser = response.getData();
 | 
			
		||||
        String token = loginService.socialLogin(authUser);
 | 
			
		||||
        return R.ok(LoginResp.builder().token(token).build());
 | 
			
		||||
        return LoginResp.builder().token(token).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private AuthRequest getAuthRequest(String source) {
 | 
			
		||||
 
 | 
			
		||||
@@ -90,33 +90,33 @@ public class CaptchaController {
 | 
			
		||||
    @Log(ignore = true)
 | 
			
		||||
    @Operation(summary = "获取行为验证码", description = "获取行为验证码(Base64编码)")
 | 
			
		||||
    @GetMapping("/behavior")
 | 
			
		||||
    public R<Object> getBehaviorCaptcha(CaptchaVO captchaReq, HttpServletRequest request) {
 | 
			
		||||
    public Object getBehaviorCaptcha(CaptchaVO captchaReq, HttpServletRequest request) {
 | 
			
		||||
        captchaReq.setBrowserInfo(JakartaServletUtil.getClientIP(request) + request.getHeader(HttpHeaders.USER_AGENT));
 | 
			
		||||
        ResponseModel responseModel = behaviorCaptchaService.get(captchaReq);
 | 
			
		||||
        CheckUtils.throwIf(() -> !StrUtil.equals(RepCodeEnum.SUCCESS.getCode(), responseModel
 | 
			
		||||
            .getRepCode()), responseModel.getRepMsg());
 | 
			
		||||
        return R.ok(responseModel.getRepData());
 | 
			
		||||
        return responseModel.getRepData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Log(ignore = true)
 | 
			
		||||
    @Operation(summary = "校验行为验证码", description = "校验行为验证码")
 | 
			
		||||
    @PostMapping("/behavior")
 | 
			
		||||
    public R<Object> checkBehaviorCaptcha(@RequestBody CaptchaVO captchaReq, HttpServletRequest request) {
 | 
			
		||||
    public Object checkBehaviorCaptcha(@RequestBody CaptchaVO captchaReq, HttpServletRequest request) {
 | 
			
		||||
        captchaReq.setBrowserInfo(JakartaServletUtil.getClientIP(request) + request.getHeader(HttpHeaders.USER_AGENT));
 | 
			
		||||
        return R.ok(behaviorCaptchaService.check(captchaReq));
 | 
			
		||||
        return behaviorCaptchaService.check(captchaReq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Log(ignore = true)
 | 
			
		||||
    @Operation(summary = "获取图片验证码", description = "获取图片验证码(Base64编码,带图片格式:data:image/gif;base64)")
 | 
			
		||||
    @GetMapping("/image")
 | 
			
		||||
    public R<CaptchaResp> getImageCaptcha() {
 | 
			
		||||
    public CaptchaResp getImageCaptcha() {
 | 
			
		||||
        String uuid = IdUtil.fastUUID();
 | 
			
		||||
        String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + uuid;
 | 
			
		||||
        Captcha captcha = graphicCaptchaService.getCaptcha();
 | 
			
		||||
        long expireTime = LocalDateTimeUtil.toEpochMilli(LocalDateTime.now()
 | 
			
		||||
            .plusMinutes(captchaProperties.getExpirationInMinutes()));
 | 
			
		||||
        RedisUtils.set(captchaKey, captcha.text(), Duration.ofMinutes(captchaProperties.getExpirationInMinutes()));
 | 
			
		||||
        return R.ok(CaptchaResp.builder().uuid(uuid).img(captcha.toBase64()).expireTime(expireTime).build());
 | 
			
		||||
        return CaptchaResp.builder().uuid(uuid).img(captcha.toBase64()).expireTime(expireTime).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -140,7 +140,7 @@ public class CaptchaController {
 | 
			
		||||
        @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "DAY'", key = "#email + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.mail.templatePath')", rate = 20, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
 | 
			
		||||
        @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#email", rate = 100, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
 | 
			
		||||
        @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#email", rate = 30, interval = 1, unit = RateIntervalUnit.MINUTES, type = LimitType.IP, message = "获取验证码操作太频繁,请稍后再试")})
 | 
			
		||||
    public R<Void> getMailCaptcha(@NotBlank(message = "邮箱不能为空") @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") String email) throws MessagingException {
 | 
			
		||||
    public R getMailCaptcha(@NotBlank(message = "邮箱不能为空") @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") String email) throws MessagingException {
 | 
			
		||||
        // 生成验证码
 | 
			
		||||
        CaptchaProperties.CaptchaMail captchaMail = captchaProperties.getMail();
 | 
			
		||||
        String captcha = RandomUtil.randomNumbers(captchaMail.getLength());
 | 
			
		||||
@@ -182,7 +182,7 @@ public class CaptchaController {
 | 
			
		||||
        @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "DAY'", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 20, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
 | 
			
		||||
        @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 100, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
 | 
			
		||||
        @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 30, interval = 1, unit = RateIntervalUnit.MINUTES, type = LimitType.IP, message = "获取验证码操作太频繁,请稍后再试")})
 | 
			
		||||
    public R<Void> getSmsCaptcha(@NotBlank(message = "手机号不能为空") @Pattern(regexp = RegexPool.MOBILE, message = "手机号格式错误") String phone,
 | 
			
		||||
    public R getSmsCaptcha(@NotBlank(message = "手机号不能为空") @Pattern(regexp = RegexPool.MOBILE, message = "手机号格式错误") String phone,
 | 
			
		||||
                           CaptchaVO captchaReq) {
 | 
			
		||||
        // 行为验证码校验
 | 
			
		||||
        ResponseModel verificationRes = behaviorCaptchaService.verification(captchaReq);
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,6 @@ import top.continew.starter.core.util.validate.ValidationUtils;
 | 
			
		||||
import top.continew.starter.extension.crud.model.query.SortQuery;
 | 
			
		||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
 | 
			
		||||
import top.continew.starter.log.core.annotation.Log;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -71,49 +70,49 @@ public class CommonController {
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "上传文件", description = "上传文件")
 | 
			
		||||
    @PostMapping("/file")
 | 
			
		||||
    public R<FileUploadResp> upload(@NotNull(message = "文件不能为空") MultipartFile file) {
 | 
			
		||||
    public FileUploadResp upload(@NotNull(message = "文件不能为空") MultipartFile file) {
 | 
			
		||||
        ValidationUtils.throwIf(projectProperties.isProduction(), "演示环境不支持上传文件");
 | 
			
		||||
        ValidationUtils.throwIf(file::isEmpty, "文件不能为空");
 | 
			
		||||
        FileInfo fileInfo = fileService.upload(file);
 | 
			
		||||
        return R.ok(FileUploadResp.builder().url(fileInfo.getUrl()).build());
 | 
			
		||||
        return FileUploadResp.builder().url(fileInfo.getUrl()).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询部门树", description = "查询树结构的部门列表")
 | 
			
		||||
    @GetMapping("/tree/dept")
 | 
			
		||||
    public R<List<Tree<Long>>> listDeptTree(DeptQuery query, SortQuery sortQuery) {
 | 
			
		||||
        return R.ok(deptService.tree(query, sortQuery, true));
 | 
			
		||||
    public List<Tree<Long>> listDeptTree(DeptQuery query, SortQuery sortQuery) {
 | 
			
		||||
        return deptService.tree(query, sortQuery, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询菜单树", description = "查询树结构的菜单列表")
 | 
			
		||||
    @GetMapping("/tree/menu")
 | 
			
		||||
    public R<List<Tree<Long>>> listMenuTree(MenuQuery query, SortQuery sortQuery) {
 | 
			
		||||
        return R.ok(menuService.tree(query, sortQuery, true));
 | 
			
		||||
    public List<Tree<Long>> listMenuTree(MenuQuery query, SortQuery sortQuery) {
 | 
			
		||||
        return menuService.tree(query, sortQuery, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询角色字典", description = "查询角色字典列表")
 | 
			
		||||
    @GetMapping("/dict/role")
 | 
			
		||||
    public R<List<LabelValueResp>> listRoleDict(RoleQuery query, SortQuery sortQuery) {
 | 
			
		||||
        return R.ok(roleService.listDict(query, sortQuery));
 | 
			
		||||
    public List<LabelValueResp> listRoleDict(RoleQuery query, SortQuery sortQuery) {
 | 
			
		||||
        return roleService.listDict(query, sortQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询字典", description = "查询字典列表")
 | 
			
		||||
    @Parameter(name = "code", description = "字典编码", example = "notice_type", in = ParameterIn.PATH)
 | 
			
		||||
    @GetMapping("/dict/{code}")
 | 
			
		||||
    public R<List<LabelValueResp>> listDict(@PathVariable String code) {
 | 
			
		||||
        return R.ok(dictItemService.listByDictCode(code));
 | 
			
		||||
    public List<LabelValueResp> listDict(@PathVariable String code) {
 | 
			
		||||
        return dictItemService.listByDictCode(code);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SaIgnore
 | 
			
		||||
    @Operation(summary = "查询参数字典", description = "查询参数字典")
 | 
			
		||||
    @GetMapping("/dict/option")
 | 
			
		||||
    @Cached(key = "#category", name = CacheConstants.OPTION_KEY_PREFIX)
 | 
			
		||||
    public R<List<LabelValueResp<String>>> listOptionDict(@NotBlank(message = "类别不能为空") @RequestParam String category) {
 | 
			
		||||
    public List<LabelValueResp<String>> listOptionDict(@NotBlank(message = "类别不能为空") String category) {
 | 
			
		||||
        OptionQuery optionQuery = new OptionQuery();
 | 
			
		||||
        optionQuery.setCategory(category);
 | 
			
		||||
        return R.ok(optionService.list(optionQuery)
 | 
			
		||||
        return optionService.list(optionQuery)
 | 
			
		||||
            .stream()
 | 
			
		||||
            .map(option -> new LabelValueResp<>(option.getCode(), StrUtil.nullToDefault(option.getValue(), option
 | 
			
		||||
                .getDefaultValue())))
 | 
			
		||||
            .toList());
 | 
			
		||||
            .toList();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,6 @@ import top.continew.admin.system.model.resp.DashboardTotalResp;
 | 
			
		||||
import top.continew.admin.system.service.DashboardService;
 | 
			
		||||
import top.continew.admin.system.model.resp.DashboardNoticeResp;
 | 
			
		||||
import top.continew.starter.core.util.validate.ValidationUtils;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
import top.continew.starter.log.core.annotation.Log;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -61,8 +60,8 @@ public class DashboardController {
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询总计信息", description = "查询总计信息")
 | 
			
		||||
    @GetMapping("/total")
 | 
			
		||||
    public R<DashboardTotalResp> getTotal() {
 | 
			
		||||
        return R.ok(dashboardService.getTotal());
 | 
			
		||||
    public DashboardTotalResp getTotal() {
 | 
			
		||||
        return dashboardService.getTotal();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询访问趋势信息", description = "查询访问趋势信息")
 | 
			
		||||
@@ -71,26 +70,26 @@ public class DashboardController {
 | 
			
		||||
    @CachePenetrationProtect
 | 
			
		||||
    @CacheRefresh(refresh = 7200)
 | 
			
		||||
    @Cached(key = "#days", name = CacheConstants.DASHBOARD_KEY_PREFIX, cacheType = CacheType.BOTH, syncLocal = true)
 | 
			
		||||
    public R<List<DashboardAccessTrendResp>> listAccessTrend(@PathVariable Integer days) {
 | 
			
		||||
    public List<DashboardAccessTrendResp> listAccessTrend(@PathVariable Integer days) {
 | 
			
		||||
        ValidationUtils.throwIf(7 != days && 30 != days, "仅支持查询近 7/30 天访问趋势信息");
 | 
			
		||||
        return R.ok(dashboardService.listAccessTrend(days));
 | 
			
		||||
        return dashboardService.listAccessTrend(days);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询热门模块列表", description = "查询热门模块列表")
 | 
			
		||||
    @GetMapping("/popular/module")
 | 
			
		||||
    public R<List<DashboardPopularModuleResp>> listPopularModule() {
 | 
			
		||||
        return R.ok(dashboardService.listPopularModule());
 | 
			
		||||
    public List<DashboardPopularModuleResp> listPopularModule() {
 | 
			
		||||
        return dashboardService.listPopularModule();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询访客地域分布信息", description = "查询访客地域分布信息")
 | 
			
		||||
    @GetMapping("/geo/distribution")
 | 
			
		||||
    public R<DashboardGeoDistributionResp> getGeoDistribution() {
 | 
			
		||||
        return R.ok(dashboardService.getGeoDistribution());
 | 
			
		||||
    public DashboardGeoDistributionResp getGeoDistribution() {
 | 
			
		||||
        return dashboardService.getGeoDistribution();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询公告列表", description = "查询公告列表")
 | 
			
		||||
    @GetMapping("/notice")
 | 
			
		||||
    public R<List<DashboardNoticeResp>> listNotice() {
 | 
			
		||||
        return R.ok(dashboardService.listNotice());
 | 
			
		||||
    public List<DashboardNoticeResp> listNotice() {
 | 
			
		||||
        return dashboardService.listNotice();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,6 @@ import top.continew.admin.auth.service.OnlineUserService;
 | 
			
		||||
import top.continew.starter.core.util.validate.CheckUtils;
 | 
			
		||||
import top.continew.starter.extension.crud.model.query.PageQuery;
 | 
			
		||||
import top.continew.starter.extension.crud.model.resp.PageResp;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 在线用户 API
 | 
			
		||||
@@ -54,18 +53,17 @@ public class OnlineUserController {
 | 
			
		||||
    @Operation(summary = "分页查询列表", description = "分页查询列表")
 | 
			
		||||
    @SaCheckPermission("monitor:online:list")
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    public R<PageResp<OnlineUserResp>> page(OnlineUserQuery query, @Validated PageQuery pageQuery) {
 | 
			
		||||
        return R.ok(baseService.page(query, pageQuery));
 | 
			
		||||
    public PageResp<OnlineUserResp> page(OnlineUserQuery query, @Validated PageQuery pageQuery) {
 | 
			
		||||
        return baseService.page(query, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "强退在线用户", description = "强退在线用户")
 | 
			
		||||
    @Parameter(name = "token", description = "令牌", example = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiTUd6djdyOVFoeHEwdVFqdFAzV3M5YjVJRzh4YjZPSEUifQ.7q7U3ouoN7WPhH2kUEM7vPe5KF3G_qavSG-vRgIxKvE", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("monitor:online:kickout")
 | 
			
		||||
    @DeleteMapping("/{token}")
 | 
			
		||||
    public R<Void> kickout(@PathVariable String token) {
 | 
			
		||||
    public void kickout(@PathVariable String token) {
 | 
			
		||||
        String currentToken = StpUtil.getTokenValue();
 | 
			
		||||
        CheckUtils.throwIfEqual(token, currentToken, "不能强退自己");
 | 
			
		||||
        StpUtil.kickoutByTokenValue(token);
 | 
			
		||||
        return R.ok("强退成功");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@ import top.continew.admin.job.service.JobService;
 | 
			
		||||
import top.continew.starter.extension.crud.model.resp.PageResp;
 | 
			
		||||
import top.continew.starter.extension.crud.util.ValidateGroup;
 | 
			
		||||
import top.continew.starter.log.core.annotation.Log;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -55,54 +54,53 @@ public class JobController {
 | 
			
		||||
    @Operation(summary = "分页查询任务列表", description = "分页查询任务列表")
 | 
			
		||||
    @SaCheckPermission("schedule:job:list")
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    public R<PageResp<JobResp>> page(JobQuery query) {
 | 
			
		||||
        return R.ok(baseService.page(query));
 | 
			
		||||
    public PageResp<JobResp> page(JobQuery query) {
 | 
			
		||||
        return baseService.page(query);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "新增任务", description = "新增任务")
 | 
			
		||||
    @SaCheckPermission("schedule:job:add")
 | 
			
		||||
    @PostMapping
 | 
			
		||||
    public R<Void> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody JobReq req) {
 | 
			
		||||
        return baseService.add(req) ? R.ok() : R.fail();
 | 
			
		||||
    public void add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody JobReq req) {
 | 
			
		||||
        baseService.add(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "修改任务", description = "修改任务")
 | 
			
		||||
    @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("schedule:job:update")
 | 
			
		||||
    @PutMapping("/{id}")
 | 
			
		||||
    public R<Void> update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody JobReq req, @PathVariable Long id) {
 | 
			
		||||
        return baseService.update(req, id) ? R.ok() : R.fail();
 | 
			
		||||
    public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody JobReq req, @PathVariable Long id) {
 | 
			
		||||
        baseService.update(req, id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "修改任务状态", description = "修改任务状态")
 | 
			
		||||
    @SaCheckPermission("schedule:job:update")
 | 
			
		||||
    @PatchMapping("/{id}/status")
 | 
			
		||||
    public R<Void> updateStatus(@Validated @RequestBody JobStatusReq req, @PathVariable Long id) {
 | 
			
		||||
        return baseService.updateStatus(req, id) ? R.ok() : R.fail();
 | 
			
		||||
    public void updateStatus(@Validated @RequestBody JobStatusReq req, @PathVariable Long id) {
 | 
			
		||||
        baseService.updateStatus(req, id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "删除任务", description = "删除任务")
 | 
			
		||||
    @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("schedule:job:delete")
 | 
			
		||||
    @DeleteMapping("/{id}")
 | 
			
		||||
    public R<Void> delete(@PathVariable Long id) {
 | 
			
		||||
        return baseService.delete(id) ? R.ok() : R.fail();
 | 
			
		||||
    public void delete(@PathVariable Long id) {
 | 
			
		||||
        baseService.delete(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "执行任务", description = "执行任务")
 | 
			
		||||
    @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("schedule:job:trigger")
 | 
			
		||||
    @PostMapping("/trigger/{id}")
 | 
			
		||||
    public R<Void> trigger(@PathVariable Long id) {
 | 
			
		||||
        return baseService.trigger(id) ? R.ok() : R.fail();
 | 
			
		||||
    public void trigger(@PathVariable Long id) {
 | 
			
		||||
        baseService.trigger(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Log(ignore = true)
 | 
			
		||||
    @Operation(summary = "查询任务分组列表", description = "查询任务分组列表")
 | 
			
		||||
    @SaCheckPermission("schedule:job:list")
 | 
			
		||||
    @GetMapping("/group")
 | 
			
		||||
    public R<List<String>> listGroup() {
 | 
			
		||||
        List<String> groupList = baseService.listGroup();
 | 
			
		||||
        return R.ok(groupList);
 | 
			
		||||
    public List<String> listGroup() {
 | 
			
		||||
        return baseService.listGroup();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@ import top.continew.admin.job.model.resp.JobLogResp;
 | 
			
		||||
import top.continew.admin.job.model.resp.JobInstanceResp;
 | 
			
		||||
import top.continew.admin.job.service.JobLogService;
 | 
			
		||||
import top.continew.starter.extension.crud.model.resp.PageResp;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -55,37 +54,37 @@ public class JobLogController {
 | 
			
		||||
    @Operation(summary = "分页查询任务日志列表", description = "分页查询任务日志列表")
 | 
			
		||||
    @SaCheckPermission("schedule:log:list")
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    public R<PageResp<JobLogResp>> page(JobLogQuery query) {
 | 
			
		||||
        return R.ok(baseService.page(query));
 | 
			
		||||
    public PageResp<JobLogResp> page(JobLogQuery query) {
 | 
			
		||||
        return baseService.page(query);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "停止任务", description = "停止任务")
 | 
			
		||||
    @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("schedule:log:stop")
 | 
			
		||||
    @PostMapping("/stop/{id}")
 | 
			
		||||
    public R<Void> stop(@PathVariable Long id) {
 | 
			
		||||
        return baseService.stop(id) ? R.ok() : R.fail();
 | 
			
		||||
    public void stop(@PathVariable Long id) {
 | 
			
		||||
        baseService.stop(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "重试任务", description = "重试任务")
 | 
			
		||||
    @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("schedule:log:retry")
 | 
			
		||||
    @PostMapping("/retry/{id}")
 | 
			
		||||
    public R<Void> retry(@PathVariable Long id) {
 | 
			
		||||
        return baseService.retry(id) ? R.ok() : R.fail();
 | 
			
		||||
    public void retry(@PathVariable Long id) {
 | 
			
		||||
        baseService.retry(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询任务实例列表", description = "查询任务实例列表")
 | 
			
		||||
    @SaCheckPermission("schedule:log:list")
 | 
			
		||||
    @GetMapping("/instance")
 | 
			
		||||
    public R<List<JobInstanceResp>> listInstance(JobInstanceQuery query) {
 | 
			
		||||
        return R.ok(baseService.listInstance(query));
 | 
			
		||||
    public List<JobInstanceResp> listInstance(JobInstanceQuery query) {
 | 
			
		||||
        return baseService.listInstance(query);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "分页查询任务实例日志列表", description = "分页查询任务实例日志列表")
 | 
			
		||||
    @SaCheckPermission("schedule:log:list")
 | 
			
		||||
    @GetMapping("/instance/log")
 | 
			
		||||
    public R<JobInstanceLogPageResult> pageInstanceLog(JobInstanceLogQuery query) {
 | 
			
		||||
        return R.ok(baseService.pageInstanceLog(query));
 | 
			
		||||
    public JobInstanceLogPageResult pageInstanceLog(JobInstanceLogQuery query) {
 | 
			
		||||
        return baseService.pageInstanceLog(query);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@ import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
 | 
			
		||||
import top.continew.starter.extension.crud.controller.BaseController;
 | 
			
		||||
import top.continew.starter.extension.crud.enums.Api;
 | 
			
		||||
import top.continew.starter.log.core.annotation.Log;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 文件管理 API
 | 
			
		||||
@@ -48,7 +47,7 @@ public class FileController extends BaseController<FileService, FileResp, FileRe
 | 
			
		||||
    @Operation(summary = "查询文件资源统计", description = "查询文件资源统计")
 | 
			
		||||
    @SaCheckPermission("system:file:list")
 | 
			
		||||
    @GetMapping("/statistics")
 | 
			
		||||
    public R<FileStatisticsResp> statistics() {
 | 
			
		||||
        return R.ok(baseService.statistics());
 | 
			
		||||
    public FileStatisticsResp statistics() {
 | 
			
		||||
        return baseService.statistics();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
package top.continew.admin.controller.system;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
			
		||||
import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
			
		||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
 | 
			
		||||
@@ -35,7 +36,6 @@ import top.continew.admin.system.service.LogService;
 | 
			
		||||
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.web.model.R;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 系统日志 API
 | 
			
		||||
@@ -54,18 +54,19 @@ public class LogController {
 | 
			
		||||
    @Operation(summary = "分页查询列表", description = "分页查询列表")
 | 
			
		||||
    @SaCheckPermission("monitor:log:list")
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    public R<PageResp<LogResp>> page(LogQuery query, @Validated PageQuery pageQuery) {
 | 
			
		||||
        return R.ok(baseService.page(query, pageQuery));
 | 
			
		||||
    public PageResp<LogResp> page(LogQuery query, @Validated PageQuery pageQuery) {
 | 
			
		||||
        return baseService.page(query, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询详情", description = "查询详情")
 | 
			
		||||
    @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("monitor:log:list")
 | 
			
		||||
    @GetMapping("/{id}")
 | 
			
		||||
    public R<LogDetailResp> get(@PathVariable Long id) {
 | 
			
		||||
        return R.ok(baseService.get(id));
 | 
			
		||||
    public LogDetailResp get(@PathVariable Long id) {
 | 
			
		||||
        return baseService.get(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExcludeFromGracefulResponse
 | 
			
		||||
    @Operation(summary = "导出登录日志", description = "导出登录日志")
 | 
			
		||||
    @SaCheckPermission("monitor:log:export")
 | 
			
		||||
    @GetMapping("/export/login")
 | 
			
		||||
@@ -73,6 +74,7 @@ public class LogController {
 | 
			
		||||
        baseService.exportLoginLog(query, sortQuery, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ExcludeFromGracefulResponse
 | 
			
		||||
    @Operation(summary = "导出操作日志", description = "导出操作日志")
 | 
			
		||||
    @SaCheckPermission("monitor:log:export")
 | 
			
		||||
    @GetMapping("/export/operation")
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,8 @@ import top.continew.starter.core.util.validate.ValidationUtils;
 | 
			
		||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
 | 
			
		||||
import top.continew.starter.extension.crud.controller.BaseController;
 | 
			
		||||
import top.continew.starter.extension.crud.enums.Api;
 | 
			
		||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
 | 
			
		||||
import top.continew.starter.extension.crud.util.ValidateGroup;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 菜单管理 API
 | 
			
		||||
@@ -48,15 +48,15 @@ import top.continew.starter.web.model.R;
 | 
			
		||||
public class MenuController extends BaseController<MenuService, MenuResp, MenuResp, MenuQuery, MenuReq> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public R<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody MenuReq req) {
 | 
			
		||||
    public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody MenuReq req) {
 | 
			
		||||
        this.checkPath(req);
 | 
			
		||||
        return super.add(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public R<Void> update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody MenuReq req, @PathVariable Long id) {
 | 
			
		||||
    public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody MenuReq req, @PathVariable Long id) {
 | 
			
		||||
        this.checkPath(req);
 | 
			
		||||
        return super.update(req, id);
 | 
			
		||||
        super.update(req, id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@ import top.continew.admin.system.service.MessageUserService;
 | 
			
		||||
import top.continew.starter.extension.crud.model.query.PageQuery;
 | 
			
		||||
import top.continew.starter.extension.crud.model.resp.PageResp;
 | 
			
		||||
import top.continew.starter.log.core.annotation.Log;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -53,32 +52,30 @@ public class MessageController {
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "分页查询列表", description = "分页查询列表")
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    public R<PageResp<MessageResp>> page(MessageQuery query, @Validated PageQuery pageQuery) {
 | 
			
		||||
    public PageResp<MessageResp> page(MessageQuery query, @Validated PageQuery pageQuery) {
 | 
			
		||||
        query.setUserId(LoginHelper.getUserId());
 | 
			
		||||
        return R.ok(baseService.page(query, pageQuery));
 | 
			
		||||
        return baseService.page(query, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "删除数据", description = "删除数据")
 | 
			
		||||
    @Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
 | 
			
		||||
    @DeleteMapping("/{ids}")
 | 
			
		||||
    public R<Void> delete(@PathVariable List<Long> ids) {
 | 
			
		||||
    public void delete(@PathVariable List<Long> ids) {
 | 
			
		||||
        baseService.delete(ids);
 | 
			
		||||
        return R.ok("删除成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "标记已读", description = "将消息标记为已读状态")
 | 
			
		||||
    @Parameter(name = "ids", description = "消息ID列表", example = "1,2", in = ParameterIn.QUERY)
 | 
			
		||||
    @PatchMapping("/read")
 | 
			
		||||
    public R<Void> readMessage(@RequestParam(required = false) List<Long> ids) {
 | 
			
		||||
    public void readMessage(@RequestParam(required = false) List<Long> ids) {
 | 
			
		||||
        messageUserService.readMessage(ids);
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Log(ignore = true)
 | 
			
		||||
    @Operation(summary = "查询未读消息数量", description = "查询当前用户的未读消息数量")
 | 
			
		||||
    @Parameter(name = "isDetail", description = "是否查询详情", example = "true", in = ParameterIn.QUERY)
 | 
			
		||||
    @GetMapping("/unread")
 | 
			
		||||
    public R<MessageUnreadResp> countUnreadMessage(@RequestParam(required = false) Boolean detail) {
 | 
			
		||||
        return R.ok(messageUserService.countUnreadMessageByUserId(LoginHelper.getUserId(), detail));
 | 
			
		||||
    public MessageUnreadResp countUnreadMessage(@RequestParam(required = false) Boolean detail) {
 | 
			
		||||
        return messageUserService.countUnreadMessageByUserId(LoginHelper.getUserId(), detail);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -30,8 +30,8 @@ import top.continew.starter.core.util.validate.ValidationUtils;
 | 
			
		||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
 | 
			
		||||
import top.continew.starter.extension.crud.controller.BaseController;
 | 
			
		||||
import top.continew.starter.extension.crud.enums.Api;
 | 
			
		||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
 | 
			
		||||
import top.continew.starter.extension.crud.util.ValidateGroup;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
 | 
			
		||||
@@ -47,16 +47,15 @@ import java.time.LocalDateTime;
 | 
			
		||||
public class NoticeController extends BaseController<NoticeService, NoticeResp, NoticeDetailResp, NoticeQuery, NoticeReq> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public R<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody NoticeReq req) {
 | 
			
		||||
    public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody NoticeReq req) {
 | 
			
		||||
        this.checkTime(req);
 | 
			
		||||
        return super.add(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public R<Void> update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody NoticeReq req,
 | 
			
		||||
                          @PathVariable Long id) {
 | 
			
		||||
    public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody NoticeReq req, @PathVariable Long id) {
 | 
			
		||||
        this.checkTime(req);
 | 
			
		||||
        return super.update(req, id);
 | 
			
		||||
        super.update(req, id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,6 @@ import top.continew.admin.system.model.req.OptionReq;
 | 
			
		||||
import top.continew.admin.system.model.req.OptionResetValueReq;
 | 
			
		||||
import top.continew.admin.system.model.resp.OptionResp;
 | 
			
		||||
import top.continew.admin.system.service.OptionService;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -50,23 +49,21 @@ public class OptionController {
 | 
			
		||||
    @Operation(summary = "查询参数列表", description = "查询参数列表")
 | 
			
		||||
    @SaCheckPermission("system:config:list")
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    public R<List<OptionResp>> list(@Validated OptionQuery query) {
 | 
			
		||||
        return R.ok(baseService.list(query));
 | 
			
		||||
    public List<OptionResp> list(@Validated OptionQuery query) {
 | 
			
		||||
        return baseService.list(query);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "修改参数", description = "修改参数")
 | 
			
		||||
    @SaCheckPermission("system:config:update")
 | 
			
		||||
    @PutMapping
 | 
			
		||||
    public R<Void> update(@Valid @RequestBody List<OptionReq> options) {
 | 
			
		||||
    public void update(@Valid @RequestBody List<OptionReq> options) {
 | 
			
		||||
        baseService.update(options);
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "重置参数", description = "重置参数")
 | 
			
		||||
    @SaCheckPermission("system:config:reset")
 | 
			
		||||
    @PatchMapping("/value")
 | 
			
		||||
    public R<Void> resetValue(@Validated @RequestBody OptionResetValueReq req) {
 | 
			
		||||
    public void resetValue(@Validated @RequestBody OptionResetValueReq req) {
 | 
			
		||||
        baseService.resetValue(req);
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -31,9 +31,9 @@ import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
import top.continew.admin.common.constant.CacheConstants;
 | 
			
		||||
import top.continew.admin.system.enums.SocialSourceEnum;
 | 
			
		||||
import top.continew.admin.common.util.SecureUtils;
 | 
			
		||||
import top.continew.admin.common.util.helper.LoginHelper;
 | 
			
		||||
import top.continew.admin.system.enums.SocialSourceEnum;
 | 
			
		||||
import top.continew.admin.system.model.entity.UserSocialDO;
 | 
			
		||||
import top.continew.admin.system.model.req.UserBasicInfoUpdateReq;
 | 
			
		||||
import top.continew.admin.system.model.req.UserEmailUpdateRequest;
 | 
			
		||||
@@ -46,7 +46,6 @@ import top.continew.admin.system.service.UserSocialService;
 | 
			
		||||
import top.continew.starter.cache.redisson.util.RedisUtils;
 | 
			
		||||
import top.continew.starter.core.util.ExceptionUtils;
 | 
			
		||||
import top.continew.starter.core.util.validate.ValidationUtils;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -72,22 +71,21 @@ public class UserCenterController {
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "修改头像", description = "用户修改个人头像")
 | 
			
		||||
    @PostMapping("/avatar")
 | 
			
		||||
    public R<AvatarResp> updateAvatar(@NotNull(message = "头像不能为空") MultipartFile avatarFile) throws IOException {
 | 
			
		||||
    public AvatarResp updateAvatar(@NotNull(message = "头像不能为空") MultipartFile avatarFile) throws IOException {
 | 
			
		||||
        ValidationUtils.throwIf(avatarFile::isEmpty, "头像不能为空");
 | 
			
		||||
        String newAvatar = userService.updateAvatar(avatarFile, LoginHelper.getUserId());
 | 
			
		||||
        return R.ok("修改成功", AvatarResp.builder().avatar(newAvatar).build());
 | 
			
		||||
        return AvatarResp.builder().avatar(newAvatar).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "修改基础信息", description = "修改用户基础信息")
 | 
			
		||||
    @PatchMapping("/basic/info")
 | 
			
		||||
    public R<Void> updateBasicInfo(@Validated @RequestBody UserBasicInfoUpdateReq req) {
 | 
			
		||||
    public void updateBasicInfo(@Validated @RequestBody UserBasicInfoUpdateReq req) {
 | 
			
		||||
        userService.updateBasicInfo(req, LoginHelper.getUserId());
 | 
			
		||||
        return R.ok("修改成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "修改密码", description = "修改用户登录密码")
 | 
			
		||||
    @PatchMapping("/password")
 | 
			
		||||
    public R<Void> updatePassword(@Validated @RequestBody UserPasswordUpdateReq updateReq) {
 | 
			
		||||
    public void updatePassword(@Validated @RequestBody UserPasswordUpdateReq updateReq) {
 | 
			
		||||
        String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
 | 
			
		||||
            .getOldPassword()));
 | 
			
		||||
        ValidationUtils.throwIfNull(rawOldPassword, DECRYPT_FAILED);
 | 
			
		||||
@@ -95,12 +93,11 @@ public class UserCenterController {
 | 
			
		||||
            .getNewPassword()));
 | 
			
		||||
        ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败");
 | 
			
		||||
        userService.updatePassword(rawOldPassword, rawNewPassword, LoginHelper.getUserId());
 | 
			
		||||
        return R.ok("修改成功,请牢记你的新密码");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "修改手机号", description = "修改手机号")
 | 
			
		||||
    @PatchMapping("/phone")
 | 
			
		||||
    public R<Void> updatePhone(@Validated @RequestBody UserPhoneUpdateReq updateReq) {
 | 
			
		||||
    public void updatePhone(@Validated @RequestBody UserPhoneUpdateReq updateReq) {
 | 
			
		||||
        String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
 | 
			
		||||
            .getOldPassword()));
 | 
			
		||||
        ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED);
 | 
			
		||||
@@ -110,12 +107,11 @@ public class UserCenterController {
 | 
			
		||||
        ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误");
 | 
			
		||||
        RedisUtils.delete(captchaKey);
 | 
			
		||||
        userService.updatePhone(updateReq.getPhone(), rawOldPassword, LoginHelper.getUserId());
 | 
			
		||||
        return R.ok("修改成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "修改邮箱", description = "修改用户邮箱")
 | 
			
		||||
    @PatchMapping("/email")
 | 
			
		||||
    public R<Void> updateEmail(@Validated @RequestBody UserEmailUpdateRequest updateReq) {
 | 
			
		||||
    public void updateEmail(@Validated @RequestBody UserEmailUpdateRequest updateReq) {
 | 
			
		||||
        String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
 | 
			
		||||
            .getOldPassword()));
 | 
			
		||||
        ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED);
 | 
			
		||||
@@ -125,40 +121,36 @@ public class UserCenterController {
 | 
			
		||||
        ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误");
 | 
			
		||||
        RedisUtils.delete(captchaKey);
 | 
			
		||||
        userService.updateEmail(updateReq.getEmail(), rawOldPassword, LoginHelper.getUserId());
 | 
			
		||||
        return R.ok("修改成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询绑定的三方账号", description = "查询绑定的三方账号")
 | 
			
		||||
    @GetMapping("/social")
 | 
			
		||||
    public R<List<UserSocialBindResp>> listSocialBind() {
 | 
			
		||||
    public List<UserSocialBindResp> listSocialBind() {
 | 
			
		||||
        List<UserSocialDO> userSocialList = userSocialService.listByUserId(LoginHelper.getUserId());
 | 
			
		||||
        List<UserSocialBindResp> userSocialBindList = userSocialList.stream().map(userSocial -> {
 | 
			
		||||
        return userSocialList.stream().map(userSocial -> {
 | 
			
		||||
            String source = userSocial.getSource();
 | 
			
		||||
            UserSocialBindResp userSocialBind = new UserSocialBindResp();
 | 
			
		||||
            userSocialBind.setSource(source);
 | 
			
		||||
            userSocialBind.setDescription(SocialSourceEnum.valueOf(source).getDescription());
 | 
			
		||||
            return userSocialBind;
 | 
			
		||||
        }).toList();
 | 
			
		||||
        return R.ok(userSocialBindList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "绑定三方账号", description = "绑定三方账号")
 | 
			
		||||
    @Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
 | 
			
		||||
    @PostMapping("/social/{source}")
 | 
			
		||||
    public R<Void> bindSocial(@PathVariable String source, @RequestBody AuthCallback callback) {
 | 
			
		||||
    public void bindSocial(@PathVariable String source, @RequestBody AuthCallback callback) {
 | 
			
		||||
        AuthRequest authRequest = authRequestFactory.get(source);
 | 
			
		||||
        AuthResponse<AuthUser> response = authRequest.login(callback);
 | 
			
		||||
        ValidationUtils.throwIf(!response.ok(), response.getMsg());
 | 
			
		||||
        AuthUser authUser = response.getData();
 | 
			
		||||
        userSocialService.bind(authUser, LoginHelper.getUserId());
 | 
			
		||||
        return R.ok("绑定成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "解绑三方账号", description = "解绑三方账号")
 | 
			
		||||
    @Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH)
 | 
			
		||||
    @DeleteMapping("/social/{source}")
 | 
			
		||||
    public R<Void> unbindSocial(@PathVariable String source) {
 | 
			
		||||
    public void unbindSocial(@PathVariable String source) {
 | 
			
		||||
        userSocialService.deleteBySourceAndUserId(source, LoginHelper.getUserId());
 | 
			
		||||
        return R.ok("解绑成功");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -43,8 +43,8 @@ import top.continew.starter.core.util.validate.ValidationUtils;
 | 
			
		||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
 | 
			
		||||
import top.continew.starter.extension.crud.controller.BaseController;
 | 
			
		||||
import top.continew.starter.extension.crud.enums.Api;
 | 
			
		||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
 | 
			
		||||
import top.continew.starter.extension.crud.util.ValidateGroup;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
@@ -64,7 +64,7 @@ public class UserController extends BaseController<UserService, UserResp, UserDe
 | 
			
		||||
    private final UserService userService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public R<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody UserReq req) {
 | 
			
		||||
    public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody UserReq req) {
 | 
			
		||||
        String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getPassword()));
 | 
			
		||||
        ValidationUtils.throwIfNull(rawPassword, "密码解密失败");
 | 
			
		||||
        ValidationUtils.throwIf(!ReUtil
 | 
			
		||||
@@ -83,38 +83,36 @@ public class UserController extends BaseController<UserService, UserResp, UserDe
 | 
			
		||||
    @Operation(summary = "解析用户导入数据", description = "解析用户导入数据")
 | 
			
		||||
    @SaCheckPermission("system:user:import")
 | 
			
		||||
    @PostMapping(value = "parseImportUser")
 | 
			
		||||
    public R<UserImportParseResp> parseImportUser(@NotNull(message = "文件不能为空") MultipartFile file) {
 | 
			
		||||
    public UserImportParseResp parseImportUser(@NotNull(message = "文件不能为空") MultipartFile file) {
 | 
			
		||||
        ValidationUtils.throwIf(file::isEmpty, "文件不能为空");
 | 
			
		||||
        return R.ok(userService.parseImportUser(file));
 | 
			
		||||
        return userService.parseImportUser(file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "导入用户", description = "导入用户")
 | 
			
		||||
    @SaCheckPermission("system:user:import")
 | 
			
		||||
    @PostMapping(value = "import")
 | 
			
		||||
    public R<UserImportResp> importUser(@Validated @RequestBody UserImportReq req) {
 | 
			
		||||
        return R.ok(userService.importUser(req));
 | 
			
		||||
    public UserImportResp importUser(@Validated @RequestBody UserImportReq req) {
 | 
			
		||||
        return userService.importUser(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "重置密码", description = "重置用户登录密码")
 | 
			
		||||
    @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("system:user:resetPwd")
 | 
			
		||||
    @PatchMapping("/{id}/password")
 | 
			
		||||
    public R<Void> resetPassword(@Validated @RequestBody UserPasswordResetReq req, @PathVariable Long id) {
 | 
			
		||||
    public void resetPassword(@Validated @RequestBody UserPasswordResetReq req, @PathVariable Long id) {
 | 
			
		||||
        String rawNewPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getNewPassword()));
 | 
			
		||||
        ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败");
 | 
			
		||||
        ValidationUtils.throwIf(!ReUtil
 | 
			
		||||
            .isMatch(RegexConstants.PASSWORD, rawNewPassword), "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字");
 | 
			
		||||
        req.setNewPassword(rawNewPassword);
 | 
			
		||||
        baseService.resetPassword(req, id);
 | 
			
		||||
        return R.ok("重置密码成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "分配角色", description = "为用户新增或移除角色")
 | 
			
		||||
    @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("system:user:updateRole")
 | 
			
		||||
    @PatchMapping("/{id}/role")
 | 
			
		||||
    public R<Void> updateRole(@Validated @RequestBody UserRoleUpdateReq updateReq, @PathVariable Long id) {
 | 
			
		||||
    public void updateRole(@Validated @RequestBody UserRoleUpdateReq updateReq, @PathVariable Long id) {
 | 
			
		||||
        baseService.updateRole(updateReq, id);
 | 
			
		||||
        return R.ok("分配成功");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ import io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
			
		||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
 | 
			
		||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
@@ -35,7 +34,6 @@ import top.continew.admin.generator.model.resp.TableResp;
 | 
			
		||||
import top.continew.admin.generator.service.GeneratorService;
 | 
			
		||||
import top.continew.starter.extension.crud.model.query.PageQuery;
 | 
			
		||||
import top.continew.starter.extension.crud.model.resp.PageResp;
 | 
			
		||||
import top.continew.starter.web.model.R;
 | 
			
		||||
 | 
			
		||||
import java.sql.SQLException;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
@@ -58,8 +56,8 @@ public class GeneratorController {
 | 
			
		||||
    @Operation(summary = "分页查询数据表", description = "分页查询数据表")
 | 
			
		||||
    @SaCheckPermission("tool:generator:list")
 | 
			
		||||
    @GetMapping("/table")
 | 
			
		||||
    public R<PageResp<TableResp>> pageTable(TableQuery query, @Validated PageQuery pageQuery) throws SQLException {
 | 
			
		||||
        return R.ok(baseService.pageTable(query, pageQuery));
 | 
			
		||||
    public PageResp<TableResp> pageTable(TableQuery query, @Validated PageQuery pageQuery) throws SQLException {
 | 
			
		||||
        return baseService.pageTable(query, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询字段配置列表", description = "查询字段配置列表")
 | 
			
		||||
@@ -67,43 +65,40 @@ public class GeneratorController {
 | 
			
		||||
    @Parameter(name = "requireSync", description = "是否需要同步", example = "false", in = ParameterIn.QUERY)
 | 
			
		||||
    @SaCheckPermission("tool:generator:list")
 | 
			
		||||
    @GetMapping("/field/{tableName}")
 | 
			
		||||
    public R<List<FieldConfigDO>> listFieldConfig(@PathVariable String tableName,
 | 
			
		||||
    public List<FieldConfigDO> listFieldConfig(@PathVariable String tableName,
 | 
			
		||||
                                               @RequestParam(required = false, defaultValue = "false") Boolean requireSync) {
 | 
			
		||||
        return R.ok(baseService.listFieldConfig(tableName, requireSync));
 | 
			
		||||
        return baseService.listFieldConfig(tableName, requireSync);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询生成配置信息", description = "查询生成配置信息")
 | 
			
		||||
    @Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("tool:generator:list")
 | 
			
		||||
    @GetMapping("/config/{tableName}")
 | 
			
		||||
    public R<GenConfigDO> getGenConfig(@PathVariable String tableName) throws SQLException {
 | 
			
		||||
        return R.ok(baseService.getGenConfig(tableName));
 | 
			
		||||
    public GenConfigDO getGenConfig(@PathVariable String tableName) throws SQLException {
 | 
			
		||||
        return baseService.getGenConfig(tableName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "保存配置信息", description = "保存配置信息")
 | 
			
		||||
    @Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("tool:generator:list")
 | 
			
		||||
    @PostMapping("/config/{tableName}")
 | 
			
		||||
    public R<Void> saveConfig(@Validated @RequestBody GenConfigReq req, @PathVariable String tableName) {
 | 
			
		||||
    public void saveConfig(@Validated @RequestBody GenConfigReq req, @PathVariable String tableName) {
 | 
			
		||||
        baseService.saveConfig(req, tableName);
 | 
			
		||||
        return R.ok("保存成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "生成预览", description = "预览生成代码")
 | 
			
		||||
    @Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("tool:generator:list")
 | 
			
		||||
    @GetMapping("/preview/{tableName}")
 | 
			
		||||
    public R<List<GeneratePreviewResp>> preview(@PathVariable String tableName) {
 | 
			
		||||
        return R.ok(baseService.preview(tableName));
 | 
			
		||||
    public List<GeneratePreviewResp> preview(@PathVariable String tableName) {
 | 
			
		||||
        return baseService.preview(tableName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "生成代码", description = "生成代码")
 | 
			
		||||
    @Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH)
 | 
			
		||||
    @SaCheckPermission("tool:generator:list")
 | 
			
		||||
    @PostMapping("/{tableNames}")
 | 
			
		||||
    public void generate(@PathVariable List<String> tableNames,
 | 
			
		||||
                         HttpServletRequest request,
 | 
			
		||||
                         HttpServletResponse response) {
 | 
			
		||||
        baseService.generate(tableNames, request, response);
 | 
			
		||||
    public void generate(@PathVariable List<String> tableNames, HttpServletResponse response) {
 | 
			
		||||
        baseService.generate(tableNames, response);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,5 +5,5 @@
 | 
			
		||||
  \____|\___/ |_| |_| \__||_||_| \_| \___|  \_/\_/       /_/   \_\\__,_||_| |_| |_||_||_| |_|
 | 
			
		||||
 | 
			
		||||
 :: ${project.name}   ::                                               v${project.version}
 | 
			
		||||
 :: ContiNew Starter ::                                               v2.4.0
 | 
			
		||||
 :: ContiNew Starter ::                                               v2.5.0
 | 
			
		||||
 :: Spring Boot      ::                                               v${spring-boot.version}
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,20 @@ continew-starter.web:
 | 
			
		||||
      pattern: '[$spanId][$traceId]'
 | 
			
		||||
      mdc-enable: false
 | 
			
		||||
 | 
			
		||||
--- ### 全局响应配置
 | 
			
		||||
continew-starter.web:
 | 
			
		||||
  response:
 | 
			
		||||
    # 是否开启国际化(默认:false)
 | 
			
		||||
    i18n: false
 | 
			
		||||
    # 自定义成功响应码(默认:0)
 | 
			
		||||
    default-success-code: 0
 | 
			
		||||
    # 自定义成功提示(默认:ok)
 | 
			
		||||
    default-success-msg: ok
 | 
			
		||||
    # 自定义失败响应码(默认:1)
 | 
			
		||||
    default-error-code: 1
 | 
			
		||||
    # 自定义失败提示(默认:error)
 | 
			
		||||
    default-error-msg: error
 | 
			
		||||
 | 
			
		||||
--- ### 接口文档配置
 | 
			
		||||
springdoc:
 | 
			
		||||
  # 设置对象型参数的展示形式(设为 true 表示将对象型参数平展开,即对象内的属性直接作为参数展示而不是嵌套在对象内,默认 false)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user