1、请求租户不存在时,记录访问日志、操作日志异常的问题
2、LoginUser 新增过期时间,方便判断 token 过期
3、增加 ConfigApi,支持参数配置的读取
| | |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | |
| | | * 授权范围 |
| | | */ |
| | | private List<String> scopes; |
| | | /** |
| | | * 过期时间 |
| | | */ |
| | | private LocalDateTime expiresTime; |
| | | |
| | | } |
| | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.iailab.framework.common.core.KeyValue; |
| | | import com.iailab.framework.common.pojo.CommonResult; |
| | | import com.iailab.framework.common.util.date.LocalDateTimeUtils; |
| | | import com.iailab.framework.common.util.json.JsonUtils; |
| | | import com.iailab.gateway.util.SecurityFrameworkUtils; |
| | | import com.iailab.gateway.util.WebFrameworkUtils; |
| | |
| | | // 重要说明:defaultIfEmpty 作用,保证 Mono.empty() 情况,可以继续执行 `flatMap 的 chain.filter(exchange)` 逻辑,避免返回给前端空的 Response!! |
| | | return getLoginUser(exchange, token).defaultIfEmpty(LOGIN_USER_EMPTY).flatMap(user -> { |
| | | // 1. 无用户,直接 filter 继续请求 |
| | | if (user == LOGIN_USER_EMPTY) { |
| | | if (user == LOGIN_USER_EMPTY || // 下面 expiresTime 的判断,为了解决 token 实际已经过期的情况 |
| | | user.getExpiresTime() == null || LocalDateTimeUtils.afterNow(user.getExpiresTime())) { |
| | | return chain.filter(exchange); |
| | | } |
| | | |
| | |
| | | OAuth2AccessTokenCheckRespDTO tokenInfo = result.getData(); |
| | | return new LoginUser().setId(tokenInfo.getUserId()).setUserType(tokenInfo.getUserType()) |
| | | .setInfo(tokenInfo.getUserInfo()) // 额外的用户信息 |
| | | .setTenantId(tokenInfo.getTenantId()).setScopes(tokenInfo.getScopes()); |
| | | .setTenantId(tokenInfo.getTenantId()).setScopes(tokenInfo.getScopes()) |
| | | .setExpiresTime(tokenInfo.getExpiresTime()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP |
| | | config: # 【注册中心】配置项 |
| | | namespace: a7112341-c9e2-4177-bc5b-0d2e8cf0b3bb # 命名空间。这里使用 dev 开发环境 |
| | | group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP |
| | | group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP |
| | | |
| | | # 日志文件配置 |
| | | logging: |
| | | level: |
| | | org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 |
| | |
| | | - Path=/admin-api/statistics/** |
| | | filters: |
| | | - RewritePath=/admin-api/statistics/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs |
| | | - id: xxl-job-server |
| | | uri: grayLb://iailab-job |
| | | ## xxl-job |
| | | - id: xxl-job-admin |
| | | uri: grayLb://xxl-job-server |
| | | predicates: |
| | | - Path=/iailab-job/** |
| | | - Path=/xxl-job-admin/** |
| | | ## monitor |
| | | - id: monitor-server |
| | | uri: grayLb://iailab-monitor |
| | | uri: grayLb://monitor-server |
| | | predicates: |
| | | - Path=/iailab-monitor/** |
| | | ## data-server 服务 |
| | |
| | | db: |
| | | num: 1 |
| | | url: |
| | | '0': jdbc:mysql://172.16.8.100:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true |
| | | '0': jdbc:mysql://localhost:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true |
| | | password: |
| | | '0': 123456 |
| | | user: |
| | |
| | | type:"post", |
| | | data : function ( d ) { |
| | | var obj = {}; |
| | | obj.jobGroup = $('#jobGroup').val(); |
| | | obj.jobGroup = Number($('#jobGroup').val()); |
| | | obj.triggerStatus = $('#triggerStatus').val(); |
| | | obj.jobDesc = $('#jobDesc').val(); |
| | | obj.executorHandler = $('#executorHandler').val(); |
| | |
| | | |
| | | <div class="info-box-content"> |
| | | <span class="info-box-text">${I18n.job_dashboard_job_num}</span> |
| | | <span class="info-box-number">${jobInfoCount}</span> |
| | | <span class="info-box-number">${jobInfoCount?number?string(',##0')}</span> |
| | | |
| | | <div class="progress"> |
| | | <div class="progress-bar" style="width: 100%"></div> |
| | |
| | | |
| | | <div class="info-box-content"> |
| | | <span class="info-box-text">${I18n.job_dashboard_trigger_num}</span> |
| | | <span class="info-box-number">${jobLogCount}</span> |
| | | <span class="info-box-number">${jobLogCount?number?string(',##0')}</span> |
| | | |
| | | <div class="progress"> |
| | | <div class="progress-bar" style="width: 100%" ></div> |
| | |
| | | |
| | | <div class="info-box-content"> |
| | | <span class="info-box-text">${I18n.job_dashboard_jobgroup_num}</span> |
| | | <span class="info-box-number">${executorCount}</span> |
| | | <span class="info-box-number">${executorCount?number?string(',##0')}</span> |
| | | |
| | | <div class="progress"> |
| | | <div class="progress-bar" style="width: 100%"></div> |
| | |
| | | <span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span> |
| | | <select class="form-control" id="jobGroup" > |
| | | <#list JobGroupList as group> |
| | | <option value="${group.id}" <#if jobGroup==group.id>selected</#if> >${group.title}</option> |
| | | <option value="${group.id?number?string(',##0')}" <#if jobGroup==group.id>selected</#if> >${group.title}</option> |
| | | </#list> |
| | | </select> |
| | | </div> |
| | |
| | | <div class="col-sm-4"> |
| | | <select class="form-control" name="jobGroup" > |
| | | <#list JobGroupList as group> |
| | | <option value="${group.id}" <#if jobGroup==group.id>selected</#if> >${group.title}</option> |
| | | <option value="${group.id?number?string(',##0')}" <#if jobGroup==group.id>selected</#if> >${group.title}</option> |
| | | </#list> |
| | | </select> |
| | | </div> |
| | |
| | | <div class="col-sm-4"> |
| | | <select class="form-control" name="jobGroup" > |
| | | <#list JobGroupList as group> |
| | | <option value="${group.id}" >${group.title}</option> |
| | | <option value="${group.id?number?string(',##0')}" >${group.title}</option> |
| | | </#list> |
| | | </select> |
| | | </div> |
| | |
| | | <option value="0" >${I18n.system_all}</option> <#-- 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup --> |
| | | </#if> |
| | | <#list JobGroupList as group> |
| | | <option value="${group.id}" >${group.title}</option> |
| | | <option value="${group.id?number?string(',##0')}" >${group.title}</option> |
| | | </#list> |
| | | </select> |
| | | </div> |
| | |
| | | <modules> |
| | | <module>iailab-gateway</module> |
| | | <module>iailab-xxl-job</module> |
| | | <module>iailab-monitor</module> |
| | | <module>iailab-nacos</module> |
| | | </modules> |
| | | <artifactId>iailab-cloud</artifactId> |
| | | <packaging>pom</packaging> |
| | | <name>${project.artifactId}</name> |
| | | |
| | | |
| | | <properties> |
| | | <maven.compiler.source>8</maven.compiler.source> |
| | | <maven.compiler.target>8</maven.compiler.target> |
| | | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| | | </properties> |
| | | |
| | | |
| | | </project> |
| | |
| | | <artifactId>iailab-common-mybatis</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- RPC 远程调用相关 --> |
| | | <dependency> |
| | | <groupId>com.iailab</groupId> |
| | | <artifactId>iailab-common-rpc</artifactId> |
| | | <optional>true</optional> |
| | | </dependency> |
| | | |
| | | <!-- 业务组件 --> |
| | | <dependency> |
| | | <groupId>com.iailab</groupId> |
对比新文件 |
| | |
| | | package com.iailab.framework.datapermission.config; |
| | | |
| | | import com.iailab.framework.datapermission.core.rpc.DataPermissionRequestInterceptor; |
| | | import com.iailab.framework.datapermission.core.rpc.DataPermissionRpcWebFilter; |
| | | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
| | | import org.springframework.boot.web.servlet.FilterRegistrationBean; |
| | | import org.springframework.context.annotation.Bean; |
| | | |
| | | import static com.iailab.framework.common.enums.WebFilterOrderEnum.TENANT_CONTEXT_FILTER; |
| | | |
| | | |
| | | /** |
| | | * 数据权限针对 RPC 的自动配置类 |
| | | * |
| | | * @author 芋道源码 |
| | | */ |
| | | @AutoConfiguration |
| | | @ConditionalOnClass(name = "feign.RequestInterceptor") |
| | | public class IailabDataPermissionRpcAutoConfiguration { |
| | | |
| | | @Bean |
| | | public DataPermissionRequestInterceptor dataPermissionRequestInterceptor() { |
| | | return new DataPermissionRequestInterceptor(); |
| | | } |
| | | |
| | | @Bean |
| | | public FilterRegistrationBean<DataPermissionRpcWebFilter> dataPermissionRpcFilter() { |
| | | FilterRegistrationBean<DataPermissionRpcWebFilter> registrationBean = new FilterRegistrationBean<>(); |
| | | registrationBean.setFilter(new DataPermissionRpcWebFilter()); |
| | | registrationBean.setOrder(TENANT_CONTEXT_FILTER - 1); // 顺序没有绝对的要求,在租户 Filter 前面稳妥点 |
| | | return registrationBean; |
| | | } |
| | | |
| | | } |
对比新文件 |
| | |
| | | package com.iailab.framework.datapermission.core.rpc; |
| | | |
| | | import com.iailab.framework.datapermission.core.annotation.DataPermission; |
| | | import com.iailab.framework.datapermission.core.aop.DataPermissionContextHolder; |
| | | import feign.RequestInterceptor; |
| | | import feign.RequestTemplate; |
| | | |
| | | /** |
| | | * DataPermission 的 RequestInterceptor 实现类:Feign 请求时,将 {@link DataPermission} 设置到 header 中,继续透传给被调用的服务 |
| | | * |
| | | * 注意:由于 {@link DataPermission} 不支持序列化和反序列化,所以暂时只能传递它的 enable 属性 |
| | | * |
| | | * @author 芋道源码 |
| | | */ |
| | | public class DataPermissionRequestInterceptor implements RequestInterceptor { |
| | | |
| | | public static final String ENABLE_HEADER_NAME = "data-permission-enable"; |
| | | |
| | | @Override |
| | | public void apply(RequestTemplate requestTemplate) { |
| | | DataPermission dataPermission = DataPermissionContextHolder.get(); |
| | | if (dataPermission != null && Boolean.FALSE.equals(dataPermission.enable())) { |
| | | requestTemplate.header(ENABLE_HEADER_NAME, "false"); |
| | | } |
| | | } |
| | | |
| | | } |
对比新文件 |
| | |
| | | package com.iailab.framework.datapermission.core.rpc; |
| | | |
| | | import com.iailab.framework.datapermission.core.util.DataPermissionUtils; |
| | | import org.springframework.web.filter.OncePerRequestFilter; |
| | | |
| | | import javax.servlet.FilterChain; |
| | | import javax.servlet.ServletException; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * 针对 {@link DataPermissionRequestInterceptor} 的 RPC 调用,设置 {@link com.iailab.framework.datapermission.core.aop.DataPermissionContextHolder} 的上下文 |
| | | * |
| | | * @author 芋道源码 |
| | | */ |
| | | public class DataPermissionRpcWebFilter extends OncePerRequestFilter { |
| | | |
| | | @Override |
| | | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) |
| | | throws ServletException, IOException { |
| | | String enable = request.getHeader(DataPermissionRequestInterceptor.ENABLE_HEADER_NAME); |
| | | if (Objects.equals(enable, Boolean.FALSE.toString())) { |
| | | DataPermissionUtils.executeIgnore(() -> { |
| | | try { |
| | | chain.doFilter(request, response); |
| | | } catch (IOException | ServletException e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | }); |
| | | } else { |
| | | chain.doFilter(request, response); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | com.iailab.framework.datapermission.config.IailabDataPermissionAutoConfiguration |
| | | com.iailab.framework.datapermission.config.IailabDeptDataPermissionAutoConfiguration |
| | | com.iailab.framework.datapermission.config.IailabDataPermissionRpcAutoConfiguration |
| | |
| | | import com.fasterxml.jackson.annotation.JsonIgnore; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | * 授权范围 |
| | | */ |
| | | private List<String> scopes; |
| | | /** |
| | | * 过期时间 |
| | | */ |
| | | private LocalDateTime expiresTime; |
| | | |
| | | // ========== 上下文 ========== |
| | | /** |
| | |
| | | // 构建登录用户 |
| | | return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) |
| | | .setInfo(accessToken.getUserInfo()) // 额外的用户信息 |
| | | .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes()); |
| | | .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes()) |
| | | .setExpiresTime(accessToken.getExpiresTime()); |
| | | } catch (ServiceException serviceException) { |
| | | // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可 |
| | | return null; |
| | |
| | | import com.iailab.module.infra.api.logger.ApiAccessLogApi; |
| | | import com.iailab.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | |
| | | /** |
| | |
| | | * @author iailab |
| | | */ |
| | | @RequiredArgsConstructor |
| | | @Slf4j |
| | | public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkService { |
| | | |
| | | private final ApiAccessLogApi apiAccessLogApi; |
| | |
| | | @Override |
| | | @Async |
| | | public void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO) { |
| | | apiAccessLogApi.createApiAccessLog(reqDTO); |
| | | try { |
| | | apiAccessLogApi.createApiAccessLog(reqDTO); |
| | | } catch (Throwable ex) { |
| | | // 由于 @Async 异步调用,这里打印下日志,更容易跟进 |
| | | log.error("[createApiAccessLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | import com.iailab.module.infra.api.logger.ApiErrorLogApi; |
| | | import com.iailab.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | |
| | | /** |
| | |
| | | * @author iailab |
| | | */ |
| | | @RequiredArgsConstructor |
| | | @Slf4j |
| | | public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkService { |
| | | |
| | | private final ApiErrorLogApi apiErrorLogApi; |
| | |
| | | @Override |
| | | @Async |
| | | public void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO) { |
| | | apiErrorLogApi.createApiErrorLog(reqDTO); |
| | | try { |
| | | apiErrorLogApi.createApiErrorLog(reqDTO); |
| | | } catch (Throwable ex) { |
| | | // 由于 @Async 异步调用,这里打印下日志,更容易跟进 |
| | | log.error("[createApiErrorLog][url({}) log({}) 发生异常]", reqDTO.getRequestUrl(), reqDTO, ex); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import com.iailab.framework.common.util.json.JsonUtils; |
| | | import com.iailab.framework.jackson.core.databind.NumberSerializer; |
| | | import com.iailab.framework.jackson.core.databind.TimestampLocalDateTimeDeserializer; |
| | | import com.iailab.framework.jackson.core.databind.TimestampLocalDateTimeSerializer; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.fasterxml.jackson.databind.module.SimpleModule; |
| | | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; |
| | | import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; |
| | | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; |
| | | import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; |
| | | import com.iailab.framework.common.util.json.databind.NumberSerializer; |
| | | import com.iailab.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer; |
| | | import com.iailab.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| | | import org.springframework.context.annotation.Bean; |
| | |
| | | */ |
| | | @ExceptionHandler(value = ServiceException.class) |
| | | public CommonResult<?> serviceExceptionHandler(ServiceException ex) { |
| | | // 不包含的时候,才进行打印,避免 ex 堆栈过多 |
| | | if (!IGNORE_ERROR_MESSAGES.contains(ex.getMessage())) { |
| | | // 不包含的时候,才进行打印,避免 ex 堆栈过多 |
| | | log.info("[serviceExceptionHandler]", ex); |
| | | // 即使打印,也只打印第一层 StackTraceElement,并且使用 warn 在控制台输出,更容易看到 |
| | | StackTraceElement[] stackTrace = ex.getStackTrace(); |
| | | log.warn("[serviceExceptionHandler]\n\t{}", stackTrace[0]); |
| | | } |
| | | return CommonResult.error(ex.getCode(), ex.getMessage()); |
| | | } |
| | |
| | | } |
| | | // 1. 数据报表 |
| | | if (message.contains("report_")) { |
| | | log.error("[报表模块 iailab-module-report - 表结构未导入][参考 https://doc.iocoder.cn/report/ 开启]"); |
| | | log.error("[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]"); |
| | | return CommonResult.error(NOT_IMPLEMENTED.getCode(), |
| | | "[报表模块 iailab-module-report - 表结构未导入][参考 https://doc.iocoder.cn/report/ 开启]"); |
| | | "[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]"); |
| | | } |
| | | // 2. 工作流 |
| | | if (message.contains("bpm_")) { |
| | | log.error("[工作流模块 iailab-module-bpm - 表结构未导入][参考 https://doc.iocoder.cn/bpm/ 开启]"); |
| | | log.error("[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]"); |
| | | return CommonResult.error(NOT_IMPLEMENTED.getCode(), |
| | | "[工作流模块 iailab-module-bpm - 表结构未导入][参考 https://doc.iocoder.cn/bpm/ 开启]"); |
| | | "[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]"); |
| | | } |
| | | // 3. 微信公众号 |
| | | if (message.contains("mp_")) { |
| | | log.error("[微信公众号 iailab-module-mp - 表结构未导入][参考 https://doc.iocoder.cn/mp/build/ 开启]"); |
| | | log.error("[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]"); |
| | | return CommonResult.error(NOT_IMPLEMENTED.getCode(), |
| | | "[微信公众号 iailab-module-mp - 表结构未导入][参考 https://doc.iocoder.cn/mp/build/ 开启]"); |
| | | "[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]"); |
| | | } |
| | | // 4. 商城系统 |
| | | if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) { |
| | | log.error("[商城系统 iailab-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]"); |
| | | log.error("[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]"); |
| | | return CommonResult.error(NOT_IMPLEMENTED.getCode(), |
| | | "[商城系统 iailab-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]"); |
| | | "[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]"); |
| | | } |
| | | // 5. ERP 系统 |
| | | if (message.contains("erp_")) { |
| | | log.error("[ERP 系统 iailab-module-erp - 表结构未导入][参考 https://doc.iocoder.cn/erp/build/ 开启]"); |
| | | log.error("[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]"); |
| | | return CommonResult.error(NOT_IMPLEMENTED.getCode(), |
| | | "[ERP 系统 iailab-module-erp - 表结构未导入][参考 https://doc.iocoder.cn/erp/build/ 开启]"); |
| | | "[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]"); |
| | | } |
| | | // 6. CRM 系统 |
| | | if (message.contains("crm_")) { |
| | | log.error("[CRM 系统 iailab-module-crm - 表结构未导入][参考 https://doc.iocoder.cn/crm/build/ 开启]"); |
| | | log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); |
| | | return CommonResult.error(NOT_IMPLEMENTED.getCode(), |
| | | "[CRM 系统 iailab-module-crm - 表结构未导入][参考 https://doc.iocoder.cn/crm/build/ 开启]"); |
| | | "[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]"); |
| | | } |
| | | // 7. 支付平台 |
| | | if (message.contains("pay_")) { |
| | | log.error("[支付模块 iailab-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]"); |
| | | log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]"); |
| | | return CommonResult.error(NOT_IMPLEMENTED.getCode(), |
| | | "[支付模块 iailab-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]"); |
| | | "[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]"); |
| | | } |
| | | // 8. AI 大模型 |
| | | if (message.contains("ai_")) { |
| | | log.error("[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]"); |
| | | return CommonResult.error(NOT_IMPLEMENTED.getCode(), |
| | | "[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]"); |
| | | } |
| | | return null; |
| | | } |
对比新文件 |
| | |
| | | package com.iailab.framework.common.util.json.databind; |
| | | |
| | | import com.fasterxml.jackson.core.JsonGenerator; |
| | | import com.fasterxml.jackson.databind.SerializerProvider; |
| | | import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * Long 序列化规则 |
| | | * |
| | | * 会将超长 long 值转换为 string,解决前端 JavaScript 最大安全整数是 2^53-1 的问题 |
| | | * |
| | | * @author 星语 |
| | | */ |
| | | @JacksonStdImpl |
| | | public class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.NumberSerializer { |
| | | |
| | | private static final long MAX_SAFE_INTEGER = 9007199254740991L; |
| | | private static final long MIN_SAFE_INTEGER = -9007199254740991L; |
| | | |
| | | public static final NumberSerializer INSTANCE = new NumberSerializer(Number.class); |
| | | |
| | | public NumberSerializer(Class<? extends Number> rawType) { |
| | | super(rawType); |
| | | } |
| | | |
| | | @Override |
| | | public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException { |
| | | // 超出范围 序列化位字符串 |
| | | if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { |
| | | super.serialize(value, gen, serializers); |
| | | } else { |
| | | gen.writeString(value.toString()); |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | package com.iailab.framework.common.util.json.databind; |
| | | |
| | | import com.fasterxml.jackson.core.JsonParser; |
| | | import com.fasterxml.jackson.databind.DeserializationContext; |
| | | import com.fasterxml.jackson.databind.JsonDeserializer; |
| | | |
| | | import java.io.IOException; |
| | | import java.time.Instant; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | |
| | | /** |
| | | * 基于时间戳的 LocalDateTime 反序列化器 |
| | | * |
| | | * @author 老五 |
| | | */ |
| | | public class TimestampLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> { |
| | | |
| | | public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer(); |
| | | |
| | | @Override |
| | | public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { |
| | | // 将 Long 时间戳,转换为 LocalDateTime 对象 |
| | | return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault()); |
| | | } |
| | | |
| | | } |
对比新文件 |
| | |
| | | package com.iailab.framework.common.util.json.databind; |
| | | |
| | | import com.fasterxml.jackson.core.JsonGenerator; |
| | | import com.fasterxml.jackson.databind.JsonSerializer; |
| | | import com.fasterxml.jackson.databind.SerializerProvider; |
| | | |
| | | import java.io.IOException; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | |
| | | /** |
| | | * 基于时间戳的 LocalDateTime 序列化器 |
| | | * |
| | | * @author 老五 |
| | | */ |
| | | public class TimestampLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { |
| | | |
| | | public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer(); |
| | | |
| | | @Override |
| | | public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { |
| | | // 将 LocalDateTime 对象,转换为 Long 时间戳 |
| | | gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
| | | } |
| | | |
| | | } |
| | |
| | | iailab: |
| | | env: # 多环境的配置项 |
| | | tag: ${HOSTNAME} |
| | | captcha: |
| | | enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试 |
| | | security: |
| | | mock-enable: true |
| | | xss: |
| | | enable: false |
| | | exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 |
| | | - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 |
| | | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| | | access-log: # 访问日志的配置项 |
| | | enable: false |
| | | demo: false # 关闭演示模式 |
| | |
| | | web: |
| | | admin-ui: |
| | | url: http://dashboard.iailab.iocoder.cn # Admin 管理后台 UI 的地址 |
| | | xss: |
| | | enable: false |
| | | exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 |
| | | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| | | swagger: |
| | | title: 管理后台 |
| | | description: 提供管理员管理的所有功能 |
| | | version: ${iailab.info.version} |
| | | base-package: ${iailab.info.base-package} |
| | | captcha: |
| | | timeout: 5m |
| | | width: 160 |
| | | height: 60 |
| | | tenant: # 多租户相关配置项 |
| | | enable: true |
| | | |
| | |
| | | package com.iailab.module.data.job.task; |
| | | |
| | | import com.iailab.module.data.point.collection.PointCollector; |
| | | import com.xxl.job.core.handler.annotation.XxlJob; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import javax.annotation.Resource; |
| | |
| | | private PointCollector pointCollector; |
| | | |
| | | @Override |
| | | @XxlJob("pointCollectTaskNet10") |
| | | public void run(String params){ |
| | | // 0/10 * * * * ? |
| | | logger.debug("PointCollectTaskNet10定时任务正在执行,参数为:{}", params); |
| | |
| | | package com.iailab.module.infra.api.config; |
| | | |
| | | import com.iailab.framework.common.pojo.CommonResult; |
| | | import com.iailab.module.infra.enums.ApiConstants; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | @FeignClient(name = ApiConstants.NAME) |
| | | @Tag(name = "配置文件") |
| | | @FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = |
| | | @Tag(name = "RPC 服务 - 参数配置") |
| | | public interface ConfigApi { |
| | | |
| | | @Operation(summary = "Feign接口-查询配置参数") |
| | | @GetMapping("/api/feign/infra-config/{configCode}") |
| | | String queryConfigByCode(@PathVariable("configCode") String configCode); |
| | | String PREFIX = ApiConstants.PREFIX + "/config"; |
| | | |
| | | @GetMapping(PREFIX + "/get-value-by-key") |
| | | @Operation(summary = "根据参数键查询参数值") |
| | | CommonResult<String> getConfigValueByKey(@RequestParam("key") String key); |
| | | |
| | | } |
| | |
| | | package com.iailab.module.infra.api.config; |
| | | |
| | | import com.iailab.framework.common.pojo.CommonResult; |
| | | import com.iailab.module.infra.api.file.FileApi; |
| | | import com.iailab.module.infra.api.file.dto.FileCreateReqDTO; |
| | | import com.iailab.module.infra.dal.dataobject.config.ConfigDO; |
| | | import com.iailab.module.infra.service.config.ConfigService; |
| | | import com.iailab.module.infra.service.file.FileService; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | | |
| | | import static com.iailab.framework.common.pojo.CommonResult.success; |
| | | |
| | |
| | | @Resource |
| | | private ConfigService configService; |
| | | |
| | | // /** |
| | | // * 查月初或者年初(根据code) |
| | | // * |
| | | // * @return |
| | | // */ |
| | | // @Override |
| | | // public String queryConfigByCode(String configCode, String type){ |
| | | // String result = ""; |
| | | // String value = configService.getValue(configCode); |
| | | // Calendar calendar = Calendar.getInstance(); |
| | | // calendar.setTime(new Date()); |
| | | // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | // if("month".equals(type)){ |
| | | // int nowDay = calendar.get(Calendar.DAY_OF_MONTH); |
| | | // if(nowDay < Integer.parseInt(value)){ |
| | | // calendar.add(Calendar.MONTH, -1); |
| | | // } |
| | | // calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(value)); |
| | | // } else { |
| | | // int month = Integer.parseInt(value.substring(0,value.indexOf("-"))); |
| | | // int day = Integer.parseInt(value.substring(value.indexOf("-") + 1)); |
| | | // int nowMonth = calendar.get(Calendar.MONTH) + 1; |
| | | // int nowDay = calendar.get(Calendar.DAY_OF_MONTH); |
| | | // |
| | | // if(nowMonth == month) { |
| | | // if(nowDay < day){ |
| | | // calendar.add(Calendar.YEAR, -1); |
| | | // } |
| | | // } else if(nowMonth < month) { |
| | | // calendar.add(Calendar.YEAR, -1); |
| | | // } |
| | | // calendar.set(Calendar.MONTH, month - 1); |
| | | // calendar.set(Calendar.DAY_OF_MONTH, day); |
| | | // } |
| | | // |
| | | // result = sdf.format(calendar.getTime()); |
| | | // |
| | | // return result; |
| | | // } |
| | | |
| | | @Override |
| | | public String queryConfigByCode(String configCode) { |
| | | return configService.getValue(configCode); |
| | | public CommonResult<String> getConfigValueByKey(String key) { |
| | | ConfigDO config = configService.getConfigByKey(key); |
| | | return success(config != null ? config.getValue() : null); |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.iailab.framework.common.pojo.PageResult; |
| | | import com.iailab.framework.common.util.object.BeanUtils; |
| | | import com.iailab.framework.tenant.core.context.TenantContextHolder; |
| | | import com.iailab.framework.tenant.core.util.TenantUtils; |
| | | import com.iailab.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; |
| | | import com.iailab.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO; |
| | | import com.iailab.module.infra.dal.dataobject.logger.ApiAccessLogDO; |
| | |
| | | ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class); |
| | | apiAccessLog.setRequestParams(StrUtil.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH)); |
| | | apiAccessLog.setResultMsg(StrUtil.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH)); |
| | | apiAccessLogMapper.insert(apiAccessLog); |
| | | if (TenantContextHolder.getTenantId() != null) { |
| | | apiAccessLogMapper.insert(apiAccessLog); |
| | | } else { |
| | | // 极端情况下,上下文中没有租户时,此时忽略租户上下文,避免插入失败! |
| | | TenantUtils.executeIgnore(() -> apiAccessLogMapper.insert(apiAccessLog)); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.iailab.framework.common.pojo.PageResult; |
| | | import com.iailab.framework.common.util.object.BeanUtils; |
| | | import com.iailab.framework.tenant.core.context.TenantContextHolder; |
| | | import com.iailab.framework.tenant.core.util.TenantUtils; |
| | | import com.iailab.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; |
| | | import com.iailab.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO; |
| | | import com.iailab.module.infra.dal.dataobject.logger.ApiErrorLogDO; |
| | |
| | | * |
| | | * @author iailab |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | @Validated |
| | | @Slf4j |
| | | public class ApiErrorLogServiceImpl implements ApiErrorLogService { |
| | | |
| | | @Resource |
| | |
| | | ApiErrorLogDO apiErrorLog = BeanUtils.toBean(createDTO, ApiErrorLogDO.class) |
| | | .setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); |
| | | apiErrorLog.setRequestParams(StrUtil.maxLength(apiErrorLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH)); |
| | | apiErrorLogMapper.insert(apiErrorLog); |
| | | if (TenantContextHolder.getTenantId() != null) { |
| | | apiErrorLogMapper.insert(apiErrorLog); |
| | | } else { |
| | | // 极端情况下,上下文中没有租户时,此时忽略租户上下文,避免插入失败! |
| | | TenantUtils.executeIgnore(() -> apiErrorLogMapper.insert(apiErrorLog)); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | autoconfigure: |
| | | exclude: |
| | | - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 |
| | | - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 |
| | | - de.codecentric.boot.admin.server.cloud.config.AdminServerDiscoveryAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 |
| | | - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 |
| | | - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 |
| | | # - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 |
| | | # - de.codecentric.boot.admin.server.cloud.config.AdminServerDiscoveryAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 |
| | | # - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 |
| | | # - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 |
| | | datasource: |
| | | druid: # Druid 【监控】相关的全局配置 |
| | | web-stat-filter: |
| | |
| | | tag: ${HOSTNAME} |
| | | security: |
| | | mock-enable: true |
| | | xss: |
| | | enable: false |
| | | exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 |
| | | - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 |
| | | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| | | access-log: # 访问日志的配置项 |
| | | enable: false |
| | | demo: false # 关闭演示模式 |
| | |
| | | web: |
| | | admin-ui: |
| | | url: http://dashboard.iailab.iocoder.cn # Admin 管理后台 UI 的地址 |
| | | xss: |
| | | enable: false |
| | | exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 |
| | | - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 |
| | | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| | | websocket: |
| | | enable: true # websocket的开关 |
| | | path: /infra/ws # 路径 |
| | |
| | | title: 管理后台 |
| | | description: 提供管理员管理的所有功能 |
| | | version: ${iailab.info.version} |
| | | base-package: ${iailab.info.base-package} |
| | | codegen: |
| | | base-package: com.iailab |
| | | db-schemas: ${spring.datasource.dynamic.datasource.master.name} |
| | |
| | | import com.iailab.module.model.sample.constructor.SampleInfoConstructor; |
| | | import com.iailab.module.model.sample.dto.ColumnItem; |
| | | import com.iailab.module.model.sample.entity.DataEntity; |
| | | import com.xxl.job.core.handler.annotation.XxlJob; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | |
| | | } |
| | | |
| | | @Override |
| | | @XxlJob("deviceHealthTask") |
| | | public void run(String params) { |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | |
| | | @Schema(description = "授权范围的数组", example = "user_info") |
| | | private List<String> scopes; |
| | | |
| | | @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private LocalDateTime expiresTime; |
| | | |
| | | |
| | | } |
| | |
| | | ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_002_000_001, "登录失败,账号被禁用"); |
| | | ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_004, "验证码不正确,原因:{}"); |
| | | ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定"); |
| | | ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1_002_000_006, "Token 已经过期"); |
| | | ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在"); |
| | | |
| | | // ========== 菜单模块 1-002-001-000 ========== |
| | |
| | | ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1_002_003_005, "用户密码校验失败"); |
| | | ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用"); |
| | | ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!"); |
| | | ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); |
| | | |
| | | // ========== 部门模块 1-002-004-000 ========== |
| | | ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); |
| | |
| | | ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在"); |
| | | ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除"); |
| | | ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门"); |
| | | ErrorCode DEPT_EXISTS_USER = new ErrorCode(1_002_004_005, "部门中存在员工,无法删除"); |
| | | ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择"); |
| | | ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门"); |
| | | |
| | |
| | | ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在"); |
| | | ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1_002_014_001, "验证码已过期"); |
| | | ErrorCode SMS_CODE_USED = new ErrorCode(1_002_014_002, "验证码已使用"); |
| | | ErrorCode SMS_CODE_NOT_CORRECT = new ErrorCode(1_002_014_003, "验证码不正确"); |
| | | ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量"); |
| | | ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁"); |
| | | ErrorCode SMS_CODE_IS_EXISTS = new ErrorCode(1_002_014_006, "手机号已被使用"); |
| | | ErrorCode SMS_CODE_IS_UNUSED = new ErrorCode(1_002_014_007, "验证码未被使用"); |
| | | |
| | | // ========== 租户信息 1-002-015-000 ========== |
| | | ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在"); |
| | |
| | | ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1_002_021_000, "client_id 不匹配"); |
| | | ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1_002_021_001, "redirect_uri 不匹配"); |
| | | ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1_002_021_002, "state 不匹配"); |
| | | ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1_002_021_003, "code 不存在"); |
| | | |
| | | // ========== OAuth2 授权 1-002-022-000 ========= |
| | | ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在"); |
| | |
| | | private OAuth2TokenService oauth2TokenService; |
| | | |
| | | @Override |
| | | @Operation(description = "创建访问令牌") |
| | | public CommonResult<OAuth2AccessTokenRespDTO> createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) { |
| | | OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken( |
| | | reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId(), reqDTO.getScopes()); |
| | |
| | | package com.iailab.module.system.framework.rpc.config; |
| | | |
| | | import com.iailab.module.infra.api.config.ConfigApi; |
| | | import com.iailab.module.infra.api.file.FileApi; |
| | | import com.iailab.module.infra.api.websocket.WebSocketSenderApi; |
| | | import org.springframework.cloud.openfeign.EnableFeignClients; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | @Configuration(proxyBeanMethods = false) |
| | | @EnableFeignClients(clients = {FileApi.class, WebSocketSenderApi.class}) |
| | | @EnableFeignClients(clients = {FileApi.class, WebSocketSenderApi.class, ConfigApi.class}) |
| | | public class RpcConfiguration { |
| | | } |
| | |
| | | import me.chanjar.weixin.mp.api.WxMpService; |
| | | import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; |
| | | import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.data.redis.core.StringRedisTemplate; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | |
| | | @Slf4j |
| | | public class SocialClientServiceImpl implements SocialClientService { |
| | | |
| | | /** |
| | | * 小程序版本 |
| | | * |
| | | * 1. release:正式版 |
| | | * 2. trial:体验版 |
| | | * 3. developer:开发版 |
| | | */ |
| | | @Value("${yudao.wxa-code.env-version:release}") |
| | | public String envVersion; |
| | | |
| | | @Resource |
| | | private AuthRequestFactory authRequestFactory; |
| | | |
| | |
| | | import com.iailab.framework.common.util.collection.CollectionUtils; |
| | | import com.iailab.framework.common.util.object.BeanUtils; |
| | | import com.iailab.framework.datapermission.core.util.DataPermissionUtils; |
| | | import com.iailab.module.infra.api.config.ConfigApi; |
| | | import com.iailab.module.infra.api.file.FileApi; |
| | | import com.iailab.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; |
| | | import com.iailab.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; |
| | |
| | | @Slf4j |
| | | public class AdminUserServiceImpl implements AdminUserService { |
| | | |
| | | @Value("${sys.user.init-password:iailabyuanma}") |
| | | private String userInitPassword; |
| | | static final String USER_INIT_PASSWORD_KEY = "system.user.init-password"; |
| | | |
| | | @Resource |
| | | private AdminUserMapper userMapper; |
| | |
| | | |
| | | @Resource |
| | | private FileApi fileApi; |
| | | |
| | | @Resource |
| | | private ConfigApi configApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 |
| | | public UserImportRespVO importUserList(List<UserImportExcelVO> importUsers, boolean isUpdateSupport) { |
| | | // 1.1 参数校验 |
| | | if (CollUtil.isEmpty(importUsers)) { |
| | | throw exception(USER_IMPORT_LIST_IS_EMPTY); |
| | | } |
| | | // 1.2 初始化密码不能为空 |
| | | String initPassword = configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY).getCheckedData(); |
| | | if (StrUtil.isEmpty(initPassword)) { |
| | | throw exception(USER_IMPORT_INIT_PASSWORD); |
| | | } |
| | | |
| | | // 2. 遍历,逐个创建 or 更新 |
| | | UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>()) |
| | | .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build(); |
| | | importUsers.forEach(importUser -> { |
| | |
| | | enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试 |
| | | security: |
| | | mock-enable: true |
| | | xss: |
| | | enable: false |
| | | exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 |
| | | - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 |
| | | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| | | access-log: # 访问日志的配置项 |
| | | enable: false |
| | | demo: false # 关闭演示模式 |
| | | |
| | | justauth: |
| | | enabled: true |
| | |
| | | web: |
| | | admin-ui: |
| | | url: http://dashboard.iailab.iocoder.cn # Admin 管理后台 UI 的地址 |
| | | xss: |
| | | enable: false |
| | | exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 |
| | | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| | | swagger: |
| | | title: 管理后台 |
| | | description: 提供管理员管理的所有功能 |
| | | version: ${iailab.info.version} |
| | | base-package: ${iailab.info.base-package} |
| | | captcha: |
| | | enable: false # 验证码的开关,默认为 true; |
| | | tenant: # 多租户相关配置项 |
| | | enable: true |
| | | ignore-urls: |