Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
# Conflicts: # yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.java # yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java # yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java # yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java # yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImpl.java # yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImpl.java # yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java
This commit is contained in:
commit
a000048196
@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.date;
|
|||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.date.DatePattern;
|
import cn.hutool.core.date.DatePattern;
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
import cn.hutool.core.date.TemporalAccessorUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
|
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
|
||||||
@ -19,7 +20,7 @@ import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN;
|
|||||||
import static cn.hutool.core.date.DatePattern.createFormatter;
|
import static cn.hutool.core.date.DatePattern.createFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 时间工具类,用于 {@link java.time.LocalDateTime}
|
* 时间工具类,用于 {@link LocalDateTime}
|
||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@ -312,4 +313,16 @@ public class LocalDateTimeUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将给定的 {@link LocalDateTime} 转换为自 Unix 纪元时间(1970-01-01T00:00:00Z)以来的秒数。
|
||||||
|
*
|
||||||
|
* @param sourceDateTime 需要转换的本地日期时间,不能为空
|
||||||
|
* @return 自 1970-01-01T00:00:00Z 起的秒数(epoch second)
|
||||||
|
* @throws NullPointerException 如果 {@code sourceDateTime} 为 {@code null}
|
||||||
|
* @throws DateTimeException 如果转换过程中发生时间超出范围或其他时间处理异常
|
||||||
|
*/
|
||||||
|
public static Long toEpochSecond(LocalDateTime sourceDateTime) {
|
||||||
|
return TemporalAccessorUtil.toInstant(sourceDateTime).getEpochSecond();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.excel.core.handler;
|
|||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import cn.hutool.poi.excel.ExcelUtil;
|
import cn.hutool.poi.excel.ExcelUtil;
|
||||||
@ -10,6 +11,8 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
|
|||||||
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
|
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
|
||||||
import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;
|
import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;
|
||||||
import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction;
|
import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction;
|
||||||
|
import com.alibaba.excel.annotation.ExcelIgnore;
|
||||||
|
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
import com.alibaba.excel.annotation.ExcelProperty;
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
||||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||||
@ -20,6 +23,7 @@ import org.apache.poi.ss.usermodel.*;
|
|||||||
import org.apache.poi.ss.util.CellRangeAddressList;
|
import org.apache.poi.ss.util.CellRangeAddressList;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -56,7 +60,20 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
|||||||
public SelectSheetWriteHandler(Class<?> head) {
|
public SelectSheetWriteHandler(Class<?> head) {
|
||||||
// 解析下拉数据
|
// 解析下拉数据
|
||||||
int colIndex = 0;
|
int colIndex = 0;
|
||||||
|
boolean ignoreUnannotated = head.isAnnotationPresent(ExcelIgnoreUnannotated.class);
|
||||||
for (Field field : head.getDeclaredFields()) {
|
for (Field field : head.getDeclaredFields()) {
|
||||||
|
// 关联 https://github.com/YunaiV/ruoyi-vue-pro/pull/853
|
||||||
|
// 1.1 忽略 static final 或 transient 的字段
|
||||||
|
if (isStaticFinalOrTransient(field) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 1.2 忽略的字段跳过
|
||||||
|
if ((ignoreUnannotated && !field.isAnnotationPresent(ExcelProperty.class))
|
||||||
|
|| field.isAnnotationPresent(ExcelIgnore.class)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 核心:处理有 ExcelColumnSelect 注解的字段
|
||||||
if (field.isAnnotationPresent(ExcelColumnSelect.class)) {
|
if (field.isAnnotationPresent(ExcelColumnSelect.class)) {
|
||||||
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
||||||
if (excelProperty != null && excelProperty.index() != -1) {
|
if (excelProperty != null && excelProperty.index() != -1) {
|
||||||
@ -68,6 +85,19 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字段是否是静态的、最终的、 transient 的
|
||||||
|
* 原因:EasyExcel 默认是忽略 static final 或 transient 的字段,所以需要判断
|
||||||
|
*
|
||||||
|
* @param field 字段
|
||||||
|
* @return 是否是静态的、最终的、transient 的
|
||||||
|
*/
|
||||||
|
private boolean isStaticFinalOrTransient(Field field) {
|
||||||
|
return (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()))
|
||||||
|
|| Modifier.isTransient(field.getModifiers());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得下拉数据,并添加到 {@link #selectMap} 中
|
* 获得下拉数据,并添加到 {@link #selectMap} 中
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package cn.iocoder.yudao.framework.mybatis.core.util;
|
package cn.iocoder.yudao.framework.mybatis.core.util;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.lang.func.Func1;
|
||||||
|
import cn.hutool.core.lang.func.LambdaUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.SortingField;
|
import cn.iocoder.yudao.framework.common.pojo.SortingField;
|
||||||
@ -103,4 +105,18 @@ public class MyBatisUtils {
|
|||||||
.replace("#{value}", StrUtil.toString(value));
|
.replace("#{value}", StrUtil.toString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将驼峰命名转换为下划线命名
|
||||||
|
*
|
||||||
|
* 使用场景:
|
||||||
|
* 1. <a href="https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1357/files">fix:修复"商品统计聚合函数的别名与排序字段不符"导致的 SQL 异常</a>
|
||||||
|
*
|
||||||
|
* @param func 字段名函数(驼峰命名)
|
||||||
|
* @return 字段名(下划线命名)
|
||||||
|
*/
|
||||||
|
public static <T> String toUnderlineCase(Func1<T, ?> func) {
|
||||||
|
String fieldName = LambdaUtil.getFieldName(func);
|
||||||
|
return StrUtil.toUnderlineCase(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,6 +122,15 @@ public class CodegenController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "批量删除数据库的表和字段定义")
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Parameter(name = "tableIds", description = "表编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:codegen:delete')")
|
||||||
|
public CommonResult<Boolean> deleteCodegenList(@RequestParam("tableIds") List<Long> tableIds) {
|
||||||
|
codegenService.deleteCodegenList(tableIds);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "预览生成代码")
|
@Operation(summary = "预览生成代码")
|
||||||
@GetMapping("/preview")
|
@GetMapping("/preview")
|
||||||
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
|
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
|
||||||
|
|||||||
@ -62,6 +62,15 @@ public class ConfigController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Operation(summary = "批量删除参数配置")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:config:delete')")
|
||||||
|
public CommonResult<Boolean> deleteConfigList(@RequestParam("ids") List<Long> ids) {
|
||||||
|
configService.deleteConfigList(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/get")
|
@GetMapping(value = "/get")
|
||||||
@Operation(summary = "获得参数配置")
|
@Operation(summary = "获得参数配置")
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
@ -92,7 +101,7 @@ public class ConfigController {
|
|||||||
return success(ConfigConvert.INSTANCE.convertPage(page));
|
return success(ConfigConvert.INSTANCE.convertPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/export")
|
@GetMapping("/export-excel")
|
||||||
@Operation(summary = "导出参数配置")
|
@Operation(summary = "导出参数配置")
|
||||||
@PreAuthorize("@ss.hasPermission('infra:config:export')")
|
@PreAuthorize("@ss.hasPermission('infra:config:export')")
|
||||||
@ApiAccessLog(operateType = EXPORT)
|
@ApiAccessLog(operateType = EXPORT)
|
||||||
|
|||||||
@ -52,6 +52,15 @@ public class DataSourceConfigController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Operation(summary = "批量删除数据源配置")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')")
|
||||||
|
public CommonResult<Boolean> deleteDataSourceConfigList(@RequestParam("ids") List<Long> ids) {
|
||||||
|
dataSourceConfigService.deleteDataSourceConfigList(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/get")
|
@GetMapping("/get")
|
||||||
@Operation(summary = "获得数据源配置")
|
@Operation(summary = "获得数据源配置")
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import java.time.LocalDateTime;
|
|||||||
public class DataSourceConfigRespVO {
|
public class DataSourceConfigRespVO {
|
||||||
|
|
||||||
@Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
@Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
private Integer id;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "数据源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
|
@Schema(description = "数据源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
|
||||||
private String name;
|
private String name;
|
||||||
|
|||||||
@ -11,12 +11,13 @@ import cn.iocoder.yudao.module.infra.service.file.FileConfigService;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import java.util.List;
|
||||||
import javax.validation.Valid;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
|
||||||
@ -61,6 +62,15 @@ public class FileConfigController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Operation(summary = "批量删除文件配置")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:file-config:delete')")
|
||||||
|
public CommonResult<Boolean> deleteFileConfigList(@RequestParam("ids") List<Long> ids) {
|
||||||
|
fileConfigService.deleteFileConfigList(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/get")
|
@GetMapping("/get")
|
||||||
@Operation(summary = "获得文件配置")
|
@Operation(summary = "获得文件配置")
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
|||||||
@ -14,6 +14,11 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.Parameters;
|
import io.swagger.v3.oas.annotations.Parameters;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.annotation.security.PermitAll;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@ -21,11 +26,7 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import java.util.List;
|
||||||
import javax.annotation.security.PermitAll;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.validation.Valid;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
|
import static cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
|
||||||
@ -76,6 +77,15 @@ public class FileController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Operation(summary = "批量删除文件")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:file:delete')")
|
||||||
|
public CommonResult<Boolean> deleteFileList(@RequestParam("ids") List<Long> ids) throws Exception {
|
||||||
|
fileService.deleteFileList(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/{configId}/get/**")
|
@GetMapping("/{configId}/get/**")
|
||||||
@PermitAll
|
@PermitAll
|
||||||
@TenantIgnore
|
@TenantIgnore
|
||||||
|
|||||||
@ -81,6 +81,16 @@ public class JobController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Operation(summary = "批量删除定时任务")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:job:delete')")
|
||||||
|
public CommonResult<Boolean> deleteJobList(@RequestParam("ids") List<Long> ids)
|
||||||
|
throws SchedulerException {
|
||||||
|
jobService.deleteJobList(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@PutMapping("/trigger")
|
@PutMapping("/trigger")
|
||||||
@Operation(summary = "触发定时任务")
|
@Operation(summary = "触发定时任务")
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
@ -17,8 +18,12 @@ public interface CodegenColumnMapper extends BaseMapperX<CodegenColumnDO> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default void deleteListByTableId(Long tableId) {
|
default void deleteListByTableId(Long tableId) {
|
||||||
|
delete(CodegenColumnDO::getTableId, tableId);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void deleteListByTableId(Collection<Long> tableIds) {
|
||||||
delete(new LambdaQueryWrapperX<CodegenColumnDO>()
|
delete(new LambdaQueryWrapperX<CodegenColumnDO>()
|
||||||
.eq(CodegenColumnDO::getTableId, tableId));
|
.in(CodegenColumnDO::getTableId, tableIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,13 @@ public interface CodegenService {
|
|||||||
*/
|
*/
|
||||||
void deleteCodegen(Long tableId);
|
void deleteCodegen(Long tableId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除数据库的表和字段定义
|
||||||
|
*
|
||||||
|
* @param tableIds 数据编号列表
|
||||||
|
*/
|
||||||
|
void deleteCodegenList(List<Long> tableIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得表定义列表
|
* 获得表定义列表
|
||||||
*
|
*
|
||||||
|
|||||||
@ -222,6 +222,15 @@ public class CodegenServiceImpl implements CodegenService {
|
|||||||
codegenColumnMapper.deleteListByTableId(tableId);
|
codegenColumnMapper.deleteListByTableId(tableId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteCodegenList(List<Long> tableIds) {
|
||||||
|
// 批量删除 table 表定义
|
||||||
|
codegenTableMapper.deleteByIds(tableIds);
|
||||||
|
// 批量删除 column 字段定义
|
||||||
|
codegenColumnMapper.deleteListByTableId(tableIds);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<CodegenTableDO> getCodegenTableList(Long dataSourceConfigId) {
|
public List<CodegenTableDO> getCodegenTableList(Long dataSourceConfigId) {
|
||||||
return codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId);
|
return codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId);
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigSaveReqVO;
|
|||||||
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
|
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参数配置 Service 接口
|
* 参数配置 Service 接口
|
||||||
*
|
*
|
||||||
@ -35,6 +37,13 @@ public interface ConfigService {
|
|||||||
*/
|
*/
|
||||||
void deleteConfig(Long id);
|
void deleteConfig(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除参数配置
|
||||||
|
*
|
||||||
|
* @param ids 配置编号列表
|
||||||
|
*/
|
||||||
|
void deleteConfigList(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得参数配置
|
* 获得参数配置
|
||||||
*
|
*
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
@ -63,6 +65,20 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
configMapper.deleteById(id);
|
configMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteConfigList(List<Long> ids) {
|
||||||
|
// 校验是否有内置配置
|
||||||
|
List<ConfigDO> configs = configMapper.selectByIds(ids);
|
||||||
|
configs.forEach(config -> {
|
||||||
|
if (ConfigTypeEnum.SYSTEM.getType().equals(config.getType())) {
|
||||||
|
throw exception(CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
configMapper.deleteByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigDO getConfig(Long id) {
|
public ConfigDO getConfig(Long id) {
|
||||||
return configMapper.selectById(id);
|
return configMapper.selectById(id);
|
||||||
|
|||||||
@ -35,6 +35,13 @@ public interface DataSourceConfigService {
|
|||||||
*/
|
*/
|
||||||
void deleteDataSourceConfig(Long id);
|
void deleteDataSourceConfig(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除数据源配置
|
||||||
|
*
|
||||||
|
* @param ids 编号列表
|
||||||
|
*/
|
||||||
|
void deleteDataSourceConfigList(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得数据源配置
|
* 获得数据源配置
|
||||||
*
|
*
|
||||||
|
|||||||
@ -63,6 +63,11 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService {
|
|||||||
dataSourceConfigMapper.deleteById(id);
|
dataSourceConfigMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteDataSourceConfigList(List<Long> ids) {
|
||||||
|
dataSourceConfigMapper.deleteByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
private void validateDataSourceConfigExists(Long id) {
|
private void validateDataSourceConfigExists(Long id) {
|
||||||
if (dataSourceConfigMapper.selectById(id) == null) {
|
if (dataSourceConfigMapper.selectById(id) == null) {
|
||||||
throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS);
|
throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS);
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
|
|||||||
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
|
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件配置 Service 接口
|
* 文件配置 Service 接口
|
||||||
*
|
*
|
||||||
@ -43,6 +45,13 @@ public interface FileConfigService {
|
|||||||
*/
|
*/
|
||||||
void deleteFileConfig(Long id);
|
void deleteFileConfig(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除文件配置
|
||||||
|
*
|
||||||
|
* @param ids 编号列表
|
||||||
|
*/
|
||||||
|
void deleteFileConfigList(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得文件配置
|
* 获得文件配置
|
||||||
*
|
*
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@ -134,6 +135,23 @@ public class FileConfigServiceImpl implements FileConfigService {
|
|||||||
clearCache(id, null);
|
clearCache(id, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteFileConfigList(List<Long> ids) {
|
||||||
|
// 校验是否有主配置
|
||||||
|
List<FileConfigDO> configs = fileConfigMapper.selectByIds(ids);
|
||||||
|
for (FileConfigDO config : configs) {
|
||||||
|
if (Boolean.TRUE.equals(config.getMaster())) {
|
||||||
|
throw exception(FILE_CONFIG_DELETE_FAIL_MASTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
fileConfigMapper.deleteByIds(ids);
|
||||||
|
|
||||||
|
// 清空缓存
|
||||||
|
ids.forEach(id -> clearCache(id, null));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清空指定文件配置
|
* 清空指定文件配置
|
||||||
*
|
*
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresigned
|
|||||||
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
|
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件 Service 接口
|
* 文件 Service 接口
|
||||||
*
|
*
|
||||||
@ -59,6 +61,13 @@ public interface FileService {
|
|||||||
*/
|
*/
|
||||||
void deleteFile(Long id) throws Exception;
|
void deleteFile(Long id) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除文件
|
||||||
|
*
|
||||||
|
* @param ids 编号列表
|
||||||
|
*/
|
||||||
|
void deleteFileList(List<Long> ids) throws Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得文件内容
|
* 获得文件内容
|
||||||
*
|
*
|
||||||
|
|||||||
@ -16,10 +16,11 @@ import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
|
|||||||
import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
|
import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
|
||||||
import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils;
|
import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN;
|
import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN;
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
@ -157,6 +158,23 @@ public class FileServiceImpl implements FileService {
|
|||||||
fileMapper.deleteById(id);
|
fileMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public void deleteFileList(List<Long> ids) {
|
||||||
|
// 删除文件
|
||||||
|
List<FileDO> files = fileMapper.selectByIds(ids);
|
||||||
|
for (FileDO file : files) {
|
||||||
|
// 获取客户端
|
||||||
|
FileClient client = fileConfigService.getFileClient(file.getConfigId());
|
||||||
|
Assert.notNull(client, "客户端({}) 不能为空", file.getPath());
|
||||||
|
// 删除文件
|
||||||
|
client.delete(file.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除记录
|
||||||
|
fileMapper.deleteByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
private FileDO validateFileExists(Long id) {
|
private FileDO validateFileExists(Long id) {
|
||||||
FileDO fileDO = fileMapper.selectById(id);
|
FileDO fileDO = fileMapper.selectById(id);
|
||||||
if (fileDO == null) {
|
if (fileDO == null) {
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
|
|||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import org.quartz.SchedulerException;
|
import org.quartz.SchedulerException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定时任务 Service 接口
|
* 定时任务 Service 接口
|
||||||
*
|
*
|
||||||
@ -58,6 +60,13 @@ public interface JobService {
|
|||||||
*/
|
*/
|
||||||
void deleteJob(Long id) throws SchedulerException;
|
void deleteJob(Long id) throws SchedulerException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除定时任务
|
||||||
|
*
|
||||||
|
* @param ids 编号列表
|
||||||
|
*/
|
||||||
|
void deleteJobList(List<Long> ids) throws SchedulerException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得定时任务
|
* 获得定时任务
|
||||||
*
|
*
|
||||||
|
|||||||
@ -169,6 +169,19 @@ public class JobServiceImpl implements JobService {
|
|||||||
schedulerManager.deleteJob(job.getHandlerName());
|
schedulerManager.deleteJob(job.getHandlerName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteJobList(List<Long> ids) throws SchedulerException {
|
||||||
|
// 批量删除
|
||||||
|
List<JobDO> jobs = jobMapper.selectByIds(ids);
|
||||||
|
jobMapper.deleteByIds(ids);
|
||||||
|
|
||||||
|
// 删除 Job 到 Quartz 中
|
||||||
|
for (JobDO job : jobs) {
|
||||||
|
schedulerManager.deleteJob(job.getHandlerName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private JobDO validateJobExists(Long id) {
|
private JobDO validateJobExists(Long id) {
|
||||||
JobDO job = jobMapper.selectById(id);
|
JobDO job = jobMapper.selectById(id);
|
||||||
if (job == null) {
|
if (job == null) {
|
||||||
|
|||||||
@ -124,7 +124,7 @@ public class ProductSpuController {
|
|||||||
return success(productSpuService.getTabsCount());
|
return success(productSpuService.getTabsCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/export")
|
@GetMapping("/export-excel")
|
||||||
@Operation(summary = "导出商品")
|
@Operation(summary = "导出商品")
|
||||||
@PreAuthorize("@ss.hasPermission('product:spu:export')")
|
@PreAuthorize("@ss.hasPermission('product:spu:export')")
|
||||||
@ApiAccessLog(operateType = EXPORT)
|
@ApiAccessLog(operateType = EXPORT)
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import org.apache.ibatis.annotations.Param;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils.toUnderlineCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品统计 Mapper
|
* 商品统计 Mapper
|
||||||
*
|
*
|
||||||
@ -49,16 +51,16 @@ public interface ProductStatisticsMapper extends BaseMapperX<ProductStatisticsDO
|
|||||||
static MPJLambdaWrapperX<ProductStatisticsDO> buildWrapper(ProductStatisticsReqVO reqVO) {
|
static MPJLambdaWrapperX<ProductStatisticsDO> buildWrapper(ProductStatisticsReqVO reqVO) {
|
||||||
return new MPJLambdaWrapperX<ProductStatisticsDO>()
|
return new MPJLambdaWrapperX<ProductStatisticsDO>()
|
||||||
.betweenIfPresent(ProductStatisticsDO::getTime, reqVO.getTimes())
|
.betweenIfPresent(ProductStatisticsDO::getTime, reqVO.getTimes())
|
||||||
.selectSum(ProductStatisticsDO::getBrowseCount)
|
.selectSum(ProductStatisticsDO::getBrowseCount, toUnderlineCase(ProductStatisticsDO::getBrowseCount))
|
||||||
.selectSum(ProductStatisticsDO::getBrowseUserCount)
|
.selectSum(ProductStatisticsDO::getBrowseUserCount, toUnderlineCase(ProductStatisticsDO::getBrowseUserCount))
|
||||||
.selectSum(ProductStatisticsDO::getFavoriteCount)
|
.selectSum(ProductStatisticsDO::getFavoriteCount, toUnderlineCase(ProductStatisticsDO::getFavoriteCount))
|
||||||
.selectSum(ProductStatisticsDO::getCartCount)
|
.selectSum(ProductStatisticsDO::getCartCount, toUnderlineCase(ProductStatisticsDO::getCartCount))
|
||||||
.selectSum(ProductStatisticsDO::getOrderCount)
|
.selectSum(ProductStatisticsDO::getOrderCount, toUnderlineCase(ProductStatisticsDO::getOrderCount))
|
||||||
.selectSum(ProductStatisticsDO::getOrderPayCount)
|
.selectSum(ProductStatisticsDO::getOrderPayCount, toUnderlineCase(ProductStatisticsDO::getOrderPayCount))
|
||||||
.selectSum(ProductStatisticsDO::getOrderPayPrice)
|
.selectSum(ProductStatisticsDO::getOrderPayPrice, toUnderlineCase(ProductStatisticsDO::getOrderPayPrice))
|
||||||
.selectSum(ProductStatisticsDO::getAfterSaleCount)
|
.selectSum(ProductStatisticsDO::getAfterSaleCount, toUnderlineCase(ProductStatisticsDO::getAfterSaleCount))
|
||||||
.selectSum(ProductStatisticsDO::getAfterSaleRefundPrice)
|
.selectSum(ProductStatisticsDO::getAfterSaleRefundPrice, toUnderlineCase(ProductStatisticsDO::getAfterSaleRefundPrice))
|
||||||
.selectAvg(ProductStatisticsDO::getBrowseConvertPercent);
|
.selectAvg(ProductStatisticsDO::getBrowseConvertPercent, toUnderlineCase(ProductStatisticsDO::getBrowseConvertPercent));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -91,7 +91,7 @@ public class PostController {
|
|||||||
return success(BeanUtils.toBean(pageResult, PostRespVO.class));
|
return success(BeanUtils.toBean(pageResult, PostRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/export")
|
@GetMapping("/export-excel")
|
||||||
@Operation(summary = "岗位管理")
|
@Operation(summary = "岗位管理")
|
||||||
@PreAuthorize("@ss.hasPermission('system:post:export')")
|
@PreAuthorize("@ss.hasPermission('system:post:export')")
|
||||||
@ApiAccessLog(operateType = EXPORT)
|
@ApiAccessLog(operateType = EXPORT)
|
||||||
|
|||||||
@ -58,11 +58,20 @@ public class DictDataController {
|
|||||||
@Operation(summary = "删除字典数据")
|
@Operation(summary = "删除字典数据")
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
@PreAuthorize("@ss.hasPermission('system:dict:delete')")
|
@PreAuthorize("@ss.hasPermission('system:dict:delete')")
|
||||||
public CommonResult<Boolean> deleteDictData(Long id) {
|
public CommonResult<Boolean> deleteDictData(@RequestParam("id") Long id) {
|
||||||
dictDataService.deleteDictData(id);
|
dictDataService.deleteDictData(id);
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Operation(summary = "批量删除字典数据")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('system:dict:delete')")
|
||||||
|
public CommonResult<Boolean> deleteDictDataList(@RequestParam("ids") List<Long> ids) {
|
||||||
|
dictDataService.deleteDictDataList(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(value = {"/list-all-simple", "simple-list"})
|
@GetMapping(value = {"/list-all-simple", "simple-list"})
|
||||||
@Operation(summary = "获得全部字典数据列表", description = "一般用于管理后台缓存字典数据在本地")
|
@Operation(summary = "获得全部字典数据列表", description = "一般用于管理后台缓存字典数据在本地")
|
||||||
// 无需添加权限认证,因为前端全局都需要
|
// 无需添加权限认证,因为前端全局都需要
|
||||||
@ -73,7 +82,7 @@ public class DictDataController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "/获得字典类型的分页列表")
|
@Operation(summary = "获得字典类型的分页")
|
||||||
@PreAuthorize("@ss.hasPermission('system:dict:query')")
|
@PreAuthorize("@ss.hasPermission('system:dict:query')")
|
||||||
public CommonResult<PageResult<DictDataRespVO>> getDictTypePage(@Valid DictDataPageReqVO pageReqVO) {
|
public CommonResult<PageResult<DictDataRespVO>> getDictTypePage(@Valid DictDataPageReqVO pageReqVO) {
|
||||||
PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(pageReqVO);
|
PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(pageReqVO);
|
||||||
@ -89,7 +98,7 @@ public class DictDataController {
|
|||||||
return success(BeanUtils.toBean(dictData, DictDataRespVO.class));
|
return success(BeanUtils.toBean(dictData, DictDataRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/export")
|
@GetMapping("/export-excel")
|
||||||
@Operation(summary = "导出字典数据")
|
@Operation(summary = "导出字典数据")
|
||||||
@PreAuthorize("@ss.hasPermission('system:dict:export')")
|
@PreAuthorize("@ss.hasPermission('system:dict:export')")
|
||||||
@ApiAccessLog(operateType = EXPORT)
|
@ApiAccessLog(operateType = EXPORT)
|
||||||
|
|||||||
@ -62,6 +62,15 @@ public class DictTypeController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Operation(summary = "批量删除字典类型")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('system:dict:delete')")
|
||||||
|
public CommonResult<Boolean> deleteDictTypeList(@RequestParam("ids") List<Long> ids) {
|
||||||
|
dictTypeService.deleteDictTypeList(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "获得字典类型的分页列表")
|
@Operation(summary = "获得字典类型的分页列表")
|
||||||
@PreAuthorize("@ss.hasPermission('system:dict:query')")
|
@PreAuthorize("@ss.hasPermission('system:dict:query')")
|
||||||
@ -88,7 +97,7 @@ public class DictTypeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "导出数据类型")
|
@Operation(summary = "导出数据类型")
|
||||||
@GetMapping("/export")
|
@GetMapping("/export-excel")
|
||||||
@PreAuthorize("@ss.hasPermission('system:dict:query')")
|
@PreAuthorize("@ss.hasPermission('system:dict:query')")
|
||||||
@ApiAccessLog(operateType = EXPORT)
|
@ApiAccessLog(operateType = EXPORT)
|
||||||
public void export(HttpServletResponse response, @Valid DictTypePageReqVO exportReqVO) throws IOException {
|
public void export(HttpServletResponse response, @Valid DictTypePageReqVO exportReqVO) throws IOException {
|
||||||
|
|||||||
@ -44,7 +44,7 @@ public class LoginLogController {
|
|||||||
return success(BeanUtils.toBean(pageResult, LoginLogRespVO.class));
|
return success(BeanUtils.toBean(pageResult, LoginLogRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/export")
|
@GetMapping("/export-excel")
|
||||||
@Operation(summary = "导出登录日志 Excel")
|
@Operation(summary = "导出登录日志 Excel")
|
||||||
@PreAuthorize("@ss.hasPermission('system:login-log:export')")
|
@PreAuthorize("@ss.hasPermission('system:login-log:export')")
|
||||||
@ApiAccessLog(operateType = EXPORT)
|
@ApiAccessLog(operateType = EXPORT)
|
||||||
|
|||||||
@ -46,7 +46,7 @@ public class OperateLogController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "导出操作日志")
|
@Operation(summary = "导出操作日志")
|
||||||
@GetMapping("/export")
|
@GetMapping("/export-excel")
|
||||||
@PreAuthorize("@ss.hasPermission('system:operate-log:export')")
|
@PreAuthorize("@ss.hasPermission('system:operate-log:export')")
|
||||||
@ApiAccessLog(operateType = EXPORT)
|
@ApiAccessLog(operateType = EXPORT)
|
||||||
public void exportOperateLog(HttpServletResponse response, @Valid OperateLogPageReqVO exportReqVO) throws IOException {
|
public void exportOperateLog(HttpServletResponse response, @Valid OperateLogPageReqVO exportReqVO) throws IOException {
|
||||||
|
|||||||
@ -12,12 +12,12 @@ import cn.iocoder.yudao.module.system.service.permission.MenuService;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.validation.Valid;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -57,6 +57,15 @@ public class MenuController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Operation(summary = "批量删除菜单")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('system:menu:delete')")
|
||||||
|
public CommonResult<Boolean> deleteMenuList(@RequestParam("ids") List<Long> ids) {
|
||||||
|
menuService.deleteMenuList(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
@Operation(summary = "获取菜单列表", description = "用于【菜单管理】界面")
|
@Operation(summary = "获取菜单列表", description = "用于【菜单管理】界面")
|
||||||
@PreAuthorize("@ss.hasPermission('system:menu:query')")
|
@PreAuthorize("@ss.hasPermission('system:menu:query')")
|
||||||
|
|||||||
@ -7,7 +7,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
|||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.*;
|
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleRespVO;
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@ -61,6 +63,15 @@ public class RoleController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete-list")
|
||||||
|
@Operation(summary = "批量删除角色")
|
||||||
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('system:role:delete')")
|
||||||
|
public CommonResult<Boolean> deleteRoleList(@RequestParam("ids") List<Long> ids) {
|
||||||
|
roleService.deleteRoleList(ids);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/get")
|
@GetMapping("/get")
|
||||||
@Operation(summary = "获得角色信息")
|
@Operation(summary = "获得角色信息")
|
||||||
@PreAuthorize("@ss.hasPermission('system:role:query')")
|
@PreAuthorize("@ss.hasPermission('system:role:query')")
|
||||||
|
|||||||
@ -136,7 +136,7 @@ public class UserController {
|
|||||||
return success(UserConvert.INSTANCE.convert(user, dept));
|
return success(UserConvert.INSTANCE.convert(user, dept));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/export")
|
@GetMapping("/export-excel")
|
||||||
@Operation(summary = "导出用户")
|
@Operation(summary = "导出用户")
|
||||||
@PreAuthorize("@ss.hasPermission('system:user:export')")
|
@PreAuthorize("@ss.hasPermission('system:user:export')")
|
||||||
@ApiAccessLog(operateType = EXPORT)
|
@ApiAccessLog(operateType = EXPORT)
|
||||||
|
|||||||
@ -38,6 +38,13 @@ public interface PostService {
|
|||||||
*/
|
*/
|
||||||
void deletePost(Long id);
|
void deletePost(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除岗位信息
|
||||||
|
*
|
||||||
|
* @param ids 岗位编号数组
|
||||||
|
*/
|
||||||
|
void deletePostList(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得岗位列表
|
* 获得岗位列表
|
||||||
*
|
*
|
||||||
|
|||||||
@ -8,10 +8,10 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqV
|
|||||||
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -58,10 +58,15 @@ public class PostServiceImpl implements PostService {
|
|||||||
public void deletePost(Long id) {
|
public void deletePost(Long id) {
|
||||||
// 校验是否存在
|
// 校验是否存在
|
||||||
validatePostExists(id);
|
validatePostExists(id);
|
||||||
// 删除部门
|
// 删除岗位
|
||||||
postMapper.deleteById(id);
|
postMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deletePostList(List<Long> ids) {
|
||||||
|
postMapper.deleteByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
private void validatePostForCreateOrUpdate(Long id, String name, String code) {
|
private void validatePostForCreateOrUpdate(Long id, String name, String code) {
|
||||||
// 校验自己存在
|
// 校验自己存在
|
||||||
validatePostExists(id);
|
validatePostExists(id);
|
||||||
|
|||||||
@ -38,6 +38,13 @@ public interface DictDataService {
|
|||||||
*/
|
*/
|
||||||
void deleteDictData(Long id);
|
void deleteDictData(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除字典数据
|
||||||
|
*
|
||||||
|
* @param ids 字典数据编号列表
|
||||||
|
*/
|
||||||
|
void deleteDictDataList(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得字典数据列表
|
* 获得字典数据列表
|
||||||
*
|
*
|
||||||
|
|||||||
@ -98,6 +98,11 @@ public class DictDataServiceImpl implements DictDataService {
|
|||||||
dictDataMapper.deleteById(id);
|
dictDataMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteDictDataList(List<Long> ids) {
|
||||||
|
dictDataMapper.deleteByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getDictDataCountByDictType(String dictType) {
|
public long getDictDataCountByDictType(String dictType) {
|
||||||
return dictDataMapper.selectCountByDictType(dictType);
|
return dictDataMapper.selectCountByDictType(dictType);
|
||||||
|
|||||||
@ -36,6 +36,13 @@ public interface DictTypeService {
|
|||||||
*/
|
*/
|
||||||
void deleteDictType(Long id);
|
void deleteDictType(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除字典类型
|
||||||
|
*
|
||||||
|
* @param ids 字典类型编号列表
|
||||||
|
*/
|
||||||
|
void deleteDictTypeList(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得字典类型分页列表
|
* 获得字典类型分页列表
|
||||||
*
|
*
|
||||||
|
|||||||
@ -9,9 +9,9 @@ import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSave
|
|||||||
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.dict.DictTypeMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.dict.DictTypeMapper;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -87,6 +87,21 @@ public class DictTypeServiceImpl implements DictTypeService {
|
|||||||
dictTypeMapper.updateToDelete(id, LocalDateTime.now());
|
dictTypeMapper.updateToDelete(id, LocalDateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteDictTypeList(List<Long> ids) {
|
||||||
|
// 1. 校验是否有字典数据
|
||||||
|
List<DictTypeDO> dictTypes = dictTypeMapper.selectByIds(ids);
|
||||||
|
dictTypes.forEach(dictType -> {
|
||||||
|
if (dictDataService.getDictDataCountByDictType(dictType.getType()) > 0) {
|
||||||
|
throw exception(DICT_TYPE_HAS_CHILDREN);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 批量删除字典类型
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
ids.forEach(id -> dictTypeMapper.updateToDelete(id, now));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DictTypeDO> getDictTypeList() {
|
public List<DictTypeDO> getDictTypeList() {
|
||||||
return dictTypeMapper.selectList();
|
return dictTypeMapper.selectList();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.permission;
|
package cn.iocoder.yudao.module.system.service.permission;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO;
|
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -36,6 +36,13 @@ public interface MenuService {
|
|||||||
*/
|
*/
|
||||||
void deleteMenu(Long id);
|
void deleteMenu(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除菜单
|
||||||
|
*
|
||||||
|
* @param ids 菜单编号数组
|
||||||
|
*/
|
||||||
|
void deleteMenuList(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得所有菜单列表
|
* 获得所有菜单列表
|
||||||
*
|
*
|
||||||
|
|||||||
@ -104,6 +104,24 @@ public class MenuServiceImpl implements MenuService {
|
|||||||
permissionService.processMenuDeleted(id);
|
permissionService.processMenuDeleted(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST,
|
||||||
|
allEntries = true) // allEntries 清空所有缓存,因为 Spring Cache 不支持按照 ids 批量删除
|
||||||
|
public void deleteMenuList(List<Long> ids) {
|
||||||
|
// 校验是否还有子菜单
|
||||||
|
ids.forEach(id -> {
|
||||||
|
if (menuMapper.selectCountByParentId(id) > 0) {
|
||||||
|
throw exception(MENU_EXISTS_CHILDREN);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 标记删除
|
||||||
|
menuMapper.deleteByIds(ids);
|
||||||
|
// 删除授予给角色的权限
|
||||||
|
ids.forEach(id -> permissionService.processMenuDeleted(id));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<MenuDO> getMenuList() {
|
public List<MenuDO> getMenuList() {
|
||||||
return menuMapper.selectList();
|
return menuMapper.selectList();
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -40,6 +40,13 @@ public interface RoleService {
|
|||||||
*/
|
*/
|
||||||
void deleteRole(Long id);
|
void deleteRole(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除角色
|
||||||
|
*
|
||||||
|
* @param ids 角色编号数组
|
||||||
|
*/
|
||||||
|
void deleteRoleList(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置角色的数据权限
|
* 设置角色的数据权限
|
||||||
*
|
*
|
||||||
|
|||||||
@ -122,6 +122,18 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
LogRecordContext.putVariable("role", role);
|
LogRecordContext.putVariable("role", role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteRoleList(List<Long> ids) {
|
||||||
|
// 1. 校验是否可以删除
|
||||||
|
ids.forEach(this::validateRoleForUpdate);
|
||||||
|
|
||||||
|
// 2.1 标记删除
|
||||||
|
roleMapper.deleteByIds(ids);
|
||||||
|
// 2.2 删除相关数据
|
||||||
|
ids.forEach(id -> permissionService.processRoleDeleted(id));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验角色的唯一字段是否重复
|
* 校验角色的唯一字段是否重复
|
||||||
*
|
*
|
||||||
|
|||||||
@ -69,6 +69,7 @@ import java.util.Objects;
|
|||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.UTC_MS_WITH_XXX_OFFSET_FORMATTER;
|
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.UTC_MS_WITH_XXX_OFFSET_FORMATTER;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.toEpochSecond;
|
||||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
@ -381,7 +382,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|||||||
WxMaService service = getWxMaService(userType);
|
WxMaService service = getWxMaService(userType);
|
||||||
WxMaOrderShippingInfoNotifyConfirmRequest request = WxMaOrderShippingInfoNotifyConfirmRequest.builder()
|
WxMaOrderShippingInfoNotifyConfirmRequest request = WxMaOrderShippingInfoNotifyConfirmRequest.builder()
|
||||||
.transactionId(reqDTO.getTransactionId())
|
.transactionId(reqDTO.getTransactionId())
|
||||||
.receivedTime(LocalDateTimeUtil.toEpochMilli(reqDTO.getReceivedTime()))
|
.receivedTime(toEpochSecond(reqDTO.getReceivedTime()))
|
||||||
.build();
|
.build();
|
||||||
try {
|
try {
|
||||||
WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().notifyConfirmReceive(request);
|
WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().notifyConfirmReceive(request);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user