From 462a4ee8230b05b1fb4a9b15382ce84446e7cabf Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 25 Feb 2024 22:32:12 +0800 Subject: [PATCH 01/81] =?UTF-8?q?=E9=87=8D=E6=9E=84=20excel=20=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E6=A8=A1=E7=89=88=E4=B8=8B=E6=8B=89=E7=94=9F=E6=88=90?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E4=BD=BF=E7=94=A8=E5=AD=97=E6=AE=B5=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E7=9A=84=E6=96=B9=E5=BC=8F=E9=85=8D=E7=BD=AE=E4=B8=8B?= =?UTF-8?q?=E6=8B=89=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/annotations/ExcelColumnSelect.java | 20 ++++ .../excel/core/enums/ExcelColumn.java | 27 ----- .../core/handler/SelectSheetWriteHandler.java | 100 ++++++++++++++---- .../service/ExcelColumnSelectDataService.java | 38 +++++++ .../framework/excel/core/util/ExcelUtils.java | 20 +--- .../admin/customer/CrmCustomerController.java | 25 +---- .../vo/customer/CrmCustomerImportExcelVO.java | 9 ++ .../crm/framework/excel/package-info.java | 1 + .../AreaExcelColumnSelectDataServiceImpl.java | 38 +++++++ ...ustryExcelColumnSelectDataServiceImpl.java | 35 ++++++ ...LevelExcelColumnSelectDataServiceImpl.java | 35 ++++++ ...ourceExcelColumnSelectDataServiceImpl.java | 35 ++++++ 12 files changed, 291 insertions(+), 92 deletions(-) create mode 100644 yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/enums/ExcelColumn.java create mode 100644 yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerIndustryExcelColumnSelectDataServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerLevelExcelColumnSelectDataServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerSourceExcelColumnSelectDataServiceImpl.java diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java new file mode 100644 index 000000000..2c519523b --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.framework.excel.core.annotations; + +import java.lang.annotation.*; + +/** + * 给列添加下拉选择数据 + * + * @author HUIHUI + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelColumnSelect { + + /** + * @return 获取下拉数据源的方法名称 + */ + String value(); + +} diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/enums/ExcelColumn.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/enums/ExcelColumn.java deleted file mode 100644 index dd8a8374c..000000000 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/enums/ExcelColumn.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.framework.excel.core.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -// TODO @puhui999:列表有办法通过 field name 么?主要考虑一个点,可能导入模版的顺序可能会变 -/** - * Excel 列名枚举 - * 默认枚举 26 列列名如果有需求更多的列名请自行补充 - * - * @author HUIHUI - */ -@Getter -@AllArgsConstructor -public enum ExcelColumn { - - A(0), B(1), C(2), D(3), E(4), F(5), G(6), H(7), I(8), - J(9), K(10), L(11), M(12), N(13), O(14), P(15), Q(16), - R(17), S(18), T(19), U(20), V(21), W(22), X(23), Y(24), - Z(25); - - /** - * 列索引 - */ - private final int colNum; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java index 38d01bd87..421c59200 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java @@ -1,9 +1,12 @@ package cn.iocoder.yudao.framework.excel.core.handler; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn; +import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; +import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; +import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.write.handler.SheetWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; @@ -11,10 +14,11 @@ import org.apache.poi.hssf.usermodel.HSSFDataValidation; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; /** * 基于固定 sheet 实现下拉框 @@ -23,6 +27,9 @@ import java.util.stream.Collectors; */ public class SelectSheetWriteHandler implements SheetWriteHandler { + private static final List ALPHABET = getExcelColumnNameList(); + private static final List EXCEL_COLUMN_SELECT_DATA_SERVICES = new ArrayList<>(); + /** * 数据起始行从 0 开始 * @@ -36,21 +43,46 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { private static final String DICT_SHEET_NAME = "字典sheet"; - // TODO @puhui999:Map> 可以么?之前用 keyvalue 的原因,返回给前端,无法用 linkedhashmap,默认 key 会乱序 - private final List>> selectMap; + private final List>> selectMap = new ArrayList<>(); // 使用 List + KeyValue 组合方便排序 - public SelectSheetWriteHandler(List>> selectMap) { - if (CollUtil.isEmpty(selectMap)) { - this.selectMap = null; + public SelectSheetWriteHandler(Class head) { + // 加载下拉数据获取接口 + Map beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectDataService.class); + if (MapUtil.isEmpty(beansMap)) { return; } - // 校验一下 key 是否唯一 - Map nameCounts = selectMap.stream() - .collect(Collectors.groupingBy(item -> item.getKey().name(), Collectors.counting())); - Assert.isFalse(nameCounts.entrySet().stream().allMatch(entry -> entry.getValue() > 1), "下拉数据 key 重复请排查!!!"); - + if (CollUtil.isEmpty(EXCEL_COLUMN_SELECT_DATA_SERVICES) || EXCEL_COLUMN_SELECT_DATA_SERVICES.size() != beansMap.values().size()) { + EXCEL_COLUMN_SELECT_DATA_SERVICES.clear(); + EXCEL_COLUMN_SELECT_DATA_SERVICES.addAll(convertList(beansMap.values(), b -> b)); + } + // 解析下拉数据 + Map excelPropertyFields = getFieldsWithAnnotation(head, ExcelProperty.class); + Map excelColumnSelectFields = getFieldsWithAnnotation(head, ExcelColumnSelect.class); + int colIndex = 0; + for (String fieldName : excelPropertyFields.keySet()) { + Field field = excelColumnSelectFields.get(fieldName); + if (field != null) { + getSelectDataList(colIndex, field); + } + colIndex++; + } selectMap.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错 - this.selectMap = selectMap; + } + + /** + * 获得下拉数据 + * + * @param colIndex 列索引 + * @param field 字段 + */ + private void getSelectDataList(int colIndex, Field field) { + EXCEL_COLUMN_SELECT_DATA_SERVICES.forEach(selectDataService -> { + List stringList = selectDataService.handle(field.getAnnotation(ExcelColumnSelect.class).value()); + if (CollUtil.isEmpty(stringList)) { + return; + } + selectMap.add(new KeyValue<>(colIndex, stringList)); + }); } @Override @@ -65,7 +97,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { // 2. 创建数据字典的 sheet 页 Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME); - for (KeyValue> keyValue : selectMap) { + for (KeyValue> keyValue : selectMap) { int rowLength = keyValue.getValue().size(); // 2.1 设置字典 sheet 页的值 每一列一个字典项 for (int i = 0; i < rowLength; i++) { @@ -73,7 +105,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { if (row == null) { row = dictSheet.createRow(i); } - row.createCell(keyValue.getKey().getColNum()).setCellValue(keyValue.getValue().get(i)); + row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i)); } // 2.2 设置单元格下拉选择 setColumnSelect(writeSheetHolder, workbook, helper, keyValue); @@ -84,10 +116,10 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { * 设置单元格下拉选择 */ private static void setColumnSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper, - KeyValue> keyValue) { + KeyValue> keyValue) { // 1.1 创建可被其他单元格引用的名称 Name name = workbook.createName(); - String excelColumn = keyValue.getKey().name(); + String excelColumn = ALPHABET.get(keyValue.getKey()); // 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2 String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size(); name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字 @@ -97,7 +129,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束 // 设置下拉单元格的首行、末行、首列、末列 CellRangeAddressList rangeAddressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW, - keyValue.getKey().getColNum(), keyValue.getKey().getColNum()); + keyValue.getKey(), keyValue.getKey()); DataValidation validation = helper.createValidation(constraint, rangeAddressList); if (validation instanceof HSSFDataValidation) { validation.setSuppressDropDownArrow(false); @@ -112,4 +144,28 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { writeSheetHolder.getSheet().addValidationData(validation); } + public static Map getFieldsWithAnnotation(Class clazz, Class annotationClass) { + Map annotatedFields = new LinkedHashMap<>(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (field.isAnnotationPresent(annotationClass)) { + annotatedFields.put(field.getName(), field); + } + } + return annotatedFields; + } + + + private static List getExcelColumnNameList() { + ArrayList strings = new ArrayList<>(); + for (int i = 1; i <= 52; i++) { // 生成 52 列名称,需要更多请重写此方法 + if (i <= 26) { + strings.add(String.valueOf((char) ('A' + i - 1))); // 使用 ASCII 码值转字母 + } else { + strings.add(String.valueOf((char) ('A' + (i - 1) / 26 - 1)) + (char) ('A' + (i - 1) % 26)); + } + } + return strings; + } + } \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java new file mode 100644 index 000000000..987cf7929 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.framework.excel.core.service; + +import cn.hutool.core.util.StrUtil; + +import java.util.Collections; +import java.util.List; + +/** + * Excel 列下拉数据源获取接口 + * + * 为什么不直接解析字典还搞个接口?考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容 + * + * @author HUIHUI + */ +public interface ExcelColumnSelectDataService { + + /** + * 获得方法名称 + * + * @return 方法名称 + */ + String getFunctionName(); + + /** + * 获得列下拉数据源 + * + * @return 下拉数据源 + */ + List getSelectDataList(); + + default List handle(String funcName) { + if (StrUtil.isEmptyIfStr(funcName) || !StrUtil.equals(getFunctionName(), funcName)) { + return Collections.emptyList(); + } + return getSelectDataList(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java index 81d9a8a97..f5c4b8313 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java @@ -1,7 +1,5 @@ package cn.iocoder.yudao.framework.excel.core.util; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn; import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.converters.longconverter.LongStringConverter; @@ -34,27 +32,11 @@ public class ExcelUtils { */ public static void write(HttpServletResponse response, String filename, String sheetName, Class head, List data) throws IOException { - write(response, filename, sheetName, head, data, null); - } - - /** - * 将列表以 Excel 响应给前端 - * - * @param response 响应 - * @param filename 文件名 - * @param sheetName Excel sheet 名 - * @param head Excel head 头 - * @param data 数据列表哦 - * @param selectMap 下拉选择数据 Map<下拉所对应的列表名,下拉数据> - * @throws IOException 写入失败的情况 - */ - public static void write(HttpServletResponse response, String filename, String sheetName, - Class head, List data, List>> selectMap) throws IOException { // 输出 Excel EasyExcel.write(response.getOutputStream(), head) .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 - .registerWriteHandler(new SelectSheetWriteHandler(selectMap)) // 基于固定 sheet 实现下拉框 + .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框 .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 .sheet(sheetName).doWrite(data); // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java index e784447b2..eea4ed147 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; @@ -10,9 +9,7 @@ import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.ip.core.Area; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*; @@ -38,7 +35,6 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -49,7 +45,6 @@ import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; import static java.util.Collections.singletonList; @Tag(name = "管理后台 - CRM 客户") @@ -266,25 +261,7 @@ public class CrmCustomerController { .areaId(null).detailAddress("").remark("").build() ); // 输出 - ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list, builderSelectMap()); - } - - private List>> builderSelectMap() { - List>> selectMap = new ArrayList<>(); - // 获取地区下拉数据 - // TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么? - Area area = AreaUtils.parseArea(Area.ID_CHINA); - selectMap.add(new KeyValue<>(ExcelColumn.G, AreaUtils.getAreaNodePathList(area.getChildren()))); - // 获取客户所属行业 - List customerIndustries = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_INDUSTRY); - selectMap.add(new KeyValue<>(ExcelColumn.I, customerIndustries)); - // 获取客户等级 - List customerLevels = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_LEVEL); - selectMap.add(new KeyValue<>(ExcelColumn.J, customerLevels)); - // 获取客户来源 - List customerSources = dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE); - selectMap.add(new KeyValue<>(ExcelColumn.K, customerSources)); - return selectMap; + ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list); } @PostMapping("/import") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java index 2c39472fd..4b5fbefbd 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java @@ -1,8 +1,13 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.crm.framework.excel.service.AreaExcelColumnSelectDataServiceImpl; +import cn.iocoder.yudao.module.crm.framework.excel.service.CrmCustomerIndustryExcelColumnSelectDataServiceImpl; +import cn.iocoder.yudao.module.crm.framework.excel.service.CrmCustomerLevelExcelColumnSelectDataServiceImpl; +import cn.iocoder.yudao.module.crm.framework.excel.service.CrmCustomerSourceExcelColumnSelectDataServiceImpl; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -41,6 +46,7 @@ public class CrmCustomerImportExcelVO { private String email; @ExcelProperty(value = "地区", converter = AreaConvert.class) + @ExcelColumnSelect(AreaExcelColumnSelectDataServiceImpl.FUNCTION_NAME) private Integer areaId; @ExcelProperty("详细地址") @@ -48,14 +54,17 @@ public class CrmCustomerImportExcelVO { @ExcelProperty(value = "所属行业", converter = DictConvert.class) @DictFormat(CRM_CUSTOMER_INDUSTRY) + @ExcelColumnSelect(CrmCustomerIndustryExcelColumnSelectDataServiceImpl.FUNCTION_NAME) private Integer industryId; @ExcelProperty(value = "客户等级", converter = DictConvert.class) @DictFormat(CRM_CUSTOMER_LEVEL) + @ExcelColumnSelect(CrmCustomerLevelExcelColumnSelectDataServiceImpl.FUNCTION_NAME) private Integer level; @ExcelProperty(value = "客户来源", converter = DictConvert.class) @DictFormat(CRM_CUSTOMER_SOURCE) + @ExcelColumnSelect(CrmCustomerSourceExcelColumnSelectDataServiceImpl.FUNCTION_NAME) private Integer source; @ExcelProperty("备注") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java new file mode 100644 index 000000000..8b54b9f55 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.crm.framework.excel; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java new file mode 100644 index 000000000..c6a653449 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.crm.framework.excel.service; + +import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; +import cn.iocoder.yudao.framework.ip.core.Area; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * Excel 所属地区列下拉数据源获取接口实现类 + * + * @author HUIHUI + */ +@Service +public class AreaExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDataService { + + public static final String FUNCTION_NAME = "getCrmAreaNameList"; // 防止和别的模块重名 + + @Resource + private DictDataApi dictDataApi; + + @Override + public String getFunctionName() { + return FUNCTION_NAME; + } + + @Override + public List getSelectDataList() { + // 获取地区下拉数据 + // TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么? + Area area = AreaUtils.parseArea(Area.ID_CHINA); + return AreaUtils.getAreaNodePathList(area.getChildren()); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerIndustryExcelColumnSelectDataServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerIndustryExcelColumnSelectDataServiceImpl.java new file mode 100644 index 000000000..8a78104bf --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerIndustryExcelColumnSelectDataServiceImpl.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.crm.framework.excel.service; + +import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; + +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY; + +/** + * Excel 客户所属行业列下拉数据源获取接口实现类 + * + * @author HUIHUI + */ +@Service +public class CrmCustomerIndustryExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDataService { + + public static final String FUNCTION_NAME = "getCrmCustomerIndustryList"; + + @Resource + private DictDataApi dictDataApi; + + @Override + public String getFunctionName() { + return FUNCTION_NAME; + } + + @Override + public List getSelectDataList() { + return dictDataApi.getDictDataLabelList(CRM_CUSTOMER_INDUSTRY); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerLevelExcelColumnSelectDataServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerLevelExcelColumnSelectDataServiceImpl.java new file mode 100644 index 000000000..5948aa427 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerLevelExcelColumnSelectDataServiceImpl.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.crm.framework.excel.service; + +import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; + +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL; + +/** + * Excel 客户等级列下拉数据源获取接口实现类 + * + * @author HUIHUI + */ +@Service +public class CrmCustomerLevelExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDataService { + + public static final String FUNCTION_NAME = "getCrmCustomerLevelList"; + + @Resource + private DictDataApi dictDataApi; + + @Override + public String getFunctionName() { + return FUNCTION_NAME; + } + + @Override + public List getSelectDataList() { + return dictDataApi.getDictDataLabelList(CRM_CUSTOMER_LEVEL); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerSourceExcelColumnSelectDataServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerSourceExcelColumnSelectDataServiceImpl.java new file mode 100644 index 000000000..c160ed90f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerSourceExcelColumnSelectDataServiceImpl.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.crm.framework.excel.service; + +import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; + +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE; + +/** + * Excel 客户来源列下拉数据源获取接口实现类 + * + * @author HUIHUI + */ +@Service +public class CrmCustomerSourceExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDataService { + + public static final String FUNCTION_NAME = "getCrmCustomerSourceList"; + + @Resource + private DictDataApi dictDataApi; + + @Override + public String getFunctionName() { + return FUNCTION_NAME; + } + + @Override + public List getSelectDataList() { + return dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE); + } + +} From c3337f21cebf86eae18b1ad9186e3d03b98840c4 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 28 Feb 2024 00:07:25 +0800 Subject: [PATCH 02/81] =?UTF-8?q?fix=EF=BC=9A=E4=B8=BB=E5=AD=90=E8=A1=A8?= =?UTF-8?q?=20props?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codegen/vue3/views/components/list_sub_erp.vue.vm | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm index 71a7511be..3f0710e01 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm @@ -94,7 +94,7 @@ const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 const props = defineProps<{ - ${subJoinColumn.javaField}: undefined // ${subJoinColumn.columnComment}(主表的关联字段) + ${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段) }>() const loading = ref(false) // 列表的加载中 const list = ref([]) // 列表的数据 @@ -103,17 +103,20 @@ const total = ref(0) // 列表的总页数 const queryParams = reactive({ pageNo: 1, pageSize: 10, - ${subJoinColumn.javaField}: undefined + ${subJoinColumn.javaField}: undefined as unknown }) /** 监听主表的关联字段的变化,加载对应的子表数据 */ watch( () => props.${subJoinColumn.javaField}, - (val) => { + (val: number) => { + if (!val) { + return + } queryParams.${subJoinColumn.javaField} = val handleQuery() }, - { immediate: false } + { immediate: true, deep: true } ) #end From 720b54c353cc199d86aae346ecacce7c471070f0 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 28 Feb 2024 11:25:06 +0800 Subject: [PATCH 03/81] =?UTF-8?q?CRM:=20=E5=AE=8C=E5=96=84=E5=9B=9E?= =?UTF-8?q?=E6=AC=BE=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/crm/enums/ErrorCodeConstants.java | 2 + .../module/crm/enums/LogRecordConstants.java | 6 +-- .../vo/plan/CrmReceivablePlanRespVO.java | 19 ++++++++- .../vo/receivable/CrmReceivableRespVO.java | 21 +++++++++- .../contract/CrmContractServiceImpl.java | 11 ++++- .../receivable/CrmReceivableService.java | 8 ++++ .../receivable/CrmReceivableServiceImpl.java | 40 +++++++++++++------ 7 files changed, 87 insertions(+), 20 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java index a15a3211b..5d942d875 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java @@ -15,6 +15,7 @@ public interface ErrorCodeConstants { ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态"); ErrorCode CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_000_003, "更新合同审核状态失败,原因:合同不是审核中状态"); ErrorCode CONTRACT_NO_EXISTS = new ErrorCode(1_020_000_004, "生成合同序列号重复,请重试"); + ErrorCode CONTRACT_DELETE_FAIL = new ErrorCode(1_020_000_005, "删除合同失败,原因:有被回款所使用"); // ========== 线索管理 1-020-001-000 ========== ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); @@ -40,6 +41,7 @@ public interface ErrorCodeConstants { ErrorCode RECEIVABLE_NO_EXISTS = new ErrorCode(1_020_004_005, "生成回款序列号重复,请重试"); ErrorCode RECEIVABLE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_004_006, "创建回款失败,原因:合同不是审核通过状态"); ErrorCode RECEIVABLE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_020_004_007, "创建回款失败,原因:回款金额超出合同金额,目前剩余可退:{} 元"); + ErrorCode RECEIVABLE_DELETE_FAIL_IS_APPROVE = new ErrorCode(1_020_004_008, "删除回款失败,原因:回款审批已通过"); // ========== 回款计划 1-020-005-000 ========== ErrorCode RECEIVABLE_PLAN_NOT_EXISTS = new ErrorCode(1_020_005_000, "回款计划不存在"); diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java index c2c835b7f..aeeed316d 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java @@ -142,11 +142,11 @@ public interface LogRecordConstants { String CRM_RECEIVABLE_TYPE = "CRM 回款"; String CRM_RECEIVABLE_CREATE_SUB_TYPE = "创建回款"; - String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款"; + String CRM_RECEIVABLE_CREATE_SUCCESS = "创建了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款"; String CRM_RECEIVABLE_UPDATE_SUB_TYPE = "更新回款"; - String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款: {_DIFF{#updateReqVO}}"; + String CRM_RECEIVABLE_UPDATE_SUCCESS = "更新了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款: {_DIFF{#updateReqVO}}"; String CRM_RECEIVABLE_DELETE_SUB_TYPE = "删除回款"; - String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的第【{{#receivable.period}}】期回款"; + String CRM_RECEIVABLE_DELETE_SUCCESS = "删除了合同【{getContractById{#receivable.contractId}}】的{{#period != null ? '【第'+ #period +'期】' : '编号为【'+ #receivable.no +'】的'}}回款"; String CRM_RECEIVABLE_SUBMIT_SUB_TYPE = "提交回款审批"; String CRM_RECEIVABLE_SUBMIT_SUCCESS = "提交编号为【{{#receivableNo}}】的回款审批成功"; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java index 1c58766e1..ad1ce3a7b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -8,53 +9,69 @@ import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; -// TODO @puhui999:缺导出 @Schema(description = "管理后台 - CRM 回款计划 Response VO") @Data +@ExcelIgnoreUnannotated public class CrmReceivablePlanRespVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("编号") private Long id; @Schema(description = "期数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("期数") private Integer period; @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("客户编号") private Long customerId; @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") + @ExcelProperty("客户名字") private String customerName; @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("合同编号") private Long contractId; @Schema(description = "合同编号", example = "Q110") + @ExcelProperty("合同编号") private String contractNo; @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("负责人编号") private Long ownerUserId; @Schema(description = "负责人", example = "test") + @ExcelProperty("负责人") private String ownerUserName; @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") + @ExcelProperty("计划回款日期") private LocalDateTime returnTime; @Schema(description = "计划回款方式", example = "1") + @ExcelProperty("计划回款方式") private Integer returnType; @Schema(description = "计划回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") + @ExcelProperty("计划回款金额") private BigDecimal price; @Schema(description = "回款编号", example = "19852") + @ExcelProperty("回款编号") private Long receivableId; @Schema(description = "回款信息") + @ExcelProperty("回款信息") private CrmReceivableRespVO receivable; @Schema(description = "提前几天提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("提前几天提醒") private Integer remindDays; @Schema(description = "提醒日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") + @ExcelProperty("提醒日期") private LocalDateTime remindTime; @Schema(description = "备注", example = "备注") + @ExcelProperty("备注") private String remark; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java index a9fcb1b8b..12dcbaa02 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -8,37 +9,47 @@ import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; -// TODO 芋艿:导出的 VO,可以考虑使用 @Excel 注解,实现导出功能 @Schema(description = "管理后台 - CRM 回款 Response VO") @Data +@ExcelIgnoreUnannotated public class CrmReceivableRespVO { @Schema(description = "编号", example = "25787") + @ExcelProperty("编号") private Long id; @Schema(description = "回款编号", example = "31177") + @ExcelProperty("回款编号") private String no; @Schema(description = "回款计划编号", example = "1024") + @ExcelProperty("回款计划编号") private Long planId; @Schema(description = "回款方式", example = "2") + @ExcelProperty("回款方式") private Integer returnType; @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") + @ExcelProperty("回款金额") private BigDecimal price; @Schema(description = "计划回款日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") + @ExcelProperty("计划回款日期") private LocalDateTime returnTime; @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("客户编号") private Long customerId; @Schema(description = "客户名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") + @ExcelProperty("客户名字") private String customerName; @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("合同编号") private Long contractId; @Schema(description = "合同信息") + @ExcelProperty("合同信息") private CrmContractRespVO contract; @Schema(description = "负责人的用户编号", example = "25682") @@ -56,20 +67,26 @@ public class CrmReceivableRespVO { private String processInstanceId; @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty("审批状态") private Integer auditStatus; - @Schema(description = "备注", example = "备注") + @Schema(description = "工作流编号", example = "备注") + @ExcelProperty("工作流编号") private String remark; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") private LocalDateTime createTime; @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("更新时间") private LocalDateTime updateTime; @Schema(description = "创建人", example = "25682") + @ExcelProperty("创建人") private String creator; @Schema(description = "创建人名字", example = "test") + @ExcelProperty("创建人名字") private String creatorName; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java index e8a8dbf59..8ef0d84bb 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java @@ -30,12 +30,14 @@ import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; import cn.iocoder.yudao.module.crm.service.product.CrmProductService; +import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -86,7 +88,9 @@ public class CrmContractServiceImpl implements CrmContractService { private CrmContactService contactService; @Resource private CrmContractConfigService contractConfigService; - + @Resource + @Lazy // 延迟加载,避免循环依赖 + private CrmReceivableService receivableService; @Resource private AdminUserApi adminUserApi; @Resource @@ -222,9 +226,12 @@ public class CrmContractServiceImpl implements CrmContractService { success = CRM_CONTRACT_DELETE_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) public void deleteContract(Long id) { - // TODO @puhui999:如果被 CrmReceivableDO 所使用,则不允许删除 // 校验存在 CrmContractDO contract = validateContractExists(id); + // 如果被 CrmReceivableDO 所使用,则不允许删除 + if (CollUtil.isNotEmpty(receivableService.getReceivableByContractId(contract.getId()))) { + throw exception(CONTRACT_DELETE_FAIL); + } // 删除 contractMapper.deleteById(id); // 删除数据权限 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java index c0ca645b2..a07f33a75 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java @@ -122,4 +122,12 @@ public interface CrmReceivableService { */ Map getReceivablePriceMapByContractId(Collection contractIds); + /** + * 更具合同编号查询回款列表 + * + * @param contractId 合同编号 + * @return 回款 + */ + List getReceivableByContractId(Long contractId); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java index 7ef96effa..516556bc9 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java @@ -37,10 +37,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; @@ -81,7 +78,6 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @Resource private BpmProcessInstanceApi bpmProcessInstanceApi; - // TODO @puhui999:操作日志没记录上 @Override @Transactional(rollbackFor = Exception.class) @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}", @@ -115,6 +111,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { // 5. 记录操作日志上下文 LogRecordContext.putVariable("receivable", receivable); + LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId())); return receivable.getId(); } @@ -156,7 +153,6 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { } } - // TODO @puhui999:操作日志没记录上 @Override @Transactional(rollbackFor = Exception.class) @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", @@ -165,10 +161,13 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) { Assert.notNull(updateReqVO.getId(), "回款编号不能为空"); updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段 - // 1.1 校验可回款金额超过上限 - validateReceivablePriceExceedsLimit(updateReqVO); - // 1.2 校验存在 + // 1.1 校验存在 CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId()); + updateReqVO.setOwnerUserId(receivable.getOwnerUserId()).setCustomerId(receivable.getCustomerId()) + .setContractId(receivable.getContractId()).setPlanId(receivable.getPlanId()); // 设置已存在的值 + // 1.2 校验可回款金额超过上限 + validateReceivablePriceExceedsLimit(updateReqVO); + // 1.3 只有草稿、审批中,可以编辑; if (!ObjectUtils.equalsAny(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) { @@ -180,8 +179,17 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { receivableMapper.updateById(updateObj); // 3. 记录操作日志上下文 - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(receivable, CrmReceivableSaveReqVO.class)); LogRecordContext.putVariable("receivable", receivable); + LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId())); + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(receivable, CrmReceivableSaveReqVO.class)); + } + + private Integer getReceivablePeriod(Long planId) { + if (Objects.isNull(planId)) { + return null; + } + CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(planId); + return receivablePlan.getPeriod(); } @Override @@ -212,8 +220,10 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) { throw exception(RECEIVABLE_DELETE_FAIL); } - // TODO @puhui999:审批通过时,不允许删除; - + // 1.3 审批通过时,不允许删除 + if (ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) { + throw exception(RECEIVABLE_DELETE_FAIL_IS_APPROVE); + } // 2. 删除 receivableMapper.deleteById(id); // 3. 删除数据权限 @@ -221,6 +231,7 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { // 4. 记录操作日志上下文 LogRecordContext.putVariable("receivable", receivable); + LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId())); } @Override @@ -289,4 +300,9 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { return receivableMapper.selectReceivablePriceMapByContractId(contractIds); } + @Override + public List getReceivableByContractId(Long contractId) { + return receivableMapper.selectList(CrmReceivableDO::getContractId, contractId); + } + } From 7d120a2f3699d402c6f1d1424c57bee8fb27a78d Mon Sep 17 00:00:00 2001 From: dhb52 Date: Wed, 28 Feb 2024 21:41:10 +0800 Subject: [PATCH 04/81] =?UTF-8?q?feat:=20CRM/=E6=95=B0=E6=8D=AE=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1/=E5=AE=A2=E6=88=B7=E6=80=BB=E9=87=8F=E5=88=86?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsCustomerController.http | 19 +++ .../CrmStatisticsCustomerController.java | 44 +++++++ .../CrmStatisticsCustomerCountVO.java | 28 ++++ .../customer/CrmStatisticsCustomerReqVO.java | 47 +++++++ .../CrmStatisticsCustomerMapper.java | 21 +++ .../CrmStatisticsCustomerService.java | 31 +++++ .../CrmStatisticsCustomerServiceImpl.java | 124 ++++++++++++++++++ .../CrmStatisticsCustomerMapper.xml | 46 +++++++ 8 files changed, 360 insertions(+) create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http new file mode 100644 index 000000000..70a4fd43d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http @@ -0,0 +1,19 @@ +### 新建客户总量分析(按日) +GET {{baseUrl}}/crm/statistics-customer/get-total-customer-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +### 新建客户总量分析(按月) +GET {{baseUrl}}/crm/statistics-customer/get-total-customer-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +### 成交客户总量分析(按日) +GET {{baseUrl}}/crm/statistics-customer/get-deal-total-customer-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +### 成交客户总量分析(按月) +GET {{baseUrl}}/crm/statistics-customer/get-deal-total-customer-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java new file mode 100644 index 000000000..cecddd294 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService; +import io.swagger.v3.oas.annotations.Operation; +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.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - CRM 数据统计 员工客户分析") +@RestController +@RequestMapping("/crm/statistics-customer") +@Validated +public class CrmStatisticsCustomerController { + + @Resource + private CrmStatisticsCustomerService customerService; + + @GetMapping("/get-total-customer-count") + @Operation(summary = "获得新建客户数量") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getTotalCustomerCount(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getTotalCustomerCount(reqVO)); + } + + @GetMapping("/get-deal-total-customer-count") + @Operation(summary = "获得成交客户数量") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getDealTotalCustomerCount(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getDealTotalCustomerCount(reqVO)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java new file mode 100644 index 000000000..01dbd6fc2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Schema(description = "管理后台 - CRM 数据统计 员工客户分析 VO") +@Data +public class CrmStatisticsCustomerCountVO { + + /** + * 时间轴 + *

+ * group by DATE_FORMAT(create_date, '%Y%m') + */ + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") + private String category; + + /** + * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义 + *

+ * 1. 金额:合同金额排行、回款金额排行 + * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行 + */ + @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer count; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java new file mode 100644 index 000000000..62828162b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - CRM 数据统计 员工客户分析 Request VO") +@Data +public class CrmStatisticsCustomerReqVO { + + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "部门 id 不能为空") + private Long deptId; + + /** + * 负责人用户 id, 当用户为空, 则计算部门下用户 + */ + @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1") + private Long userId; + + /** + * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 + *

+ * 后续,可能会支持选择部分用户进行查询 + */ + @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") + private List userIds; + + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @NotEmpty(message = "时间范围不能为空") + private LocalDateTime[] times; + + /** + * group by DATE_FORMAT(field, #{dateFormat}) + */ + @Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m") + private String sqlDateFormat; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java new file mode 100644 index 000000000..062110db8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.statistics; + +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * CRM 数据统计 员工客户分析 Mapper + * + * @author dhb52 + */ +@Mapper +public interface CrmStatisticsCustomerMapper { + + List selectCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + + List selectDealCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java new file mode 100644 index 000000000..b1688b55b --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.crm.service.statistics; + +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; + +import java.util.List; + +/** + * CRM 数据统计 员工客户分析 Service 接口 + * + * @author dhb52 + */ +public interface CrmStatisticsCustomerService { + + /** + * 获取新建客户数量 + * + * @param reqVO 请求参数 + * @return 新建客户数量统计 + */ + List getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO); + + /** + * 获取成交客户数量 + * + * @param reqVO 请求参数 + * @return 成交客户数量统计 + */ + List getDealTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java new file mode 100644 index 000000000..e154382be --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -0,0 +1,124 @@ +package cn.iocoder.yudao.module.crm.service.statistics; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; + +/** + * CRM 数据统计 员工客户分析 Service 实现类 + * + * @author dhb52 + */ +@Service +@Validated +public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService { + + @Resource + private CrmStatisticsCustomerMapper customerMapper; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @Override + public List getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) { + return getStat(reqVO, customerMapper::selectCustomerCountGroupbyDate); + } + + @Override + public List getDealTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) { + return getStat(reqVO, customerMapper::selectDealCustomerCountGroupbyDate); + } + + /** + * 获得统计数据 + * + * @param reqVO 参数 + * @param statFunction 统计方法 + * @return 统计数据 + */ + private List getStat(CrmStatisticsCustomerReqVO reqVO, Function> statFunction) { + // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组 + if (ObjUtil.isNotNull(reqVO.getUserId())) { + reqVO.setUserIds(List.of(reqVO.getUserId())); + } else { + reqVO.setUserIds(getUserIds(reqVO.getDeptId())); + } + if (CollUtil.isEmpty(reqVO.getUserIds())) { + return Collections.emptyList(); + } + + // 2. 生成日期格式 + LocalDateTime startTime = reqVO.getTimes()[0]; + final LocalDateTime endTime = reqVO.getTimes()[1]; + final long days = LocalDateTimeUtil.between(startTime, endTime).toDays(); + boolean byMonth = days > 31; + if (byMonth) { + // 按月 + reqVO.setSqlDateFormat("%Y%m"); + } else { + // 按日 + reqVO.setSqlDateFormat("%Y%m%d"); + } + + // 3. 获得排行数据 + List stats = statFunction.apply(reqVO); + + // 4. 生成时间序列 + List result = CollUtil.newArrayList(); + while (startTime.compareTo(endTime) <= 0) { + final String category = LocalDateTimeUtil.format(startTime, byMonth ? "yyyyMM" : "yyyyMMdd"); + result.add(new CrmStatisticsCustomerCountVO().setCategory(category).setCount(0)); + if (byMonth) + startTime = startTime.plusMonths(1); + else + startTime = startTime.plusDays(1); + } + + // 5. 使用时间序列填充结果 + final Map statMap = convertMap(stats, + CrmStatisticsCustomerCountVO::getCategory, + CrmStatisticsCustomerCountVO::getCount); + result.forEach(r -> { + if (statMap.containsKey(r.getCategory())) { + r.setCount(statMap.get(r.getCategory())); + } + }); + + return result; + } + + + /** + * 获得部门下的用户编号数组,包括子部门的 + * + * @param deptId 部门编号 + * @return 用户编号数组 + */ + public List getUserIds(Long deptId) { + // 1. 获得部门列表 + List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); + deptIds.add(deptId); + // 2. 获得用户编号 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); + } +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml new file mode 100644 index 000000000..e4d2487ce --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -0,0 +1,46 @@ + + + + + + + + + + From a0b413b3a3cf42dc1ba3c238b436a2da3be7ed04 Mon Sep 17 00:00:00 2001 From: dhb52 Date: Wed, 28 Feb 2024 21:41:53 +0800 Subject: [PATCH 05/81] =?UTF-8?q?refactor:=20CRM/=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1/=E6=8E=92=E8=A1=8C=E6=A6=9C=20=E9=87=8D?= =?UTF-8?q?=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsRankController.java | 42 +++++++++---------- .../vo/{ => rank}/CrmStatisticsRankReqVO.java | 6 +-- .../CrmStatisticsRankRespVO.java} | 8 ++-- ...pper.java => CrmStatisticsRankMapper.java} | 24 +++++------ ...ice.java => CrmStatisticsRankService.java} | 24 +++++------ ...java => CrmStatisticsRankServiceImpl.java} | 40 +++++++++--------- .../CrmStatisticsRankMapper.xml} | 18 ++++---- 7 files changed, 81 insertions(+), 81 deletions(-) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/{ => rank}/CrmStatisticsRankReqVO.java (92%) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/{CrmStatisticsRanKRespVO.java => rank/CrmStatisticsRankRespVO.java} (87%) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/{CrmStatisticsRankingMapper.java => CrmStatisticsRankMapper.java} (70%) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/{CrmStatisticsRankingService.java => CrmStatisticsRankService.java} (69%) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/{CrmStatisticsRankingServiceImpl.java => CrmStatisticsRankServiceImpl.java} (77%) rename yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/{bi/CrmBiRankingMapper.xml => statistics/CrmStatisticsRankMapper.xml} (92%) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java index e4cf61f7a..fe79b1d3c 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO; -import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsRankingService; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO; +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsRankService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -19,69 +19,69 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -@Tag(name = "管理后台 - CRM 排行榜统计") +@Tag(name = "管理后台 - CRM 数据统计 排行榜统计") @RestController @RequestMapping("/crm/statistics-rank") @Validated public class CrmStatisticsRankController { @Resource - private CrmStatisticsRankingService rankingService; + private CrmStatisticsRankService rankService; @GetMapping("/get-contract-price-rank") @Operation(summary = "获得合同金额排行榜") @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") - public CommonResult> getContractPriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { - return success(rankingService.getContractPriceRank(rankingReqVO)); + public CommonResult> getContractPriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { + return success(rankService.getContractPriceRank(rankingReqVO)); } @GetMapping("/get-receivable-price-rank") @Operation(summary = "获得回款金额排行榜") @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") - public CommonResult> getReceivablePriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { - return success(rankingService.getReceivablePriceRank(rankingReqVO)); + public CommonResult> getReceivablePriceRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { + return success(rankService.getReceivablePriceRank(rankingReqVO)); } @GetMapping("/get-contract-count-rank") @Operation(summary = "获得签约合同数量排行榜") @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") - public CommonResult> getContractCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { - return success(rankingService.getContractCountRank(rankingReqVO)); + public CommonResult> getContractCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { + return success(rankService.getContractCountRank(rankingReqVO)); } @GetMapping("/get-product-sales-rank") @Operation(summary = "获得产品销量排行榜") @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") - public CommonResult> getProductSalesRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { - return success(rankingService.getProductSalesRank(rankingReqVO)); + public CommonResult> getProductSalesRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { + return success(rankService.getProductSalesRank(rankingReqVO)); } @GetMapping("/get-customer-count-rank") @Operation(summary = "获得新增客户数排行榜") @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") - public CommonResult> getCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { - return success(rankingService.getCustomerCountRank(rankingReqVO)); + public CommonResult> getCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { + return success(rankService.getCustomerCountRank(rankingReqVO)); } @GetMapping("/get-contacts-count-rank") @Operation(summary = "获得新增联系人数排行榜") @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") - public CommonResult> getContactsCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { - return success(rankingService.getContactsCountRank(rankingReqVO)); + public CommonResult> getContactsCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { + return success(rankService.getContactsCountRank(rankingReqVO)); } @GetMapping("/get-follow-count-rank") @Operation(summary = "获得跟进次数排行榜") @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") - public CommonResult> getFollowCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { - return success(rankingService.getFollowCountRank(rankingReqVO)); + public CommonResult> getFollowCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { + return success(rankService.getFollowCountRank(rankingReqVO)); } @GetMapping("/get-follow-customer-count-rank") @Operation(summary = "获得跟进客户数排行榜") @PreAuthorize("@ss.hasPermission('crm:statistics-rank:query')") - public CommonResult> getFollowCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { - return success(rankingService.getFollowCustomerCountRank(rankingReqVO)); + public CommonResult> getFollowCustomerCountRank(@Valid CrmStatisticsRankReqVO rankingReqVO) { + return success(rankService.getFollowCustomerCountRank(rankingReqVO)); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/CrmStatisticsRankReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankReqVO.java similarity index 92% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/CrmStatisticsRankReqVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankReqVO.java index 487921957..5bd5ec295 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/CrmStatisticsRankReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo; +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; @@ -11,7 +11,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -@Schema(description = "管理后台 - CRM 排行榜统计 Request VO") +@Schema(description = "管理后台 - CRM 数据统计 排行榜统计 Request VO") @Data public class CrmStatisticsRankReqVO { @@ -21,7 +21,7 @@ public class CrmStatisticsRankReqVO { /** * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 - * + *

* 后续,可能会支持选择部分用户进行查询 */ @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/CrmStatisticsRanKRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java similarity index 87% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/CrmStatisticsRanKRespVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java index d5c865fd3..86260e74e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/CrmStatisticsRanKRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java @@ -1,12 +1,12 @@ -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo; +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - CRM BI 排行榜统计 Response VO") +@Schema(description = "管理后台 - CRM 数据统计 排行榜统计 Response VO") @Data -public class CrmStatisticsRanKRespVO { +public class CrmStatisticsRankRespVO { @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long ownerUserId; @@ -19,7 +19,7 @@ public class CrmStatisticsRanKRespVO { /** * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义 - * + *

* 1. 金额:合同金额排行、回款金额排行 * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行 */ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankingMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankMapper.java similarity index 70% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankingMapper.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankMapper.java index 4b51ab2fe..b63e42ab8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankingMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankMapper.java @@ -1,18 +1,18 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO; import org.apache.ibatis.annotations.Mapper; import java.util.List; /** - * CRM 排行榜统计 Mapper + * CRM 数据统计 排行榜统计 Mapper * * @author anhaohao */ @Mapper -public interface CrmStatisticsRankingMapper { +public interface CrmStatisticsRankMapper { /** * 查询合同金额排行榜 @@ -20,7 +20,7 @@ public interface CrmStatisticsRankingMapper { * @param rankReqVO 参数 * @return 合同金额排行榜 */ - List selectContractPriceRank(CrmStatisticsRankReqVO rankReqVO); + List selectContractPriceRank(CrmStatisticsRankReqVO rankReqVO); /** * 查询回款金额排行榜 @@ -28,7 +28,7 @@ public interface CrmStatisticsRankingMapper { * @param rankReqVO 参数 * @return 回款金额排行榜 */ - List selectReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO); + List selectReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO); /** * 查询签约合同数量排行榜 @@ -36,7 +36,7 @@ public interface CrmStatisticsRankingMapper { * @param rankReqVO 参数 * @return 签约合同数量排行榜 */ - List selectContractCountRank(CrmStatisticsRankReqVO rankReqVO); + List selectContractCountRank(CrmStatisticsRankReqVO rankReqVO); /** * 查询产品销量排行榜 @@ -44,7 +44,7 @@ public interface CrmStatisticsRankingMapper { * @param rankReqVO 参数 * @return 产品销量排行榜 */ - List selectProductSalesRank(CrmStatisticsRankReqVO rankReqVO); + List selectProductSalesRank(CrmStatisticsRankReqVO rankReqVO); /** * 查询新增客户数排行榜 @@ -52,7 +52,7 @@ public interface CrmStatisticsRankingMapper { * @param rankReqVO 参数 * @return 新增客户数排行榜 */ - List selectCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); + List selectCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); /** * 查询联系人数量排行榜 @@ -60,7 +60,7 @@ public interface CrmStatisticsRankingMapper { * @param rankReqVO 参数 * @return 联系人数量排行榜 */ - List selectContactsCountRank(CrmStatisticsRankReqVO rankReqVO); + List selectContactsCountRank(CrmStatisticsRankReqVO rankReqVO); /** * 查询跟进次数排行榜 @@ -68,7 +68,7 @@ public interface CrmStatisticsRankingMapper { * @param rankReqVO 参数 * @return 跟进次数排行榜 */ - List selectFollowCountRank(CrmStatisticsRankReqVO rankReqVO); + List selectFollowCountRank(CrmStatisticsRankReqVO rankReqVO); /** * 查询跟进客户数排行榜 @@ -76,6 +76,6 @@ public interface CrmStatisticsRankingMapper { * @param rankReqVO 参数 * @return 跟进客户数排行榜 */ - List selectFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); + List selectFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankingService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankService.java similarity index 69% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankingService.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankService.java index c9455708c..f985f53f9 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankingService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankService.java @@ -1,17 +1,17 @@ package cn.iocoder.yudao.module.crm.service.statistics; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO; import java.util.List; /** - * CRM 排行榜统计 Service 接口 + * CRM 数据统计 排行榜统计 Service 接口 * * @author anhaohao */ -public interface CrmStatisticsRankingService { +public interface CrmStatisticsRankService { /** * 获得合同金额排行榜 @@ -19,7 +19,7 @@ public interface CrmStatisticsRankingService { * @param rankReqVO 排行参数 * @return 合同金额排行榜 */ - List getContractPriceRank(CrmStatisticsRankReqVO rankReqVO); + List getContractPriceRank(CrmStatisticsRankReqVO rankReqVO); /** * 获得回款金额排行榜 @@ -27,7 +27,7 @@ public interface CrmStatisticsRankingService { * @param rankReqVO 排行参数 * @return 回款金额排行榜 */ - List getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO); + List getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO); /** * 获得签约合同数量排行榜 @@ -35,7 +35,7 @@ public interface CrmStatisticsRankingService { * @param rankReqVO 排行参数 * @return 签约合同数量排行榜 */ - List getContractCountRank(CrmStatisticsRankReqVO rankReqVO); + List getContractCountRank(CrmStatisticsRankReqVO rankReqVO); /** * 获得产品销量排行榜 @@ -43,7 +43,7 @@ public interface CrmStatisticsRankingService { * @param rankReqVO 排行参数 * @return 产品销量排行榜 */ - List getProductSalesRank(CrmStatisticsRankReqVO rankReqVO); + List getProductSalesRank(CrmStatisticsRankReqVO rankReqVO); /** * 获得新增客户数排行榜 @@ -51,7 +51,7 @@ public interface CrmStatisticsRankingService { * @param rankReqVO 排行参数 * @return 新增客户数排行榜 */ - List getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); + List getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); /** * 获得联系人数量排行榜 @@ -59,7 +59,7 @@ public interface CrmStatisticsRankingService { * @param rankReqVO 排行参数 * @return 联系人数量排行榜 */ - List getContactsCountRank(CrmStatisticsRankReqVO rankReqVO); + List getContactsCountRank(CrmStatisticsRankReqVO rankReqVO); /** * 获得跟进次数排行榜 @@ -67,7 +67,7 @@ public interface CrmStatisticsRankingService { * @param rankReqVO 排行参数 * @return 跟进次数排行榜 */ - List getFollowCountRank(CrmStatisticsRankReqVO rankReqVO); + List getFollowCountRank(CrmStatisticsRankReqVO rankReqVO); /** * 获得跟进客户数排行榜 @@ -75,6 +75,6 @@ public interface CrmStatisticsRankingService { * @param rankReqVO 排行参数 * @return 跟进客户数排行榜 */ - List getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); + List getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankingServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankServiceImpl.java similarity index 77% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankingServiceImpl.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankServiceImpl.java index 428ec1763..ff2acfef5 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankingServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankServiceImpl.java @@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRanKRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.CrmStatisticsRankReqVO; -import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankingMapper; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO; +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsRankMapper; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; @@ -23,16 +23,16 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; /** - * CRM 排行榜统计 Service 实现类 + * CRM 数据统计 排行榜统计 Service 实现类 * * @author anhaohao */ @Service @Validated -public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingService { +public class CrmStatisticsRankServiceImpl implements CrmStatisticsRankService { @Resource - private CrmStatisticsRankingMapper rankMapper; + private CrmStatisticsRankMapper rankMapper; @Resource private AdminUserApi adminUserApi; @@ -40,64 +40,64 @@ public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingServ private DeptApi deptApi; @Override - public List getContractPriceRank(CrmStatisticsRankReqVO rankReqVO) { + public List getContractPriceRank(CrmStatisticsRankReqVO rankReqVO) { return getRank(rankReqVO, rankMapper::selectContractPriceRank); } @Override - public List getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO) { + public List getReceivablePriceRank(CrmStatisticsRankReqVO rankReqVO) { return getRank(rankReqVO, rankMapper::selectReceivablePriceRank); } @Override - public List getContractCountRank(CrmStatisticsRankReqVO rankReqVO) { + public List getContractCountRank(CrmStatisticsRankReqVO rankReqVO) { return getRank(rankReqVO, rankMapper::selectContractCountRank); } @Override - public List getProductSalesRank(CrmStatisticsRankReqVO rankReqVO) { + public List getProductSalesRank(CrmStatisticsRankReqVO rankReqVO) { return getRank(rankReqVO, rankMapper::selectProductSalesRank); } @Override - public List getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) { + public List getCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) { return getRank(rankReqVO, rankMapper::selectCustomerCountRank); } @Override - public List getContactsCountRank(CrmStatisticsRankReqVO rankReqVO) { + public List getContactsCountRank(CrmStatisticsRankReqVO rankReqVO) { return getRank(rankReqVO, rankMapper::selectContactsCountRank); } @Override - public List getFollowCountRank(CrmStatisticsRankReqVO rankReqVO) { + public List getFollowCountRank(CrmStatisticsRankReqVO rankReqVO) { return getRank(rankReqVO, rankMapper::selectFollowCountRank); } @Override - public List getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) { + public List getFollowCustomerCountRank(CrmStatisticsRankReqVO rankReqVO) { return getRank(rankReqVO, rankMapper::selectFollowCustomerCountRank); } /** * 获得排行版数据 * - * @param rankReqVO 参数 + * @param rankReqVO 参数 * @param rankFunction 排行榜方法 * @return 排行版数据 */ - private List getRank(CrmStatisticsRankReqVO rankReqVO, Function> rankFunction) { + private List getRank(CrmStatisticsRankReqVO rankReqVO, Function> rankFunction) { // 1. 获得用户编号数组 rankReqVO.setUserIds(getUserIds(rankReqVO.getDeptId())); if (CollUtil.isEmpty(rankReqVO.getUserIds())) { return Collections.emptyList(); } // 2. 获得排行数据 - List ranks = rankFunction.apply(rankReqVO); + List ranks = rankFunction.apply(rankReqVO); if (CollUtil.isEmpty(ranks)) { return Collections.emptyList(); } - ranks.sort(Comparator.comparing(CrmStatisticsRanKRespVO::getCount).reversed()); + ranks.sort(Comparator.comparing(CrmStatisticsRankRespVO::getCount).reversed()); // 3. 拼接用户信息 appendUserInfo(ranks); return ranks; @@ -108,8 +108,8 @@ public class CrmStatisticsRankingServiceImpl implements CrmStatisticsRankingServ * * @param ranks 排行榜数据 */ - private void appendUserInfo(List ranks) { - Map userMap = adminUserApi.getUserMap(convertSet(ranks, CrmStatisticsRanKRespVO::getOwnerUserId)); + private void appendUserInfo(List ranks) { + Map userMap = adminUserApi.getUserMap(convertSet(ranks, CrmStatisticsRankRespVO::getOwnerUserId)); Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); ranks.forEach(rank -> MapUtils.findAndThen(userMap, rank.getOwnerUserId(), user -> { rank.setNickname(user.getNickname()); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml similarity index 92% rename from yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml rename to yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml index c193873ce..c82c55412 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/bi/CrmBiRankingMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml @@ -1,9 +1,9 @@ - + + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatisticsRankRespVO"> SELECT COUNT(1) AS count, owner_user_id FROM crm_contract WHERE deleted = 0 @@ -64,7 +64,7 @@ + + + + From 34f68ce4c63876b02484ba30aab5b37364663232 Mon Sep 17 00:00:00 2001 From: dhb52 Date: Fri, 1 Mar 2024 00:10:23 +0800 Subject: [PATCH 07/81] =?UTF-8?q?feat:=20CRM/=E6=95=B0=E6=8D=AE=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1/=E5=91=98=E5=B7=A5=E5=AE=A2=E6=88=B7=E5=88=86?= =?UTF-8?q?=E6=9E=90=20=E5=88=9D=E7=A8=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsCustomerController.java | 14 +++++ .../CrmStatisticsCustomerCountVO.java | 8 ++- .../CrmStatisticsCustomerMapper.java | 4 ++ .../CrmStatisticsCustomerService.java | 16 ++++++ .../CrmStatisticsCustomerServiceImpl.java | 53 +++++++++++++++--- .../CrmStatisticsCustomerMapper.xml | 55 +++++++++++++++---- 6 files changed, 130 insertions(+), 20 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index ffd88e97a..5644c512b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -55,4 +55,18 @@ public class CrmStatisticsCustomerController { return success(customerService.getDistinctRecordCount(reqVO)); } + @GetMapping("/get-record-type-count") + @Operation(summary = "获取客户跟进方式统计数") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getRecordTypeCount(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getRecordTypeCount(reqVO)); + } + + @GetMapping("/get-customer-cycle") + @Operation(summary = "获取客户成交周期") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getCustomerCycle(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerCycle(reqVO)); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java index 01dbd6fc2..a2537db9a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java @@ -23,6 +23,12 @@ public class CrmStatisticsCustomerCountVO { * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行 */ @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer count; + private Integer count = 0; + + /** + * 成交周期(天) + */ + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") + private Double cycle = 0.0; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 1be2dc1ff..464c521c6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -22,4 +22,8 @@ public interface CrmStatisticsCustomerMapper { List selectDistinctRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO); + + List selectCustomerCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java index 908f02c99..e568816d6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -45,4 +45,20 @@ public interface CrmStatisticsCustomerService { */ List getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO); + /** + * 获取客户跟进方式统计数 + * + * @param reqVO 请求参数 + * @return 客户跟进方式统计数 + */ + List getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO); + + /** + * 获取客户成交周期 + * + * @param reqVO 请求参数 + * @return 客户成交周期 + */ + List getCustomerCycle(CrmStatisticsCustomerReqVO reqVO); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index 08cd1c480..94eb560d1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -3,12 +3,14 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; @@ -21,7 +23,8 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * CRM 数据统计 员工客户分析 Service 实现类 @@ -39,6 +42,8 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; + @Resource + private DictDataApi dictDataApi; @Override public List getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) { @@ -62,6 +67,37 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe return getStat(reqVO, customerMapper::selectDistinctRecordCountGroupbyDate); } + @Override + public List getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组 + if (ObjUtil.isNotNull(reqVO.getUserId())) { + reqVO.setUserIds(List.of(reqVO.getUserId())); + } else { + reqVO.setUserIds(getUserIds(reqVO.getDeptId())); + } + if (CollUtil.isEmpty(reqVO.getUserIds())) { + return Collections.emptyList(); + } + + // 2. 获得排行数据 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + List stats = customerMapper.selectRecordCountGroupbyType(reqVO); + + // 3. 获取字典数据 + List followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type"); + final Map followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel); + stats.forEach(stat -> { + stat.setCategory(followUpTypeMap.get(stat.getCategory())); + }); + + return stats; + } + + @Override + public List getCustomerCycle(CrmStatisticsCustomerReqVO reqVO) { + return getStat(reqVO, customerMapper::selectCustomerCycleGroupbyDate); + } + /** * 获得统计数据 * @@ -98,9 +134,9 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 4. 生成时间序列 List result = CollUtil.newArrayList(); - while (startTime.compareTo(endTime) <= 0) { + while (!startTime.isAfter(endTime)) { final String category = LocalDateTimeUtil.format(startTime, byMonth ? "yyyyMM" : "yyyyMMdd"); - result.add(new CrmStatisticsCustomerCountVO().setCategory(category).setCount(0)); + result.add(new CrmStatisticsCustomerCountVO().setCategory(category)); if (byMonth) startTime = startTime.plusMonths(1); else @@ -108,12 +144,13 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe } // 5. 使用时间序列填充结果 - final Map statMap = convertMap(stats, - CrmStatisticsCustomerCountVO::getCategory, - CrmStatisticsCustomerCountVO::getCount); + final Map statMap = convertMap(stats, + CrmStatisticsCustomerCountVO::getCategory, + Function.identity()); result.forEach(r -> { if (statMap.containsKey(r.getCategory())) { - r.setCount(statMap.get(r.getCategory())); + r.setCount(statMap.get(r.getCategory()).getCount()) + .setCycle(statMap.get(r.getCategory()).getCycle()); } }); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index cbc87f59f..06affccab 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -6,8 +6,8 @@ + + + + From a787f2a0cd17164f6cb72b417ba6dc150657bd3a Mon Sep 17 00:00:00 2001 From: zhanhong <283456800@qq.com> Date: Fri, 1 Mar 2024 18:57:12 +0800 Subject: [PATCH 08/81] =?UTF-8?q?fix:=E7=A7=AF=E6=9C=A8=E6=8A=A5=E8=A1=A8?= =?UTF-8?q?=E5=AF=BC=E5=87=BAExcel=E6=8A=A5=E9=94=99=EF=BC=88=E5=8C=85?= =?UTF-8?q?=E5=86=B2=E7=AA=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 16 ++++++++-------- .../yudao-module-report-biz/pom.xml | 10 ++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 3a66524bc..8933d64a1 100644 --- a/pom.xml +++ b/pom.xml @@ -15,14 +15,14 @@ yudao-module-system yudao-module-infra - - - - - - - - + yudao-module-member + yudao-module-bpm + yudao-module-report + yudao-module-mp + yudao-module-pay + yudao-module-mall + yudao-module-crm + yudao-module-erp diff --git a/yudao-module-report/yudao-module-report-biz/pom.xml b/yudao-module-report/yudao-module-report-biz/pom.xml index 62aeb3ee0..f38b38e75 100644 --- a/yudao-module-report/yudao-module-report-biz/pom.xml +++ b/yudao-module-report/yudao-module-report-biz/pom.xml @@ -77,6 +77,16 @@ com.bstek.ureport ureport2-console + + + org.apache.poi + poi + + + org.apache.poi + poi-ooxml + + From 01695567a1ed9f27357b70b0e61f95a3be533b85 Mon Sep 17 00:00:00 2001 From: zhanhong <283456800@qq.com> Date: Fri, 1 Mar 2024 18:59:11 +0800 Subject: [PATCH 09/81] re --- pom.xml | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index 8933d64a1..094dc5433 100644 --- a/pom.xml +++ b/pom.xml @@ -15,16 +15,16 @@ yudao-module-system yudao-module-infra - yudao-module-member - yudao-module-bpm - yudao-module-report - yudao-module-mp - yudao-module-pay - yudao-module-mall - yudao-module-crm - yudao-module-erp + + + + + + + + - + ${project.artifactId} @@ -32,17 +32,17 @@ https://github.com/YunaiV/ruoyi-vue-pro - 2.0.1-snapshot + 2.0.1-jdk8-snapshot - 21 + 1.8 ${java.version} ${java.version} - 3.2.2 - 3.11.0 + 3.0.0-M5 + 3.8.1 1.5.0 1.18.30 - 3.2.2 + 2.7.18 1.5.5.Final UTF-8 @@ -93,11 +93,6 @@ ${mapstruct.version} - - false - - -parameters - From 6846d429c0c0fab746c588e02769194567cc2fad Mon Sep 17 00:00:00 2001 From: zhanhong <283456800@qq.com> Date: Fri, 1 Mar 2024 18:59:49 +0800 Subject: [PATCH 10/81] re --- yudao-module-report/yudao-module-report-biz/pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/yudao-module-report/yudao-module-report-biz/pom.xml b/yudao-module-report/yudao-module-report-biz/pom.xml index f38b38e75..62aeb3ee0 100644 --- a/yudao-module-report/yudao-module-report-biz/pom.xml +++ b/yudao-module-report/yudao-module-report-biz/pom.xml @@ -77,16 +77,6 @@ com.bstek.ureport ureport2-console - - - org.apache.poi - poi - - - org.apache.poi - poi-ooxml - - From 7841fba59bb70071ead10de00bc413a409d9fea2 Mon Sep 17 00:00:00 2001 From: zhanhong <283456800@qq.com> Date: Fri, 1 Mar 2024 19:00:10 +0800 Subject: [PATCH 11/81] =?UTF-8?q?fix:=E7=A7=AF=E6=9C=A8=E6=8A=A5=E8=A1=A8?= =?UTF-8?q?=E5=AF=BC=E5=87=BAExcel=E6=8A=A5=E9=94=99=EF=BC=88=E5=8C=85?= =?UTF-8?q?=E5=86=B2=E7=AA=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-report/yudao-module-report-biz/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/yudao-module-report/yudao-module-report-biz/pom.xml b/yudao-module-report/yudao-module-report-biz/pom.xml index 62aeb3ee0..f38b38e75 100644 --- a/yudao-module-report/yudao-module-report-biz/pom.xml +++ b/yudao-module-report/yudao-module-report-biz/pom.xml @@ -77,6 +77,16 @@ com.bstek.ureport ureport2-console + + + org.apache.poi + poi + + + org.apache.poi + poi-ooxml + + From 0bd7d310a18d66df2d1b24bf7414ef2d65313262 Mon Sep 17 00:00:00 2001 From: zhanhong <283456800@qq.com> Date: Fri, 1 Mar 2024 19:05:54 +0800 Subject: [PATCH 12/81] pom --- pom.xml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 094dc5433..439120219 100644 --- a/pom.xml +++ b/pom.xml @@ -32,17 +32,17 @@ https://github.com/YunaiV/ruoyi-vue-pro - 2.0.1-jdk8-snapshot + 2.0.1-snapshot - 1.8 + 21 ${java.version} ${java.version} - 3.0.0-M5 - 3.8.1 + 3.2.2 + 3.11.0 1.5.0 1.18.30 - 2.7.18 + 3.2.2 1.5.5.Final UTF-8 @@ -93,6 +93,11 @@ ${mapstruct.version} + + false + + -parameters + From ca7b19c40b3f2723678c005745cde611b2e8112a Mon Sep 17 00:00:00 2001 From: zhanhong <283456800@qq.com> Date: Fri, 1 Mar 2024 19:06:24 +0800 Subject: [PATCH 13/81] pom --- pom.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 439120219..3a66524bc 100644 --- a/pom.xml +++ b/pom.xml @@ -15,16 +15,16 @@ yudao-module-system yudao-module-infra - - - - - - - - + + + + + + + + - + ${project.artifactId} From 379bd958400c43c3f4844b15b0ec1f4f91fd9ae2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 1 Mar 2024 23:37:07 +0800 Subject: [PATCH 14/81] =?UTF-8?q?CRM=EF=BC=9Acode=20review=20=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E5=88=86=E6=9E=90=EF=BC=88=E5=AE=A2=E6=88=B7=E6=80=BB?= =?UTF-8?q?=E9=87=8F=E5=88=86=E6=9E=90=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/statistics/CrmStatisticsCustomerController.java | 5 +++++ .../admin/statistics/CrmStatisticsRankController.java | 2 +- .../admin/statistics/vo/rank/CrmStatisticsRankReqVO.java | 2 +- .../admin/statistics/vo/rank/CrmStatisticsRankRespVO.java | 2 +- .../crm/dal/mysql/statistics/CrmStatisticsRankMapper.java | 2 +- .../crm/service/statistics/CrmStatisticsRankService.java | 2 +- .../crm/service/statistics/CrmStatisticsRankServiceImpl.java | 2 +- 7 files changed, 11 insertions(+), 6 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index 5644c512b..b51740daf 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -18,6 +18,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +// TODO @dhb52:数据统计 员工客户分析,改成“客户统计” @Tag(name = "管理后台 - CRM 数据统计 员工客户分析") @RestController @RequestMapping("/crm/statistics-customer") @@ -27,6 +28,10 @@ public class CrmStatisticsCustomerController { @Resource private CrmStatisticsCustomerService customerService; + // TODO @dhb52:建议 getCustomerCount 和 getDealTotalCustomerCount 搞成一个接口; + // 1. 数量接口:【方法:getCustomerSummaryByDate】,VO:CrmStatisticsCustomerSummaryByDateRespVO,然后里面是 time、customerCreateCount customerDealCount + // 2. 按人统计:【方法:getCustomerSummaryByUser】,VO:CrmStatisticsCustomerSummaryByOwnerRespVO,然后里面是 ownerUserId、ownerUserName、customerCreateCount customerDealCount、contractPrice、receivablePrice;客户成交率、未回款金额、回款完成率,交给前端计算; + @GetMapping("/get-total-customer-count") @Operation(summary = "获得新建客户数量") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java index fe79b1d3c..f3757ea28 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java @@ -19,7 +19,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -@Tag(name = "管理后台 - CRM 数据统计 排行榜统计") +@Tag(name = "管理后台 - CRM 排行榜统计") @RestController @RequestMapping("/crm/statistics-rank") @Validated diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankReqVO.java index 5bd5ec295..c9b1ae7e7 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankReqVO.java @@ -11,7 +11,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -@Schema(description = "管理后台 - CRM 数据统计 排行榜统计 Request VO") +@Schema(description = "管理后台 - CRM 排行榜统计 Request VO") @Data public class CrmStatisticsRankReqVO { diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java index 86260e74e..101cd8bcc 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java @@ -4,7 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - CRM 数据统计 排行榜统计 Response VO") +@Schema(description = "管理后台 - CRM 排行榜统计 Response VO") @Data public class CrmStatisticsRankRespVO { diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankMapper.java index b63e42ab8..d58241cf8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsRankMapper.java @@ -7,7 +7,7 @@ import org.apache.ibatis.annotations.Mapper; import java.util.List; /** - * CRM 数据统计 排行榜统计 Mapper + * CRM 排行榜统计 Mapper * * @author anhaohao */ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankService.java index f985f53f9..2fc8a5be9 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankService.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank.CrmStatis import java.util.List; /** - * CRM 数据统计 排行榜统计 Service 接口 + * CRM 排行榜统计 Service 接口 * * @author anhaohao */ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankServiceImpl.java index ff2acfef5..e591c35c0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsRankServiceImpl.java @@ -23,7 +23,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; /** - * CRM 数据统计 排行榜统计 Service 实现类 + * CRM 排行榜统计 Service 实现类 * * @author anhaohao */ From 1f35ef59d11a1c1d96a102aa770d5f88d79ddbfc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 2 Mar 2024 00:25:51 +0800 Subject: [PATCH 15/81] =?UTF-8?q?CRM=EF=BC=9Acode=20review=20excel=20?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E7=9A=84=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/core/handler/SelectSheetWriteHandler.java | 8 ++++++++ .../core/service/ExcelColumnSelectDataService.java | 5 +++++ .../module/crm/framework/excel/package-info.java | 1 + .../service/AreaExcelColumnSelectDataServiceImpl.java | 7 +------ .../crm/service/contract/CrmContractServiceImpl.java | 11 ++++++----- .../crm/service/receivable/CrmReceivableService.java | 1 + .../service/receivable/CrmReceivableServiceImpl.java | 7 ++++--- 7 files changed, 26 insertions(+), 14 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java index 421c59200..c50158de3 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.excel.core.handler; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.poi.excel.ExcelUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; @@ -27,7 +28,9 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. */ public class SelectSheetWriteHandler implements SheetWriteHandler { + // TODO @puhui999:注释哈; private static final List ALPHABET = getExcelColumnNameList(); + private static final List EXCEL_COLUMN_SELECT_DATA_SERVICES = new ArrayList<>(); /** @@ -43,6 +46,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { private static final String DICT_SHEET_NAME = "字典sheet"; + // TODO @puhui999:这个 selectMap 可以改成 Map>,然后在 afterSheetCreate 里面进行排序。这样整体的理解成本会更简单 private final List>> selectMap = new ArrayList<>(); // 使用 List + KeyValue 组合方便排序 public SelectSheetWriteHandler(Class head) { @@ -51,11 +55,14 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { if (MapUtil.isEmpty(beansMap)) { return; } + // TODO @puhui999:static 是共享。如果并发情况下,会不会存在问题? 其实 EXCEL_COLUMN_SELECT_DATA_SERVICES 不用全局声明,直接 按照 ExcelColumnSelect 去拿下就好了; if (CollUtil.isEmpty(EXCEL_COLUMN_SELECT_DATA_SERVICES) || EXCEL_COLUMN_SELECT_DATA_SERVICES.size() != beansMap.values().size()) { EXCEL_COLUMN_SELECT_DATA_SERVICES.clear(); EXCEL_COLUMN_SELECT_DATA_SERVICES.addAll(convertList(beansMap.values(), b -> b)); } + // 解析下拉数据 + // TODO @puhui999:感觉可以 head 循环 field,如果有 ExcelColumnSelect 则进行处理;而 ExcelProperty 可能是非必须的 Map excelPropertyFields = getFieldsWithAnnotation(head, ExcelProperty.class); Map excelColumnSelectFields = getFieldsWithAnnotation(head, ExcelColumnSelect.class); int colIndex = 0; @@ -157,6 +164,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { private static List getExcelColumnNameList() { + // TODO @puhui999:是不是可以使用 ExcelUtil.indexToColName() 替代 ArrayList strings = new ArrayList<>(); for (int i = 1; i <= 52; i++) { // 生成 52 列名称,需要更多请重写此方法 if (i <= 26) { diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java index 987cf7929..c4dc6e3bc 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java @@ -9,11 +9,14 @@ import java.util.List; * Excel 列下拉数据源获取接口 * * 为什么不直接解析字典还搞个接口?考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容 + * TODO @puhui999:是不是 @ExcelColumnSelect 可以搞两个属性,一个 dictType,一个 functionName;如果 dictType,则默认走字典,否则走 functionName + * 这样的话,ExcelColumnSelectDataService 改成 ExcelColumnSelectFunction 用于获取数据。 * * @author HUIHUI */ public interface ExcelColumnSelectDataService { + // TODO @puhui999:可以考虑改成 getName /** * 获得方法名称 * @@ -21,6 +24,7 @@ public interface ExcelColumnSelectDataService { */ String getFunctionName(); + // TODO @puhui999:可以考虑改成 getOptions;因为 select 下面是 option 哈,和前端 html 类似的标签; /** * 获得列下拉数据源 * @@ -28,6 +32,7 @@ public interface ExcelColumnSelectDataService { */ List getSelectDataList(); + // TODO @puhui999:这个建议放到 SelectSheetWriteHandler 里 default List handle(String funcName) { if (StrUtil.isEmptyIfStr(funcName) || !StrUtil.equals(getFunctionName(), funcName)) { return Collections.emptyList(); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java index 8b54b9f55..bd9cb957a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java @@ -1 +1,2 @@ +// TODO @puhui999:在 framework 目录,保持 config 和 core 目录的风格哈; package cn.iocoder.yudao.module.crm.framework.excel; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java index c6a653449..bd2bbd9e9 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.crm.framework.excel.service; import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; import cn.iocoder.yudao.framework.ip.core.Area; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; -import cn.iocoder.yudao.module.system.api.dict.DictDataApi; -import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import java.util.List; @@ -19,9 +17,6 @@ public class AreaExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDa public static final String FUNCTION_NAME = "getCrmAreaNameList"; // 防止和别的模块重名 - @Resource - private DictDataApi dictDataApi; - @Override public String getFunctionName() { return FUNCTION_NAME; @@ -31,7 +26,7 @@ public class AreaExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDa public List getSelectDataList() { // 获取地区下拉数据 // TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么? - Area area = AreaUtils.parseArea(Area.ID_CHINA); + Area area = AreaUtils.getArea(Area.ID_CHINA); return AreaUtils.getAreaNodePathList(area.getChildren()); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java index ec12acfd5..1a8772415 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java @@ -226,18 +226,19 @@ public class CrmContractServiceImpl implements CrmContractService { success = CRM_CONTRACT_DELETE_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) public void deleteContract(Long id) { - // 校验存在 + // 1.1 校验存在 CrmContractDO contract = validateContractExists(id); - // 如果被 CrmReceivableDO 所使用,则不允许删除 + // 1.2 如果被 CrmReceivableDO 所使用,则不允许删除 if (CollUtil.isNotEmpty(receivableService.getReceivableByContractId(contract.getId()))) { throw exception(CONTRACT_DELETE_FAIL); } - // 删除 + + // 2.1 删除合同 contractMapper.deleteById(id); - // 删除数据权限 + // 2.2 删除数据权限 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CONTRACT.getType(), id); - // 记录操作日志上下文 + // 3. 记录操作日志上下文 LogRecordContext.putVariable("contractName", contract.getName()); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java index a07f33a75..ed84ad03f 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java @@ -122,6 +122,7 @@ public interface CrmReceivableService { */ Map getReceivablePriceMapByContractId(Collection contractIds); + // TODO @puhui999:这个搞成根据数量判断,会更好一点哈; /** * 更具合同编号查询回款列表 * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java index 516556bc9..52db93273 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java @@ -224,12 +224,13 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { if (ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) { throw exception(RECEIVABLE_DELETE_FAIL_IS_APPROVE); } - // 2. 删除 + + // 2.1 删除回款 receivableMapper.deleteById(id); - // 3. 删除数据权限 + // 2.2 删除数据权限 permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), id); - // 4. 记录操作日志上下文 + // 3. 记录操作日志上下文 LogRecordContext.putVariable("receivable", receivable); LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId())); } From 1e13365ef4fee023ff119b92b7b5e2defa6f5158 Mon Sep 17 00:00:00 2001 From: zhanhong <283456800@qq.com> Date: Sat, 2 Mar 2024 13:23:54 +0800 Subject: [PATCH 16/81] pom --- yudao-module-report/yudao-module-report-biz/pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/yudao-module-report/yudao-module-report-biz/pom.xml b/yudao-module-report/yudao-module-report-biz/pom.xml index f38b38e75..62aeb3ee0 100644 --- a/yudao-module-report/yudao-module-report-biz/pom.xml +++ b/yudao-module-report/yudao-module-report-biz/pom.xml @@ -77,16 +77,6 @@ com.bstek.ureport ureport2-console - - - org.apache.poi - poi - - - org.apache.poi - poi-ooxml - - From 29b7106985719aa727364236eff6a1185c9680fb Mon Sep 17 00:00:00 2001 From: zhanhong <283456800@qq.com> Date: Sat, 2 Mar 2024 13:26:05 +0800 Subject: [PATCH 17/81] =?UTF-8?q?fix:=E5=8C=85=E5=86=B2=E7=AA=81=20?= =?UTF-8?q?=E7=A7=AF=E6=9C=A8=E6=8A=A5=E8=A1=A8=20=E5=AF=BC=E5=87=BAExcel?= =?UTF-8?q?=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 7c7ca609b..c9667aea2 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -626,6 +626,16 @@ com.bstek.ureport ureport2-console ${ureport2.version} + + + org.apache.poi + poi + + + org.apache.poi + poi-ooxml + + From f56fab932a34369b49457de7ab1569cd966710e9 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sat, 2 Mar 2024 23:24:42 +0800 Subject: [PATCH 18/81] =?UTF-8?q?CRM=EF=BC=9A=E5=AE=8C=E5=96=84=20excel=20?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=20review=20=E6=8F=90=E5=88=B0=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dict/core/DictFrameworkUtils.java | 22 ++++++ .../core/annotations/ExcelColumnSelect.java | 7 +- .../function/ExcelColumnSelectFunction.java | 28 +++++++ .../core/handler/SelectSheetWriteHandler.java | 75 ++++++++++--------- .../service/ExcelColumnSelectDataService.java | 43 ----------- .../vo/customer/CrmCustomerImportExcelVO.java | 13 ++-- .../AreaExcelColumnSelectFunctionImpl.java} | 14 ++-- .../crm/framework/excel/package-info.java | 1 - ...ustryExcelColumnSelectDataServiceImpl.java | 35 --------- ...LevelExcelColumnSelectDataServiceImpl.java | 35 --------- ...ourceExcelColumnSelectDataServiceImpl.java | 35 --------- .../contract/CrmContractServiceImpl.java | 2 +- .../receivable/CrmReceivableService.java | 3 +- .../receivable/CrmReceivableServiceImpl.java | 4 +- 14 files changed, 110 insertions(+), 207 deletions(-) create mode 100644 yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/function/ExcelColumnSelectFunction.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/{service/AreaExcelColumnSelectDataServiceImpl.java => core/AreaExcelColumnSelectFunctionImpl.java} (56%) delete mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerIndustryExcelColumnSelectDataServiceImpl.java delete mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerLevelExcelColumnSelectDataServiceImpl.java delete mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerSourceExcelColumnSelectDataServiceImpl.java diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java index 9fb0f692e..e51ef16e7 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java @@ -11,6 +11,9 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import java.time.Duration; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; /** * 字典工具类 @@ -38,6 +41,20 @@ public class DictFrameworkUtils { }); + /** + * 针对 {@link #getDictDataLabelList(String)} 的缓存 + */ + private static final LoadingCache> GET_DICT_DATA_LIST_CACHE = CacheUtils.buildAsyncReloadingCache( + Duration.ofMinutes(1L), // 过期时间 1 分钟 + new CacheLoader>() { + + @Override + public List load(String dictType) { + return dictDataApi.getDictDataLabelList(dictType); + } + + }); + /** * 针对 {@link #parseDictDataValue(String, String)} 的缓存 */ @@ -67,6 +84,11 @@ public class DictFrameworkUtils { return GET_DICT_DATA_CACHE.get(new KeyValue<>(dictType, value)).getLabel(); } + @SneakyThrows + public static List getDictDataLabelList(String dictType) { + return GET_DICT_DATA_LIST_CACHE.get(dictType); + } + @SneakyThrows public static String parseDictDataValue(String dictType, String label) { return PARSE_DICT_DATA_CACHE.get(new KeyValue<>(dictType, label)).getValue(); diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java index 2c519523b..5daa1e064 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java @@ -12,9 +12,14 @@ import java.lang.annotation.*; @Inherited public @interface ExcelColumnSelect { + /** + * @return 字典类型 + */ + String dictType() default ""; + /** * @return 获取下拉数据源的方法名称 */ - String value(); + String functionName() default ""; } diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/function/ExcelColumnSelectFunction.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/function/ExcelColumnSelectFunction.java new file mode 100644 index 000000000..51953c463 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/function/ExcelColumnSelectFunction.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.framework.excel.core.function; + +import java.util.List; + +/** + * Excel 列下拉数据源获取接口 + * + * 为什么不直接解析字典还搞个接口?考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容 + + * @author HUIHUI + */ +public interface ExcelColumnSelectFunction { + + /** + * 获得方法名称 + * + * @return 方法名称 + */ + String getName(); + + /** + * 获得列下拉数据源 + * + * @return 下拉数据源 + */ + List getOptions(); + +} diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java index c50158de3..df4601b94 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java @@ -2,15 +2,19 @@ package cn.iocoder.yudao.framework.excel.core.handler; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.hutool.poi.excel.ExcelUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; -import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; +import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.write.handler.SheetWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import lombok.extern.slf4j.Slf4j; import org.apache.poi.hssf.usermodel.HSSFDataValidation; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; @@ -26,13 +30,9 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. * * @author HUIHUI */ +@Slf4j public class SelectSheetWriteHandler implements SheetWriteHandler { - // TODO @puhui999:注释哈; - private static final List ALPHABET = getExcelColumnNameList(); - - private static final List EXCEL_COLUMN_SELECT_DATA_SERVICES = new ArrayList<>(); - /** * 数据起始行从 0 开始 * @@ -46,34 +46,35 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { private static final String DICT_SHEET_NAME = "字典sheet"; - // TODO @puhui999:这个 selectMap 可以改成 Map>,然后在 afterSheetCreate 里面进行排序。这样整体的理解成本会更简单 - private final List>> selectMap = new ArrayList<>(); // 使用 List + KeyValue 组合方便排序 + /** + * key: 列 value: 下拉数据源 + */ + private final Map> selectMap = new HashMap<>(); public SelectSheetWriteHandler(Class head) { // 加载下拉数据获取接口 - Map beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectDataService.class); + Map beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectFunction.class); if (MapUtil.isEmpty(beansMap)) { return; } - // TODO @puhui999:static 是共享。如果并发情况下,会不会存在问题? 其实 EXCEL_COLUMN_SELECT_DATA_SERVICES 不用全局声明,直接 按照 ExcelColumnSelect 去拿下就好了; - if (CollUtil.isEmpty(EXCEL_COLUMN_SELECT_DATA_SERVICES) || EXCEL_COLUMN_SELECT_DATA_SERVICES.size() != beansMap.values().size()) { - EXCEL_COLUMN_SELECT_DATA_SERVICES.clear(); - EXCEL_COLUMN_SELECT_DATA_SERVICES.addAll(convertList(beansMap.values(), b -> b)); - } // 解析下拉数据 - // TODO @puhui999:感觉可以 head 循环 field,如果有 ExcelColumnSelect 则进行处理;而 ExcelProperty 可能是非必须的 + // TODO @puhui999:感觉可以 head 循环 field,如果有 ExcelColumnSelect 则进行处理;而 ExcelProperty 可能是非必须的。回答:主要是用于定位到列索引 Map excelPropertyFields = getFieldsWithAnnotation(head, ExcelProperty.class); Map excelColumnSelectFields = getFieldsWithAnnotation(head, ExcelColumnSelect.class); int colIndex = 0; for (String fieldName : excelPropertyFields.keySet()) { Field field = excelColumnSelectFields.get(fieldName); if (field != null) { + // ExcelProperty 有一个自定义列索引的属性 index 兼容这个字段 + int index = field.getAnnotation(ExcelProperty.class).index(); + if (index != -1) { + colIndex = index; + } getSelectDataList(colIndex, field); } colIndex++; } - selectMap.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错 } /** @@ -83,12 +84,25 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { * @param field 字段 */ private void getSelectDataList(int colIndex, Field field) { - EXCEL_COLUMN_SELECT_DATA_SERVICES.forEach(selectDataService -> { - List stringList = selectDataService.handle(field.getAnnotation(ExcelColumnSelect.class).value()); - if (CollUtil.isEmpty(stringList)) { + // 获得下拉注解信息 + ExcelColumnSelect columnSelect = field.getAnnotation(ExcelColumnSelect.class); + String dictType = columnSelect.dictType(); + if (StrUtil.isNotEmpty(dictType)) { // 情况一: 字典数据 (默认) + selectMap.put(colIndex, DictFrameworkUtils.getDictDataLabelList(dictType)); + return; + } + String functionName = columnSelect.functionName(); + if (StrUtil.isEmpty(functionName)) { // 情况二: 获取自定义数据 + log.warn("[getSelectDataList]解析下拉数据失败,参数信息 dictType[{}] functionName[{}]", dictType, functionName); + return; + } + // 获得所有的下拉数据源获取方法 + Map functionMap = SpringUtil.getApplicationContext().getBeansOfType(ExcelColumnSelectFunction.class); + functionMap.values().forEach(func -> { + if (ObjUtil.notEqual(func.getName(), functionName)) { return; } - selectMap.add(new KeyValue<>(colIndex, stringList)); + selectMap.put(colIndex, func.getOptions()); }); } @@ -101,10 +115,11 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { // 1. 获取相应操作对象 DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手 Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿 - + List>> keyValues = convertList(selectMap.entrySet(), entry -> new KeyValue<>(entry.getKey(), entry.getValue())); + keyValues.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错 // 2. 创建数据字典的 sheet 页 Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME); - for (KeyValue> keyValue : selectMap) { + for (KeyValue> keyValue : keyValues) { int rowLength = keyValue.getValue().size(); // 2.1 设置字典 sheet 页的值 每一列一个字典项 for (int i = 0; i < rowLength; i++) { @@ -126,7 +141,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { KeyValue> keyValue) { // 1.1 创建可被其他单元格引用的名称 Name name = workbook.createName(); - String excelColumn = ALPHABET.get(keyValue.getKey()); + String excelColumn = ExcelUtil.indexToColName(keyValue.getKey()); // 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2 String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size(); name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字 @@ -162,18 +177,4 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { return annotatedFields; } - - private static List getExcelColumnNameList() { - // TODO @puhui999:是不是可以使用 ExcelUtil.indexToColName() 替代 - ArrayList strings = new ArrayList<>(); - for (int i = 1; i <= 52; i++) { // 生成 52 列名称,需要更多请重写此方法 - if (i <= 26) { - strings.add(String.valueOf((char) ('A' + i - 1))); // 使用 ASCII 码值转字母 - } else { - strings.add(String.valueOf((char) ('A' + (i - 1) / 26 - 1)) + (char) ('A' + (i - 1) % 26)); - } - } - return strings; - } - } \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java deleted file mode 100644 index c4dc6e3bc..000000000 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/service/ExcelColumnSelectDataService.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.framework.excel.core.service; - -import cn.hutool.core.util.StrUtil; - -import java.util.Collections; -import java.util.List; - -/** - * Excel 列下拉数据源获取接口 - * - * 为什么不直接解析字典还搞个接口?考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容 - * TODO @puhui999:是不是 @ExcelColumnSelect 可以搞两个属性,一个 dictType,一个 functionName;如果 dictType,则默认走字典,否则走 functionName - * 这样的话,ExcelColumnSelectDataService 改成 ExcelColumnSelectFunction 用于获取数据。 - * - * @author HUIHUI - */ -public interface ExcelColumnSelectDataService { - - // TODO @puhui999:可以考虑改成 getName - /** - * 获得方法名称 - * - * @return 方法名称 - */ - String getFunctionName(); - - // TODO @puhui999:可以考虑改成 getOptions;因为 select 下面是 option 哈,和前端 html 类似的标签; - /** - * 获得列下拉数据源 - * - * @return 下拉数据源 - */ - List getSelectDataList(); - - // TODO @puhui999:这个建议放到 SelectSheetWriteHandler 里 - default List handle(String funcName) { - if (StrUtil.isEmptyIfStr(funcName) || !StrUtil.equals(getFunctionName(), funcName)) { - return Collections.emptyList(); - } - return getSelectDataList(); - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java index 4b5fbefbd..f06122c3b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java @@ -4,10 +4,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.crm.framework.excel.service.AreaExcelColumnSelectDataServiceImpl; -import cn.iocoder.yudao.module.crm.framework.excel.service.CrmCustomerIndustryExcelColumnSelectDataServiceImpl; -import cn.iocoder.yudao.module.crm.framework.excel.service.CrmCustomerLevelExcelColumnSelectDataServiceImpl; -import cn.iocoder.yudao.module.crm.framework.excel.service.CrmCustomerSourceExcelColumnSelectDataServiceImpl; +import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunctionImpl; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -46,7 +43,7 @@ public class CrmCustomerImportExcelVO { private String email; @ExcelProperty(value = "地区", converter = AreaConvert.class) - @ExcelColumnSelect(AreaExcelColumnSelectDataServiceImpl.FUNCTION_NAME) + @ExcelColumnSelect(functionName = AreaExcelColumnSelectFunctionImpl.NAME) private Integer areaId; @ExcelProperty("详细地址") @@ -54,17 +51,17 @@ public class CrmCustomerImportExcelVO { @ExcelProperty(value = "所属行业", converter = DictConvert.class) @DictFormat(CRM_CUSTOMER_INDUSTRY) - @ExcelColumnSelect(CrmCustomerIndustryExcelColumnSelectDataServiceImpl.FUNCTION_NAME) + @ExcelColumnSelect(dictType = CRM_CUSTOMER_INDUSTRY) private Integer industryId; @ExcelProperty(value = "客户等级", converter = DictConvert.class) @DictFormat(CRM_CUSTOMER_LEVEL) - @ExcelColumnSelect(CrmCustomerLevelExcelColumnSelectDataServiceImpl.FUNCTION_NAME) + @ExcelColumnSelect(dictType = CRM_CUSTOMER_LEVEL) private Integer level; @ExcelProperty(value = "客户来源", converter = DictConvert.class) @DictFormat(CRM_CUSTOMER_SOURCE) - @ExcelColumnSelect(CrmCustomerSourceExcelColumnSelectDataServiceImpl.FUNCTION_NAME) + @ExcelColumnSelect(dictType = CRM_CUSTOMER_SOURCE) private Integer source; @ExcelProperty("备注") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunctionImpl.java similarity index 56% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunctionImpl.java index bd2bbd9e9..9df93ac6d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/AreaExcelColumnSelectDataServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunctionImpl.java @@ -1,6 +1,6 @@ -package cn.iocoder.yudao.module.crm.framework.excel.service; +package cn.iocoder.yudao.module.crm.framework.excel.core; -import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; +import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction; import cn.iocoder.yudao.framework.ip.core.Area; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; import org.springframework.stereotype.Service; @@ -13,17 +13,17 @@ import java.util.List; * @author HUIHUI */ @Service -public class AreaExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDataService { +public class AreaExcelColumnSelectFunctionImpl implements ExcelColumnSelectFunction { - public static final String FUNCTION_NAME = "getCrmAreaNameList"; // 防止和别的模块重名 + public static final String NAME = "getCrmAreaNameList"; // 防止和别的模块重名 @Override - public String getFunctionName() { - return FUNCTION_NAME; + public String getName() { + return NAME; } @Override - public List getSelectDataList() { + public List getOptions() { // 获取地区下拉数据 // TODO @puhui999:嘿嘿,这里改成省份、城市、区域,三个选项,难度大么? Area area = AreaUtils.getArea(Area.ID_CHINA); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java index bd9cb957a..8b54b9f55 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java @@ -1,2 +1 @@ -// TODO @puhui999:在 framework 目录,保持 config 和 core 目录的风格哈; package cn.iocoder.yudao.module.crm.framework.excel; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerIndustryExcelColumnSelectDataServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerIndustryExcelColumnSelectDataServiceImpl.java deleted file mode 100644 index 8a78104bf..000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerIndustryExcelColumnSelectDataServiceImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.crm.framework.excel.service; - -import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; -import cn.iocoder.yudao.module.system.api.dict.DictDataApi; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; - -import java.util.List; - -import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY; - -/** - * Excel 客户所属行业列下拉数据源获取接口实现类 - * - * @author HUIHUI - */ -@Service -public class CrmCustomerIndustryExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDataService { - - public static final String FUNCTION_NAME = "getCrmCustomerIndustryList"; - - @Resource - private DictDataApi dictDataApi; - - @Override - public String getFunctionName() { - return FUNCTION_NAME; - } - - @Override - public List getSelectDataList() { - return dictDataApi.getDictDataLabelList(CRM_CUSTOMER_INDUSTRY); - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerLevelExcelColumnSelectDataServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerLevelExcelColumnSelectDataServiceImpl.java deleted file mode 100644 index 5948aa427..000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerLevelExcelColumnSelectDataServiceImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.crm.framework.excel.service; - -import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; -import cn.iocoder.yudao.module.system.api.dict.DictDataApi; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; - -import java.util.List; - -import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL; - -/** - * Excel 客户等级列下拉数据源获取接口实现类 - * - * @author HUIHUI - */ -@Service -public class CrmCustomerLevelExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDataService { - - public static final String FUNCTION_NAME = "getCrmCustomerLevelList"; - - @Resource - private DictDataApi dictDataApi; - - @Override - public String getFunctionName() { - return FUNCTION_NAME; - } - - @Override - public List getSelectDataList() { - return dictDataApi.getDictDataLabelList(CRM_CUSTOMER_LEVEL); - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerSourceExcelColumnSelectDataServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerSourceExcelColumnSelectDataServiceImpl.java deleted file mode 100644 index c160ed90f..000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/service/CrmCustomerSourceExcelColumnSelectDataServiceImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.crm.framework.excel.service; - -import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService; -import cn.iocoder.yudao.module.system.api.dict.DictDataApi; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; - -import java.util.List; - -import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE; - -/** - * Excel 客户来源列下拉数据源获取接口实现类 - * - * @author HUIHUI - */ -@Service -public class CrmCustomerSourceExcelColumnSelectDataServiceImpl implements ExcelColumnSelectDataService { - - public static final String FUNCTION_NAME = "getCrmCustomerSourceList"; - - @Resource - private DictDataApi dictDataApi; - - @Override - public String getFunctionName() { - return FUNCTION_NAME; - } - - @Override - public List getSelectDataList() { - return dictDataApi.getDictDataLabelList(CRM_CUSTOMER_SOURCE); - } - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java index 1a8772415..fdab61428 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java @@ -229,7 +229,7 @@ public class CrmContractServiceImpl implements CrmContractService { // 1.1 校验存在 CrmContractDO contract = validateContractExists(id); // 1.2 如果被 CrmReceivableDO 所使用,则不允许删除 - if (CollUtil.isNotEmpty(receivableService.getReceivableByContractId(contract.getId()))) { + if (receivableService.getReceivableByContractId(contract.getId()) != 0) { throw exception(CONTRACT_DELETE_FAIL); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java index ed84ad03f..c4a68fd70 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java @@ -122,13 +122,12 @@ public interface CrmReceivableService { */ Map getReceivablePriceMapByContractId(Collection contractIds); - // TODO @puhui999:这个搞成根据数量判断,会更好一点哈; /** * 更具合同编号查询回款列表 * * @param contractId 合同编号 * @return 回款 */ - List getReceivableByContractId(Long contractId); + Long getReceivableByContractId(Long contractId); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java index 52db93273..889d94e1f 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java @@ -302,8 +302,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { } @Override - public List getReceivableByContractId(Long contractId) { - return receivableMapper.selectList(CrmReceivableDO::getContractId, contractId); + public Long getReceivableByContractId(Long contractId) { + return receivableMapper.selectCount(CrmReceivableDO::getContractId, contractId); } } From 79c51c6433eb3e1eacbc22002f07eb5f26fb5a06 Mon Sep 17 00:00:00 2001 From: E1 <876394202@qq.com> Date: Sun, 3 Mar 2024 10:26:28 +0000 Subject: [PATCH 19/81] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20Java21=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E4=B8=8Bdocker-compose=E4=B8=ADRedis=20Host=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=90=8D=E7=A7=B0=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: E1 <876394202@qq.com> --- script/docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml index e440d96c3..ae977dd97 100644 --- a/script/docker/docker-compose.yml +++ b/script/docker/docker-compose.yml @@ -50,7 +50,7 @@ services: --spring.datasource.dynamic.datasource.slave.url=${SLAVE_DATASOURCE_URL:-jdbc:mysql://yudao-mysql:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true} --spring.datasource.dynamic.datasource.slave.username=${SLAVE_DATASOURCE_USERNAME:-root} --spring.datasource.dynamic.datasource.slave.password=${SLAVE_DATASOURCE_PASSWORD:-123456} - --spring.redis.host=${REDIS_HOST:-yudao-redis} + --spring.data.redis.host=${REDIS_HOST:-yudao-redis} depends_on: - mysql - redis From ba1b02800187705b09d043a6dc772aafd19f5e90 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 3 Mar 2024 20:10:30 +0800 Subject: [PATCH 20/81] =?UTF-8?q?CRM=EF=BC=9A=E5=AE=8C=E5=96=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=9D=83=E9=99=90=EF=BC=8C=E5=AE=9E=E7=8E=B0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=9D=83=E9=99=90=E5=90=8C=E6=97=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E3=80=81=E5=90=8C=E6=97=B6=E8=BD=AC=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/business/CrmBusinessTransferReqVO.java | 6 +- .../admin/clue/vo/CrmClueTransferReqVO.java | 2 +- .../contact/vo/CrmContactTransferReqVO.java | 6 +- .../vo/contract/CrmContractTransferReqVO.java | 6 +- .../admin/customer/CrmCustomerController.java | 2 - .../vo/customer/CrmCustomerTransferReqVO.java | 10 +++- .../permission/CrmPermissionController.java | 58 ++++++++++++++++--- .../vo/CrmPermissionCreateReqVO.java | 14 ----- .../permission/vo/CrmPermissionRespVO.java | 24 +++++++- ...aseVO.java => CrmPermissionSaveReqVO.java} | 17 +++--- .../dal/mysql/business/CrmBusinessMapper.java | 8 +++ .../dal/mysql/contact/CrmContactMapper.java | 6 ++ .../dal/mysql/contract/CrmContractMapper.java | 6 ++ .../service/business/CrmBusinessService.java | 15 ++++- .../business/CrmBusinessServiceImpl.java | 21 ++++--- .../crm/service/clue/CrmClueServiceImpl.java | 10 ++-- .../service/contact/CrmContactService.java | 13 ++++- .../contact/CrmContactServiceImpl.java | 17 ++++-- .../service/contract/CrmContractService.java | 15 ++++- .../contract/CrmContractServiceImpl.java | 17 ++++-- .../customer/CrmCustomerServiceImpl.java | 52 +++++++++++++++-- 21 files changed, 253 insertions(+), 72 deletions(-) delete mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionCreateReqVO.java rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/{CrmPermissionBaseVO.java => CrmPermissionSaveReqVO.java} (74%) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java index a76c48cae..083ea3570 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java @@ -3,15 +3,19 @@ package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Schema(description = "管理后台 - 商机转移 Request VO") @Data +@NoArgsConstructor +@AllArgsConstructor public class CrmBusinessTransferReqVO { @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") @NotNull(message = "商机编号不能为空") - private Long id; + private Long bizId; /** * 新负责人的用户编号 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java index 63bdc1838..7e6b8f0dd 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java @@ -12,7 +12,7 @@ public class CrmClueTransferReqVO { @Schema(description = "线索编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") @NotNull(message = "线索编号不能为空") - private Long id; + private Long bizId; @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") @NotNull(message = "新负责人的用户编号不能为空") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java index c65b205c8..8b5a6a6f4 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java @@ -2,17 +2,21 @@ package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; import lombok.Data; import jakarta.validation.constraints.NotNull; +import lombok.NoArgsConstructor; @Schema(description = "管理后台 - CRM 联系人转移 Request VO") @Data +@NoArgsConstructor +@AllArgsConstructor public class CrmContactTransferReqVO { @Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") @NotNull(message = "联系人编号不能为空") - private Long id; + private Long bizId; /** * 新负责人的用户编号 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractTransferReqVO.java index 4b8245c40..b59e21e16 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractTransferReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractTransferReqVO.java @@ -3,17 +3,21 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; import lombok.Data; import jakarta.validation.constraints.NotNull; +import lombok.NoArgsConstructor; @Schema(description = "管理后台 - CRM 合同转移 Request VO") @Data +@NoArgsConstructor +@AllArgsConstructor public class CrmContractTransferReqVO { @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") @NotNull(message = "联系人编号不能为空") - private Long id; + private Long bizId; @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") @NotNull(message = "新负责人的用户编号不能为空") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java index 7745a6e6c..9111c26f7 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java @@ -62,8 +62,6 @@ public class CrmCustomerController { private DeptApi deptApi; @Resource private AdminUserApi adminUserApi; - @Resource - private DictDataApi dictDataApi; @PostMapping("/create") @Operation(summary = "创建客户") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java index e62a84fd7..98d0d4a64 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java @@ -5,13 +5,15 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; +import java.util.List; + @Schema(description = "管理后台 - CRM 客户转移 Request VO") @Data public class CrmCustomerTransferReqVO { @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") @NotNull(message = "客户编号不能为空") - private Long id; + private Long bizId; /** * 新负责人的用户编号 @@ -28,4 +30,10 @@ public class CrmCustomerTransferReqVO { @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Integer oldOwnerPermissionLevel; + /** + * 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择 + */ + @Schema(description = "同时转移", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") + private List toBizTypes; + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java index 428bd07ad..1cf8d7591 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java @@ -5,12 +5,19 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO; import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.system.api.dept.DeptApi; @@ -27,13 +34,11 @@ 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.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -50,7 +55,12 @@ public class CrmPermissionController { @Resource private CrmPermissionService permissionService; - + @Resource + private CrmContactService contactService; + @Resource + private CrmBusinessService businessService; + @Resource + private CrmContractService contractService; @Resource private AdminUserApi adminUserApi; @Resource @@ -60,13 +70,47 @@ public class CrmPermissionController { @PostMapping("/create") @Operation(summary = "创建数据权限") + @Transactional(rollbackFor = Exception.class) @PreAuthorize("@ss.hasPermission('crm:permission:create')") @CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) - public CommonResult addPermission(@Valid @RequestBody CrmPermissionCreateReqVO reqVO) { + public CommonResult addPermission(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) { permissionService.createPermission(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class)); + if (CollUtil.isNotEmpty(reqVO.getToBizTypes())) { + createBizTypePermissions(reqVO); + } return success(true); } + + private void createBizTypePermissions(CrmPermissionSaveReqVO reqVO) { + List createPermissions = new ArrayList<>(); + if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTACT.getType())) { + List contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getBizId(), getLoginUserId()); + contactList.forEach(item -> { + createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()) + .setBizId(item.getId()).setUserId(reqVO.getUserId()).setLevel(reqVO.getLevel())); + }); + } + if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_BUSINESS.getType())) { + List businessList = businessService.getBusinessListByCustomerIdOwnerUserId(reqVO.getBizId(), getLoginUserId()); + businessList.forEach(item -> { + createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()) + .setBizId(item.getId()).setUserId(reqVO.getUserId()).setLevel(reqVO.getLevel())); + }); + } + if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTRACT.getType())) { + List contractList = contractService.getContractListByCustomerIdOwnerUserId(reqVO.getBizId(), getLoginUserId()); + contractList.forEach(item -> { + createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()) + .setBizId(item.getId()).setUserId(reqVO.getUserId()).setLevel(reqVO.getLevel())); + }); + } + if (CollUtil.isEmpty(createPermissions)) { + return; + } + permissionService.createPermissionBatch(createPermissions); + } + @PutMapping("/update") @Operation(summary = "编辑数据权限") @PreAuthorize("@ss.hasPermission('crm:permission:update')") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionCreateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionCreateReqVO.java deleted file mode 100644 index 99793389b..000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionCreateReqVO.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.iocoder.yudao.module.crm.controller.admin.permission.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -@Schema(description = "管理后台 - CRM 数据权限创建 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class CrmPermissionCreateReqVO extends CrmPermissionBaseVO { - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java index 10f1ce198..aeddf1a5d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionRespVO.java @@ -1,6 +1,10 @@ package cn.iocoder.yudao.module.crm.controller.admin.permission.vo; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; import java.time.LocalDateTime; @@ -8,11 +12,29 @@ import java.util.Set; @Schema(description = "管理后台 - CRM 数据权限 Response VO") @Data -public class CrmPermissionRespVO extends CrmPermissionBaseVO { +public class CrmPermissionRespVO { @Schema(description = "数据权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") private Long id; + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @NotNull(message = "用户编号不能为空") + private Long userId; + + @Schema(description = "CRM 类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(CrmBizTypeEnum.class) + @NotNull(message = "CRM 类型不能为空") + private Integer bizType; + + @Schema(description = "CRM 类型数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "CRM 类型数据编号不能为空") + private Long bizId; + + @Schema(description = "权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @InEnum(CrmPermissionLevelEnum.class) + @NotNull(message = "权限级别不能为空") + private Integer level; + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") private String nickname; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionBaseVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java similarity index 74% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionBaseVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java index 796b3cd46..9635cc627 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionBaseVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java @@ -8,14 +8,11 @@ import lombok.Data; import jakarta.validation.constraints.NotNull; -/** - * 数据权限 Base VO,提供给添加、修改、详细的子 VO 使用 - * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 - * - * @author HUIHUI - */ +import java.util.List; + +@Schema(description = "管理后台 - CRM 数据权限创建/更新 Request VO") @Data -public class CrmPermissionBaseVO { +public class CrmPermissionSaveReqVO { @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") @NotNull(message = "用户编号不能为空") @@ -35,4 +32,10 @@ public class CrmPermissionBaseVO { @NotNull(message = "权限级别不能为空") private Integer level; + /** + * 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择 + */ + @Schema(description = "同时添加", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") + private List toBizTypes; + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java index 4718a8d7a..fc5b070f4 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java @@ -6,12 +6,14 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.Collection; +import java.util.List; /** * 商机 Mapper @@ -57,4 +59,10 @@ public interface CrmBusinessMapper extends BaseMapperX { return selectCount(CrmBusinessDO::getStatusTypeId, statusTypeId); } + default List selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId){ + return selectList(new LambdaQueryWrapperX() + .eq(CrmBusinessDO::getCustomerId, customerId) + .eq(CrmBusinessDO::getOwnerUserId, ownerUserId)); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java index 4a77665ad..d4244bce0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java @@ -73,4 +73,10 @@ public interface CrmContactMapper extends BaseMapperX { return selectList(CrmContactDO::getCustomerId, customerId); } + default List selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { + return selectList(new LambdaQueryWrapperX() + .eq(CrmContactDO::getCustomerId, customerId) + .eq(CrmContactDO::getOwnerUserId, ownerUserId)); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java index e06afb257..14d743291 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java @@ -117,4 +117,10 @@ public interface CrmContractMapper extends BaseMapperX { return selectCount(query); } + default List selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { + return selectList(new LambdaQueryWrapperX() + .eq(CrmContractDO::getCustomerId, customerId) + .eq(CrmContractDO::getOwnerUserId, ownerUserId)); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java index ab7982024..7bd899b64 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java @@ -46,8 +46,8 @@ public interface CrmBusinessService { /** * 更新商机相关跟进信息 * - * @param id 编号 - * @param contactNextTime 下次联系时间 + * @param id 编号 + * @param contactNextTime 下次联系时间 * @param contactLastContent 最后联系内容 */ void updateBusinessFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent); @@ -55,7 +55,7 @@ public interface CrmBusinessService { /** * 更新商机的下次联系时间 * - * @param ids 编号数组 + * @param ids 编号数组 * @param contactNextTime 下次联系时间 */ void updateBusinessContactNextTime(Collection ids, LocalDateTime contactNextTime); @@ -185,4 +185,13 @@ public interface CrmBusinessService { return status.getName(); } + /** + * 获得商机列表 + * + * @param customerId 客户编号 + * @param ownerUserId 负责人编号 + * @return 商机列表 + */ + List getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java index e709b6547..9f2e4ceb6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.service.business; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -23,6 +24,7 @@ import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService; import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerServiceImpl; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; @@ -204,7 +206,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { private List validateBusinessProducts(List list) { // 1. 校验产品存在 - productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.Product::getProductId)); + productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.Product::getProductId)); // 2. 转化为 CrmBusinessProductDO 列表 return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class, item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount())))); @@ -234,7 +236,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { } // 1.4 校验是不是状态没变更 if ((reqVO.getStatusId() != null && reqVO.getStatusId().equals(business.getStatusId())) - || (reqVO.getEndStatus() != null && reqVO.getEndStatus().equals(business.getEndStatus()))) { + || (reqVO.getEndStatus() != null && reqVO.getEndStatus().equals(business.getEndStatus()))) { throw exception(BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS); } @@ -292,18 +294,18 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @Override @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.bizId}}", success = CRM_BUSINESS_TRANSFER_SUCCESS) - @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) public void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId) { // 1 校验商机是否存在 - CrmBusinessDO business = validateBusinessExists(reqVO.getId()); + CrmBusinessDO business = validateBusinessExists(reqVO.getBizId()); // 2.1 数据权限转移 permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_BUSINESS.getType(), - reqVO.getNewOwnerUserId(), reqVO.getId(), CrmPermissionLevelEnum.OWNER.getLevel())); + reqVO.getBizId(), reqVO.getNewOwnerUserId(), CrmPermissionLevelEnum.OWNER.getLevel())); // 2.2 设置新的负责人 - businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); + businessMapper.updateOwnerUserIdById(reqVO.getBizId(), reqVO.getNewOwnerUserId()); // 记录操作日志上下文 LogRecordContext.putVariable("business", business); @@ -370,4 +372,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { return businessMapper.selectCountByStatusTypeId(statusTypeId); } + @Override + public List getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { + return businessMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java index 70ebeb44d..3b4f0bc34 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java @@ -159,18 +159,18 @@ public class CrmClueServiceImpl implements CrmClueService { @Override @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.bizId}}", success = CRM_CLUE_TRANSFER_SUCCESS) - @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) public void transferClue(CrmClueTransferReqVO reqVO, Long userId) { // 1 校验线索是否存在 - CrmClueDO clue = validateClueExists(reqVO.getId()); + CrmClueDO clue = validateClueExists(reqVO.getBizId()); // 2.1 数据权限转移 crmPermissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CLUE.getType(), - reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); + reqVO.getBizId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); // 2.2 设置新的负责人 - clueMapper.updateById(new CrmClueDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId())); + clueMapper.updateById(new CrmClueDO().setId(reqVO.getBizId()).setOwnerUserId(reqVO.getNewOwnerUserId())); // 3. 记录转移日志 LogRecordContext.putVariable("clue", clue); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java index 23c29d3bc..971d413af 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java @@ -75,8 +75,8 @@ public interface CrmContactService { /** * 更新联系人的下次联系时间 * - * @param ids 编号数组 - * @param contactNextTime 下次联系时间 + * @param ids 编号数组 + * @param contactNextTime 下次联系时间 */ void updateContactContactNextTime(Collection ids, LocalDateTime contactNextTime); @@ -160,4 +160,13 @@ public interface CrmContactService { */ Long getContactCountByCustomerId(Long customerId); + /** + * 获得联系人列表 + * + * @param customerId 客户编号 + * @param ownerUserId 负责人编号 + * @return 联系人列表 + */ + List getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java index d8e356124..ea46edf11 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.service.contact; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO; @@ -17,6 +18,7 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerServiceImpl; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; @@ -177,18 +179,18 @@ public class CrmContactServiceImpl implements CrmContactService { @Override @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.bizId}}", success = CRM_CONTACT_TRANSFER_SUCCESS) - @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) public void transferContact(CrmContactTransferReqVO reqVO, Long userId) { // 1 校验联系人是否存在 - CrmContactDO contact = validateContactExists(reqVO.getId()); + CrmContactDO contact = validateContactExists(reqVO.getBizId()); // 2.1 数据权限转移 permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTACT.getType(), - reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); + reqVO.getBizId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); // 2.2 设置新的负责人 - contactMapper.updateById(new CrmContactDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId())); + contactMapper.updateById(new CrmContactDO().setId(reqVO.getBizId()).setOwnerUserId(reqVO.getNewOwnerUserId())); // 3. 记录转移日志 LogRecordContext.putVariable("contact", contact); @@ -298,4 +300,9 @@ public class CrmContactServiceImpl implements CrmContactService { return contactMapper.selectCount(CrmContactDO::getCustomerId, customerId); } + @Override + public List getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { + return contactMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId); + } + } \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java index 25f5537dc..0d8964f18 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java @@ -58,8 +58,8 @@ public interface CrmContractService { /** * 更新合同相关的更进信息 * - * @param id 合同编号 - * @param contactNextTime 下次联系时间 + * @param id 合同编号 + * @param contactNextTime 下次联系时间 * @param contactLastContent 最后联系内容 */ void updateContractFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent); @@ -75,7 +75,7 @@ public interface CrmContractService { /** * 更新合同流程审批结果 * - * @param id 合同编号 + * @param id 合同编号 * @param bpmResult BPM 审批结果 */ void updateContractAuditStatus(Long id, Integer bpmResult); @@ -193,4 +193,13 @@ public interface CrmContractService { */ Long getRemindContractCount(Long userId); + /** + * 获得合同列表 + * + * @param customerId 客户编号 + * @param ownerUserId 负责人编号 + * @return 合同列表 + */ + List getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java index fdab61428..39ad8f5df 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -26,6 +27,7 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerServiceImpl; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; @@ -252,18 +254,18 @@ public class CrmContractServiceImpl implements CrmContractService { @Override @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.bizId}}", success = CRM_CONTRACT_TRANSFER_SUCCESS) - @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) public void transferContract(CrmContractTransferReqVO reqVO, Long userId) { // 1. 校验合同是否存在 - CrmContractDO contract = validateContractExists(reqVO.getId()); + CrmContractDO contract = validateContractExists(reqVO.getBizId()); // 2.1 数据权限转移 crmPermissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTRACT.getType(), - reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); + reqVO.getBizId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); // 2.2 设置负责人 - contractMapper.updateById(new CrmContractDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId())); + contractMapper.updateById(new CrmContractDO().setId(reqVO.getBizId()).setOwnerUserId(reqVO.getNewOwnerUserId())); // 3. 记录转移日志 LogRecordContext.putVariable("contract", contract); @@ -407,4 +409,9 @@ public class CrmContractServiceImpl implements CrmContractService { return contractMapper.selectCountByRemind(userId, config); } + @Override + public List getContractListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { + return contractMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java index 314349865..d314b823c 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java @@ -9,7 +9,13 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO; import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; @@ -193,26 +199,60 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @Override @Transactional(rollbackFor = Exception.class) - @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.bizId}}", success = CRM_CUSTOMER_TRANSFER_SUCCESS) - @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) { // 1.1 校验客户是否存在 - CrmCustomerDO customer = validateCustomerExists(reqVO.getId()); + CrmCustomerDO customer = validateCustomerExists(reqVO.getBizId()); // 1.2 校验拥有客户是否到达上限 validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1); - // 2.1 数据权限转移 permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CUSTOMER.getType(), - reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); + reqVO.getBizId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); // 2.2 转移后重新设置负责人 - customerMapper.updateById(new CrmCustomerDO().setId(reqVO.getId()) + customerMapper.updateById(new CrmCustomerDO().setId(reqVO.getBizId()) .setOwnerUserId(reqVO.getNewOwnerUserId()).setOwnerTime(LocalDateTime.now())); + // 2.3 同时转移 + if (CollUtil.isNotEmpty(reqVO.getToBizTypes())) { + transfer(reqVO, userId); + } + // 3. 记录转移日志 LogRecordContext.putVariable("customer", customer); } + /** + * 转移客户时,需要额外有【联系人】【商机】【合同】 + * + * @param reqVO 请求 + * @param userId 用户编号 + */ + private void transfer(CrmCustomerTransferReqVO reqVO, Long userId) { + if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTACT.getType())) { + List contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getBizId(), userId); + contactList.forEach(item -> { + contactService.transferContact(new CrmContactTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(), + reqVO.getOldOwnerPermissionLevel()), userId); + }); + } + if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_BUSINESS.getType())) { + List businessList = businessService.getBusinessListByCustomerIdOwnerUserId(reqVO.getBizId(), userId); + businessList.forEach(item -> { + businessService.transferBusiness(new CrmBusinessTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(), + reqVO.getOldOwnerPermissionLevel()), userId); + }); + } + if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTRACT.getType())) { + List contractList = contractService.getContractListByCustomerIdOwnerUserId(reqVO.getBizId(), userId); + contractList.forEach(item -> { + contractService.transferContract(new CrmContractTransferReqVO(item.getId(), reqVO.getNewOwnerUserId(), + reqVO.getOldOwnerPermissionLevel()), userId); + }); + } + } + @Override @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_LOCK_SUB_TYPE, bizNo = "{{#lockReqVO.id}}", success = CRM_CUSTOMER_LOCK_SUCCESS) From a23ea45632640ec50c5472aa89796b8fcb160531 Mon Sep 17 00:00:00 2001 From: dhb52 Date: Mon, 4 Mar 2024 23:51:25 +0800 Subject: [PATCH 21/81] =?UTF-8?q?fix:=20[CRM-=E5=AE=A2=E6=88=B7=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1]=E6=A0=B9=E6=8D=AECode-Review=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsCustomerController.http | 61 ++- .../CrmStatisticsCustomerController.java | 72 ++-- ...CrmStatisticsCustomerByUserBaseRespVO.java | 18 + ...atisticsCustomerContractSummaryRespVO.java | 51 +++ .../CrmStatisticsCustomerCountVO.java | 34 -- ...atisticsCustomerDealCycleByDateRespVO.java | 16 + ...atisticsCustomerDealCycleByUserRespVO.java | 16 + ...StatisticsCustomerSummaryByDateRespVO.java | 19 + ...StatisticsCustomerSummaryByUserRespVO.java | 24 ++ ...StatisticsFollowupSummaryByDateRespVO.java | 19 + ...StatisticsFollowupSummaryByTypeRespVO.java | 17 + ...StatisticsFollowupSummaryByUserRespVO.java | 16 + .../CrmStatisticsCustomerMapper.java | 31 +- .../CrmStatisticsCustomerService.java | 58 ++- .../CrmStatisticsCustomerServiceImpl.java | 375 +++++++++++++----- .../CrmStatisticsCustomerMapper.xml | 325 ++++++++++----- 16 files changed, 847 insertions(+), 305 deletions(-) create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java delete mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http index 13ae81228..0c422f986 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http @@ -1,39 +1,68 @@ -### 新建客户总量分析(按日) -GET {{baseUrl}}/crm/statistics-customer/get-total-customer-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 +# == 1. 客户总量分析 == +### 1.1 客户总量分析(按日) +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 新建客户总量分析(按月) -GET {{baseUrl}}/crm/statistics-customer/get-total-customer-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +### 1.2 客户总量分析(按月) +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 成交客户总量分析(按日) -GET {{baseUrl}}/crm/statistics-customer/get-deal-total-customer-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 +### 1.3 客户总量统计(按用户) +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 成交客户总量分析(按月) -GET {{baseUrl}}/crm/statistics-customer/get-deal-total-customer-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 + +# == 2. 客户跟进次数分析 == +### 2.1 客户跟进次数分析(按日) +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 获取客户跟进次数(按日) -GET {{baseUrl}}/crm/statistics-customer/get-record-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 +### 2.2 客户跟进次数分析(按月) +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 获取客户跟进次数(按月) -GET {{baseUrl}}/crm/statistics-customer/get-record-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +### 2.3 客户总量统计(按用户) +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -### 获取已跟进客户数(按日) -GET {{baseUrl}}/crm/statistics-customer/get-distinct-record-count?deptId=100×[0]=2024-12-01 00:00:00×[1]=2024-12-12 23:59:59 + +# == 3. 客户跟进方式分析 == +### 3.1 客户跟进方式分析 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-type?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} +tenant-id: {{adminTenentId}} -### 获取已跟进客户数(按月) -GET {{baseUrl}}/crm/statistics-customer/get-distinct-record-count?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 + +# == 4. 客户成交周期 == +### 4.1 合同摘要信息(客户转化率页面) +GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} +tenant-id: {{adminTenentId}} + + +# == 5. 客户成交周期 == +### 5.1 客户成交周期(按日) +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} +tenant-id: {{adminTenentId}} + +### 5.2 客户成交周期(按月) +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} +tenant-id: {{adminTenentId}} + +### 5.3 获取客户成交周期(按用户) +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index b51740daf..4a0b2e760 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -1,8 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -18,8 +17,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -// TODO @dhb52:数据统计 员工客户分析,改成“客户统计” -@Tag(name = "管理后台 - CRM 数据统计 员工客户分析") +@Tag(name = "管理后台 - CRM 客户统计") @RestController @RequestMapping("/crm/statistics-customer") @Validated @@ -28,50 +26,60 @@ public class CrmStatisticsCustomerController { @Resource private CrmStatisticsCustomerService customerService; - // TODO @dhb52:建议 getCustomerCount 和 getDealTotalCustomerCount 搞成一个接口; - // 1. 数量接口:【方法:getCustomerSummaryByDate】,VO:CrmStatisticsCustomerSummaryByDateRespVO,然后里面是 time、customerCreateCount customerDealCount - // 2. 按人统计:【方法:getCustomerSummaryByUser】,VO:CrmStatisticsCustomerSummaryByOwnerRespVO,然后里面是 ownerUserId、ownerUserName、customerCreateCount customerDealCount、contractPrice、receivablePrice;客户成交率、未回款金额、回款完成率,交给前端计算; - - @GetMapping("/get-total-customer-count") - @Operation(summary = "获得新建客户数量") + @GetMapping("/get-customer-summary-by-date") + @Operation(summary = "获取客户总量分析(按日期)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getTotalCustomerCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getTotalCustomerCount(reqVO)); + public CommonResult> getCustomerSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerSummaryByDate(reqVO)); } - @GetMapping("/get-deal-total-customer-count") - @Operation(summary = "获得成交客户数量") + @GetMapping("/get-customer-summary-by-user") + @Operation(summary = "获取客户总量分析(按用户)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getDealTotalCustomerCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getDealTotalCustomerCount(reqVO)); + public CommonResult> getCustomerSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerSummaryByUser(reqVO)); } - @GetMapping("/get-record-count") - @Operation(summary = "获取客户跟进次数") + @GetMapping("/get-followup-summary-by-date") + @Operation(summary = "获取客户跟进次数分析(按日期)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getRecordCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getRecordCount(reqVO)); + public CommonResult> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getFollowupSummaryByDate(reqVO)); } - @GetMapping("/get-distinct-record-count") - @Operation(summary = "获取已跟进客户数") + @GetMapping("/get-followup-summary-by-user") + @Operation(summary = "获取客户跟进次数分析(按用户)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getDistinctRecordCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getDistinctRecordCount(reqVO)); + public CommonResult> getFollowupSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getFollowupSummaryByUser(reqVO)); } - @GetMapping("/get-record-type-count") - @Operation(summary = "获取客户跟进方式统计数") + @GetMapping("/get-followup-summary-by-type") + @Operation(summary = "获取客户跟进次数分析(按类型)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getRecordTypeCount(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getRecordTypeCount(reqVO)); + public CommonResult> getFollowupSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getFollowupSummaryByType(reqVO)); } - @GetMapping("/get-customer-cycle") - @Operation(summary = "获取客户成交周期") + @GetMapping("/get-contract-summary") + @Operation(summary = "获取合同摘要信息(客户转化率页面)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getCustomerCycle(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getCustomerCycle(reqVO)); + public CommonResult> getContractSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getContractSummary(reqVO)); + } + + @GetMapping("/get-customer-deal-cycle-by-date") + @Operation(summary = "获取客户成交周期(按日期)") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getCustomerDealCycleByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerDealCycleByDate(reqVO)); + } + + @GetMapping("/get-customer-deal-cycle-by-user") + @Operation(summary = "获取客户成交周期(按用户)") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getCustomerDealCycleByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerDealCycleByUser(reqVO)); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java new file mode 100644 index 000000000..82f74f230 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 用户客户统计响应 Base VO + */ +@Data +public class CrmStatisticsCustomerByUserBaseRespVO { + + @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long ownerUserId; + + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String ownerUserName; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java new file mode 100644 index 000000000..7cb02e521 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - CRM 客户转化率分析 VO") +@Data +public class CrmStatisticsCustomerContractSummaryRespVO { + + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String customerName; + + @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "演示合同") + private String contractName; + + @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") + private BigDecimal totalPrice; + + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") + private BigDecimal receivablePrice; + + @Schema(description = "客户行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "金融") + private String customerType; + + @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼") + private String customerSource; + + @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long ownerUserId; + + @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String ownerUserName; + + @Schema(description = "创建人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Long creatorUserId; + + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码") + private String creatorUserName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-01 13:24:26") + private LocalDateTime createTime; + + @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00") + private LocalDate orderDate; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java deleted file mode 100644 index a2537db9a..000000000 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - - -@Schema(description = "管理后台 - CRM 数据统计 员工客户分析 VO") -@Data -public class CrmStatisticsCustomerCountVO { - - /** - * 时间轴 - *

- * group by DATE_FORMAT(create_date, '%Y%m') - */ - @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") - private String category; - - /** - * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义 - *

- * 1. 金额:合同金额排行、回款金额排行 - * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行 - */ - @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer count = 0; - - /** - * 成交周期(天) - */ - @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") - private Double cycle = 0.0; - -} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java new file mode 100644 index 000000000..44b2526e8 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 客户成交周期分析(按日期) VO") +@Data +public class CrmStatisticsCustomerDealCycleByDateRespVO { + + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") + private String time; + + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") + private Double customerDealCycle = 0.0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java new file mode 100644 index 000000000..6c8137983 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 成交周期分析(按用户) VO") +@Data +public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { + + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") + private Double customerDealCycle = 0.0; + + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerDealCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java new file mode 100644 index 000000000..866a58f1e --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 客户总量分析(按日期) VO") +@Data +public class CrmStatisticsCustomerSummaryByDateRespVO { + + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") + private String time; + + @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerCreateCount = 0; + + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerDealCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java new file mode 100644 index 000000000..ffa5e21ae --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - CRM 客户总量分析(按用户) VO") +@Data +public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { + + @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerCreateCount = 0; + + @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerDealCount = 0; + + @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal contractPrice = BigDecimal.valueOf(0); + + @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") + private BigDecimal receivablePrice = BigDecimal.valueOf(0); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java new file mode 100644 index 000000000..6bd6f1d4d --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO") +@Data +public class CrmStatisticsFollowupSummaryByDateRespVO { + + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") + private String time; + + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupRecordCount = 0; + + @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupCustomerCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java new file mode 100644 index 000000000..c722305a5 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO") +@Data +public class CrmStatisticsFollowupSummaryByTypeRespVO { + + @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String followupType; + + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupRecordCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java new file mode 100644 index 000000000..5cd610c36 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO") +@Data +public class CrmStatisticsFollowupSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { + + @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupRecordCount = 0; + + @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer followupCustomerCount = 0; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 464c521c6..6bababa26 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -14,16 +13,32 @@ import java.util.List; @Mapper public interface CrmStatisticsCustomerMapper { - List selectCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); - List selectDealCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); - List selectRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); - List selectDistinctRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); - List selectRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO); + List selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); - List selectCustomerCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); + + List selectFollowupRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + + List selectFollowupCustomerCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + + List selectFollowupRecordCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); + + List selectFollowupCustomerCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); + + List selectContractSummary(CrmStatisticsCustomerReqVO reqVO); + + List selectFollowupRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO); + + List selectCustomerDealCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + + List selectCustomerDealCycleGroupbyUser(CrmStatisticsCustomerReqVO reqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java index e568816d6..546124701 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -1,64 +1,80 @@ package cn.iocoder.yudao.module.crm.service.statistics; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import java.util.List; /** - * CRM 数据统计 员工客户分析 Service 接口 + * CRM 客户分析 Service 接口 * * @author dhb52 */ public interface CrmStatisticsCustomerService { /** - * 获取新建客户数量 + * 总量分析(按日期) * * @param reqVO 请求参数 - * @return 新建客户数量统计 + * @return 统计数据 */ - List getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO); + List getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO); /** - * 获取成交客户数量 + * 总量分析(按用户) * * @param reqVO 请求参数 - * @return 成交客户数量统计 + * @return 统计数据 */ - List getDealTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO); + List getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO); /** - * 获取客户跟进次数 + * 跟进次数分析(按日期) * * @param reqVO 请求参数 - * @return 客户跟进次数 + * @return 统计数据 */ - List getRecordCount(CrmStatisticsCustomerReqVO reqVO); + List getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO); /** - * 获取已跟进客户数 + * 跟进次数分析(按用户) * * @param reqVO 请求参数 - * @return 已跟进客户数 + * @return 统计数据 */ - List getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO); + List getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO); /** - * 获取客户跟进方式统计数 + * 客户跟进次数分析(按类型) * * @param reqVO 请求参数 - * @return 客户跟进方式统计数 + * @return 统计数据 */ - List getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO); + List getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO); + /** - * 获取客户成交周期 + * 获取合同摘要信息(客户转化率页面) * * @param reqVO 请求参数 - * @return 客户成交周期 + * @return 合同摘要列表 */ - List getCustomerCycle(CrmStatisticsCustomerReqVO reqVO); + List getContractSummary(CrmStatisticsCustomerReqVO reqVO); + + /** + * 客户成交周期(按日期) + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO); + + /** + * 客户成交周期(按用户) + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index 94eb560d1..77bd0fcf6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.system.api.dept.DeptApi; @@ -17,17 +17,14 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.math.BigDecimal; import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Function; +import java.util.*; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; /** - * CRM 数据统计 员工客户分析 Service 实现类 + * CRM 客户分析 Service 实现类 * * @author dhb52 */ @@ -35,6 +32,13 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @Validated public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService { + private static final String SQL_DATE_FORMAT_BY_MONTH = "%Y%m"; + private static final String SQL_DATE_FORMAT_BY_DAY = "%Y%m%d"; + + private static final String TIME_FORMAT_BY_MONTH = "yyyyMM"; + private static final String TIME_FORMAT_BY_DAY = "yyyyMMdd"; + + @Resource private CrmStatisticsCustomerMapper customerMapper; @@ -45,130 +49,315 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @Resource private DictDataApi dictDataApi; - @Override - public List getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) { - return getStat(reqVO, customerMapper::selectCustomerCountGroupbyDate); - } @Override - public List getDealTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) { - return getStat(reqVO, customerMapper::selectDealCustomerCountGroupbyDate); - } - - @Override - public List getRecordCount(CrmStatisticsCustomerReqVO reqVO) { - reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - return getStat(reqVO, customerMapper::selectRecordCountGroupbyDate); - } - - @Override - public List getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO) { - reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - return getStat(reqVO, customerMapper::selectDistinctRecordCountGroupbyDate); - } - - @Override - public List getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO) { - // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组 - if (ObjUtil.isNotNull(reqVO.getUserId())) { - reqVO.setUserIds(List.of(reqVO.getUserId())); - } else { - reqVO.setUserIds(getUserIds(reqVO.getDeptId())); - } - if (CollUtil.isEmpty(reqVO.getUserIds())) { + public List getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + final List customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyDate(reqVO); + final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyDate(reqVO); + + // 3. 获取时间序列 + final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); + + // 4. 合并统计数据 + List result = new ArrayList<>(times.size()); + final Map customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount); + final Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount); + times.forEach(time -> result.add( + new CrmStatisticsCustomerSummaryByDateRespVO().setTime(time) + .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) + .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)) + )); + + return result; + } + + @Override + public List getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + final List customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyUser(reqVO); + final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO); + final List contractPrice = customerMapper.selectContractPriceGroupbyUser(reqVO); + final List receivablePrice = customerMapper.selectReceivablePriceGroupbyUser(reqVO); + + // 3. 合并统计数据 + final Map customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount); + final Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); + final Map contractPriceMap = convertMap(contractPrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice); + final Map receivablePriceMap = convertMap(receivablePrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice); + List result = new ArrayList<>(userIds.size()); + userIds.forEach(userId -> { + final CrmStatisticsCustomerSummaryByUserRespVO respVO = new CrmStatisticsCustomerSummaryByUserRespVO(); + respVO.setOwnerUserId(userId); + respVO.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0)) + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)) + .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.valueOf(0))) + .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.valueOf(0))); + result.add(respVO); + }); + + // 4. 拼接用户信息 + appendUserInfo(result); + + return result; + } + + @Override + public List getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + final List followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyDate(reqVO); + final List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyDate(reqVO); + + // 3. 获取时间序列 + final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); + + // 4. 合并统计数据 + List result = new ArrayList<>(times.size()); + final Map followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount); + final Map followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount); + times.forEach(time -> result.add( + new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time) + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0)) + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0)) + )); + + return result; + } + + @Override + public List getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + final List followupRecordCount = customerMapper.selectFollowupRecordCountGroupbyUser(reqVO); + final List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyUser(reqVO); + + // 3. 合并统计数据 + final Map followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount); + final Map followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount); + List result = new ArrayList<>(userIds.size()); + userIds.forEach(userId -> { + final CrmStatisticsFollowupSummaryByUserRespVO stat = new CrmStatisticsFollowupSummaryByUserRespVO() + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0)) + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0)); + stat.setOwnerUserId(userId); + result.add(stat); + }); + + // 4. 拼接用户信息 + appendUserInfo(result); + + return result; + } + + @Override + public List getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); // 2. 获得排行数据 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - List stats = customerMapper.selectRecordCountGroupbyType(reqVO); + List stats = customerMapper.selectFollowupRecordCountGroupbyType(reqVO); // 3. 获取字典数据 List followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type"); final Map followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel); stats.forEach(stat -> { - stat.setCategory(followUpTypeMap.get(stat.getCategory())); + stat.setFollowupType(followUpTypeMap.get(stat.getFollowupType())); }); return stats; } @Override - public List getCustomerCycle(CrmStatisticsCustomerReqVO reqVO) { - return getStat(reqVO, customerMapper::selectCustomerCycleGroupbyDate); + public List getContractSummary(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + List contractSummary = customerMapper.selectContractSummary(reqVO); + + // 2. 拼接用户信息 + final Set userIdSet = new HashSet<>(); + userIdSet.addAll(userIds); + userIdSet.addAll(convertSet(contractSummary, CrmStatisticsCustomerContractSummaryRespVO::getCreatorUserId)); + final Map userMap = adminUserApi.getUserMap(userIdSet); + contractSummary.forEach(contract -> contract.setCreatorUserName(userMap.get(contract.getCreatorUserId()).getNickname()) + .setOwnerUserName(userMap.get(contract.getOwnerUserId()).getNickname())); + + return contractSummary; + } + + @Override + public List getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + final List customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyDate(reqVO); + + // 3. 获取时间序列 + final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); + + // 4. 合并统计数据 + List result = new ArrayList<>(times.size()); + final Map customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByDateRespVO::getTime, CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle); + times.forEach(time -> result.add( + new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, Double.valueOf(0))) + )); + + return result; + } + + @Override + public List getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + final List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + + // 2. 获取分项统计数据 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + final List customerDealCycle = customerMapper.selectCustomerDealCycleGroupbyUser(reqVO); + final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO); + + // 3. 合并统计数据 + final Map customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle); + final Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); + List result = new ArrayList<>(userIds.size()); + userIds.forEach(userId -> { + final CrmStatisticsCustomerDealCycleByUserRespVO stat = new CrmStatisticsCustomerDealCycleByUserRespVO() + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0)) + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)); + stat.setOwnerUserId(userId); + result.add(stat); + }); + + // 4. 拼接用户信息 + appendUserInfo(result); + + return result; } /** - * 获得统计数据 + * 拼接用户信息(昵称) * - * @param reqVO 参数 - * @param statFunction 统计方法 - * @return 统计数据 + * @param stats 统计数据 */ - private List getStat(CrmStatisticsCustomerReqVO reqVO, Function> statFunction) { - // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组 + private void appendUserInfo(List stats) { + Map userMap = adminUserApi.getUserMap(convertSet(stats, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); + stats.forEach(stat -> MapUtils.findAndThen(userMap, stat.getOwnerUserId(), user -> { + stat.setOwnerUserName(user.getNickname()); + })); + } + + /** + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号 + * + * @param reqVO 请求参数 + * @return 用户编号数组 + */ + private List getUserIds(CrmStatisticsCustomerReqVO reqVO) { if (ObjUtil.isNotNull(reqVO.getUserId())) { - reqVO.setUserIds(List.of(reqVO.getUserId())); + return List.of(reqVO.getUserId()); } else { - reqVO.setUserIds(getUserIds(reqVO.getDeptId())); - } - if (CollUtil.isEmpty(reqVO.getUserIds())) { - return Collections.emptyList(); + // 1. 获得部门列表 + final Long deptId = reqVO.getDeptId(); + List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); + deptIds.add(deptId); + // 2. 获得用户编号 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); } + } - // 2. 生成日期格式 - LocalDateTime startTime = reqVO.getTimes()[0]; - final LocalDateTime endTime = reqVO.getTimes()[1]; - final long days = LocalDateTimeUtil.between(startTime, endTime).toDays(); - boolean byMonth = days > 31; - if (byMonth) { - // 按月 - reqVO.setSqlDateFormat("%Y%m"); - } else { - // 按日 - reqVO.setSqlDateFormat("%Y%m%d"); - } - // 3. 获得排行数据 - List stats = statFunction.apply(reqVO); + /** + * 判断是否按照 月粒度 统计 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 是, 按月粒度, 否则按天粒度统计。 + */ + private boolean queryByMonth(LocalDateTime startTime, LocalDateTime endTime) { + return LocalDateTimeUtil.between(startTime, endTime).toDays() > 31; + } - // 4. 生成时间序列 - List result = CollUtil.newArrayList(); + /** + * 生成时间序列 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 时间序列 + */ + private List generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) { + boolean byMonth = queryByMonth(startTime, endTime); + List times = CollUtil.newArrayList(); while (!startTime.isAfter(endTime)) { - final String category = LocalDateTimeUtil.format(startTime, byMonth ? "yyyyMM" : "yyyyMMdd"); - result.add(new CrmStatisticsCustomerCountVO().setCategory(category)); + times.add(LocalDateTimeUtil.format(startTime, byMonth ? TIME_FORMAT_BY_MONTH : TIME_FORMAT_BY_DAY)); if (byMonth) startTime = startTime.plusMonths(1); else startTime = startTime.plusDays(1); } - // 5. 使用时间序列填充结果 - final Map statMap = convertMap(stats, - CrmStatisticsCustomerCountVO::getCategory, - Function.identity()); - result.forEach(r -> { - if (statMap.containsKey(r.getCategory())) { - r.setCount(statMap.get(r.getCategory()).getCount()) - .setCycle(statMap.get(r.getCategory()).getCycle()); - } - }); - - return result; + return times; } - /** - * 获得部门下的用户编号数组,包括子部门的 + * 获取 SQL 查询 GROUP BY 的时间格式 * - * @param deptId 部门编号 - * @return 用户编号数组 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return SQL 查询 GROUP BY 的时间格式 */ - public List getUserIds(Long deptId) { - // 1. 获得部门列表 - List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); - deptIds.add(deptId); - // 2. 获得用户编号 - return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); + private String getSqlDateFormat(LocalDateTime startTime, LocalDateTime endTime) { + return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY; } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index 06affccab..c612d4a24 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -2,115 +2,238 @@ - - SELECT - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category, - count(*) AS count + DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, + count(*) AS customerCreateCount + FROM crm_customer + WHERE deleted = 0 + AND owner_user_id IN + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND + #{times[1],javaType=java.time.LocalDateTime} + GROUP BY time + + + + + + + + + + + + + + + + + + + + + + + + - SELECT - DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category, - count( DISTINCT a.id ) AS count - FROM - crm_customer AS a - LEFT JOIN crm_contract AS b ON b.customer_id = a.id - WHERE - a.deleted = 0 AND b.deleted = 0 - AND b.audit_status = 20 - AND a.owner_user_id IN - - #{userId} - - AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND - #{times[1],javaType=java.time.LocalDateTime} - GROUP BY category + DATE_FORMAT( b.order_date, #{sqlDateFormat} ) AS time, + IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, a.create_time, b.order_date )), 1 ), 0 ) AS customer_deal_cycle + FROM crm_customer AS a + LEFT JOIN crm_contract AS b ON b.customer_id = a.id + WHERE a.deleted = 0 AND b.deleted = 0 + AND b.audit_status = 20 + AND a.owner_user_id IN + + #{userId} + + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND + #{times[1],javaType=java.time.LocalDateTime} + GROUP BY time - SELECT - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category, - count(*) AS count - FROM - crm_follow_up_record - WHERE - creator IN - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND - #{times[1],javaType=java.time.LocalDateTime} - AND biz_type = #{bizType,javaType=java.lang.Integer} - GROUP BY category - - - - - - - From 06c0d865447c8065a06d5d1dfce327a195ad91f6 Mon Sep 17 00:00:00 2001 From: dhb52 Date: Tue, 5 Mar 2024 15:08:40 +0000 Subject: [PATCH 22/81] =?UTF-8?q?fix:=20=E4=BD=BF=E7=94=A8convertSetByFlat?= =?UTF-8?q?Map=E9=81=BF=E5=85=8D=20ID=20=E9=87=8D=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: dhb52 --- .../crm/controller/admin/customer/CrmCustomerController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java index 7745a6e6c..f5a0a4e50 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java @@ -137,7 +137,7 @@ public class CrmCustomerController { return java.util.Collections.emptyList(); } // 1.1 获取创建人、负责人列表 - Map userMap = adminUserApi.getUserMap(convertListByFlatMap(list, + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); // 1.2 获取距离进入公海的时间 From 0386820a1c80354917afe20e31cc5048a1f5be3d Mon Sep 17 00:00:00 2001 From: dhb52 Date: Tue, 5 Mar 2024 23:16:16 +0800 Subject: [PATCH 23/81] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0[=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E8=A1=8C=E4=B8=9A=E3=80=81=E5=AE=A2=E6=88=B7=E6=9D=A5?= =?UTF-8?q?=E6=BA=90]=E7=AD=89=E5=AD=97=E6=AE=B5=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=EF=BC=8C=E9=87=8D=E6=9E=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/customer/CrmCustomerController.java | 2 +- ...CrmStatisticsCustomerByUserBaseRespVO.java | 2 + ...atisticsCustomerContractSummaryRespVO.java | 22 ++- ...StatisticsCustomerSummaryByUserRespVO.java | 4 +- .../CrmStatisticsCustomerServiceImpl.java | 177 +++++++++++------- .../CrmStatisticsCustomerMapper.xml | 4 +- 6 files changed, 138 insertions(+), 73 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java index 36b3ee813..a4adeee3e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java @@ -142,7 +142,7 @@ public class CrmCustomerController { return java.util.Collections.emptyList(); } // 1.1 获取创建人、负责人列表 - Map userMap = adminUserApi.getUserMap(convertListByFlatMap(list, + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(list, contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); // 1.2 获取距离进入公海的时间 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java index 82f74f230..340e93066 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -10,6 +11,7 @@ import lombok.Data; public class CrmStatisticsCustomerByUserBaseRespVO { @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @JsonIgnore private Long ownerUserId; @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java index 7cb02e521..019309f8e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java @@ -1,13 +1,18 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; + @Schema(description = "管理后台 - CRM 客户转化率分析 VO") @Data public class CrmStatisticsCustomerContractSummaryRespVO { @@ -24,20 +29,30 @@ public class CrmStatisticsCustomerContractSummaryRespVO { @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") private BigDecimal receivablePrice; + @Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @JsonIgnore + private String industryId; + @Schema(description = "客户行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "金融") - private String customerType; + private String industryName; + + @Schema(description = "客户来源ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @JsonIgnore + private String source; @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼") - private String customerSource; + private String sourceName; @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @JsonIgnore private Long ownerUserId; @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") private String ownerUserName; @Schema(description = "创建人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Long creatorUserId; + @JsonIgnore + private String creatorUserId; @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码") private String creatorUserName; @@ -46,6 +61,7 @@ public class CrmStatisticsCustomerContractSummaryRespVO { private LocalDateTime createTime; @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00") + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT) private LocalDate orderDate; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java index ffa5e21ae..bd719a1b8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java @@ -16,9 +16,9 @@ public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCusto private Integer customerDealCount = 0; @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") - private BigDecimal contractPrice = BigDecimal.valueOf(0); + private BigDecimal contractPrice = BigDecimal.ZERO; @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") - private BigDecimal receivablePrice = BigDecimal.valueOf(0); + private BigDecimal receivablePrice = BigDecimal.ZERO; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index 77bd0fcf6..6b664dce5 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; @@ -19,9 +20,14 @@ import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.time.LocalDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; /** * CRM 客户分析 Service 实现类 @@ -68,16 +74,20 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); // 4. 合并统计数据 - List result = new ArrayList<>(times.size()); - final Map customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount); - final Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByDateRespVO::getTime, CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount); - times.forEach(time -> result.add( + List respVoList = new ArrayList<>(times.size()); + final Map customerCreateCountMap = convertMap(customerCreateCount, + CrmStatisticsCustomerSummaryByDateRespVO::getTime, + CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount); + final Map customerDealCountMap = convertMap(customerDealCount, + CrmStatisticsCustomerSummaryByDateRespVO::getTime, + CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount); + times.forEach(time -> respVoList.add( new CrmStatisticsCustomerSummaryByDateRespVO().setTime(time) .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)) )); - return result; + return respVoList; } @Override @@ -96,25 +106,33 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe final List receivablePrice = customerMapper.selectReceivablePriceGroupbyUser(reqVO); // 3. 合并统计数据 - final Map customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount); - final Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); - final Map contractPriceMap = convertMap(contractPrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice); - final Map receivablePriceMap = convertMap(receivablePrice, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice); - List result = new ArrayList<>(userIds.size()); + final Map customerCreateCountMap = convertMap(customerCreateCount, + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount); + final Map customerDealCountMap = convertMap(customerDealCount, + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); + final Map contractPriceMap = convertMap(contractPrice, + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice); + final Map receivablePriceMap = convertMap(receivablePrice, + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice); + List respVoList = new ArrayList<>(userIds.size()); userIds.forEach(userId -> { - final CrmStatisticsCustomerSummaryByUserRespVO respVO = new CrmStatisticsCustomerSummaryByUserRespVO(); - respVO.setOwnerUserId(userId); - respVO.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0)) + final CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO(); + vo.setOwnerUserId(userId); + vo.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0)) .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)) - .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.valueOf(0))) - .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.valueOf(0))); - result.add(respVO); + .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.ZERO)) + .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.ZERO)); + respVoList.add(vo); }); // 4. 拼接用户信息 - appendUserInfo(result); + appendUserInfo(respVoList); - return result; + return respVoList; } @Override @@ -136,16 +154,20 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); // 4. 合并统计数据 - List result = new ArrayList<>(times.size()); - final Map followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount); - final Map followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByDateRespVO::getTime, CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount); - times.forEach(time -> result.add( + List respVoList = new ArrayList<>(times.size()); + final Map followupRecordCountMap = convertMap(followupRecordCount, + CrmStatisticsFollowupSummaryByDateRespVO::getTime, + CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount); + final Map followupCustomerCountMap = convertMap(followupCustomerCount, + CrmStatisticsFollowupSummaryByDateRespVO::getTime, + CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount); + times.forEach(time -> respVoList.add( new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time) .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0)) .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0)) )); - return result; + return respVoList; } @Override @@ -163,21 +185,25 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe final List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupbyUser(reqVO); // 3. 合并统计数据 - final Map followupRecordCountMap = convertMap(followupRecordCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount); - final Map followupCustomerCountMap = convertMap(followupCustomerCount, CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount); - List result = new ArrayList<>(userIds.size()); + final Map followupRecordCountMap = convertMap(followupRecordCount, + CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount); + final Map followupCustomerCountMap = convertMap(followupCustomerCount, + CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount); + List respVoList = new ArrayList<>(userIds.size()); userIds.forEach(userId -> { - final CrmStatisticsFollowupSummaryByUserRespVO stat = new CrmStatisticsFollowupSummaryByUserRespVO() + final CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO() .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0)) .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0)); - stat.setOwnerUserId(userId); - result.add(stat); + vo.setOwnerUserId(userId); + respVoList.add(vo); }); // 4. 拼接用户信息 - appendUserInfo(result); + appendUserInfo(respVoList); - return result; + return respVoList; } @Override @@ -191,16 +217,17 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 2. 获得排行数据 reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); - List stats = customerMapper.selectFollowupRecordCountGroupbyType(reqVO); + List respVoList = customerMapper.selectFollowupRecordCountGroupbyType(reqVO); // 3. 获取字典数据 - List followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type"); - final Map followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel); - stats.forEach(stat -> { - stat.setFollowupType(followUpTypeMap.get(stat.getFollowupType())); + List followUpTypes = dictDataApi.getDictDataList(CRM_FOLLOW_UP_TYPE); + final Map followUpTypeMap = convertMap(followUpTypes, + DictDataRespDTO::getValue, DictDataRespDTO::getLabel); + respVoList.forEach(vo -> { + vo.setFollowupType(followUpTypeMap.get(vo.getFollowupType())); }); - return stats; + return respVoList; } @Override @@ -212,17 +239,29 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe } reqVO.setUserIds(userIds); - List contractSummary = customerMapper.selectContractSummary(reqVO); + // 2. 获取统计数据 + List respVoList = customerMapper.selectContractSummary(reqVO); - // 2. 拼接用户信息 - final Set userIdSet = new HashSet<>(); - userIdSet.addAll(userIds); - userIdSet.addAll(convertSet(contractSummary, CrmStatisticsCustomerContractSummaryRespVO::getCreatorUserId)); - final Map userMap = adminUserApi.getUserMap(userIdSet); - contractSummary.forEach(contract -> contract.setCreatorUserName(userMap.get(contract.getCreatorUserId()).getNickname()) - .setOwnerUserName(userMap.get(contract.getOwnerUserId()).getNickname())); + // 3. 设置 创建人、负责人、行业、来源 + // 获取客户所属行业 + Map industryMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_INDUSTRY), + DictDataRespDTO::getValue, DictDataRespDTO::getLabel); + // 获取客户来源 + Map sourceMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_SOURCE), + DictDataRespDTO::getValue, DictDataRespDTO::getLabel); + // 获取创建人、负责人列表 + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(respVoList, + vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId()))); - return contractSummary; + respVoList.forEach(vo -> { + MapUtils.findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName); + MapUtils.findAndThen(sourceMap, vo.getSource(), vo::setSourceName); + MapUtils.findAndThen(userMap, NumberUtils.parseLong(vo.getCreatorUserId()), + user -> vo.setCreatorUserName(user.getNickname())); + MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())); + }); + + return respVoList; } @Override @@ -243,14 +282,16 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); // 4. 合并统计数据 - List result = new ArrayList<>(times.size()); - final Map customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByDateRespVO::getTime, CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle); - times.forEach(time -> result.add( + List respVoList = new ArrayList<>(times.size()); + final Map customerDealCycleMap = convertMap(customerDealCycle, + CrmStatisticsCustomerDealCycleByDateRespVO::getTime, + CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle); + times.forEach(time -> respVoList.add( new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) - .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, Double.valueOf(0))) + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D)) )); - return result; + return respVoList; } @Override @@ -268,33 +309,37 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyUser(reqVO); // 3. 合并统计数据 - final Map customerDealCycleMap = convertMap(customerDealCycle, CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle); - final Map customerDealCountMap = convertMap(customerDealCount, CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); - List result = new ArrayList<>(userIds.size()); + final Map customerDealCycleMap = convertMap(customerDealCycle, + CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle); + final Map customerDealCountMap = convertMap(customerDealCount, + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); + List respVoList = new ArrayList<>(userIds.size()); userIds.forEach(userId -> { - final CrmStatisticsCustomerDealCycleByUserRespVO stat = new CrmStatisticsCustomerDealCycleByUserRespVO() + final CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO() .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0)) .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)); - stat.setOwnerUserId(userId); - result.add(stat); + vo.setOwnerUserId(userId); + respVoList.add(vo); }); // 4. 拼接用户信息 - appendUserInfo(result); + appendUserInfo(respVoList); - return result; + return respVoList; } /** * 拼接用户信息(昵称) * - * @param stats 统计数据 + * @param respVoList 统计数据 */ - private void appendUserInfo(List stats) { - Map userMap = adminUserApi.getUserMap(convertSet(stats, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); - stats.forEach(stat -> MapUtils.findAndThen(userMap, stat.getOwnerUserId(), user -> { - stat.setOwnerUserName(user.getNickname()); - })); + private void appendUserInfo(List respVoList) { + Map userMap = adminUserApi.getUserMap(convertSet(respVoList, + CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); + respVoList.forEach(vo -> MapUtils.findAndThen(userMap, + vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); } /** diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index c612d4a24..3fd52f3e7 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -180,8 +180,10 @@ SELECT a.`name` AS customer_name, b.`name` AS contract_name, - b.total_price AS contract_price, + b.total_price, IFNULL( c.price, 0 ) AS receivable_price, + a.industry_id, + a.source, a.owner_user_id, a.creator AS creator_user_id, a.create_time, From d30700d06003ed72dfed27b547ad2544f088c2ee Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 9 Mar 2024 11:03:17 +0800 Subject: [PATCH 24/81] =?UTF-8?q?=E3=80=90=E4=BF=AE=E5=A4=8D=E3=80=91?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98=E6=97=B6=EF=BC=8C=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E4=BF=9D=E8=AF=81=E7=88=B6=E7=BA=BF=E7=A8=8B=E7=9A=84?= =?UTF-8?q?=20ThreadLocal=20=E4=BC=A0=E5=85=A5=E5=AD=90=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/framework/common/util/cache/CacheUtils.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java index 41f75405e..acd48aa12 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java @@ -7,6 +7,7 @@ import com.google.common.cache.LoadingCache; import java.time.Duration; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** @@ -17,8 +18,11 @@ import java.util.concurrent.Executors; public class CacheUtils { public static LoadingCache buildAsyncReloadingCache(Duration duration, CacheLoader loader) { - Executor executor = Executors.newCachedThreadPool( // TODO 芋艿:可能要思考下,未来要不要做成可配置 - TtlExecutors.getDefaultDisableInheritableThreadFactory()); // TTL 保证 ThreadLocal 可以透传 + // 1. 使用 TTL 包装 ExecutorService,实现 ThreadLocal 的透传 + // https://github.com/YunaiV/ruoyi-vue-pro/issues/432 + ExecutorService executorService = Executors.newCachedThreadPool(); // TODO 芋艿:可能要思考下,未来要不要做成可配置 + Executor executor = TtlExecutors.getTtlExecutorService(executorService); + // 2. 创建 Guava LoadingCache return CacheBuilder.newBuilder() // 只阻塞当前数据加载线程,其他线程返回旧值 .refreshAfterWrite(duration) From 89fc83c41938d0fd88337507c0190fdcaba6ff41 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 9 Mar 2024 11:58:05 +0800 Subject: [PATCH 25/81] =?UTF-8?q?CRM=EF=BC=9Acode=20review=20=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E6=9D=83=E9=99=90=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/mysql/ruoyi-vue-pro.sql | 2 +- yudao-dependencies/pom.xml | 4 +- .../common/util/collection/MapUtils.java | 4 +- .../dict/core/DictFrameworkUtils.java | 3 +- .../core/annotations/ExcelColumnSelect.java | 4 +- .../core/handler/SelectSheetWriteHandler.java | 46 +++++++++++-------- .../vo/business/CrmBusinessTransferReqVO.java | 1 + .../vo/customer/CrmCustomerImportExcelVO.java | 4 +- .../vo/customer/CrmCustomerTransferReqVO.java | 2 +- .../permission/CrmPermissionController.java | 6 ++- .../permission/vo/CrmPermissionSaveReqVO.java | 3 +- .../dal/mysql/contact/CrmContactMapper.java | 1 + .../mysql/receivable/CrmReceivableMapper.java | 4 ++ ...ava => AreaExcelColumnSelectFunction.java} | 4 +- .../crm/framework/excel/package-info.java | 3 ++ .../framework/operatelog/package-info.java | 3 ++ .../framework/permission/package-info.java | 3 ++ .../crm/framework/web/package-info.java | 2 +- .../contract/CrmContractServiceImpl.java | 4 +- .../receivable/CrmReceivableService.java | 6 +-- .../receivable/CrmReceivableServiceImpl.java | 4 +- 21 files changed, 71 insertions(+), 42 deletions(-) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/{AreaExcelColumnSelectFunctionImpl.java => AreaExcelColumnSelectFunction.java} (79%) diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index 49da40058..bdc70b63f 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -11,7 +11,7 @@ Target Server Version : 80200 (8.2.0) File Encoding : 65001 - Date: 01/03/2024 19:39:45 + Date: 05/03/2024 23:36:35 */ SET NAMES utf8mb4; diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index c9667aea2..3a024933b 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -627,11 +627,11 @@ ureport2-console ${ureport2.version} - + org.apache.poi poi - + org.apache.poi poi-ooxml diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java index f4a17b523..a59b53fd4 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/MapUtils.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.collection; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; @@ -40,6 +41,7 @@ public class MapUtils { /** * 从哈希表查找到 key 对应的 value,然后进一步处理 + * key 为 null 时, 不处理 * 注意,如果查找到的 value 为 null 时,不进行处理 * * @param map 哈希表 @@ -47,7 +49,7 @@ public class MapUtils { * @param consumer 进一步处理的逻辑 */ public static void findAndThen(Map map, K key, Consumer consumer) { - if (CollUtil.isEmpty(map)) { + if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) { return; } V value = map.get(key); diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java index e51ef16e7..8dada3f75 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java @@ -13,8 +13,6 @@ import lombok.extern.slf4j.Slf4j; import java.time.Duration; import java.util.List; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - /** * 字典工具类 * @@ -27,6 +25,7 @@ public class DictFrameworkUtils { private static final DictDataRespDTO DICT_DATA_NULL = new DictDataRespDTO(); + // TODO @puhui999:GET_DICT_DATA_CACHE、GET_DICT_DATA_LIST_CACHE、PARSE_DICT_DATA_CACHE 这 3 个缓存是有点重叠,可以思考下,有没可能减少 1 个。微信讨论好私聊,再具体改哈 /** * 针对 {@link #getDictDataLabel(String, String)} 的缓存 */ diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java index 5daa1e064..b4ff140af 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/annotations/ExcelColumnSelect.java @@ -3,7 +3,9 @@ package cn.iocoder.yudao.framework.excel.core.annotations; import java.lang.annotation.*; /** - * 给列添加下拉选择数据 + * 给 Excel 列添加下拉选择数据 + * + * 其中 {@link #dictType()} 和 {@link #functionName()} 二选一 * * @author HUIHUI */ diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java index df4601b94..fd66c4018 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java @@ -1,8 +1,9 @@ package cn.iocoder.yudao.framework.excel.core.handler; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.hutool.poi.excel.ExcelUtil; @@ -59,7 +60,6 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { } // 解析下拉数据 - // TODO @puhui999:感觉可以 head 循环 field,如果有 ExcelColumnSelect 则进行处理;而 ExcelProperty 可能是非必须的。回答:主要是用于定位到列索引 Map excelPropertyFields = getFieldsWithAnnotation(head, ExcelProperty.class); Map excelColumnSelectFields = getFieldsWithAnnotation(head, ExcelColumnSelect.class); int colIndex = 0; @@ -71,39 +71,47 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { if (index != -1) { colIndex = index; } - getSelectDataList(colIndex, field); + buildSelectDataList(colIndex, field); } colIndex++; } + // TODO @puhui999:感觉可以 head 循环 field,如果有 ExcelColumnSelect 则进行处理;而 ExcelProperty 可能是非必须的。回答:主要是用于定位到列索引;补充:可以看看下面这样写? +// for (Field field : head.getDeclaredFields()) { +// if (field.isAnnotationPresent(ExcelColumnSelect.class)) { +// ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); +// if (excelProperty != null) { +// colIndex = excelProperty.index(); +// } +// getSelectDataList(colIndex, field); +// } +// colIndex++; +// } } /** - * 获得下拉数据 + * 获得下拉数据,并添加到 {@link #selectMap} 中 * * @param colIndex 列索引 * @param field 字段 */ - private void getSelectDataList(int colIndex, Field field) { - // 获得下拉注解信息 + private void buildSelectDataList(int colIndex, Field field) { ExcelColumnSelect columnSelect = field.getAnnotation(ExcelColumnSelect.class); String dictType = columnSelect.dictType(); + String functionName = columnSelect.functionName(); + Assert.isTrue(ObjectUtil.isNotEmpty(dictType) || ObjectUtil.isNotEmpty(functionName), + "Field({}) 的 @ExcelColumnSelect 注解,dictType 和 functionName 不能同时为空", field.getName()); + + // 情况一:使用 dictType 获得下拉数据 if (StrUtil.isNotEmpty(dictType)) { // 情况一: 字典数据 (默认) selectMap.put(colIndex, DictFrameworkUtils.getDictDataLabelList(dictType)); return; } - String functionName = columnSelect.functionName(); - if (StrUtil.isEmpty(functionName)) { // 情况二: 获取自定义数据 - log.warn("[getSelectDataList]解析下拉数据失败,参数信息 dictType[{}] functionName[{}]", dictType, functionName); - return; - } - // 获得所有的下拉数据源获取方法 + + // 情况二:使用 functionName 获得下拉数据 Map functionMap = SpringUtil.getApplicationContext().getBeansOfType(ExcelColumnSelectFunction.class); - functionMap.values().forEach(func -> { - if (ObjUtil.notEqual(func.getName(), functionName)) { - return; - } - selectMap.put(colIndex, func.getOptions()); - }); + ExcelColumnSelectFunction function = CollUtil.findOne(functionMap.values(), item -> item.getName().equals(functionName)); + Assert.notNull(function, "未找到对应的 function({})", functionName); + selectMap.put(colIndex, function.getOptions()); } @Override @@ -121,7 +129,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME); for (KeyValue> keyValue : keyValues) { int rowLength = keyValue.getValue().size(); - // 2.1 设置字典 sheet 页的值 每一列一个字典项 + // 2.1 设置字典 sheet 页的值,每一列一个字典项 for (int i = 0; i < rowLength; i++) { Row row = dictSheet.getRow(i); if (row == null) { diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java index 083ea3570..9fe7e2316 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java @@ -13,6 +13,7 @@ import lombok.NoArgsConstructor; @AllArgsConstructor public class CrmBusinessTransferReqVO { + // TODO @puhui999:这里最好还是用 id 哈,主要还是在 Business 业务里 @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") @NotNull(message = "商机编号不能为空") private Long bizId; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java index f06122c3b..a45e9115f 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunctionImpl; +import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunction; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -43,7 +43,7 @@ public class CrmCustomerImportExcelVO { private String email; @ExcelProperty(value = "地区", converter = AreaConvert.class) - @ExcelColumnSelect(functionName = AreaExcelColumnSelectFunctionImpl.NAME) + @ExcelColumnSelect(functionName = AreaExcelColumnSelectFunction.NAME) private Integer areaId; @ExcelProperty("详细地址") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java index 98d0d4a64..38f6f1756 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java @@ -31,7 +31,7 @@ public class CrmCustomerTransferReqVO { private Integer oldOwnerPermissionLevel; /** - * 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择 + * 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。选中时,也一起转移 */ @Schema(description = "同时转移", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") private List toBizTypes; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java index 1cf8d7591..11289b0ed 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java @@ -68,22 +68,24 @@ public class CrmPermissionController { @Resource private PostApi postApi; + // TODO @puhui999:是不是还是叫 create 好点哈。 @PostMapping("/create") @Operation(summary = "创建数据权限") @Transactional(rollbackFor = Exception.class) @PreAuthorize("@ss.hasPermission('crm:permission:create')") @CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) - public CommonResult addPermission(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) { + public CommonResult savePermission(@Valid @RequestBody CrmPermissionSaveReqVO reqVO) { permissionService.createPermission(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class)); + // 处理【同时添加至】的权限 if (CollUtil.isNotEmpty(reqVO.getToBizTypes())) { createBizTypePermissions(reqVO); } return success(true); } - private void createBizTypePermissions(CrmPermissionSaveReqVO reqVO) { List createPermissions = new ArrayList<>(); + // TODO @puhui999:需要考虑,被添加人,是不是应该有对应的权限了; if (reqVO.getToBizTypes().contains(CrmBizTypeEnum.CRM_CONTACT.getType())) { List contactList = contactService.getContactListByCustomerIdOwnerUserId(reqVO.getBizId(), getLoginUserId()); contactList.forEach(item -> { diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java index 9635cc627..2ec7ed8db 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/vo/CrmPermissionSaveReqVO.java @@ -33,7 +33,8 @@ public class CrmPermissionSaveReqVO { private Integer level; /** - * 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择 + * 添加客户团队成员时,需要额外有【联系人】【商机】【合同】的 checkbox 选择。 + * 选中时,同时添加对应的权限 */ @Schema(description = "同时添加", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") private List toBizTypes; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java index d4244bce0..f94b50f49 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java @@ -74,6 +74,7 @@ public interface CrmContactMapper extends BaseMapperX { } default List selectListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId) { + // TODO @puhui999:父类有 selectList,查询 2 个字段的简化方法哈,可以用下 return selectList(new LambdaQueryWrapperX() .eq(CrmContactDO::getCustomerId, customerId) .eq(CrmContactDO::getOwnerUserId, ownerUserId)); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java index 5a5e9ce2b..0c821c8c2 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java @@ -99,4 +99,8 @@ public interface CrmReceivableMapper extends BaseMapperX { return convertMap(result, obj -> (Long) obj.get("contract_id"), obj -> (BigDecimal) obj.get("total_price")); } + default Long selectCountByContractId(Long contractId) { + return selectCount(CrmReceivableDO::getContractId, contractId); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunctionImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunction.java similarity index 79% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunctionImpl.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunction.java index 9df93ac6d..b407ed470 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunctionImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/core/AreaExcelColumnSelectFunction.java @@ -10,10 +10,12 @@ import java.util.List; /** * Excel 所属地区列下拉数据源获取接口实现类 * + * // TODO @puhui999:类名叫:地区下拉框数据源的 {@link ExcelColumnSelectFunction} 实现类,这样看起来会更简洁一点哈 + * * @author HUIHUI */ @Service -public class AreaExcelColumnSelectFunctionImpl implements ExcelColumnSelectFunction { +public class AreaExcelColumnSelectFunction implements ExcelColumnSelectFunction { public static final String NAME = "getCrmAreaNameList"; // 防止和别的模块重名 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java index 8b54b9f55..c2deef8b8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/excel/package-info.java @@ -1 +1,4 @@ +/** + * crm 模块的 excel 拓展封装 + */ package cn.iocoder.yudao.module.crm.framework.excel; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java index 975a2eb51..413b652c1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java @@ -1 +1,4 @@ +/** + * crm 模块的 operatelog 拓展封装 + */ package cn.iocoder.yudao.module.crm.framework.operatelog; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java index 44f408016..97f76dbe1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java @@ -1 +1,4 @@ +/** + * crm 模块的 permission 拓展封装 + */ package cn.iocoder.yudao.module.crm.framework.permission; \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/web/package-info.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/web/package-info.java index e18c3cdb5..09de7263c 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/web/package-info.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/web/package-info.java @@ -1,4 +1,4 @@ /** - * trade 模块的 web 配置 + * crm 模块的 web 拓展封装 */ package cn.iocoder.yudao.module.crm.framework.web; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java index 39ad8f5df..beb3ef1a8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; -import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -27,7 +26,6 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPerm import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; -import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerServiceImpl; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; @@ -231,7 +229,7 @@ public class CrmContractServiceImpl implements CrmContractService { // 1.1 校验存在 CrmContractDO contract = validateContractExists(id); // 1.2 如果被 CrmReceivableDO 所使用,则不允许删除 - if (receivableService.getReceivableByContractId(contract.getId()) != 0) { + if (receivableService.getReceivableCountByContractId(contract.getId()) > 0) { throw exception(CONTRACT_DELETE_FAIL); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java index c4a68fd70..f6ac8c3fd 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java @@ -123,11 +123,11 @@ public interface CrmReceivableService { Map getReceivablePriceMapByContractId(Collection contractIds); /** - * 更具合同编号查询回款列表 + * 根据合同编号查询回款数量 * * @param contractId 合同编号 - * @return 回款 + * @return 回款数量 */ - Long getReceivableByContractId(Long contractId); + Long getReceivableCountByContractId(Long contractId); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java index 889d94e1f..94e1bfb41 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java @@ -302,8 +302,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { } @Override - public Long getReceivableByContractId(Long contractId) { - return receivableMapper.selectCount(CrmReceivableDO::getContractId, contractId); + public Long getReceivableCountByContractId(Long contractId) { + return receivableMapper.selectCountByContractId(contractId); } } From 0043d02d0a10027ba0151a6e7ff4aef51c55e64c Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sat, 9 Mar 2024 14:31:43 +0800 Subject: [PATCH 26/81] =?UTF-8?q?fix=EF=BC=9A=E5=95=86=E6=9C=BA=20api=20?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E7=94=9F=E6=88=90=E4=B8=8D=E5=AF=B9=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/vo/business/CrmBusinessSaveReqVO.java | 4 ++-- .../crm/service/business/CrmBusinessServiceImpl.java | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java index a9ebaae05..fa86692e7 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java @@ -66,13 +66,13 @@ public class CrmBusinessSaveReqVO { private Long contactId; // 使用场景,在【联系人详情】添加商机时,如果需要关联两者,需要传递 contactId 字段 @Schema(description = "产品列表") - private List products; + private List businessProducts; @Schema(description = "产品列表") @Data @NoArgsConstructor @AllArgsConstructor - public static class Product { + public static class BusinessProduct { @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529") @NotNull(message = "产品编号不能为空") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java index 9f2e4ceb6..5ec0a4b41 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.service.business; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; -import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -24,7 +23,6 @@ import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService; import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; -import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerServiceImpl; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; @@ -90,7 +88,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { success = CRM_BUSINESS_CREATE_SUCCESS) public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) { // 1.1 校验产品项的有效性 - List businessProducts = validateBusinessProducts(createReqVO.getProducts()); + List businessProducts = validateBusinessProducts(createReqVO.getBusinessProducts()); // 1.2 校验关联字段 validateRelationDataExists(createReqVO); @@ -131,7 +129,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { // 1.1 校验存在 CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId()); // 1.2 校验产品项的有效性 - List businessProducts = validateBusinessProducts(updateReqVO.getProducts()); + List businessProducts = validateBusinessProducts(updateReqVO.getBusinessProducts()); // 1.3 校验关联字段 validateRelationDataExists(updateReqVO); @@ -204,9 +202,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { } } - private List validateBusinessProducts(List list) { + private List validateBusinessProducts(List list) { // 1. 校验产品存在 - productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.Product::getProductId)); + productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.BusinessProduct::getProductId)); // 2. 转化为 CrmBusinessProductDO 列表 return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class, item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount())))); From 8dcc12f21560c6c3e6b617795227d87d4cb3de0c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 9 Mar 2024 16:32:18 +0800 Subject: [PATCH 27/81] =?UTF-8?q?CRM=EF=BC=9Acode=20review=20=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E6=80=BB=E9=87=8F=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 +-- ...CrmStatisticsCustomerByUserBaseRespVO.java | 6 ++-- ...atisticsCustomerContractSummaryRespVO.java | 8 ++--- .../customer/CrmStatisticsCustomerReqVO.java | 6 +++- .../CrmStatisticsCustomerMapper.java | 13 ++++---- .../CrmStatisticsCustomerServiceImpl.java | 29 +++++++++--------- .../CrmStatisticsCustomerMapper.xml | 13 +++++++- yudao-server/pom.xml | 30 +++++++++---------- 8 files changed, 63 insertions(+), 46 deletions(-) diff --git a/pom.xml b/pom.xml index 3a66524bc..9cf34eb37 100644 --- a/pom.xml +++ b/pom.xml @@ -16,12 +16,12 @@ yudao-module-system yudao-module-infra - + yudao-module-bpm - + yudao-module-crm diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java index 340e93066..41fa9152e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java @@ -5,12 +5,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** - * 用户客户统计响应 Base VO + * 用户客户统计响应 Base Response VO + * + * 目的:可以统一拼接子 VO 的 ownerUserId、ownerUserName 属性 */ @Data public class CrmStatisticsCustomerByUserBaseRespVO { - @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @JsonIgnore private Long ownerUserId; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java index 019309f8e..ec1d27303 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java @@ -5,13 +5,13 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; @Schema(description = "管理后台 - CRM 客户转化率分析 VO") @Data @@ -43,14 +43,14 @@ public class CrmStatisticsCustomerContractSummaryRespVO { @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼") private String sourceName; - @Schema(description = "负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @JsonIgnore private Long ownerUserId; @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") private String ownerUserName; - @Schema(description = "创建人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") @JsonIgnore private String creatorUserId; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java index a7aaa4da5..c5d5acbc3 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java @@ -12,7 +12,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -@Schema(description = "管理后台 - CRM 数据统计 员工客户分析 Request VO") +@Schema(description = "管理后台 - CRM 数据统计的员工客户分析 Request VO") @Data public class CrmStatisticsCustomerReqVO { @@ -39,12 +39,16 @@ public class CrmStatisticsCustomerReqVO { @NotEmpty(message = "时间范围不能为空") private LocalDateTime[] times; + // TODO @dhb52:这个时间间隔,建议前端传递;例如说:字段叫 interval,枚举有天、周、月、季度、年。因为一般分析类的系统,都是交给用户选择筛选时间间隔,而我们这里是默认根据日期选项,默认对应的 interval 而已 + // 然后实现上,可以在 common 包的 enums 加个 DateIntervalEnum,里面一个是 interval 字段,枚举过去,然后有个 pattern 字段,用于格式化时间格式; + // 这样的话,可以通过 interval 获取到 pattern,然后前端就可以根据 pattern 格式化时间,计算还是交给数据库 /** * group by DATE_FORMAT(field, #{dateFormat}) */ @Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m") private String sqlDateFormat; + // TODO @dhb52:这个字段,目前是不是没啥用呀? /** * 数据类型 {@link CrmBizTypeEnum} */ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 6bababa26..19bd76fc3 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -13,17 +13,18 @@ import java.util.List; @Mapper public interface CrmStatisticsCustomerMapper { - List selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + // TODO @dhb52:拼写,GroupBy。一般 idea 如果出现绿色的警告,可能是单词拼写错误,建议是要修改的哈; + List selectCustomerCreateCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review - List selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerDealCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review - List selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerCreateCountGroupbyUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review - List selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); + List selectCustomerDealCountGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review - List selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); + List selectContractPriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review - List selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); + List selectReceivablePriceGroupbyUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review List selectFollowupRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index 6b664dce5..c9654a68e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -55,7 +55,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @Resource private DictDataApi dictDataApi; - @Override public List getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 @@ -66,14 +65,17 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe reqVO.setUserIds(userIds); // 2. 获取分项统计数据 + // TODO @dhb52:如果是 list 变量,要么 List 要么 s 后缀 reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); final List customerCreateCount = customerMapper.selectCustomerCreateCountGroupbyDate(reqVO); final List customerDealCount = customerMapper.selectCustomerDealCountGroupbyDate(reqVO); // 3. 获取时间序列 + // TODO @dhb52:3 和 4 其实做的是一类事情,所以可以考虑 3.1 获取时间序列、3.2 合并统计数据 这样注释;然后中间就不空行了;就是说,一般空行的目的,是让逻辑分片,看着整体性更好,但是不能让逻辑感觉碎碎的; final List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); // 4. 合并统计数据 + // TODO @dhb52:这个是不是要 add 到 respVoList 里?或者还可以 convertList(times, time -> new CrmStatisticsCustomerDealCycleByDateRespVO()...) List respVoList = new ArrayList<>(times.size()); final Map customerCreateCountMap = convertMap(customerCreateCount, CrmStatisticsCustomerSummaryByDateRespVO::getTime, @@ -86,7 +88,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)) )); - return respVoList; } @@ -131,7 +132,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 4. 拼接用户信息 appendUserInfo(respVoList); - return respVoList; } @@ -202,7 +202,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 4. 拼接用户信息 appendUserInfo(respVoList); - return respVoList; } @@ -290,7 +289,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D)) )); - return respVoList; } @@ -337,9 +335,8 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe */ private void appendUserInfo(List respVoList) { Map userMap = adminUserApi.getUserMap(convertSet(respVoList, - CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); - respVoList.forEach(vo -> MapUtils.findAndThen(userMap, - vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); + CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); + respVoList.forEach(vo -> MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); } /** @@ -349,16 +346,17 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe * @return 用户编号数组 */ private List getUserIds(CrmStatisticsCustomerReqVO reqVO) { + // 情况一:选中某个用户 if (ObjUtil.isNotNull(reqVO.getUserId())) { return List.of(reqVO.getUserId()); - } else { - // 1. 获得部门列表 - final Long deptId = reqVO.getDeptId(); - List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); - deptIds.add(deptId); - // 2. 获得用户编号 - return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); } + // 情况二:选中某个部门 + // 2.1 获得部门列表 + final Long deptId = reqVO.getDeptId(); + List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); + deptIds.add(deptId); + // 2.2 获得用户编号 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); } @@ -380,6 +378,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe * @param endTime 结束时间 * @return 时间序列 */ + // TODO @dhb52:可以抽象到 DateUtils 里,开始时间、结束时间,事件间隔,然后返回这个哈; private List generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) { boolean byMonth = queryByMonth(startTime, endTime); List times = CollUtil.newArrayList(); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index 3fd52f3e7..fce255651 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -2,11 +2,14 @@ + + @@ -21,6 +25,8 @@ + + + + SELECT - - DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, - COUNT(*) AS customerCreateCount - FROM crm_customer - WHERE deleted = 0 - AND owner_user_id IN - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND - - #{times[1],javaType=java.time.LocalDateTime} - GROUP BY time + DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, + COUNT(*) AS customerCreateCount + FROM crm_customer + WHERE deleted = 0 + AND owner_user_id IN + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + GROUP BY time - - - - - - - - - - - - - - + + - - - - From 4ac28603b8df276a6f402dc170e8a3330888e0cd Mon Sep 17 00:00:00 2001 From: dhb52 Date: Sun, 10 Mar 2024 10:30:25 +0800 Subject: [PATCH 32/81] =?UTF-8?q?feat:=20[CRM-=E5=AE=A2=E6=88=B7=E5=88=86?= =?UTF-8?q?=E6=9E=90]=E5=A2=9E=E5=8A=A0[=E6=97=B6=E9=97=B4=E9=97=B4?= =?UTF-8?q?=E9=9A=94]=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/enums/DateIntervalEnum.java | 48 ++++++++++++ .../module/crm/enums/ErrorCodeConstants.java | 3 + .../customer/CrmStatisticsCustomerReqVO.java | 19 +++-- .../CrmStatisticsCustomerServiceImpl.java | 75 ++++++++++++++++++- 4 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java new file mode 100644 index 000000000..9a614ddc9 --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.framework.common.enums; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 时间间隔类型枚举 + * + * @author dhb52 + */ +@Getter +@AllArgsConstructor +public enum DateIntervalEnum implements IntArrayValuable { + + TODAY(1, "今天"), + YESTERDAY(2, "昨天"), + THIS_WEEK(3, "本周"), + LAST_WEEK(4, "上周"), + THIS_MONTH(5, "本月"), + LAST_MONTH(6, "上月"), + THIS_QUARTER(7, "本季度"), + LAST_QUARTER(8, "上季度"), + THIS_YEAR(9, "本年"), + LAST_YEAR(10, "去年"), + CUSTOMER(11, "自定义"), + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getType).toArray(); + + /** + * 类型 + */ + private final Integer type; + + /** + * 名称 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java index 4a0976bb1..d49f6068c 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java @@ -103,4 +103,7 @@ public interface ErrorCodeConstants { ErrorCode FOLLOW_UP_RECORD_NOT_EXISTS = new ErrorCode(1_020_013_000, "跟进记录不存在"); ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限"); + // ========== 数据统计 1_020_014_000 ========== + ErrorCode STATISTICS_CUSTOMER_TIMES_NOT_SET = new ErrorCode(1_020_014_000, "自定义时间间隔,必须输入时间区间"); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java index 13b3adda7..38f15cf03 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java @@ -1,7 +1,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; @@ -27,22 +28,26 @@ public class CrmStatisticsCustomerReqVO { /** * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 - *

* 后续,可能会支持选择部分用户进行查询 */ @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") private List userIds; - @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") + private Integer intervalType; + + /** + * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间 + * 并作为参数传递给Mapper + */ + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @NotEmpty(message = "时间范围不能为空") private LocalDateTime[] times; - // TODO @dhb52:这个时间间隔,建议前端传递;例如说:字段叫 interval,枚举有天、周、月、季度、年。因为一般分析类的系统,都是交给用户选择筛选时间间隔,而我们这里是默认根据日期选项,默认对应的 interval 而已 - // 然后实现上,可以在 common 包的 enums 加个 DateIntervalEnum,里面一个是 interval 字段,枚举过去,然后有个 pattern 字段,用于格式化时间格式; - // 这样的话,可以通过 interval 获取到 pattern,然后前端就可以根据 pattern 格式化时间,计算还是交给数据库 /** * group by DATE_FORMAT(field, #{dateFormat}) + * 非前端传递, 由Service计算后传递给Mapper的参数 */ @Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m") private String sqlDateFormat; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index a4c3f4ddf..f951e25f5 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -1,8 +1,11 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; @@ -24,8 +27,10 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CUSTOMER_TIMES_NOT_SET; /** * CRM 客户分析 Service 实现类 @@ -63,7 +68,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe reqVO.setUserIds(userIds); // 2. 获取分项统计数据 - reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + initParams(reqVO); List customerCreateCountVoList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO); List customerDealCountVoList = customerMapper.selectCustomerDealCountGroupByDate(reqVO); @@ -94,6 +99,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe reqVO.setUserIds(userIds); // 2. 获取分项统计数据 + initParams(reqVO); List customerCreateCount = customerMapper.selectCustomerCreateCountGroupByUser(reqVO); List customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO); List contractPrice = customerMapper.selectContractPriceGroupByUser(reqVO); @@ -139,7 +145,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe reqVO.setUserIds(userIds); // 2. 获取分项统计数据 - reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + initParams(reqVO); List followupRecordCount = customerMapper.selectFollowupRecordCountGroupByDate(reqVO); List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByDate(reqVO); @@ -170,6 +176,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe reqVO.setUserIds(userIds); // 2. 获取分项统计数据 + initParams(reqVO); List followupRecordCount = customerMapper.selectFollowupRecordCountGroupByUser(reqVO); List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByUser(reqVO); @@ -204,6 +211,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe reqVO.setUserIds(userIds); // 2. 获得排行数据 + initParams(reqVO); List respVoList = customerMapper.selectFollowupRecordCountGroupByType(reqVO); // 3. 获取字典数据 @@ -227,6 +235,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe reqVO.setUserIds(userIds); // 2. 获取统计数据 + initParams(reqVO); List respVoList = customerMapper.selectContractSummary(reqVO); // 3. 设置 创建人、负责人、行业、来源 @@ -261,7 +270,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe reqVO.setUserIds(userIds); // 2. 获取分项统计数据 - reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + initParams(reqVO); List customerDealCycle = customerMapper.selectCustomerDealCycleGroupByDate(reqVO); // 3. 合并统计数据 @@ -287,6 +296,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe reqVO.setUserIds(userIds); // 2. 获取分项统计数据 + initParams(reqVO); List customerDealCycle = customerMapper.selectCustomerDealCycleGroupByUser(reqVO); List customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO); @@ -363,7 +373,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe * @param endTime 结束时间 * @return 时间序列 */ - // TODO @dhb52:可以抽象到 DateUtils 里,开始时间、结束时间,事件间隔,然后返回这个哈; private List generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) { boolean byMonth = queryByMonth(startTime, endTime); List times = CollUtil.newArrayList(); @@ -389,4 +398,62 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY; } + private void initParams(CrmStatisticsCustomerReqVO reqVO) { + final Integer intervalType = reqVO.getIntervalType(); + + // 1. 自定义时间间隔,必须输入起始日期-结束日期 + if (DateIntervalEnum.CUSTOMER.getType().equals(intervalType)) { + if (ObjUtil.isEmpty(reqVO.getTimes()) || reqVO.getTimes().length != 2) { + throw exception(STATISTICS_CUSTOMER_TIMES_NOT_SET); + } + // 设置 mapper sqlDateFormat 参数 + reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); + // 自定义日期无需计算日期参数 + return; + } + + // 2. 根据时间区间类型计算时间段区间日期 + DateTime beginDate = null; + DateTime endDate = null; + if (DateIntervalEnum.TODAY.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfDay(DateUtil.date()); + endDate = DateUtil.endOfDay(DateUtil.date()); + } else if (DateIntervalEnum.YESTERDAY.getType().equals(intervalType)) { + beginDate = DateUtil.offsetDay(DateUtil.date(), -1); + endDate = DateUtil.offsetDay(DateUtil.date(), -1); + } else if (DateIntervalEnum.THIS_WEEK.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfWeek(DateUtil.date()); + endDate = DateUtil.endOfWeek(DateUtil.date()); + } else if (DateIntervalEnum.LAST_WEEK.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1)); + endDate = DateUtil.endOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1)); + } else if (DateIntervalEnum.THIS_MONTH.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfMonth(DateUtil.date()); + endDate = DateUtil.endOfMonth(DateUtil.date()); + } else if (DateIntervalEnum.LAST_MONTH.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1)); + endDate = DateUtil.endOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1)); + } else if (DateIntervalEnum.THIS_QUARTER.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfQuarter(DateUtil.date()); + endDate = DateUtil.endOfQuarter(DateUtil.date()); + } else if (DateIntervalEnum.LAST_QUARTER.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3)); + endDate = DateUtil.endOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3)); + } else if (DateIntervalEnum.THIS_YEAR.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfYear(DateUtil.date()); + endDate = DateUtil.endOfYear(DateUtil.date()); + } else if (DateIntervalEnum.LAST_YEAR.getType().equals(intervalType)) { + beginDate = DateUtil.beginOfYear(DateUtil.offsetMonth(DateUtil.date(), -12)); + endDate = DateUtil.endOfYear(DateUtil.offsetMonth(DateUtil.date(), -12)); + } + + // 3. 计算开始、结束日期时间,并设置reqVo + LocalDateTime[] times = new LocalDateTime[2]; + times[0] = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.of(beginDate)); + times[1] = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.of(endDate)); + // 3.1 设置 mapper 时间区间 参数 + reqVO.setTimes(times); + // 3.2 设置 mapper sqlDateFormat 参数 + reqVO.setSqlDateFormat(getSqlDateFormat(times[0], times[1])); + } } From 03213e3254a798101189a9560d1a62bf6f1587ff Mon Sep 17 00:00:00 2001 From: dhb52 Date: Sun, 10 Mar 2024 14:30:59 +0800 Subject: [PATCH 33/81] =?UTF-8?q?fix:=20[CRM-=E5=AE=A2=E6=88=B7=E5=88=86?= =?UTF-8?q?=E6=9E=90]=E6=9B=B4=E6=96=B0http-client=E8=84=9A=E6=9C=AC,?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0intervalType=E8=AF=B7=E6=B1=82=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsCustomerController.http | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http index 0c422f986..9d96e159a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http @@ -1,40 +1,40 @@ # == 1. 客户总量分析 == ### 1.1 客户总量分析(按日) -GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&intervalType=11×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} ### 1.2 客户总量分析(按月) -GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} ### 1.3 客户总量统计(按用户) -GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} # == 2. 客户跟进次数分析 == ### 2.1 客户跟进次数分析(按日) -GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100&intervalType=11×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} ### 2.2 客户跟进次数分析(按月) -GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-date?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} ### 2.3 客户总量统计(按用户) -GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-user?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} # == 3. 客户跟进方式分析 == ### 3.1 客户跟进方式分析 -GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-type?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-followup-summary-by-type?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} @@ -42,7 +42,7 @@ tenant-id: {{adminTenentId}} # == 4. 客户成交周期 == ### 4.1 合同摘要信息(客户转化率页面) -GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} @@ -50,19 +50,19 @@ tenant-id: {{adminTenentId}} # == 5. 客户成交周期 == ### 5.1 客户成交周期(按日) -GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&intervalType=11×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} ### 5.2 客户成交周期(按月) -GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} ### 5.3 获取客户成交周期(按用户) -GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100&intervalType=11×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} tenant-id: {{adminTenentId}} \ No newline at end of file From 0dd36f6c5c563f922d484f74b9a45df73e50e93e Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 10 Mar 2024 19:07:24 +0800 Subject: [PATCH 34/81] =?UTF-8?q?MALL:=20fix=20=E8=AE=A2=E5=8D=95=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E5=88=86=E6=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade/service/order/TradeOrderUpdateServiceImpl.java | 8 +++++--- .../price/calculator/TradePriceCalculatorHelper.java | 7 +++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java index 13fe2f0c2..4065ed05e 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java @@ -624,7 +624,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { throw exception(ORDER_UPDATE_PRICE_FAIL_ALREADY); } // 1.3 支付价格不能为 0 - int newPayPrice = order.getPayPrice() + order.getAdjustPrice(); + int newPayPrice = order.getPayPrice() + reqVO.getAdjustPrice(); if (newPayPrice <= 0) { throw exception(ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR); } @@ -635,12 +635,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { // 3. 更新 TradeOrderItem,需要做 adjustPrice 的分摊 List orderOrderItems = tradeOrderItemMapper.selectListByOrderId(order.getId()); - List dividePrices = TradePriceCalculatorHelper.dividePrice2(orderOrderItems, newPayPrice); + List dividePrices = TradePriceCalculatorHelper.dividePrice2(orderOrderItems, reqVO.getAdjustPrice()); List updateItems = new ArrayList<>(); for (int i = 0; i < orderOrderItems.size(); i++) { TradeOrderItemDO item = orderOrderItems.get(i); + // TODO puhui999: 已有分摊记录的情况下价格是否会不对,也就是说之前订单项 1 分摊了 10 块这次是 -100 + // 那么 setPayPrice 是否改为 (item.getPayPrice()-item.getAdjustPrice()) + dividePrices.get(i) 先减掉原来的价格再加上调价。经过验证可行,修改后订单价格增减都能正确分摊 updateItems.add(new TradeOrderItemDO().setId(item.getId()).setAdjustPrice(dividePrices.get(i)) - .setPayPrice(item.getPayPrice() + dividePrices.get(i))); + .setPayPrice((item.getPayPrice() - item.getAdjustPrice()) + dividePrices.get(i))); } tradeOrderItemMapper.updateBatch(updateItems); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java index 7def3e34e..850226fbb 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java @@ -254,12 +254,15 @@ public class TradePriceCalculatorHelper { TradeOrderItemDO orderItem = items.get(i); int partPrice; if (i < items.size() - 1) { // 减一的原因,是因为拆分时,如果按照比例,可能会出现.所以最后一个,使用反减 - partPrice = (int) (price * (1.0D * orderItem.getPayPrice() / total)); + // partPrice = (int) (price * (1.0D * orderItem.getPayPrice() / total)); + // pr fix: 改为了使用订单原价来计算比例 + partPrice = (int) (price * (1.0D * orderItem.getPrice() / total)); remainPrice -= partPrice; } else { partPrice = remainPrice; } - Assert.isTrue(partPrice >= 0, "分摊金额必须大于等于 0"); + // TODO puhui999: 如果是减价的情况这里过不了 + // Assert.isTrue(partPrice >= 0, "分摊金额必须大于等于 0"); prices.add(partPrice); } return prices; From 705a3e53cae4df60995c9e27274bcdf6e5a7607d Mon Sep 17 00:00:00 2001 From: scholar <1145227973@qq.com> Date: Fri, 15 Mar 2024 12:09:19 +0800 Subject: [PATCH 35/81] =?UTF-8?q?CRM=E5=91=98=E5=B7=A5=E4=B8=9A=E7=BB=A9?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1PR=20v2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsPerformanceController.java | 52 ++++++ .../CrmStatisticsPerformanceReqVO.java | 41 +++++ .../CrmStatisticsPerformanceRespVO.java | 24 +++ .../CrmStatisticsPerformanceMapper.java | 41 +++++ .../CrmStatisticsPerformanceService.java | 42 +++++ .../CrmStatisticsPerformanceServiceImpl.java | 99 ++++++++++++ .../CrmStatisticsPerformanceMapper.xml | 152 ++++++++++++++++++ 7 files changed, 451 insertions(+) create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPerformanceController.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceReqVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPerformanceMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPerformanceMapper.xml diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPerformanceController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPerformanceController.java new file mode 100644 index 000000000..1438a12db --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPerformanceController.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPerformanceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + + +@Tag(name = "管理后台 - CRM 员工业绩统计") +@RestController +@RequestMapping("/crm/statistics-performance") +@Validated +public class CrmStatisticsPerformanceController { + + @Resource + private CrmStatisticsPerformanceService performanceService; + + @GetMapping("/get-contract-count-performance") + @Operation(summary = "员工业绩-签约合同数量") + @PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')") + public CommonResult> getContractCountPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) { + return success(performanceService.getContractCountPerformance(performanceReqVO)); + } + + @GetMapping("/get-contract-price-performance") + @Operation(summary = "员工业绩-获得合同金额") + @PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')") + public CommonResult> getContractPriceStaffPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) { + return success(performanceService.getContractPricePerformance(performanceReqVO)); + } + + @GetMapping("/get-receivable-price-performance") + @Operation(summary = "员工业绩-获得回款金额") + @PreAuthorize("@ss.hasPermission('crm:statistics-performance:query')") + public CommonResult> getReceivablePriceStaffPerformance(@Valid CrmStatisticsPerformanceReqVO performanceReqVO) { + return success(performanceService.getReceivablePricePerformance(performanceReqVO)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceReqVO.java new file mode 100644 index 000000000..bfb5c840f --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceReqVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - CRM 员工业绩统计 Request VO") +@Data +public class CrmStatisticsPerformanceReqVO { + + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "部门 id 不能为空") + private Long deptId; + + /** + * 负责人用户 id, 当用户为空, 则计算部门下用户 + */ + @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1") + private Long userId; + + /** + * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 + *

+ * 后续,可能会支持选择部分用户进行查询 + */ + @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") + private List userIds; + + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @NotEmpty(message = "时间范围不能为空") + private LocalDateTime[] times; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceRespVO.java new file mode 100644 index 000000000..8b217fd41 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import java.math.BigDecimal; + + +@Schema(description = "管理后台 - CRM 员工业绩统计 Response VO") +@Data +public class CrmStatisticsPerformanceRespVO { + + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") + private String time; + + @Schema(description = "当月统计结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private BigDecimal currentMonthCount; + + @Schema(description = "上月统计结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private BigDecimal lastMonthCount; + + @Schema(description = "去年同期统计结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") + private BigDecimal lastYearCount; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPerformanceMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPerformanceMapper.java new file mode 100644 index 000000000..09702f290 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPerformanceMapper.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.statistics; + +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * CRM 员工业绩分析 Mapper + * + * @author scholar + */ +@Mapper +public interface CrmStatisticsPerformanceMapper { + + /** + * 员工签约合同数量 + * + * @param performanceReqVO 参数 + * @return 员工签约合同数量 + */ + List selectContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO); + + /** + * 员工签约合同金额 + * + * @param performanceReqVO 参数 + * @return 员工签约合同金额 + */ + List selectContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); + + /** + * 员工回款金额 + * + * @param performanceReqVO 参数 + * @return 员工回款金额 + */ + List selectReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceService.java new file mode 100644 index 000000000..354bbab25 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceService.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.crm.service.statistics; + + + +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; + +import java.util.List; + +/** + * CRM 员工绩效统计 Service 接口 + * + * @author scholar + */ +public interface CrmStatisticsPerformanceService { + + /** + * 员工签约合同数量分析 + * + * @param performanceReqVO 排行参数 + * @return 员工签约合同数量排行分析 + */ + List getContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO); + + /** + * 员工签约合同金额分析 + * + * @param performanceReqVO 排行参数 + * @return 员工签约合同金额分析 + */ + List getContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); + + /** + * 员工获得回款金额分析 + * + * @param performanceReqVO 排行参数 + * @return 员工获得回款金额分析 + */ + List getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO); + + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java new file mode 100644 index 000000000..067be4f43 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.crm.service.statistics; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceRespVO; +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceMapper; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +/** + * CRM 员工业绩分析 Service 实现类 + * + * @author scholar + */ +@Service +@Validated +public class CrmStatisticsPerformanceServiceImpl implements CrmStatisticsPerformanceService { + + @Resource + private CrmStatisticsPerformanceMapper performanceMapper; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + + @Override + public List getContractCountPerformance(CrmStatisticsPerformanceReqVO performanceReqVO) { + return getPerformance(performanceReqVO, performanceMapper::selectContractCountPerformance); + } + + @Override + public List getContractPricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO) { + return getPerformance(performanceReqVO, performanceMapper::selectContractPricePerformance); + } + + @Override + public List getReceivablePricePerformance(CrmStatisticsPerformanceReqVO performanceReqVO) { + return getPerformance(performanceReqVO, performanceMapper::selectReceivablePricePerformance); + } + + /** + * 获得员工业绩数据 + * + * @param performanceReqVO 参数 + * @param performanceFunction 排行榜方法 + * @return 排行版数据 + */ + private List getPerformance(CrmStatisticsPerformanceReqVO performanceReqVO, Function> performanceFunction) { + + // 1. 获得用户编号数组 + final List userIds = getUserIds(performanceReqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + performanceReqVO.setUserIds(userIds); + // 2. 获得排行数据 + List performance = performanceFunction.apply(performanceReqVO); + if (CollUtil.isEmpty(performance)) { + return Collections.emptyList(); + } + return performance; + } + + /** + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号 + * + * @param reqVO 请求参数 + * @return 用户编号数组 + */ + private List getUserIds(CrmStatisticsPerformanceReqVO reqVO) { + // 情况一:选中某个用户 + if (ObjUtil.isNotNull(reqVO.getUserId())) { + return List.of(reqVO.getUserId()); + } + // 情况二:选中某个部门 + // 2.1 获得部门列表 + final Long deptId = reqVO.getDeptId(); + List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); + deptIds.add(deptId); + // 2.2 获得用户编号 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); + } + +} \ No newline at end of file diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPerformanceMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPerformanceMapper.xml new file mode 100644 index 000000000..10b952bac --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPerformanceMapper.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + From cc27652f86686e71362d4c5c69ea8aefb2770c5e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 24 Mar 2024 10:43:37 +0800 Subject: [PATCH 36/81] =?UTF-8?q?=E6=9C=80=E6=96=B0=E8=8F=9C=E5=8D=95=20SQ?= =?UTF-8?q?L=20=E7=9A=84=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- yudao-server/pom.xml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 1b5fc92c1..3a66524bc 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ yudao-module-system yudao-module-infra - yudao-module-bpm + diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index e5b816b9b..bc850b590 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -46,11 +46,11 @@ - - cn.iocoder.boot - yudao-module-bpm-biz - ${revision} - + + + + + From ce013a2562883f22884ce267ac8f980dd86c73d4 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 24 Mar 2024 17:15:56 +0800 Subject: [PATCH 37/81] =?UTF-8?q?CRM:=20=E6=96=B0=E5=A2=9E=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E8=A1=8C=E4=B8=9A=E3=80=81=E6=9D=A5=E6=BA=90=E3=80=81?= =?UTF-8?q?=E7=BA=A7=E5=88=AB=E7=BB=9F=E8=AE=A1=E6=95=B0=E6=8D=AE=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsCustomerController.java | 24 +++ .../CrmStatisticCustomerIndustryRespVO.java | 27 +++ .../CrmStatisticCustomerLevelRespVO.java | 27 +++ .../CrmStatisticCustomerSourceRespVO.java | 27 +++ .../CrmStatisticsCustomerMapper.java | 9 + .../CrmStatisticsCustomerService.java | 27 +++ .../CrmStatisticsCustomerServiceImpl.java | 164 +++++++++++++----- .../CrmStatisticsCustomerMapper.xml | 64 +++++++ .../module/system/api/dict/DictDataApi.java | 17 ++ yudao-server/pom.xml | 10 +- 10 files changed, 345 insertions(+), 51 deletions(-) create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerIndustryRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerLevelRespVO.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerSourceRespVO.java diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index 4a0b2e760..4b45a9c28 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -82,4 +85,25 @@ public class CrmStatisticsCustomerController { return success(customerService.getCustomerDealCycleByUser(reqVO)); } + @GetMapping("/get-customer-industry-summary") + @Operation(summary = "获取客户行业统计数据") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getCustomerIndustry(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerIndustry(reqVO)); + } + + @GetMapping("/get-customer-source-summary") + @Operation(summary = "获取客户来源统计数据") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getCustomerSource(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerSource(reqVO)); + } + + @GetMapping("/get-customer-level-summary") + @Operation(summary = "获取客户级别统计数据") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getCustomerLevel(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerLevel(reqVO)); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerIndustryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerIndustryRespVO.java new file mode 100644 index 000000000..481174092 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerIndustryRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 客户行业分析 VO") +@Data +public class CrmStatisticCustomerIndustryRespVO { + + @Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer industryId; + @Schema(description = "客户行业名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private String industryName; + + @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerCount; + + @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer dealCount; + + @Schema(description = "行业占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Double industryPortion; + + @Schema(description = "成交占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Double dealPortion; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerLevelRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerLevelRespVO.java new file mode 100644 index 000000000..74e06918c --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerLevelRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 客户级别分析 VO") +@Data +public class CrmStatisticCustomerLevelRespVO { + + @Schema(description = "客户级别ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer level; + @Schema(description = "客户级别名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private String levelName; + + @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerCount; + + @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer dealCount; + + @Schema(description = "级别占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Double levelPortion; + + @Schema(description = "成交占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Double dealPortion; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerSourceRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerSourceRespVO.java new file mode 100644 index 000000000..2edba39ed --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerSourceRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 客户来源分析 VO") +@Data +public class CrmStatisticCustomerSourceRespVO { + + @Schema(description = "客户来源ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer source; + @Schema(description = "客户来源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private String sourceName; + + @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerCount; + + @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer dealCount; + + @Schema(description = "来源占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Double sourcePortion; + + @Schema(description = "成交占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Double dealPortion; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 19bd76fc3..0e8df5565 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -42,4 +45,10 @@ public interface CrmStatisticsCustomerMapper { List selectCustomerDealCycleGroupbyUser(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerIndustryListGroupbyIndustryId(CrmStatisticsCustomerReqVO reqVO); + + List selectCustomerSourceListGroupbySource(CrmStatisticsCustomerReqVO reqVO); + + List selectCustomerLevelListGroupbyLevel(CrmStatisticsCustomerReqVO reqVO); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java index 546124701..5402c706d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; import java.util.List; @@ -77,4 +80,28 @@ public interface CrmStatisticsCustomerService { */ List getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO); + /** + * 获取客户行业统计数据 + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO); + + /** + * 获取客户来源统计数据 + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerSource(CrmStatisticsCustomerReqVO reqVO); + + /** + * 获取客户级别统计数据 + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerLevel(CrmStatisticsCustomerReqVO reqVO); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index c9654a68e..e2a375291 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -6,6 +6,9 @@ import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.system.api.dept.DeptApi; @@ -78,15 +81,15 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // TODO @dhb52:这个是不是要 add 到 respVoList 里?或者还可以 convertList(times, time -> new CrmStatisticsCustomerDealCycleByDateRespVO()...) List respVoList = new ArrayList<>(times.size()); final Map customerCreateCountMap = convertMap(customerCreateCount, - CrmStatisticsCustomerSummaryByDateRespVO::getTime, - CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount); + CrmStatisticsCustomerSummaryByDateRespVO::getTime, + CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount); final Map customerDealCountMap = convertMap(customerDealCount, - CrmStatisticsCustomerSummaryByDateRespVO::getTime, - CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount); + CrmStatisticsCustomerSummaryByDateRespVO::getTime, + CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount); times.forEach(time -> respVoList.add( - new CrmStatisticsCustomerSummaryByDateRespVO().setTime(time) - .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) - .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)) + new CrmStatisticsCustomerSummaryByDateRespVO().setTime(time) + .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) + .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0)) )); return respVoList; } @@ -108,25 +111,25 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 3. 合并统计数据 final Map customerCreateCountMap = convertMap(customerCreateCount, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount); + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount); final Map customerDealCountMap = convertMap(customerDealCount, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); final Map contractPriceMap = convertMap(contractPrice, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice); + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice); final Map receivablePriceMap = convertMap(receivablePrice, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice); + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice); List respVoList = new ArrayList<>(userIds.size()); userIds.forEach(userId -> { final CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO(); vo.setOwnerUserId(userId); vo.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0)) - .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)) - .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.ZERO)) - .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.ZERO)); + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)) + .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.ZERO)) + .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.ZERO)); respVoList.add(vo); }); @@ -156,15 +159,15 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 4. 合并统计数据 List respVoList = new ArrayList<>(times.size()); final Map followupRecordCountMap = convertMap(followupRecordCount, - CrmStatisticsFollowupSummaryByDateRespVO::getTime, - CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount); + CrmStatisticsFollowupSummaryByDateRespVO::getTime, + CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount); final Map followupCustomerCountMap = convertMap(followupCustomerCount, - CrmStatisticsFollowupSummaryByDateRespVO::getTime, - CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount); + CrmStatisticsFollowupSummaryByDateRespVO::getTime, + CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount); times.forEach(time -> respVoList.add( - new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time) - .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0)) - .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0)) + new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time) + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0)) + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0)) )); return respVoList; @@ -186,16 +189,16 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 3. 合并统计数据 final Map followupRecordCountMap = convertMap(followupRecordCount, - CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount); + CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount); final Map followupCustomerCountMap = convertMap(followupCustomerCount, - CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount); + CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount); List respVoList = new ArrayList<>(userIds.size()); userIds.forEach(userId -> { final CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO() - .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0)) - .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0)); + .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0)) + .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0)); vo.setOwnerUserId(userId); respVoList.add(vo); }); @@ -221,7 +224,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 3. 获取字典数据 List followUpTypes = dictDataApi.getDictDataList(CRM_FOLLOW_UP_TYPE); final Map followUpTypeMap = convertMap(followUpTypes, - DictDataRespDTO::getValue, DictDataRespDTO::getLabel); + DictDataRespDTO::getValue, DictDataRespDTO::getLabel); respVoList.forEach(vo -> { vo.setFollowupType(followUpTypeMap.get(vo.getFollowupType())); }); @@ -244,19 +247,19 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 3. 设置 创建人、负责人、行业、来源 // 获取客户所属行业 Map industryMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_INDUSTRY), - DictDataRespDTO::getValue, DictDataRespDTO::getLabel); + DictDataRespDTO::getValue, DictDataRespDTO::getLabel); // 获取客户来源 Map sourceMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_SOURCE), - DictDataRespDTO::getValue, DictDataRespDTO::getLabel); + DictDataRespDTO::getValue, DictDataRespDTO::getLabel); // 获取创建人、负责人列表 Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(respVoList, - vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId()))); + vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId()))); respVoList.forEach(vo -> { MapUtils.findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName); MapUtils.findAndThen(sourceMap, vo.getSource(), vo::setSourceName); MapUtils.findAndThen(userMap, NumberUtils.parseLong(vo.getCreatorUserId()), - user -> vo.setCreatorUserName(user.getNickname())); + user -> vo.setCreatorUserName(user.getNickname())); MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())); }); @@ -283,11 +286,11 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 4. 合并统计数据 List respVoList = new ArrayList<>(times.size()); final Map customerDealCycleMap = convertMap(customerDealCycle, - CrmStatisticsCustomerDealCycleByDateRespVO::getTime, - CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle); + CrmStatisticsCustomerDealCycleByDateRespVO::getTime, + CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle); times.forEach(time -> respVoList.add( - new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) - .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D)) + new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D)) )); return respVoList; } @@ -308,16 +311,16 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 3. 合并统计数据 final Map customerDealCycleMap = convertMap(customerDealCycle, - CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle); + CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle); final Map customerDealCountMap = convertMap(customerDealCount, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); + CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, + CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); List respVoList = new ArrayList<>(userIds.size()); userIds.forEach(userId -> { final CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO() - .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0)) - .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)); + .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0)) + .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)); vo.setOwnerUserId(userId); respVoList.add(vo); }); @@ -328,6 +331,75 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe return respVoList; } + @Override + public List getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + // 2. 获取客户行业统计数据 + List industryRespVOList = customerMapper.selectCustomerIndustryListGroupbyIndustryId(reqVO); + if (CollUtil.isEmpty(industryRespVOList)) { + return Collections.emptyList(); + } + + return convertList(industryRespVOList, item -> { + if (ObjUtil.isNull(item.getIndustryId())) { + return item; + } + item.setIndustryName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, item.getIndustryId())); + return item; + }); + } + + @Override + public List getCustomerSource(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + // 2. 获取客户行业统计数据 + List sourceRespVOList = customerMapper.selectCustomerSourceListGroupbySource(reqVO); + if (CollUtil.isEmpty(sourceRespVOList)) { + return Collections.emptyList(); + } + + return convertList(sourceRespVOList, item -> { + if (ObjUtil.isNull(item.getSource())) { + return item; + } + item.setSourceName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_SOURCE, item.getSource())); + return item; + }); + } + + @Override + public List getCustomerLevel(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + // 2. 获取客户行业统计数据 + List levelRespVOList = customerMapper.selectCustomerLevelListGroupbyLevel(reqVO); + if (CollUtil.isEmpty(levelRespVOList)) { + return Collections.emptyList(); + } + + return convertList(levelRespVOList, item -> { + if (ObjUtil.isNull(item.getLevel())) { + return item; + } + item.setLevelName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_LEVEL, item.getLevel())); + return item; + }); + } + /** * 拼接用户信息(昵称) * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index fce255651..758da4fcd 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -249,4 +249,68 @@ GROUP BY a.owner_user_id + + + + diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java index 5c47f4de3..b75684de0 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.system.api.dict; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import java.util.Collection; @@ -33,6 +35,21 @@ public interface DictDataApi { */ DictDataRespDTO getDictData(String type, String value); + /** + * 获得指定的字典标签,从缓存中 + * + * @param type 字典类型 + * @param value 字典数据值 + * @return 字典标签 + */ + default String getDictDataLabel(String type, Integer value) { + DictDataRespDTO dictData = getDictData(type, String.valueOf(value)); + if (ObjUtil.isNull(dictData)) { + return StrUtil.EMPTY; + } + return dictData.getLabel(); + } + /** * 解析获得指定的字典数据,从缓存中 * diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index ff17b298d..ade54c068 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -95,11 +95,11 @@ - - cn.iocoder.boot - yudao-module-erp-biz - ${revision} - + + + + + From 22191e81e3a74f90dd20efe5d4db2d7938aa91de Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 24 Mar 2024 22:24:46 +0800 Subject: [PATCH 38/81] =?UTF-8?q?CRM:=20=E6=96=B0=E5=A2=9E=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E5=8C=BA=E5=9F=9F=E6=95=B0=E6=8D=AE=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsCustomerController.java | 8 ++++ .../CrmStatisticCustomerAreaRespVO.java | 27 ++++++++++++ .../CrmStatisticsCustomerMapper.java | 3 ++ .../CrmStatisticsCustomerService.java | 9 ++++ .../CrmStatisticsCustomerServiceImpl.java | 44 ++++++++++++++++--- .../CrmStatisticsCustomerMapper.xml | 21 +++++++++ 6 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerAreaRespVO.java diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index 4b45a9c28..0422bff4b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; @@ -106,4 +107,11 @@ public class CrmStatisticsCustomerController { return success(customerService.getCustomerLevel(reqVO)); } + @GetMapping("/get-customer-area-summary") + @Operation(summary = "获取客户地区统计数据") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult> getCustomerArea(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerArea(reqVO)); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerAreaRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerAreaRespVO.java new file mode 100644 index 000000000..5f6924d58 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerAreaRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - CRM 客户省份分析 VO") +@Data +public class CrmStatisticCustomerAreaRespVO { + + @Schema(description = "省份编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer areaId; + @Schema(description = "省份名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "浙江省") + private String areaName; + + @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer customerCount; + + @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer dealCount; + + @Schema(description = "省份占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Double areaPortion; + + @Schema(description = "成交占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Double dealPortion; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 0e8df5565..168d0fb51 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; @@ -51,4 +52,6 @@ public interface CrmStatisticsCustomerMapper { List selectCustomerLevelListGroupbyLevel(CrmStatisticsCustomerReqVO reqVO); + List selectSummaryListByAreaId(CrmStatisticsCustomerReqVO reqVO); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java index 5402c706d..73bd437d6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; @@ -104,4 +105,12 @@ public interface CrmStatisticsCustomerService { */ List getCustomerLevel(CrmStatisticsCustomerReqVO reqVO); + /** + * 获取客户地区统计数据 + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerArea(CrmStatisticsCustomerReqVO reqVO); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index e2a375291..69cbd6442 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -5,7 +5,11 @@ import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.framework.ip.core.Area; +import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; @@ -30,6 +34,7 @@ import java.util.Map; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; /** @@ -256,11 +261,11 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId()))); respVoList.forEach(vo -> { - MapUtils.findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName); - MapUtils.findAndThen(sourceMap, vo.getSource(), vo::setSourceName); - MapUtils.findAndThen(userMap, NumberUtils.parseLong(vo.getCreatorUserId()), + findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName); + findAndThen(sourceMap, vo.getSource(), vo::setSourceName); + findAndThen(userMap, NumberUtils.parseLong(vo.getCreatorUserId()), user -> vo.setCreatorUserName(user.getNickname())); - MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())); + findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())); }); return respVoList; @@ -400,6 +405,35 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe }); } + @Override + public List getCustomerArea(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + // 2. 获取客户地区统计数据 + List list = customerMapper.selectSummaryListByAreaId(reqVO); + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + + // 拼接数据 + List areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area); + areaList.add(new Area().setId(null).setName("未知")); + Map areaMap = convertMap(areaList, Area::getId); + List customerAreaRespVOList = convertList(list, item -> { + Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE); + if (parentId == null) { + return item; + } + findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName())); + return item; + }); + return customerAreaRespVOList; + } + /** * 拼接用户信息(昵称) * @@ -408,7 +442,7 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe private void appendUserInfo(List respVoList) { Map userMap = adminUserApi.getUserMap(convertSet(respVoList, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); - respVoList.forEach(vo -> MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); + respVoList.forEach(vo -> findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); } /** diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index 758da4fcd..e843d1dee 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -312,5 +312,26 @@ GROUP BY level; + From 3b8dee4963df6ddaa11baa11a8dbcd01cb298f2f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 26 Mar 2024 08:38:59 +0800 Subject: [PATCH 39/81] =?UTF-8?q?BPM=EF=BC=9A=E5=AE=8C=E5=96=84=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E4=BB=A3=E7=A0=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tatustEnum.java => BpmTaskStatusEnum.java} | 2 +- .../admin/task/BpmTaskController.java | 2 +- .../bpm/dal/dataobject/oa/BpmOALeaveDO.java | 4 +-- .../candidate/BpmTaskCandidateInvoker.java | 2 ++ .../bpm/service/oa/BpmOALeaveServiceImpl.java | 4 +-- .../bpm/service/task/BpmTaskServiceImpl.java | 36 +++++++++---------- .../module/crm/util/CrmAuditStatusUtils.java | 8 ++--- .../controller/admin/auth/AuthController.java | 2 +- 8 files changed, 31 insertions(+), 29 deletions(-) rename yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/{BpmTaskStatustEnum.java => BpmTaskStatusEnum.java} (98%) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatustEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java similarity index 98% rename from yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatustEnum.java rename to yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java index eb4af0f62..40a385a58 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatustEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java @@ -11,7 +11,7 @@ import lombok.Getter; */ @Getter @AllArgsConstructor -public enum BpmTaskStatustEnum { +public enum BpmTaskStatusEnum { RUNNING(1, "审批中"), APPROVE(2, "审批通过"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java index 7d72a133b..70eb0f5e3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java @@ -93,7 +93,7 @@ public class BpmTaskController { @GetMapping("manager-page") @Operation(summary = "获取全部任务的分页", description = "用于【流程任务】菜单") @PreAuthorize("@ss.hasPermission('bpm:task:mananger-query')") - public CommonResult> getDoneTaskPage(@Valid BpmTaskPageReqVO pageVO) { + public CommonResult> getTaskManagerPage(@Valid BpmTaskPageReqVO pageVO) { PageResult pageResult = taskService.getTaskPage(getLoginUserId(), pageVO); if (CollUtil.isEmpty(pageResult.getList())) { return success(PageResult.empty()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java index ff63f8e43..6c5b648da 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.bpm.dal.dataobject.oa; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatustEnum; +import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; @@ -57,7 +57,7 @@ public class BpmOALeaveDO extends BaseDO { /** * 审批结果 * - * 枚举 {@link BpmTaskStatustEnum} + * 枚举 {@link BpmTaskStatusEnum} * 考虑到简单,所以直接复用了 BpmProcessInstanceStatusEnum 枚举,也可以自己定义一个枚举哈 */ private Integer status; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java index 7bad35807..c0c7ca0d9 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; @@ -77,6 +78,7 @@ public class BpmTaskCandidateInvoker { * @param execution 执行任务 * @return 用户编号集合 */ + @DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人 public Set calculateUsers(DelegateExecution execution) { Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement()); String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java index 9a84f676a..53e275914 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java @@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO; import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOALeaveMapper; -import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatustEnum; +import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -48,7 +48,7 @@ public class BpmOALeaveServiceImpl implements BpmOALeaveService { // 插入 OA 请假单 long day = LocalDateTimeUtil.between(createReqVO.getStartTime(), createReqVO.getEndTime()).toDays(); BpmOALeaveDO leave = BeanUtils.toBean(createReqVO, BpmOALeaveDO.class) - .setUserId(userId).setDay(day).setStatus(BpmTaskStatustEnum.RUNNING.getStatus()); + .setUserId(userId).setDay(day).setStatus(BpmTaskStatusEnum.RUNNING.getStatus()); leaveMapper.insert(leave); // 发起 BPM 流程 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 7afed756b..176d38a0d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -17,7 +17,7 @@ import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; -import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatustEnum; +import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; @@ -202,7 +202,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 情况三:审批普通的任务。大多数情况下,都是这样 // 3.1 更新 task 状态、原因 - updateTaskStatusAndReason(task.getId(), BpmTaskStatustEnum.APPROVE.getStatus(), reqVO.getReason()); + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason()); // 3.2 添加评论 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); @@ -230,14 +230,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) { // 更新父 task 状态 + 原因 - updateTaskStatusAndReason(task.getId(), BpmTaskStatustEnum.APPROVING.getStatus(), reqVO.getReason()); + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVING.getStatus(), reqVO.getReason()); // 2. 激活子任务 List childrenTaskList = getTaskListByParentTaskId(task.getId()); for (Task childrenTask : childrenTaskList) { taskService.resolveTask(childrenTask.getId()); // 更新子 task 状态 - updateTaskStatus(childrenTask.getId(), BpmTaskStatustEnum.RUNNING.getStatus()); + updateTaskStatus(childrenTask.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); } } @@ -261,7 +261,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 1.2 只处理加签的父任务 Task parentTask = validateTaskExist(parentTaskId); String scopeType = parentTask.getScopeType(); - if (BpmTaskSignTypeEnum.of(scopeType) == null){ + if (BpmTaskSignTypeEnum.of(scopeType) == null) { return; } @@ -275,17 +275,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 3.1.1 owner 重新赋值给父任务的 assignee,这样它就可以被审批 taskService.resolveTask(parentTaskId); // 3.1.2 更新流程任务 status - updateTaskStatus(parentTaskId, BpmTaskStatustEnum.RUNNING.getStatus()); + updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus()); // 3.2 情况二:处理向【向后】加签 } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) { // 只有 parentTask 处于 APPROVING 的情况下,才可以继续 complete 完成 // 否则,一个未审批的 parentTask 任务,在加签出来的任务都被减签的情况下,就直接完成审批,这样会存在问题 Integer status = (Integer) parentTask.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); - if (ObjectUtil.notEqual(status, BpmTaskStatustEnum.APPROVING.getStatus())) { + if (ObjectUtil.notEqual(status, BpmTaskStatusEnum.APPROVING.getStatus())) { return; } // 3.2.2 完成自己(因为它已经没有子任务,所以也可以完成) - updateTaskStatus(parentTaskId, BpmTaskStatustEnum.APPROVE.getStatus()); + updateTaskStatus(parentTaskId, BpmTaskStatusEnum.APPROVE.getStatus()); taskService.complete(parentTaskId); } @@ -311,7 +311,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee taskService.resolveTask(task.getId()); // 2.2 更新 task 状态 + 原因 - updateTaskStatusAndReason(task.getId(), BpmTaskStatustEnum.RUNNING.getStatus(), reqVO.getReason()); + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus(), reqVO.getReason()); } @Override @@ -326,7 +326,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } // 2.1 更新流程实例为不通过 - updateTaskStatusAndReason(task.getId(), BpmTaskStatustEnum.REJECT.getStatus(), reqVO.getReason()); + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); // 2.2 添加评论 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); @@ -378,7 +378,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status); return; } - updateTaskStatus(task.getId(), BpmTaskStatustEnum.RUNNING.getStatus()); + updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); } @Override @@ -392,11 +392,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2. 更新 task 状态 + 原因 Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); - if (BpmTaskStatustEnum.isEndStatus(status)) { + if (BpmTaskStatusEnum.isEndStatus(status)) { log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); return; } - updateTaskStatusAndReason(taskId, BpmTaskStatustEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_BY_SYSTEM.getReason()); + updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_BY_SYSTEM.getReason()); // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 } @@ -525,7 +525,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(), BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason())); // 2.2 更新 task 状态 + 原因 - updateTaskStatusAndReason(task.getId(), BpmTaskStatustEnum.RETURN.getStatus(), reqVO.getReason()); + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason()); }); // 3. 执行驳回 @@ -562,7 +562,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString()); // 3.3 更新 task 状态。 // 为什么不更新原因?因为原因目前主要给审批通过、不通过时使用 - updateTaskStatus(taskId, BpmTaskStatustEnum.DELEGATE.getStatus()); + updateTaskStatus(taskId, BpmTaskStatusEnum.DELEGATE.getStatus()); } @Override @@ -616,7 +616,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.saveTask(taskEntity); // 2.6 更新 task 状态为 WAIT,只有在向前加签的时候 if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) { - updateTaskStatus(taskEntity.getId(), BpmTaskStatustEnum.WAIT.getStatus()); + updateTaskStatus(taskEntity.getId(), BpmTaskStatusEnum.WAIT.getStatus()); } // 3. 创建加签任务 @@ -703,7 +703,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 3. 向后前签,设置子任务的状态为 WAIT,因为需要等父任务审批完 if (BpmTaskSignTypeEnum.AFTER.getType().equals(parentTask.getScopeType())) { - updateTaskStatus(task.getId(), BpmTaskStatustEnum.WAIT.getStatus()); + updateTaskStatus(task.getId(), BpmTaskStatusEnum.WAIT.getStatus()); } } @@ -727,7 +727,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { childTaskList.add(task); // 2.2 更新子任务为已取消 String cancelReason = StrUtil.format("任务被取消,原因:由于[{}]操作[减签],", cancelUser.getNickname()); - childTaskList.forEach(childTask -> updateTaskStatusAndReason(childTask.getId(), BpmTaskStatustEnum.CANCEL.getStatus(), cancelReason)); + childTaskList.forEach(childTask -> updateTaskStatusAndReason(childTask.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), cancelReason)); // 2.2 删除任务和所有子任务 taskService.deleteTasks(convertList(childTaskList, Task::getId)); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmAuditStatusUtils.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmAuditStatusUtils.java index 43e681c5e..c1d4eaab7 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmAuditStatusUtils.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmAuditStatusUtils.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.crm.util; import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatustEnum; +import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; /** @@ -17,9 +17,9 @@ public class CrmAuditStatusUtils { * @param bpmResult BPM 审批结果 */ public static Integer convertBpmResultToAuditStatus(Integer bpmResult) { - Integer auditStatus = BpmTaskStatustEnum.APPROVE.getStatus().equals(bpmResult) ? CrmAuditStatusEnum.APPROVE.getStatus() - : BpmTaskStatustEnum.REJECT.getStatus().equals(bpmResult) ? CrmAuditStatusEnum.REJECT.getStatus() - : BpmTaskStatustEnum.CANCEL.getStatus().equals(bpmResult) ? BpmTaskStatustEnum.CANCEL.getStatus() : null; + Integer auditStatus = BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult) ? CrmAuditStatusEnum.APPROVE.getStatus() + : BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult) ? CrmAuditStatusEnum.REJECT.getStatus() + : BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult) ? BpmTaskStatusEnum.CANCEL.getStatus() : null; Assert.notNull(auditStatus, "BPM 审批结果({}) 转换失败", bpmResult); return auditStatus; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index 241cff87e..f24db16a9 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -99,7 +99,7 @@ public class AuthController { // 1.1 获得用户信息 AdminUserDO user = userService.getUser(getLoginUserId()); if (user == null) { - return null; + return success(null); } // 1.2 获得角色列表 From 62de6c36bc5fdd51968b0f0f4753fcbdf5bf6712 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 27 Mar 2024 19:42:08 +0800 Subject: [PATCH 40/81] =?UTF-8?q?BPM=EF=BC=9A=E5=AE=8C=E5=96=84=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E4=BB=A3=E7=A0=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- yudao-module-bpm/yudao-module-bpm-biz/pom.xml | 1 + .../expression/BpmTaskAssignLeaderExpression.java | 7 +++++++ .../expression/BpmTaskAssignStartUserExpression.java | 9 ++++++++- .../flowable/core/listener/BpmTaskEventListener.java | 2 +- .../demo/task/DemoDelegateClassTaskListener.java | 2 -- .../demo/task/DemoSpringExpressionTaskListener.java | 2 ++ .../module/bpm/service/task/BpmTaskServiceImpl.java | 2 +- 8 files changed, 21 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 3a66524bc..1b5fc92c1 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ yudao-module-system yudao-module-infra - + yudao-module-bpm diff --git a/yudao-module-bpm/yudao-module-bpm-biz/pom.xml b/yudao-module-bpm/yudao-module-bpm-biz/pom.xml index 1ac423e11..4ba9929e7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/pom.xml +++ b/yudao-module-bpm/yudao-module-bpm-biz/pom.xml @@ -69,6 +69,7 @@ cn.iocoder.boot yudao-spring-boot-starter-excel + org.flowable diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java index 0200dbace..7c1950f8c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java @@ -34,6 +34,13 @@ public class BpmTaskAssignLeaderExpression { @Resource private BpmProcessInstanceService processInstanceService; + /** + * 计算审批的候选人 + * + * @param execution 流程执行实体 + * @param level 指定级别 + * @return 指定级别的领导 + */ public Set calculateUsers(DelegateExecution execution, int level) { Assert.isTrue(level > 0, "level 必须大于 0"); // 获得发起人 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java index 4df9eb1a5..ac243c0f4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import jakarta.annotation.Resource; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; @@ -20,7 +21,13 @@ public class BpmTaskAssignStartUserExpression { @Resource private BpmProcessInstanceService processInstanceService; - public Set calculateUsers(org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl execution) { + /** + * 计算审批的候选人 + * + * @param execution 流程执行实体 + * @return 发起人 + */ + public Set calculateUsers(ExecutionEntityImpl execution) { ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); return SetUtils.asSet(startUserId); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java index a8f1788f3..733cc2b41 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.Set; /** - * 监听 {@link org.flowable.task.api.Task} 的开始与完成 + * 监听 {@link Task} 的开始与完成 * * @author jason */ diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java index 136c33efc..dee2b9587 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java @@ -3,14 +3,12 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.task; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.delegate.TaskListener; import org.flowable.task.service.delegate.DelegateTask; -import org.springframework.stereotype.Component; /** * 类型为 class 的 TaskListener 监听器示例 * * @author 芋道源码 */ -@Component @Slf4j public class DemoDelegateClassTaskListener implements TaskListener { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java index df1379710..0917abd57 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.task; import lombok.extern.slf4j.Slf4j; import org.flowable.task.service.delegate.DelegateTask; +import org.springframework.stereotype.Component; /** * 类型为 expression 的 TaskListener 监听器示例 @@ -9,6 +10,7 @@ import org.flowable.task.service.delegate.DelegateTask; * @author 芋道源码 */ @Slf4j +@Component public class DemoSpringExpressionTaskListener { public void notify(DelegateTask delegateTask) { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 176d38a0d..daf0cb741 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -728,7 +728,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.2 更新子任务为已取消 String cancelReason = StrUtil.format("任务被取消,原因:由于[{}]操作[减签],", cancelUser.getNickname()); childTaskList.forEach(childTask -> updateTaskStatusAndReason(childTask.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), cancelReason)); - // 2.2 删除任务和所有子任务 + // 2.3 删除任务和所有子任务 taskService.deleteTasks(convertList(childTaskList, Task::getId)); // 3. 记录日志到父任务中。先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 From aea8fb7fc61fdfcd87c23f24e76af374f8edf370 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 27 Mar 2024 19:49:06 +0800 Subject: [PATCH 41/81] =?UTF-8?q?BPM=EF=BC=9A=E5=AE=8C=E5=96=84=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E4=BB=A3=E7=A0=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1b5fc92c1..3a66524bc 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ yudao-module-system yudao-module-infra - yudao-module-bpm + From ec1ce2f358ec47777743fca1dea25765ede7e7c1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 27 Mar 2024 21:05:13 +0800 Subject: [PATCH 42/81] =?UTF-8?q?README=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=9B=BE=EF=BC=8C=E6=96=B9=E4=BE=BF=E7=90=86?= =?UTF-8?q?=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .image/common/bpm-feature.png | Bin 0 -> 16260 bytes .image/common/infra-feature.png | Bin 0 -> 16920 bytes .image/common/system-feature.png | Bin 0 -> 13584 bytes README.md | 6 ++++++ 4 files changed, 6 insertions(+) create mode 100644 .image/common/bpm-feature.png create mode 100644 .image/common/infra-feature.png create mode 100644 .image/common/system-feature.png diff --git a/.image/common/bpm-feature.png b/.image/common/bpm-feature.png new file mode 100644 index 0000000000000000000000000000000000000000..23787fb4f090154f2d914100ca91174c1e60cafc GIT binary patch literal 16260 zcmaKTWmp_du}F7A2qsR$0x_XXa4N%@2#w^Y;J8G-JXw(jk+R1Rn^tK{e96fF;);r zR(7_Lv1xEfsH2mkrqKdmxXA8e7Nz?o;y- zv-cT8x6VJW@BO1X;E$!{9dQ7Fl#r6#Tb{qJ$o|w;%BEsvc z#m2%*LiH;@pLIXPMBp?1zmGg#Jo2ECCcKi(0yH9hf6XB6Ceb*UhVq;@DT7a}hO*(y zU$42zY!6pTgL$E2Ohp$HW1(aj(?*mqn*C-Fj>Eipf9)@#*;2p+GsrQLBWcop93L5rW;J%S&-vR(*cBO5K{AOtMx}`ocWj;N<1^v04%%NRbXZtLO|y%ZJY)@ znJ|B>WWICm983pI;(sYBqfEj;*_fS^t*!Npeae!MR{hS~1MNh_AE8ebF6gg3=L)bV z-Ok-I)VeeLrAy^q_wfaLGYO#S2-UnY@|o6~z4#>J6YKb36DM@hf@c<{m9Vm(>6*M~ za&fNC{b774(l$WU^t;XI4#aHT5s^sD@+c=U+JecDUEAjh9%yyI?`Ae}x0?>ipU^TV z=eJ*W-dck$leb@_M)jYDU*AK?{lo_%AIe z#UtqQ^&$>gr8d|5zczxaW5ev?BG$%Og%WaFt0sLJjwnI2lwKf(yaC_lZSwmBX8Ma= zrCaYTwe@!(#lMn3O{$`%GI*042l}oFUQ^xLq-oEBSlm`1C=6%l+k); zeTq)mz&6yNE~-j?judMdYR7ZJkQTr}tpf-N!AC#-%+}f^@3ES-69jucp+L=!_rDgD zSEaG-dmO|1PgM6)emj<1F8%w{K%P8xvU}OoouAQYr2ov1OgtAbyZHzMCo^8NSz|wo z_Gez|kE-3Ng)Kj3x_7$9ER?b;Q?VXCDl%B{9*mZuh5S2u2V4x%0EGlW8LdQyL*tj& zfwG?YVBEQw{|m4g_qQX9*tA0eJqL_?Q!y~Tc7UcwJZcq4RV@JRRF)&+s{!!U+H49` z@3)YPN8l=kQ-c9!N|w2-dVaZ#uNkPZ*ki-Ig?uvaWu?C|R_P-ffSIjA)_WQJpTy0u zCR3#f+HdLFxz1d5@#GK2<#lA>-x^3=Afz>WM1X0Y(%^0VK|sVhC6#lfC2#alFn>n4 zWzE0E_GQhSD;c=fP*BO~z3XVQzjB_B>Dxh=pO|0UBlOYbO`QXsYc3A(j*7aSvO2G@ z5$wbEGdV~E*X+gsF$=;hOWtdeC1zAC1Zxt)IM&3aYm#4wd4mSLvo?NA{>Z6;g8Ih> zD0u2nsIl505Y0;s%FfLyg#Hf}YlfhB_-6^44R?*OPt|Im^rv}f= zBN0%E2uVy5O@U%E^1616ZiTQ(=xesRY6G3I%d0m+7}A$ZN`7i*UjKxy6_$O;c8hc) zf+WEXT(+du;82WJ30?I_JJZ<-F_fR#BHf17d&_8>cGxR+fVMkhGzfKH;s{pSsX;AM zJv${MSpX9^J&PUR5Bm1O38!53b##}=+!q@L0HJvxXaZ!S3ykTl8GG2j4HNZ*( zw#Bl)X1H5G<3e~@MyR(6U3j6V0_Opz*MLY0qP7Xog;XR+p*ca- zWX<8g#k1S1Z;)68#JxzUABnvx<`eVn#5U`jin3%7Ct&0q-waseWu2DWc!$KWf^Uo$mMXfj;7C~h2pXB-=TKp`UBO9!mK&(_QbKr2QuEl z-M*wdj46=RyCieF4zVW7iX;y0H%ZfyFNF7}CeS=1n_48+=ZCJ_ruGmk-=DH>(Y)({ zkGe`shFZSk;+~B-x(|5?M1DbY!?#`dwq-lpn{y5|n`9@pu0yj033y{=AJPAq*qX9c`l2Hf5g%bjJroeoo4364iSHMV|}2yzyS5#-XHF<%0*`cmG``&fx%_y4rbOrF61CW%w7{IEtPS&6>us z&=38CIQ@T@$t}M_8R?VBy_^Y>ks_CWAtJ66_!!55n=J>To@DRQx)e3ZB`_A8nkhJR{wzMi!p=EDPr1?lkI6=L!q+sr11vE?Fjf+gGA86TpYcTz zTR|?B(3o8+=}DXv$w{t}vSAh-UDoneU&vdo!bO%dIsCg4t=zp@N!=-)6te7(7Q>0Z7m}70aar-#SYs$0t{3?AQ^HVGU1MG9~6X#KO>X zu2yL^T%|bHUqYZvrHN(v)ZG}1Zy0(C1Xyb7~y}PyhtD0j7>oOK|5x``l92v*r6dcZ}K2A z=P};|K57Z%COf46NaiOTXmE@>ye%B?dJjV=nP3&Lyu-#_Z3Xo!08^l4BfvE;=P8L&4O8owd>x7c;F z(71ObQU%;xDX!{`>l3^L2Dhf3V6P6$9DaS6b+IavuXO{12aM3{E$w-FR)fDRek5cf zAMma;rSLp6!3u>ZSFZ%v5W1CQ(0xFC_bwnfc=^c`uplC>RN_v3wm2KHr8JnqUBVfxl_+k8l-DbzJfVY?%#NesKG?u z-t;`SfcLicXKwz=>AjM{@{WbUa)wFUnkitSWjr-w;Wu-+$c=3;c)ql~hKQk)XA5+! zR4vyPAUFTFfPS88KEFhABFbH=wQx#?+HZ>6JJv!H|Db`N@#Zyq;QsTWVqQHqA5A^K zQWTN3@+8?uV#@~J;Dq$OpOa){ICIE8{pJ%-b5F!k7Z#wxs$X;So;}4i*V@&eYvcZK`F8d^69cVqEb*RYsbP6*T7&!BHq!tjG{HqImx{ z`IXO9x<$u$D7KnhXyo7~kDKj=C^6NjrmW(7S|0;)L1IK9AX9r-U)zxAZxf9WsR)yn z2hy_YPWKb$k=8ct4Ha5%3m`q#zy~4f9GP{QjquMVIG1n*jYxEQ$_{`SyJxfqDU&hO9plV#{M`?~Gx9G|iM6t_Po zzdi^0ERU~CANSsw({FbFzVIj-EORwiG$)o5niqukEi9V4DNT>jQAfzDCtWrPk`#o? z)C4jdU!QWVqi<^m^v2@BC%rXM9HAj#quVoy`aHhMosSq%sx<-L)GWGGO@Bcq{#-CTOhe zYeC{iT*NDT4kP>3+una>CQt*RTg=YhL{-bflUPl5PK_gc{uxV&xpI$TG2m%V<{`E# z=7`}dz}SbOW+_>4itlCa)&N2X`tmn7Ip^7;$HBX3;+jwiSE6q+u(8THW}u*hN@KP5 zQt3dZUH7V37~mJj$eVs11p+fVFa-x24dR;IkFsW;a0lB}Q#mOmpKk@VE~36QA|K8t z-`i=AOXVu(*33Z4UTmf7v5B6UCaAmJCDR7`KqLxv^*=y+(e3Hb2NLVz4em>T+2I`* zUJC*Xb%T#b1c&w$bGL%QKNv3rfm#1vpf%yIN&(-?=pQGlpmPdTQ`80NeL7>zM9Aq% znl++Ly!)kef%r9dt@EyP%xgs3Xy&aTvkGWt17dr_4$RUz`w^z?nQ9?Xbb1$jHqoFv z)9?onr0cs46_0Pt!bwykZi=iRA*Qjk7xbBEtk941HcFBQE6`bjBvsH~YxlWbsZY*)r8YC3$@_Cgn;g6DO4fkdj z;v`reT{b7;R`aJAwfY6u3+1XtEWjscc~b?n{?_Ex%~RJ5HDq{#a7*N&q$FB~#0*b0 z{K_916+!DKhMQ3TA^E{sCPakvFtYWD1FEVDC5=Dhk!D0hcv(5zP!7r8%YWY_U_{^| zuy52gNUDyYJ5;<1kZ8@$2w&0`vDS{05xOG-npX4)@m_hck}cG){QZ{CKleuuBplMA zPR#MH18}RzZALPL-E7qIoWfn@`j+{M<-7Ez=h0IU4+h2pe56o}@$uA}_1_Q$$W&$E@h3RgjrH4A8J!{&x2 zIn6HB$eX_sW}$1z72S!nYK|g?y&Di8Jl5Sz)NoJ*`I!(X!u)yq0OYdp+Sh)@ ztfKdi|Iy*RZ)12(&AhpVrZ=ZUlwE{pyNc?O6Wle@c1e*aFch{D@3%OL9}*wap`Ly& zbJs>8u2m{ls546`{_-U2(Ny~Rp&xvX#3*v(O}_jHq&tcbbzJz`;gSyIniS0u@?G|t z4isF5-PwOQDQKQn#XNrl3Pw0o90&%HL-r{IZeOjOCh~kH0Si4<>4TOz z(AzElzV6Iem9NRo*sQ$kSsZ0h7u(4nKvL)hI;P(Vb~ZR2?K)VcM$k1Z6c)Qd0&m*L z>*u~dZF({NJm4emSWP%#8*1EZt;dJ+GX4<{2hm!WhGL6M&Oo_qF|i^GDH0N0I1~!{ zh?JQs(x{W=%FiOP|J=wKKBaZ~sOOU5)njZHP)775f&}s;g2|t%svx=lbNW0b%1_sS zsgIvg^o|(!z#i#I2EpW`@Dn8^@Xz}a`oMen*F^6V1#ZuA@y5#JEZu-76Wcs>5OzHK zYDCyw2S*~nLcC!PEAxWcKr^afxGZ|wio!`)__pU*{rUt-8D@m2nWPUh+C6!BGg^bM z$`Jkz^BxU%)}vp^E}w*aMhGnrfM!NH_KuLI9AlL&IB9y@ATWjBx<-WEKnHybi_cmx zOoao8Dz=UEA(nF*gDaUD+D}K>XGYpvYNOR*(P+}JsvA6?WRbI)V#S;JV;IoQW;0^p zOu}l$Bf@WrG(P}e6c22MBPihbDej!j^s`>7-nfdM+nGC zLaaKeUdiFLMk!8R<4Cmz1bWl0m`a;9wHUb6@(08uG+I(jN2zD@K~U*I ztfmioTL&^SD1yKl`gn|r8PZ)dn7v-*S)Y8)Zf}!-J7@DP8qH&HaJ(h$GvGznCwYEO zl|GWe!!q|womj7V$B;`MX_dpAQsjlVQ<|yQfP41QPlBKBZ_m%)wO6OaJ^q@;JQHj| z2I$QI&TetO-8o?_(?+>J9~>$M=oyP8lS9?GQte~4) zg;QF??}?{F3KsWJx zf^T-8djdOD4s91D(*KO5<|1fhJ0qFX#zsK1@5u~LK)R$SAO|5$bAyo0Q;}@x_Q!Lq z9=pxc;5Od{&F2vtq7T%uf@y0EQ91mS-B@pZ7zrw+MDZeV5L~)HfC0t9&o$4$Y-O{ zZ>_8>Rlkyl_;X@kqV1yFVR{{kw!6@9d5)O1M{d6e8dwd)|L1$W=#dz^qpX4LiuBFP@8)hBF>kC067{>KB$dz_Ny`u+vDJF)eX(WRHs(@%3BqtHFyAIs;Y*t${Rr z#rL+h;m$oMEBD&@iQyulfHK3<7>8e%+fV^)sFzUI=M6AtCP9NET;(e@8~P&d-)MAw zN2b0;4$HS@HI%F0XZ8Pl6>pF1BO`v!#Ty|;;&?46ep%O7=jolI;{V}qCM?_pRIt0j z@IHU0j_ut-Qh7&7^hya!#XPxOEZMSSRU}=IO?|s72r;$=YbH+m;71*A zQ!@I+KbxJu1=7nDmg0Fiv*V?t%Z_^Zi1_ES5V{$z}Cu z3erI(60%^|bl|5&vaf4cmr$o!_X6n7>a!~W(?_6w(VgK7%oMt9ViXfV_e54-wp`m> z%^psV($XFJSl>35HXh?Tok-ub&k*&sU)lxO+T?=(tQ{Z<$xFZsK7A`+seT z)iPlmx(;ZtGqsW8dSnv#ly^vmOp7ge+YI~>F%@rId8m|N&nBs>VI9-JdRL?!2OavQ zt5v)2HknBi_25Ns<-Y6AqJfZL|NX@>ys~=fiV_m4$!=r_{--{Tbtr}?iUCTJ@!l?Rpi9TKZtc=x*@jT)r0k2vW91pPt_eM}!C6 ztyVsy%2_IRk~Y zw_Et$u0YXF$4wK@YWftvf1fC4#Y)Z57(jyw{mFb+sn$zr#0=!h>1=tS<_SkT{!D%(}MB{Uw>xeWot#tIo1 zq~OtYJ-<4)Lct&nM6!%GTm`|rFvZ@;(viINc9@xUE*Z(o|0GG+xZqRBqC1xV*n8%J zJx!fVRgx(GM!=9FE6%7t=+G0V7T(Ve#M8P!WUwdrTg05mf{5%QlYg`@d)~z!Y%6=L z2y7-JgYBB{|A6-x;W#|66+ZZbn7t708p-~kz7zeI3=7)XFwLYkDXhM=P%w+9&kvdp z|4waY#Hga?br$l5&Nz{=TDn$ue-NpEO;Hlt7A-D^CsI!AeKEmV_TAVIku&kW9Pn{t z&;<-Z!y9}r1#jktG%&NbfPMOgD4>X--E@Xx8XuFAZ;e@ zYB6w=i4pSa6WK>2%mrmTN*}2lnu?PO2o=N2mJAC`Bs>f!n*o^@=YwZ3SB={H z;1*w^H<*%|0GL=ggugH3vS5UoUZX_QKR<%@XDl8AoZ9)}>eJs~(nG8(N#2u94>0i9 zksBNt4!^lrRoaxb^+uc{?~Us$>d+ZO$|9$}pE2*!MNeMKOPMf8@f*M|< zGgbWoLX{839po+TPAM+@DbfQgiO9MW%Fp*OurBB{5DB@w%H9P(40dL%RkeL5_@PR= zZqs~t-X|aBDckG^votuIt$l$;qx6RS;~rovVWENXW+2WTcs&(}C6B|%=`wrN%`6r6C()h>p2_%&?C;>p%6Gg2Q zm}+6gMbfFurx*x7FM=p1tIZWHoIr zAjpvgC5^vAyEy@|LF%pA?=J-lL;&t?6y?C1H~jh!LpuSNO|q?>o#CD(1P z!Uo>zNm^-6!Sr=fbNj51BJLD#9h1;D6j>Nx^Nk?#kJT8fYcmZ$zxshb-_H)mtNbWpVmi|t$|2v`K8A7jY#6NZM? zCa#JX1m7ZGIC~kbVn(pUUs)zNXW_D3JXr}(rE~Ekhz)rzywq5`dvE{A5WJJk!!|9{ z0yP|NuP%d*>3svA!NKjtjYsV!ex?uZ?fGZZaOPz}T?>$)ug0>w9T)zZBFEAou2^bB z#=#hc2X2Z*lqyStCK|v0b6xSL@o8Ikx@woFvaD>uod@dRLZduV=uC3;&>|WXetrAS z)B{vSxxn1UY=Fvh^R@?x%*qCe6pn4NC#ND~B>zmyKfxt_DgwFIH+#2$BJU^{8eDnW z29}@rJK#^41G&9mY)|^{WAdoeZON2*r}v&O`qKRdTp3=}&suuR7L85|_PMeP!i%x$ zmZa#{41B~TOx8q`(~#?U^VEKl^(J&3>x#)~D&ta}JRJ0EpL~?};YZV!X*}MbCjliN zohQ;ssx1tQ&#jtL5LUk2^|#*_LUD$El|F_C{ZdauNbG?392~C5rBTwwx`!PJl>xQj zy=qp2viYUy>D8iRmNb1`<15KudI&^x?(g64E(44COggzsok#DzK_34-!O@oYZ_xVZ zFS+o$zyuYZOZ`tT_vdwt%W0i ze&ITM3jRZ?a#dRl?!Tm7%u)W8@050h`&0_Fr|+SjaYxx^_E;ks~itYrbS8ypwY8YBgY~W zy01Vx z06zD5%y-+ZdRX7>%HlmRaHdBLu42K|x`~FwUb93c?_($a5Jk5Lb`e}#x=O7=hONGJ!oJnk3w&X>=W%a!g234>$+1(twVML#od{-P}2{_2rX7E$F}@2v+Y zB{w;2S;C)kw(TURqY(_CYY5U{=##|mBg2ugSeF-e3t5v_1T*B=YI9*S#K?lsxf!|- z>Ym95;fl%vgBrnmcjJhO!SKocggl>)tt{v+uTR^^8?wDLt~)d*iL(;~50YC`zTcN5 zB#BPJ+gm2l9|QXdq${T_ALxE^ZxlQPOLM~4T=*!7%@@u{1D}4}IMjI77Br^i47bDv7wQ*3!2)M%vvkqcKf4OPQV(H63WQyPmJz#_-C%v`6Z z)NfRfSoxW4++h<48oss=4t}6&|342w-$`DImA5G9Ebi~;){<~CwF9v~J78kE=`Njb z1g7GKrYI=QdC1&I;pO}NeXtbq&>y~AkO3onp1z{W?MCoTD!9VU;6FAkuHfQ1@uS3D z``Sdew1wxb6Nq2Kt8L@WxXpuJ_CT|DGgBAbb~oA-X%z@@ z&`KUWyg(jOhJk``U6r&*daAP6H_mTq*pn_Gb?Z;6493slF<7Ow78w z81$M>t#Y?HkI(v)%-cOtYciIm04;Bj2>TBqAel^!qS7v_0f@FRx!A2l0#1`puS59O zGOn|6OY1KGf+N8ET0^KPDB^PpK~U@%WMIv_aE)Gza4p6kea#43@^A7s-5&m-g)4Qv z7sDNNH(ID4{kFO%ml!^JB5Ui2^H2#EwTy%*Mu?6Vrn5wjK{}G8d0)v>c1b;(n&p+f zRseZ4H$LJZSTt9A&9Vkk5V(x)pbo*NcUYo=DeuBubz)y%68zqV+Kg*z*ntG33x4a+ zjk+-cBF47fJxemY(;qtaM|GEtVyR2&gskhmI(xpRRJKto>XL$h_5LQ;0RhU;b`83L)ehAa(sCh@#f=Wq8ODlncDpBFRQvX zl3&<{%^bV~)Kw?7qYgwwNk|}M=$&A+l9RFjAv)#U+Hqxv=gr*T8X<(AoiKcagP&ht zh{o6R?r!Qql`p6&NF?y$c6#e>l1?QF`9s1<*TtS3ZqaGWy<7$HHAPmx8xzy5>z$B4 z$NXbuWvS8|Bng}WJ)gB43L?0qGrj*%gysn&p;0r6ksNflv){eyUX-nMA<)<+TV(7% zkOjs6=*R|XKe0#E=PxbB9&eXbUC{j5Cwy4Tw=3oNS#uCqX&hyEdMaaT)cHLdJ0?OU zm)2yiJ#RE% z+1=rg>thGLp!pVvtVd!<8f7=IRApz+xQs}wKwc=Ms303YXO0;yJ|OgCTcS4=5sQ}Z z3e71#@e&4$S$tDzimu0rSj!WmBzUpjIud#u;o(Ml8e$d<0t>r`+Y5qKLHV_m_Ml8p z?$U4-4)z>NV(}!Te}YOon}G;Co*3_4ayU5IC@Y!=(1LIw`Uui_k+ojlLwPBEYT4jj zS*7zGtWenWRl-$RTEv($6TEHreNR-)au~Avp2L2HlE9zy2n5~T;ksq~gNRUUZU2QL z#6FjagZeu;dGjNphpn-byoBKnEu+K;hnie;4#>Llt_G+pGv#x#%6ifun_FnsC+ORi zp64#V!K<>g)Dy>5Q6%a%8($~&v zRvwW&D^SZg>9tyD-nxTg%CmT#uiZ(^hoDG;xE|#4@D)0SNw#+;?(>qqd|#JrsQXQk zXm%B39%P&{)Rzj1wb`T`Lc=jDf}MEZU&S>v#Uqi(Be*Fa=PNjgk595t)H8$9GXllUE;R$Z@&;r>@H3-3_ZZLhU0WW^RNE)I9ddLfiU7?s0YNS@a!%k zR>csD&Q3OQ16_)MC=DJj8KiW=XEHlK=O5SN2zCsa;e8J4&UE$9yAL;oJne8~&*8H5 zoC+nH5V?3or^%(E{?V=Za@;2ceJ!Udvu)~wNL|h~7z}e(>Bk%g;>GqWlrUzfl&pa% zSmaVvfD~{!E5HS=){7@#aT>!{M?nKyyjzzJe7b;~gI~lT){b?5y8aNMX(-iiH0@;q z7MpjQV`s=!nh%;V6)TWGxOBi4PFfrIZ;MLGWPO!E!}wkDRDhyL2?f2@c>QC3F&pE( zOy(&=HN$)7>XYRCI!#|c2?7E*&9wnzz4$BHDv|-i9B^YlNMduqJ-U{ z<`lNG9eWp{S;-QnaaVoJ&0=g%=7^|D8txfAsBHJ|%6chGz*zfSHrNwJbBX=Qm>8k> zDuM&&i?iyf3W%Xe6lD&`avALNP6eq>u7-dMp0O6hw>=ZkuilDpV&}$Mc`K%?tn^HW zfWg=S!}9W406V!(lvjVuwqT@=zYy+L6aDIHFB?#bw=qWr1_) z2CA{)FM0E+UODo;XX#JAZjaMBMAHvKBRqCCGd|RM(_!^Qs4yfG_ZtyAZP1VnIPa>8 z5hzgbIy3y28_)Q6V_^^4OHt=)l&EHE1;!OEJ_d!O_K(LmEy+bg+S-v%E4G}5b}U2@ z*!?2zgG{+bN)V8F1gPK8)~i^0X##xnSksNZQ9*4 zJOoAUmxwS`%t?zx2U5&fTX=rF%ybVWA`zgg(Ij^y)qa0w%!%>N20gggt!u-N!TX5V zlqHC{TK1)2X+e-Y=$j%Y0~du2Ibx@0(ISV9AcwDdr0|muAVqx;>)0~Ff1)MS0ARFh zBs(ceahBSq3=#k&F#|GPF1jZ#2kw8{1n)g{NJl3UI+tWvO#xews3NpbWp39@juHz7cN`vS(h(AtNmI_1djKuTN55(d9VX;}pgs)C&pS~)CkdIHS^Q4(!#*av z*Fpzp(7FP!B#_L-ys@_jRQf`HbsDq*Ue$I9viWNM_ZJBG`e3k^Y>?4c$F=i|`#)XA zXivJau>&Zjrw%=~8E$U=b0`Og*K+6QC+}Z{0*1!D+L8}0Wu%b9Vc-6oJCIQIpauH< zZ=K5yvtjr#yr?=9)4ZToV4*ZMWmg0Bae2dm~-%gnz3;ex`3W z^m=4@sl16O_-U|XFi1g7$MMW{#+dsHbE4;0lXa(Wa0=FNCF7bKyN z0Gmx}Bor~>)ZgQT?pA{3W)7-*d|mLg6WVM~yEg0GZki3BiMPgmMF0NFrJV0_VwjKk6Rji5PE#hsH?&@WRT83B}7pVy1l8IqBt!;o?KzLtW) z%OSM>klJWHh*A&1VZB#0RE)}4F0@pGl^36=bGq-FZB^SHJA46bwXh9-25tj|Jmd1! z)GNyw?HQ9n97G;h5;LK?jdHY>?>tW1(Q?6mH9)>U`;&0RJq^2e)TV>sa6ZE?*yaK|>5~`8Ak$f=IN}>{v%S zPQJcp{(xxxz{>;o=Y$#lnck_T;N^f5C~e990VN~(mA7u*!_{Z#E2V2fbHK#x->Pan z5dn`&oBv)LVGcuNOZ*Bax2learBy_vW;g#2$uUo=0ph%oD)1C7JN)kH zzDCcR+J!b($50E@#vj;yiuElY zPJ^Xz^o?t=hGysZ@S28FN1q)i|1!S-9IL@;>fw%94z10O_cv>d$L3O{Ku<4dGLN$j z>&JtuN5;6>4PwnwTs#BPh0x*Dp7-D zH1LpAWNV7LXLQg^Z0l4t!0!&D@br8h0v|&CV>qKb=p_CMagrJ|zumxaa&chuj5c53 zvu>RHJ4Hc1)VD}`;cjxOt)HMaPZ6$PTTX8fMyEUr#g$dGrmhr44R7h$jj!IVIt@eUgMGQ-mF)N721J9#>P*KXHjAFCEt=(m`#JyMeyA9GXh5bqb& zCM*zhPD>KRHLujT0whw9a$6go+D@tgDpWD{*lvT6eXW{tta&eoXb8IBIbJgI=daTfpz;>ogNr-jaI0s?utU_3w9%;ldxh4gq*+PM?4Q6kS`jE5 zM)g8CMKT4pk-iteP719PR zLQCd{0uANQWY1-z-&)C&Qd%;i5*^&nn~;H5`$o!Jq_>m4{Zc50OA@{1>fmRnHzN$G z)GPp6o|fXsQ`^J5Fef-HQmUGFvwOtsgWDP(33NAvD-@T7#;F4ke7&@IMR_8T&t^(x`3+ zix%+xAU*fRfImo};W1uC-R+0@r|r#kd`bB!$g%RHBz!`ZK6nvZBy%ajysj^I)~YVOc9r}2vhntOVU!-% z$8GR1x=Z&_gIZ0HQjjB~Z=DCO{p*-sdl~ym`sPd7I0q_^>lK~Aikl3|Z;+NGtOb>vN_w4GI-?HIUluE`q=^>QosnoWWy+%9S) z;wLroov0nH&}Sb51OrIo`_M~&8``I0>LTX~c2XXjx5 zVXUjv-)~qdm_sls@sIws_z_Ht%dveD!gE9zR+w|~Fz1-R*EJ)wAJv|yts|4StB6DSHs-gbZMQgiv*UYsKo~Y@=>qK5optd1H zBf!X0#)}n@#85ib^ST8rpxN^a*K@4U&YS0LK2og7ilCTxGq0+(US3ZR9Z>c38Qyl( zW`VaIDd(#In>}eas6sVj-rM$llau?XgY}8yw%y>O4Z}Msg&;fTt%)MYqfm=4b&$MY zZ;PcwpQ`dp9w%f5jVrYj%<)-|$}F_~Q;;E*`2O3h-mSc+e7Mig)|kp2^}ehW!6Ye8)oYJM^b8dcfO9Q}Levaz6pAqkNT@6yIw8gO%hz K$W_Xkef=LKHG>cU literal 0 HcmV?d00001 diff --git a/.image/common/infra-feature.png b/.image/common/infra-feature.png new file mode 100644 index 0000000000000000000000000000000000000000..f5cef50c56ff3a4acccbfbb4f5f3e453dda5e12b GIT binary patch literal 16920 zcmeIaRa9I-*Dcz(y9Esdm&QGW5G=TB;~JoG3j`+wcXziyH{N(~cWnsn?h-7(&3FEX z|Gr!~4`-Zl$GG*hSNU42cGaAa6I57KC@U+g4KfRl2oDL1j*Lw(wFcjf z7T$J7KW+>^%+&ln+k9GVdp`Od6&3aRaC_4d*wWVVXCTAU#?i^my8>EsRqgn9bLP7K z>%&UV^W2A*jja9Ko0l2K$=cV~{pRyNalh7w;?4TkhRAF9%~BapSNiLdgHPCEPVL-N z=FsD7Z&8`ct4#jgb?5%;FRtfpRZFujz(M zeXnf?{WUg3t8MAidoMHRi=MM_ec?hoqxG}*1oZ%bgpR!QCoQj~lkQIh|NI2tF3SvS zda+_q>vF*Q=dJ(CAndN}&o__ox8Y=t2M54|jaK5kh1Pl zfQ9I#^xNq#;$#{0Qr+nRn>0`>@Xw znlq+Wq z08}a%`xH2|M_n31pZ)oMzg=>a!{EXk#m9<@$I1dB!rPdFJRt%4P=&eAani zMzC-FK1j8G$tu&9EmFkhK+=ko&0i%)$m`CLI(P+E$JE$s<=ynx1rxpIdtoX?Y4dec z+j$w6AXa1?(VAe!;A3BRgA&*Gsr&yi#vOA23>QFE$<(y!y@=fw5aL;=DTA{SbX@j#>s(ul+&(svoX@XifsqG^_Pi#VvX6O;! zOn}>U%Ee9OJnz$Jh4+_h8qLbX;fJ0!WEBEV?_MLb!JqcVOF#yRb{MsUar1RuTpy@Z z2DiN*t8cfx#9-%URq=5Mkn)gqNe;>h$ zEl%MN%v)9n5?(wDG7xsRb$XG89>PXlU0-eb7RmJ)2>4FxU=w2%NcDyU)u@zX=LNy-i)?`GWq-( z>W*7oVTI@qCtBQeXqFeUcwJ}2U$?Ifz0wV5`l8BCW%K7R1TS2jC;%d5Cr9ZLe zug0Fe8s+8_t>Y_6JTw)*+aa7t_kV4@HoMsVCRa*brRzGW9ImQUS+Ftdq7N(yJ*K&~ zE!f5*Dtz}z+NjG>tjn0VLVJ||opG7{s^g)v?StFA2IpZOB$YbO%ow_e_AW8hg85p% z1*Cza0kbQiXOa$lE^iiox7U5)`ODa88wtO1XbpP9L@@v9&HpgwF)eK$&}3B<9y z8&ZD#_Olk2>05<)*VSEjJz>pB#YZVvJ4p<|;E+sQWxp)GdZmCs`;LLecH(Uv$gdTB z?EqmhDiVu7=lq2|Uq9PCdYCmMy+BYQzJabDF6u8kx8xGhqYQNP4zTd|@lgWK7^fFjJh>pZunrv!q=5d zS@8UmX4?XAl+mlqr{b-CL)ccEw_T-=4pHav2f?+s zCzV#`-|8azxlx+;^$`I=^iJ5cw12F!^ue=?FiXLcQcK2Rhi{s8X!n~mc35%BTe?(F zZch%E?ndKEPMWm~#s*oOl^As-{(x7ZM1#f1pC&FFljD;kJH)m6j+);bG&4CNBm#}? zNPTIM!$76}xoY8)eI}3o>T%aBa`qumUMFJ;@*es=&j?FA8hxE@MDT786Yd|VB5@m( zXH!32G8PQ%$;idTOm1oCI8tiWAdb9Hk|lk5mCeQWl@+XBOvJcPeB*xoj{jib%VnfW zQ)d_VWd=`7Ow@4xz<`k>cQq8|87s&sc^ONY*Iy~JDjsu*0+A8oEFEtkofnJ%gN@X| zM#G1VwscPB6o)XFpmPUQQfLZvvjj@ZY`m^-4uTg_rVrq>uZWMsU=y25AYIc|2hfl% ze+;Epi(e)foYM<~Rdu;P>C&zHUtaA-IcV9-QJk-czHH)>+6TiN*E@agUNmxF0^pCQ z+iPo1!eKEr@anM7VV&j?I1TWZu+>$+vESi`o=?B)P5!8=)nCGw{GVdBPCw?vMH_)A zoPNFPT)m);JzT+;I_`3|)@0iItLk9l9j&b1u&;jt?2#ZI_cuPnv%OKqEla!l$Gu6E ziOg-UUct~#d;8X$_6N}A-nmzl-_iVj9elW3$j#XNhZdSx&S82{2BwExAWI*n_b3}o zcIN(KS>#-oczT%D<=vbxT{giUcD*P(I0(TL*(OHscyNDxyd}zmiFD8Yr<4&&PdLCD z1%2jtUtPdLyzz$fp z^DBcC-^>exA2g$l7L7?l_FXPAxX|Bp%`iX?Og?~5R}C*lgcm@v2|ORmZIG-b@NcP0ftK_dD&vj+^;{huxhytItAc=ov6^wJqd>&%%@e>IYd(jk$vL`Hn$ z)mB(l0*&tdv&e_nvxn#XyEAR0t!tUwo1mV!9X-UNkay90a0Ll(4@qU*{2kvFGRtg3 z5wKso_x0tr?p8qq>}xIz+Z^`|TcqGdpJ|J~9dPff4Zjjyx-eh{ogrUY|M=qR{H>T5 z8;yd&=kg3$XViJ-ToMHSJzEp*TyO07f_?=&B@kGN$c^~8NS%w4eB9u;H-<8f<^us< z3Dk&VNI_>auD~0U4m-ln4$3WuEKV)6X2XG<{e<|<1^kPs>hW@MU2N>0s(jYa8P)Ho@Q#EL`P~rxj_8h$J zdY6ZbiAb@xbL0`;qvPW zU;o?M{#E{BSDTVjjt@7dm+2_U9UB2r8ynG|dJR~N(nof+U%2})X?a{>cMZREa4&G_ zby1yh=jVLpl^6`?$Hqh@_pkyA&FxUuao}Qt1sM`;To2)i0`MHN+e-wym3)iX0T-%o z^<7Aj;(*_$PA!4MWlI{+60+ORU?j%lxE?LMxPnE}!H?OqbbqKn5hEA)Hfj%k_;?)b z-xXLTh?5bE3Sk68^3E3qCu^k-Bur7Ya>lU#j5RWM;8&KSs53z4Be$aiiM1v!I`8&K=6?)>qFf4F?0yBg$cRSn+AA$c`Q#_=Y3~*_wSO_8cvvNXQ z=>o>kT=iOXE>1l7ZTQx|S8^Ed5Nn|Cr4$TG=#UV;?idQX$2gXQFd{!U`Bo(x=x zyKxqoFrxH!4?AGatl3KD*Kp1VckG+Xn;ED_jUCjf(M5q-PV`6KfK0VAc7D=S zwP0`Zng)vMNp~ZsUdr#JH$CuHm^Czv9nQ-!s$elXLuY1u;sY_BD+Gj7@L+&Alr=w+ zA+2Zn!Zuh9X0 z4cD=PA_xA2^#3)rV?x#r8ybP?<86x(-{XShC2kC*q^P$xGhs9wkd&jUnpD;TQ`4fL zk5S%>#~L;*4LW+(F8XZXUF7Xt^N<1P?dCZsO2OJf#4ZIkf-tp7Pzgc#P@Dfly!!|A z_RJ70BSRtUkFSodkjvbc9fKfvx%d<2UW0I+2O5=oCSCIF;#>u-L-db}jkP$haRJNK z#sgx}^rXqDBwsYgiz?w;wiHi8jcS556al^+up6-~P!PR1=`#X@iv5qx8u3dV>XcM8 z!*ks+QL-lZS1|mM0E=Hdkvx;lsx@QNR?lc5lTk(;c}~SGH|2wkryv%gD+~f_vWBjI zw1TRX6|sYF|GF>XxY$A2fF5CQLbGo;_^qHt*U%EtzL0$x9z__7%3m9asX8$lFC7X% zX=pAM%g1Co-%4imqCF_=2?IR76)7D_XJU%<9k+)D8Jb>x1b3Z##9gJ|q_hQ{M>#d5pxa8~s?gi3)9&T>%gkt_FrB_+m|XrR zQ)ZOX1i|)iJfUUOh5O#Mb@2QgWdSyHdLA1FCvs~CV4SP@ zoB#^A6fXMh;^tx@rzPSZ&y@s}i>A7K*HI(-130PuRU9}vh+3kOKDts3?t|ue^z+y_jsef4iLON!a4_qqJ zhB=B&@KJ5>FQX>XI&@Qt@PAWM2$F*f4X$kluXix(2SwapIdqt`u_M2KnS%o0(#t_#yye(Qqyrqp)gTPf9h z-Eoi?%nnLDc6j6wCOmVD>jHtGm;v+8@G+(XU3iGSRDKPY{mj8r)rG>sr&zV2KWenn zAVZxK2Cg>30CwkBcNWOJeN_Eju>2zwBRi^Ai#Z-pQc*xm<|p>Npu#FX)gPuA7$0fcJDCZ zmh~zBXK7mt?q#D5!8sQM7Go9tsb_nF9DX#A0*hx9B>rXieFO7sVTlN=-?=dJ&3D2d z+A4KZ#|J@?{id=?C+GkzA_0GpQ09k1tgf;pd)mt*Z|6YtD&ML$-(&xr^mBmFHGGmy zGlj1H%hn?TcV*^fRSM%{l)rO@dKjDmjr5L@vA$sh)##8mRU`@VH=4_Vt#+b7SQ`2Q zT$jw?T4BrHHzA*|#n&-2O2Z|Urxk2yoOzXcvg-nk#bVCW?gpHy4JuP6ylfC%DhldI z^@#c2Xy)Ce_xdUBHJgDyu^pdO-=s5gi4HJad~;s~^)}RzvJ&c|j(&@X{DFEiqCe^d zYZvn0CxXu~m0v7C`r9p2f2Uakm5B|$_?HaMroa}HuX&L9qXf{imGqtJfpJ1y_f>}iO}u0GpR1|v1DQUI@lXvJda<9{#402bBz%J}sL-1yWq-3Y0x&L44#U7nsZW@fTEEONLb z_%oR{hb+q>jME_f#P<{c&{aNnF>IjyolOs@*{88#$rwM59Kb6d>^u(2g*rWK@LH|e zkGQLPhL-Glt)(niqnxyZ65tPT4ennbuv@|Z#x(NvwX|)JYGxy;XKpSll+-DPugs`e zeA-*I5O8CeLw&^|vzpvjNVKj50K5fn7bY=sAT!NcQ+O@n&C$Y*u{-6b>CL^gxcA{_VA6#-{ z_53_To+Fqhj$ppWhWF%rpG~yrJxg-@Omw{VIhh*?+UXa!((80=&KYn0& z@cNH4C6=Bb9Y+jE;fVF;IUcKCF`pK<5n4mAZ9~topc(M*-@opRFXE}(MUN&zrP6lP zgdemNtl-5+sxP_9z%2vRz!?w4Zzxa{3#T=jGgE3Ho!SkbmY2UFVz|BB2&)R6n-VQk zd~uOA1m)CbjpiSDD|6?~&v8~n8pB}yKz-Gfvxp72?pDidlBdMf?PLIdTFz+-tFBeD zE$TUykj$y$03rrCa#@tpO)t5DaFm2jI^P>b1cWk*QsDncz(6qLz0tBNp_ZYxi)clJ!>YfbXQhgontcG2?sMdTRupI(* zO>9n2Pn)?B2s!~bZo`Z|okEn;9T#6^YF_U+Ph;+%K$Z3i6bp;!VCdDy26)*bHu(8J zq?>JF_H+rf0B>!CzdgxObnNBEmL>@&ucdToJ$zPOdh-mx*_!&qzB_%pgNPTGiBNYH zM^%}U@5sMXOc6AB>K{7~8mw=kxHM!P&DU)=$lJe&$92%I=(H%qH{k4=d7&C05P4uozs>5ftVv{cY zh^5?x^OnEo2Zke$+p2loURrx|Q};)qSt%ta@yr94(gjhPk-#DtXP{e=ljKaB{6%yJ z6994wha0;#ZYF5(`AN*%713{5;}GLNZ4fAUMg2Z*WZdT!TSC%=?y^mOdDIR>ta?XV za8LSGy=kBRJoqR4sR1fi3a5pd6|4)(Qw0>*7bLdiyf1K7WK8 z_5J-xeIrOiL8?895bJs;>7#cb%`fngN=S_`Ab2>ITz4_g*#L3yj_|X}0MfJd1 zmaJ)6DEh>li7ah7@vShz8M8ZIQPlD&rK~4UQq-u!FkUv`j;J+by8rYK4t-ntaQ|-O z4w_2f6UOS;!ItOK|I6;ZDDJ2LnyD_G&F%q}N*3?&hy#*OF4Zsq-pw8^nVAHeG+5lU@qN!0)ihX>wgo`z1951f3x0$C8S@^hPUcyx zx9XZSz}NwIJ);>k0O5stL_g);P_rSoI+kMu)=iI4LQ7@EGhyBHT5793+2!H$_s zn5NZ$5Pear%NBdr{9~-j-e3~Sph4Dj)GiFVZaEG-K0!4j1kuk47Q~X69C|$TDVU)~ z$k++V$N*bOKp_G59Lt1`H;ScO<0F3kLCt_?lv_?;OKT7M9~Qn!RGes*M*4L$VfwV5 zu2MkP=eL!h*ndQQL_3}UW{h=`kbmy}D(n7`WY1qmJgNSf zyLLl14!v4}C5$d-ly;6$N74Go8d#}YThvTDay+1v2BW5U#Fm%W}X#+|>i@^(vCd#`EYlO+OCqD{eeKxZTibAcUMAqYJkc_=< z0RAvkT(L;C#&i`P$=&oN5y6O9Mk9-^Ybn)$eMp4>01Z^IFzKST&4~2f$z=#s;-1CM zEi)0DROG8Ss;Eytf4%s6-PAgMAcP-n9n<6S+OH|q)l0n*t3@e zjCKeDe&2k{!8jg2h_P`VEbD>Bp;r&vYG|l+#E3t2@$W_=lVZbO`auhCgMfy(S8vo?pxp0o_|2KV~bA4fk=c&HEPzhddsfh z{-5i!gQ?;qjWFv(*@V~u(TZFHti?t^D*({KSX{wmr2T@)+{axZfDo_ip?xDmRoKTj zj~1{5vNLpqY9frLNs_Og26~N+7!LXs35ZW#Asf~U(ZJ?nr3LjXT+|MtHfgf#N7o%|3~N;6QRw@$tG0 zMtXHm^)wsfbL%Ua+$AWDOE>S#4oPc)y?a*|JGmfSILlw|7K@VS0rg;wcC-H+s@nT< z!yF|Fa8GXZdx3*?8Rcidi3~e;P(}tcC*;5*Ph8F8D)A1OoRXc1Tb5HknUAFO_jQaB zQMAGi+rlO|8}8Ff;FqPpPq)(`C)MeE-rxiURGsK@y1Joj;r}QX-=~^*mi(qY;fl%9 z+(40p5n91ETEW(Ar|%w7R#&%T+O2>w@9A}syz$6n<>aam-hHL0cZlK;xwvgI4{`{3 zAbBXA@u9Z|>o0%cKCq26SOy7Z`8K3ZKzNZ$TdPX=r7pUcfiV%*F5LGgxX8T)9Fa@O zfNO?7@3SS}u=bJznqVzFuXid4aNk{+4%Nb*p!EehoxUOd)%-aarty;l)&0W>VSA&_ z)Q!T=W^N=ON8om=)K^3aFg2Vi^zj}}=y5i0Lmz9hzMx?nvtD%S@V%VA&j$)NV)^jd9fIkmVS|j1gHwamMR~DZI~I4t0XW(EW~Vu~RSAkpOUSGqc4}=#7i&tL z2s+(00@wD9NCqOk<3)NsIF*&FL=S2`Ar3X(zuo3yA-7%Eak0eX^lgi2Om#9z{tm74 zH2DgZK~(rH!Kw~8z`sl|%0eQAG+pGL_yZO|VzIKg655&Q z|JEWWAi>*>`3+6k00-#mv&=>g>tip*w4ONKgcq6Y!m8s!)^b8z`L!XDhVUH7AL&Gq z)#}0qD0=xz*`y-}|B#=g$u^TW2IZKsfrlnYj>8^Y(g?#|wm?FXsNl5KOnR*Zv2%>i zOl%Ee5U4_TBlr-stPCo7ZfV!^@Jq<(TM<#7Hcm;8tl7SK)e#JR|G8XqRZ&yGKY>IexVrY%9GbO|e z{xe(Cst5Nks0*OcIuQ=c1KBf?)lnRAoZgT1hy1qNJY!f7e>+II9^3F}^Bj@#v+WAO{|)YU<(C`9^4ud@wfB zN9)lIY%B79SJO{=@idc)UEkk~9PQ|^V5N71x&~`z4hf93hoDx6>s&Dplcz&HHS?Gx zPouUKm@$NZu`?LeSoXpi`dL3gCoE%6(^rPK?l>%SR%uqoYU`=JVkZanfXK_Qi#J-f zP8N@yIO9de{;rN4-kn5&JE0S9%qzy?)hK`yx~?0MLscb2IDGQ3#Ly9o5$nZFELzlm z2(8fXB!q6~U0i-eN76MwWCut{1!DXMwwU^!?e2If30rRlbuGHJo1@{z_jdON-x5C3 zbzS2;0PEtuD7mN|=^_qn&UWV1axic7O>iih{`Bh#O71a7MqTGlgl~f+4*-~{vSxa2Uq_;AX3Uyp2xh62=)veKhzL4W*3Or zGX}ulv_};ryOMGm3gdqL*iEF%``qJ5y-kZQ@d>{~%iugf*MOY}YuF&;L-!~iwl8eI zDQ@}s(;3?O86WP2SS%K%>bGdek>fwPlbx?0+TX7AnLMesZ6N%ID-dzZRe?7CBHc^Q zvM`y(8^XFC(Eh_3l)7kJ?1HZGHn5b7(gQ!a&Li%>{SWcrLHNE?fim$ikc&UY91(R` z8!J2De^3x)?8Hh^;a5P%6n`Yf8wOpV^UMF|8{qOhD-m*G$KJj-5hXK;1sXK&V%^7b zN1@jSQiuC>ypkR9$d}qu7N}EFl+EV716z~wO}2mY_KlWI{TR5*Y-l~esM94B{Wky% zoVr4*`5Q7i*4&4AADZy$1VsV808;auGsx5Q5Z1Y`UZF6F(YIY!M4WuvgzOuXvM-%5 zQkw@PJ^vmnYeRDs-`=6uCO9H605GcBJQl483G5^g+3O#|0Rrm zn7)$b(-d&Z{=n5kj9)oewY+hC#6dMQ>Jz}|M3vV6WC|$kBk*Ewn6y82rsvQDcM%BD zN}hRXNglocRKL*TrK2D|H*&V%K@`sJgW;GOY6J<0tW&!&JCk;|=MC^i>5j=Nazn&8 zeU2d5Xwhn%y-gS$vXmu-+X0cKr#YYA?2<8vW^|j2*CMhW>&+8ZoIdwnnWwvtb!a|2 zL@ETcmpfhOPp0`H=N6OBA?|r(g}CkN&6LSw;srOO0Y_QYv2FPm;A6h$Ow@7RYRB`n zUI|9J5z$WnzBX*{>$j$^r^_%TIpwNoBBO|}+w&&RT2yb{&rSCi?JC;@1hD%GkH<>C!{iK5f)*FdBSlj{PkO1kGq*ZR4eJ_~e@y zlgROGm2oHi+qGgZTln|tEB2d&54{YvJ8WVLt`~oi%NW^Cb9bc5)T-h2gJ{<}GG=r#)Fz=NV(~t>!tW$F3qXgl;M1%1{m0 zB%nN+0>b$=@r_26f?1pDJpefSq)d(#IPnqHw$Sz!ZJ{jKm>|^rZG%E@dL!m96$6rk_N@>mCZ=wq^wV8Z(VvVq1#e<~t<5pV}(CCN9V&Pf=qtjh>&@o1N%wFP_ zF|v-jQmOElvhp|5YdhcNA>xH<%dVLwl8A#5y0P0V+rqZst(5RzU=d$-eUT;y6fOJZ{ki2A}1^H z?P!IYc2`A-5f723wemUWxZn%aD^UAu8$p{y%hd!=&v*Wz)h4*c00w_}JI3-Sxtvgw zMNqkXRWBX!8)FdG(EFcFI_JNmMyc`af!@DeFZ=Gt#(pt{fs76fvwbiqqjW20fmcy+161Nh0#dfw! zW{T{1U;e_7{_*(tNsE3A$K+=uZhz--Xe(g*p?+C^^kcBkWzEsC^{nu}EWb5qCK(P^Lp`vK4cO0=jNb4CpfswkrZh_4nbUcG&ti_A3O?9=$iAaupgNkJF0HIc=$TMWom@Fs`!RubrI@r1S;&Jqn$4!r zW+^fmFK{8>h!bRce$U#->T+58NV`S7v!gk<1_sYNWADRTt3DYb*Ylcri&J#X3WleU z5Szk#Ik57lKgV4W0#xYnsZAC@vsJ%OJ>_FKpxmrqnj>~@A=<>pWr5Z9P`ZIU2E*XL zEniJ^a|HYR=t>f#7RZlrAOyU*DUeMWRPb_-rtat~Udz4N2Gd8ji{0?~#U5}RAApInW^^f9>NWNkKYQwNWQJ-nj!=%CXtqwY` zB2Sg|kt(kpNec3GY5-;}UuuEjVsaDknd#-mq0+j?L!e*;YvJ)o0L4}Mbvj@{lK}TN z4W=`PJ74Rt{5bdRiLAeQ{kSk@W&uudb?aIGoA_y5)_Jg(_lODCaqy3#PL82}HZ1l@ zIgcU2wp-3LkB^TnhlIe!*#al?pYm*~vcl;MhfZJhRGQ!lSxPST`Z3)jkzS&Pf9%j9 zpi7B6dbF0HLirzp`Sy!Ru~YLz=tQDn4)Y}|mtAm7Hpi<=j-Q+|lXUm)$6W77+IY{k>}tRuxbEFk0W?$^@601((Kecw|pKK6U-y$0As^ zZOUa8tf~plOPQ}bHp19J{W=YZWh;kilX6QRt+36@PN^|16-VIJdL|7#*TD%tz4Zc< zWYjYkjVmuW`Z7m>FCRnXI&?gYm={|Av~0VKFGX+bF*wTyn0WN5MU4YO1m2XfM0V7Z z?LQM0x)5&hX&oV`hC5z|=wr|sIw8WF;F49P@%jsAN!nkl_(Hg6#@!6uTQ>gro^&am zEDI5P4q+3+UErOn^~_FM;CmW4C_!aX6C+J4Z5CCLyjCFbVhb_U!av6;BiB*P;OTh6 zSOYR^#f&f*L*klR{qcOHodJIJ8E!GPG&ocLQR{l>~I5wxL4R}HtvSp`x2JgWKGv~rmb zZ#5jN2XLk-zI84dYmmH=q3aH}cSDp_B`NvPeD>Z)KB0iMal7FzNUyt0oNge^x`M&T z2{*E{QAdQt8|$+p#bpspq3Cr9bR|lz>8iQ=Z|*7!3CZtg!?N9}FLb}E<*#!IIYK$9 zBGiu_n@elZsUTu8)G5H)$qD2@=@hn9#j*w7ZCP0Lg2AVpWI@`@m&^K!LPiX+s-HFy znRM$pw&sp#;&D740^wWDU+YWxg2ycrFX5ox?sWH`P(}sQ@~UY1kg}R}0HMgOeyrYC zPQ2YqBFpsCD+cb|s%TJFT?Z`Of6#|#UjMx>vD1!Z^ zHMaQq3E952Le!ZEvMGLeA^B$f%9lIvSz=1k_W^-OKfUCZj8C=zlg`4lU&081)iP$2Qr>~K!Km;nmGR#@LjJ#>@eHOyT*bnf9Tx!hoCQn zT>qW71^`gaHeL+Ae&6DwQ*jO-;>YBYkXs{6UrsYc@THq&X$R1GE1Ei*1fyL1P%64W z4A}t&wfJF>c9HSM^kCs9HX-l*VblYd{Qm|2Cyn#4*i=v5AH$8^o2L~u%9&=!W?+2D zxr~PHVwy+pGV#2wW{8s`At{I=s<=NrR_%y0`>2g{zNj?$_8KSGv2&M_rt4!sZ@^yc zYY%d>S0tubzi>y{dO)l9z4QtCWg5Zb-=zJxZ?X7oSPxDeYcGF8eY+-I?8>@G2CE60 zU0+1X4nhJx!$Oc$OhqOHNVqN%Q!4qyZ~9}B3J9)zX1KcYt|4!}i!y9)qRf=`hP;UCo<9u8)%6_^<&n5#lo)z)( znE~mmqp)6HR&jJ~)*!TJa;F<#DNpgd4-NzeGyq$4f2JxxI6aE<#l3X+ev`iR`KXCD z$=A#ve1u{?2k49T`$(FAHED z#w*3B<;$9m#a^GRw|fv0@zjVd`CC&sqFq+V(<)7sI^%i86z>PKrTii9y{6@DTn2cZ zt=1hSKdiSK*19UG&WUigBjuu=)v65pHI-9lC0}Q+k7&QN%8o~Ci@;A12->VLtC`^u zkw%~?NB>CYnQ-??@bkNFo&B58zVDh(1L>|$Eq~iVv-Xw_45cc{Kj&oP3H{e28E76X zImvz_swtgDe!sERm8cmFi)uEr+cWLE8w7HEb)I6lrVkuN4EAIVZ62y7Gc*MAXBHydgSp z#EnjMAd}1Oo}IQ*efhc1c95ourhNHl)-%Y+dEzGtwTNN9Ss4%PIK9D%Q!6HanR>nbiu|W4zq$XPAZrT32ED%1TG%_dItCk+_VvZ6WlNfn{IAV~JDfevwPlkz3GP zK)A#gViYpJLE`0 z@F1uv3dV9bX~cXlUSPy3ig#XmokpWFOv_(KfdH8oweVBDtW0$ zQ<&}l4k47f9k4fv*im;yEliLcwzhY^6yh-xzXWO@E6 z*=X3+@frU2=#)C1A~a{@UqrNvZOu}ZZ*I8N>$lV81R4+0i8g7D+gVJFYrj>!!6u)v z+-oS{g6cE^(zS&(d>}Q-H!6#9(f!N?vCT(H*5hbhnhG$x-Zd?tT51{(m(du)M!zQF zD3azPx3gf%<2!&bS!DIKuhrnS*t)$%SD0OQjME&s)XwIUHwfs&aG(p2S=bp!*Pw9A zH-LdO@feKF$nSK3*}uQQP%#(F3DA~VG>adF))o?X_q0HCXUW3{sF*E;$mB>0ru!fN zUG&m^hx~(NM3?5@8FHj=3ZEmHra}8QZ@9B-Ty`W?sPLIx^lQ||4-nHpU?X26m>RFo zmQQbi?Yc$;Jq=0Z8|4-`>u541zM}BEm-dFq~^@&17xo(z#gYmd7b+DCAOx|Lc&_x{+LH4~sQDuup|4=v8sYLcCd$4ZJiu>eS^{(VA2X=gbp zFhe$_cZ*>;%7Wh50Jw_KZnh~8h!{9Qdd7h4EQ(d8UShjYAU5qe$#uEP!VzP7)T-G3 z;M7Rh_`W6l6cxVGaUDc+iFjV2Gq08SeJ;%Dz6yTr)F97K5`tWWmET>8bs`p~t-~OV z%+Apn*iZRe7-%owHOzE*a<600DOqJHG>p@rPZFtLdG{>Mc! zUe{Bl)kuVGu`(V@499&roPjM!b;1z#6LpG5X&FzFG5cUMKf@Moq?KOIB9}7fAU|BI zWTFFgFMIXRU;S)pQKs_o}&$Pnz_~{5c--j`K53c2*g3OET3p*`emW zjJtpkH3CE!Pfm{U{e!kFp5ON7gvjCd4Ylnm&>fyS+8kXyuPG-|av)3Diy0SoeZKTo zq~g_#UCVpi_#caSQ7SI?YZ~0N4j$k;?8xunfO8eyIn6)~+RDapwH&nL(*+QEw0`)U zN-3iJ&yeEJ#`3{gQZipi`(3iRl`>Q~Ww4mP3ERLC7?j%tm2H*cwIC7ST?S3+>GcI! zhn9A!^0mo`f%xtu?5Abla5h;DJ$UPO5X_=~ex<4=kZkV&Q6n8xxy=J=s3h{Aj|rvJ zn`7GAnmtr)vhY4lrG3Ij_s!U6PrCn%-BlZnUS(56j+XuJ_S5}|%&1EVI&IL24U1r! z8X)O?Im+9mT?%YHD1=<=mL4^-(iHHA`Yit4Xq{9zUIN1SX$p#970^sF9 z>;&baIT=&e{|@h=lSSyfniPLIspx%XjVSWu#^ig6Q|BVqc#8EqdQ?KkcL>bh|5WU* zaMfd=pF!zSYCj}yi8ed+H(htszz+@L#>V4UDI_N5fCmtyaTrKin}Isf<9hW`#5D>PJu{}It2Uzf;9|2yHop6*c| W#wNR7sLindBg@OENLNXk2LB%-C!B`> literal 0 HcmV?d00001 diff --git a/.image/common/system-feature.png b/.image/common/system-feature.png new file mode 100644 index 0000000000000000000000000000000000000000..366087ce0c9061c34e60feaa30af099f99694cdd GIT binary patch literal 13584 zcmc(`Ra9I}6fW3UAOsRDK%jA#;O_43?gW>{-2#Ci4GAuR;O^GAcjE+iZCulMf=&K4 z^Dwh!-TN@>&dhl@Rj2CPXMd-5?UG&VeAQ5s$HpYV1ONcoiV8AX000Uc06^l$K>1g4 z>u7QL@4;0=MMw7j&;85Gi$;Ik)%6v8Z+Coh^6=S2>|}XmWovuu0`dFo{0z{p zw6?MS_wk{&t_})=<>cl<+S}(B7D~%X1A_wf_4T`Z`w|lq%PXosWHDG-!~{cf4$ptBF|wg>*OBcwNv|d=32OB;Z%k z@4q+mzqhki+FnkNA{Vk|AuojvFE7y2zji+NCyzn{wnEvr+m-`G+f6UuHJ=*WGuF!+ z=O2I9ZL7lEUn&E0M*dn)gq@@<9Iw5+tSw&mAHK|WA6!01cKw+>c>c43FsZnEhK=v} zjX&j-^ku!Y#=bnprp7Z6!2kemQ$-m`9lw>+#eD1m2?EsV+OuI=+UNolP1@vZ^Y~0* zj+MIrVvhez^?$z-jUH0j4(_-$-~!r*FQkWc%=qfJ645`joPJ8J#%+SbF&%Em%aZg+ zN@a{3a_}6A5ph2PmqGZi1~|rF&S=c#7B~D*-i)>EevTACO_^6nW!61@+LW-)wp6fK zDGx9p5Rcokj2YQ~AI+higF0EkWA;D3ZycyrJHip6%n3vc;l43ho88?eV!>U(xi4b} zrpoXk#jU0B9#gZfpj^ZY(|;K0W1r=K`X$gP)v<0|mCKdXO^nLN-?jGVbyUUjHbv|S zM0U|Mk0-Wp0S{?i;YE0HqaP<|O{ldw;o)MdD( z+p+JLCKS@d+1miaiar>h0U5J?M>hp*1Dah-bR;dR(xJxqj7`Oeou)eekharc(oK>A z6ckG2*$gQR)-}z5>P2edf?wV!Z~PX^fMS!=QqT-uJ+j#_+GM>|iB!Bj0ka&U+xyb7 zaCWzZ%CbT=Sri1M4!}dIamARl`?aqhPubcHQN!X8lv8%{cl4ZnzaFH|p? zCqU(rW_@Sp2oNn|L>l!vO&rqYQ-`U_$|U1xo0R(`94xP&ND==K2iI0n|M0c{XHf^s zX!C;F)xkW{yXzv9nRPz=Lg)HJzD8lWK1v6c(Za8+CbBc*$CwHuWyv!`Ow6_fG}$C> z#Cd`=8#<$EGJheU9yXJn-x=kZ3H9O789$KG(x03e`kOSrTQnBS#w?QT>w0KSzMxz= zOt$gII!6IL`e^yU4?C5glW-qT(GBgt5vJO7J1smg>aQ7N={NET>a+_`x{m=(__Fco zfv!XTWIVZXB~#gho^auvdWabR^S-6eChoWEqrVwn=c?ng)u-;(=qqq}ZFVlCH}DZR z*CCsJ5Ndw5owzICKlQ}h=Q942)dHNSWpoaIJ=%U}+}HA*4_O6dnX1d^ryu{Y{#gVM zRA`Te8p}0;=^Fs%KAPV~Iw0gc8x1BQ>7?qa#>e^0iNmSTxGSM{a&sE}yGi}lM857G za`Q~)^?+;#WFU4quUyq^HdM6QF9+-vf*!mCF&2Z##3b-*{v^+_g11|?|L^81<-N?f zuP~Q((epBm4spq;zHLlAQp+mK05HT2d&&)N3K7awOkPKmxM{L$^Rn_z&aXG|Y^cj< zw;kx^5c!O+zV{=yW#2DM$kw0eJ5_Z|OLnPMp~R0J?({YplD)lx2F?%jv1((#C!d@b zL`YPTj@|fytd<=9>!PQwjoIwS_`o5*oSl$hr= zaE_khuW%1z=!b+Cp6Woj5M0Q~f_mSV15fWnVfBXpn(Hm(eD+*roy--IP`Wo*K^~l- z54EyIk0tpm!%=bd*Q@2$YoCQsbW z6LS)AxH*(l#H<9-H|!W(ZWD;_&5IzH15MAt9ujt=XKiCWS-ejbfi9(01dcC)w|FL$jlael8Iz+ zMu4H9GVEOn&)!Qf!9kR@%lcaPzV&^=;+bZ#mQu6J=AR%`zAI<*iJf&G`A1y27Rcv>^%hu z(K;~U!CoX1D$6eq7bAI>51O8sh9p9z?M=KqdCm(FIRD>4T5G?9-Qn4XU6p6DSP9}f z?5it!wJhowK{`%1?fSXzeMP!kFJ|4Gy3FVWCa}f=9OG?cIY(KWV?t($$8-wkg!4`} zJ@>@5w^R`%Y5t#N%^1>nWZa&d<~njuqq{s$K-m}rzdJ9P)C%IyK(u4NnsQcxf;BE9 zX;DFavTTlHYag3Tf2I&(e*E}x@A@JWG3Y&$CZ&ttP?y+Z?KVHKYbyQ_%nfR&O*jd~ z!7tKAoq*8tVO>(=ta%jK+_QLFi<$kZhT*;K!#0(UraU zQwZpXjmj@JV||uXVsf3ptW+?ex-MPdBF zUP96RC~>(b+KGhvQUO0`-8gCc9G=Ni_w6r0<S3y-D|?=)f>-fW|8HvB!9qT znt(rhM{)fa6ppkCFg>LNaH2DExX=Tvhz|3pSkJ5lYX~U(z8u{90~g=NzQ^J543n*o zn4)AP4m11;h9oFW7Pc#BMdw7o`Ne_vX%>`aN$yc`sya5t{N`^+m|C(WH5g%^Ssex3 z8oc+SZWX|xPm{gAs3>w1{vWpJt_^mN^9x58+Hm=*e?AoQ)mFZBi;vT17E@5;z_}v7 zlZC13f45nelwUeb;#m*?n`6aND5$lP8?cuDA+bP1rB5b4{JCt&NY4-##4V+wg^;}L z%7~xI86u;*3lBT+-x#JkAhCxkC}=_&n^dUsgFrH{)SaHnkSD?qKy&RKA251b&b!KA zlEScN4>b%Z?4TNy2itj!E`gxCae-7cv zn(Dp5e7!vj7kpNE|2tZyEyQjLL~{;&cj+qReCBKL173w8QXxY#450^>PR~`NNPYs4@XuTC*tVf@Byy8dI#?&}-t$+4!F_xm zbAB?6{8mD)k#s+V*mkT%l5=w7-F|43hq;@yJ>j2M@(M2d0x`D*nuR?U$UdEuAn$uG z3jugxRq%e75fl%2vFm!q)YE*LU7DLT`+=EKOzKE4Cf*mz{sA(d`m-{Bdg#uXwU+Bk ze4wLhHxd48i-)oA(cR*KeIRIZg?*c;4!wPiU}jir=Ahsgc#p5+3-X#~)FT~iT`D?q z&ne0)M@DhcGR=jkpL+6#Ztu(|Y4vnH66u~G#fU9Q{n74$J}|ZcdCIOJ^6l29;y6-SW&v2-Mjr*>A7v#>=lzV z*7esM66{CKML7jV_l_c{O(^I}N|nw^DW$0>hT)E6y1JypM=pHXw0NHfc-JjiLfa9s zfDHF`chhvxK4!}iC$NVORh&TyyZq0ee0WG&JMx=f!Ct@Kqe-!mvGduEQW`X$Fq>ZW z(=yc8J}Y26ADS3G{jL;+al)O5J}HLNrh?hEMK0!VzM~ktA8Pm#*0j2$Ji10lJc4kN z`t7txMU{s!AugM^A;T8+6N3=RH@v#T9fJKmb>LYpm82QBg!?rT+RZHE*yZ4l@2x$4 z7o$(tu%sjgsjG+3eF*(m6G1$S$ypv#JFA$WI@UXTwygx>wN{_UFF21#;k)5;KvC>k zT)*ChFKM6gbh1%k`fbykL!Q)u1h7z?h!^*Ks{m%67jj|ZgI6BddPsgV2u7>{0V3&9 zUV&VTueK?)hFD)G71PfyiNRwl!5bIdU8jD`qei4K2a+>*ni;YRX=9UdELc|4SfJ9b zbnAYiMw4O^9cI`x@qrqpsJ=re*J7z0_mI-EiHep>&cmeTFpoU_Y8Lr+49q2Q6l;|-TQ&V z4d9F?=#*)An6O+qw(?}*c84u$LkDE{-BiXI1f>jtI0VRfj1|w3o?EQHCCr`=6FxoY z=Y0OY((9UCodPBb>=NRFaq!{t40yZj#c4`u3CNejl{|{?^Wm;D)vzbzUX?{B`HN{j zR-_$wJ*x?TM$agg(x*?jM+{q#GI-Z6|GU5&#;Vg#`vexeMerDYSE#@nD{;4{4!yfu>jdx^h5ae2Rnu9N zEBimRf31x%VLatN&G6g~Nr}q`ZX!sPF3k{qAcF^Q6aIKcUMyetHDw(OWs6USAZJ6P zacBj+Yw7OGEci^4%nlI_&p#2uZDFfhlGt&kGIRsCkoTbUH4-ht$4`I^*x?q`DjFF8 z17!ZiNO(v&it=Rk_bbNL8s(Es-$%<(IuDyFMU6Z0hU*LIOV<3g@liS7mcOzVfwtH*~?}N^(OU@=;5}lHC{Bvfotf~F9b7F0j&dyh4v>y!jb$`FasE_2s?u) zG6Axpj@j#J1ZrAV$Rl##CNAAKhiW8j$lC(n*(wzq3L7)%W<7Y#Q?A;Gjxzo{$E$hb zV2|J5k-_vUzM->VYU&``7kaBe6Df$+fT@f9iwiZXcT0#iYJMJs_s1LX>rc>RLmP0>`mFMW)?`yu z^Z8yK*h<|bW}qywxGjDH;d($CMq``XUk!)XU)YWN7Q-tUmG6M9ZLbx{u)w4rC5{9h zj>X?B`l_dx1BJRT`I|3bNL&81M=8x#b;a;-hnh&Z>L3Q~xofPUI~Hu@UY;po;H@J7HLC7*X0>aTqCbGOxj>LN!1ymhay zJ;DXk_bPA)YV;Y(IkVuqU%Vb0NiUvPZjJ-RK6IEpjbj&g7=PlEynx*E$jRHB0vAu; zGr1fJ2Crt!Q9m9{{l|GgqU&THo8L1$!24I_qHO(pa5Ta2ewZm6Rel=?6{Z`@=#S%~V2FChf}siMAZrbj(`cOG(cF4%;01bJU=_u3bO zkK9<3-;5xx;_MhAc;Xg4ZLz?K#PudXFB>kPCKJczVC5^lZ zbUjCvXitd!V*sv{`j4!gANT`r&3ix%1&pw6L#^4!=#(qRxL#PU5T7xo*o zeN-++vFWhmVSWgP7I4^Lk{oH#;9;c>g!pek-YwxS)I6s2eq^aI*_r*<|J%KX@^`AH zE%}pUVE9J1P0H1mX^Q9hp1@I%V#ccbXIhlHnD6-Rz=Ik0D6Js(Afw+Y$W_kcsw;m| zxs}pIP`4#61PBX)PU2JfBq~fuhd?GT`(*!+VQMNXWU9#CMaN}FsIfnP=~t6M4XXr; z`ST)+E&4Mo-hU|7G)KkYHAQkw06{FqiS+^_g`l!wVT-adn__V17LtqpSdYXLBP`=jlBwcQd;>WG7li+4IDEv-pO zxiXczG)5B^UNCYUbNxK6dLJk$yEaG>a}uxP7gZ+OA&_4Lxi2NmCW)31bq@Wy!YWCH z6AY((B0I@5gON7;K8w`cN4*rGQgj}}XGdVg?+7S_!u}?r#|{X7rbf*7O!jlEg=W50 zj$WOjEI$JQDa=wl;M}K{fWfy*P+U2|FZsy7J^+M&#WDdJKxy$76xx=TSmDEz79fk)~i;4CJ^SVld0t1qL}@=Ld$T6|Efc zb|wKBWBi_g`_szVYO8oR827^nXbZ9aaCfm89FI#p1oFO%+d`6rIShIs21NVNHj*mu z!rKBUGD2T=M0a_Bn+Ksd|F&=99z{dmjjsJ2zD&h=#yJX^h6abG{ zaGr$Cx!He^hDnsCcUH!ed#pG zk4@8(3ZjZxd3}+M^5BNo8k!AnYSb;VxYxh!cDXP$pMSS4I{au`-iH6{FR=DTL$~3j zO)TP{xH`%9x%smnm7&&OQ^(DxhQ}WzH?5`bwJf=|3jIe=pW78?lt~gpXbnGoZ%JsK zHFsJFlHl4YWOCTF;O?3qTd3rfe$_(%9jPrwg^nt{3w0?=;nglRS8AlmEuMuF^@pUm zVI4e=W#zEyuQn2F)Y^)}w(Lm%PXwOa<k+4m!_kkQ z*Zpt4ncYA2%(P7V|5zq7@F}l(c$Q!pz=~IYnWEJ@2>r0@7D&G#aN-+(iVY8dWjvcE z?c!IRqJ-8{AYn1NVl;;(_`-Mc{Ij|1)$5j8P`8k_RtFfM#QAX(vC_)Fe}CKl0_r7# zC<}!AJ{_h;-k%^2d-v9DFmVN&JXa*E9=)HHh!}!Cl8W(@K81NUjY!bw z7Pdj3l4O9XZt7KaJA|_PBF?9qrY@%G2AeG^rGBJXq*QCP&_QLBv6p+fV9Nj%Td!_3 zQz4#d6+oO&PK|7#?ZMh#a1!tk<|(!Vw93D8j`r-TNTHzysvXNKHANhip`2Mlc7&&j<;jtx6z+I{5V2s#yL5d#)(ki!!wguuE<#sm z1Gx!VqkXHipPAd>11vsO5{{S+2+{~5q!1#7d*PxG?OdrsOii4fx02nF)QfBmAE2GQj6)j-u z8aAnhkcV-_x683I(TZkZ8F~5V<}DoiZDmG29}7g$iDIFB@1tHS?+*aMzFIrxz4I20 zmspo9wfwSN2(LAVwb_5b6t1_N3h9ap(^R@$L8@S12eYsiTHaA-65UsSxn`v(`2fjl*K5#GD? zYIEbyi4OZQ^!u;QqiDVve7CqHv7YySq)Y{mHB`*Cg7If*R6a(m3)Y)0gDR=@m#-9V zk#*CF@s1}xUFG?#ZMIDz!wKbCr}kC z+x~_1XVq)Xq5E{+*!2i2&iS2F`i+NPWc-lq;#CCE2mRji(dxK2ETr`jyh0&IS8FJP zvZh9uG9;3%;bM>FaJndCD`&XC-CO-WPig;g{m%4O|?3YSw2*YgI}d zBOj;*AAGyH1gE>;Lml)kWLW3vCCmPH-)(u%90Rl_RCxdS=b(3|zQ50f9~;BvTe7LP z(%E=^Mq&*U*=3$FI#PqGMX@kB^A4v~^NCfjPFE&XC8cr@)ns9+tpN|dt5M>30dbfy z<+L`1DxLuD*7IUtd&xiR*TM!qgDmfk^-&ln{E29U;Q1ZvV$AVRH%0*o(Hd@*p244R zDQJ6XnJfCWMaf5$4AV{|au%8R)rW@HT`^9s07h?7vn2FlO z6@A_-_Vc^6|E{X2Xf{D0#YxqK*Eu|;?`f!Z^eUDab&qaGUJ1MSIye9GGU@p|1C8yb zB9kzYVM6V7Otv0hW03C&WNJz9Q~VOx*IX#~tUe<3_PD*xJIAyof3!h`Fk_Ce$15L$ z4u^pFUA$tgpw7$(?K6N*>bTFN2r-Ye&UCYxh2F@sXRYh+H)r*;gh??%&d2QwsOBP~ zTs!(U#IMLEe8bNC@KM8Gyld4w8K_fHJj6HEDO6`}%E%})0Gw2_am2NVE1Ms}%PC9^ z$C!CHI)gbZlOOmSXNShK+HsPz8a`Fby!SAoQ>YI;o%f8by+`7Ap=WLJT7yqqZWv0Z zgN04+5B)AI&{(|J&iaett5s5@NI!|bbe7&>$rMk5I00V=4V20|_s4O#8@z&&SupEF z#Qa}1jSVXq-kH}_Ha;7}3R5Ns9&?lqvB($iN4U)s6AQb&tbi9}?9B@|i{PuW1q`jw zrPjJ!(JZrMPyqFz8=1Z{l>48%adN)bfcLl20s>alo;a`$UdK_ws0pP^q68+U}b;+hex1;B&tq)YrvN{PwILtISENIHlA?Nt5D=3w2Gw zmUJAfU*+;N)i#ZNzOwf2uig-`EDqCXoXSDiTT))6W`|7F2xYSYZiG?xRP2rE^c{5Y z(?b`WKF=FROodRkdhpBE_R+WL>)n*VqI`bG~qxMWt7XI@S5V=@0ZS#&${R z!!r1Q`)boCXz^BfWDMz>2emgbesiZUA5s}jrX_9~S9o<4y+I$Qn+7dFA;Hh448hQJ zS-->?ip#}%m@qYqn$`nj9e%yg5(1-;Le`3Y-HZG%F7~}wY<{UF5#owz(U4fL2NPV& zR!xXLBex7&#^v6b^NzU4C13Wzk^m%H3c3SW2m7%thYXxWVEKN2$M#x8OOORZZx7|@ zr`E;!v)uVBK;)_b#?}G=`Bm6iDK?>GMD|2v=y~ho){EOi9*SGejj-J97f_eA%F7sK z=qfh?YS?3K<3_wF486VsURwEy{gpJ^r?7qMmVEAWZ7T#|plUi}iZkv!B|16L5bn6% ze#^1&qve>L>~MK?%GIlKF>Y*~_Vu)kfQgrFP7@dYg8g}C?2K`13M1~3_t zSbEt($t4bWetMVV(AVdByxnswK~|ACQ~msAYxkq_!!P$?&Wn)1KXYs75GHE?I+)@r z819c=c@4r><9Q`SiAm@eQQDJKSq+qX>r~C;-ufyQQ%z%JTVabs@ZdhdB=5_+yMg50 zJKWk^Q3*6L-ct+zHmx%zu8tNIJP@zUR94+uMhp9PBt7bQgtaX_Ys{@mkv?m5Dp69u z!}^oNsDJL>2kF}Kqla$GDaWAFVkYD2W)XlQ0a`_T0S0by3ijOB`dipKUAIX<6nd>| zr-ss)1O@5aq8&#-Z?r!Zh*#e4_X>0f^Ik*AyElg(`6lwe50C>U!gK}!!@G64uEO!W zf1w|=MEa7=hy5(d<)d*VT(Qn$|5hT-CBB!0$!15sL%4k*YoNsjX>JRtT+j%$Ip450 z^K}@*=Poaab+Ic7))!RoWFMNK162EMgmqv~v_Vc%c*d5MoXMa1?GIk21g0BtS_9M{ z5r%ttP5^6+1WsN+Ln&`J*hoTN0)f<2VTiRYG;Dkips^B+21(hjcgaG*4T^S3qdt_X!e4?zPNKa_`rZJTX5j0Ei7&S8h z9ZEZi!j6FJRDI=ylM|Uojg4ejyE)dBlL<0qJ77VY9|w^Aku~#&h(NKux>V31YdT2( zuG^V)Uara|LmW8?b%junrFeXnBvE>xs}?HCDidSj>(N0f_$iXmZX>YLERmDE3xa@% z^5sO!kpr&v2c+TRJ>V%&^8DUj4qxxL{6!^_VvLZ%yM#A_d=;auedwPVn6m@AWz_-h zdnOwV2yHAU@7Scbk-A}S01wPdJ$=dimh|v`TvCIYZ##a<4`G$}gBSpz-9#Frwx(2w zGIow?PZHI_Q;T)H?NbKbMrm5$lrs?BmL1t8#Ho{e>AZe8g@A?kvs-v)*sHh6l{0g= zw7v2YtFXKd@I_>mNA;?n7*7aC6k9d;?!Ab%s;YF~khMy!s|ayrJm*4!@ykh=0{gu& zDL+nSKi{MW|9~F%^K+NbG}#GrwZ?8tVkPTXj3CFaZ&qhXzRp43O0MONn25S=Ua&GY z0m)D7su>)>w?Mx4?jnir&BA1tMjzWb4A;2kJ7k#@lg6t18LCNytgEg zkV&$Iea;(+hngBDBG&#wBK~tlIihT-6nQ6}^L7b=m?1#Y0$24A=xW6~hhN*4^g>*DwJ@ z3_O7gw*NHij9{YRbLH{DyrAhHJt*O9aA&70q4ot4Yem z9927NM(hqUih9>+T?qIi`TXG<@F8xy6_FE7#L)XCg5ReCz{bsTo%A3Stptg-9#tbU z$q8LBQ2%b(Nc7qm-vsj>=ED1&NaeL|GiTj@;*)cI_-Qd#mqp)K;t)lF2!?mnIOYfW@Sz5`h!b+T=vGbZE)jJ)Z8U%mkr$%n=ug@SC-4-! zoH~((T-ZHhqp-pqLpLio$vz3dj>QTFxTP-1s5((68x0_m5R9C5)Gbo>G)I=11HKwe z4&BA(ZWGc{Xvi$kk}Tys3ds!{XFqE3z$>swoB0wsObZ=A66n~=$csoODU}@)cMbIZ z-0|}x83kll=d>!*hLgM#0!s1T!Z<(A_NJGeMSmBX7&vNIbR+2%mQAr-X{}#iW2#AT zY(M}M-+6#`kgeyuaUa2H!suYd7ZW8*gEGjtt@!b!oL3B_eXMkI?2&MRM?rW)ltr*L z?l5fro9V8fRNbM3%|Oa3`CWjSA70H;34sh~23&s;t@??KAKAf=ZFF(4G-+(X>Sn*2 zH7Q=D76^^xGnmr|Njkbz%v}zTTSrNwK>k>&r=zZ<*>^Ih>@||S8}33B^!`}10b~&` zpsS*t3_|(~_4(DL_Ne$ce>I;$$Cx$zuW=nWk6!(J-GM2wSg7QFHPh9eRLI1hN-*8Q zGs&K=+?XIsr+;`AlsIxC43gvHBOUesTatjH7=iveT$);hx(+!Arfh+m7{qTivUSSb zTjZpOm$~3A3U1c8P4m3J84lx}2t5|zpS(t|{7ds1U5Ru-qQl$Ep_7=nj>NVg{IE!Q z@a$PJ;d9fG01w<=#T73Iq1Ncpvx)XaSy%rc!~(eKX9)i)isNWp3EhBlmi#D?(6GU< zJ<~yvNE)uF*~ijn&&wLU5;-=X1Z)(8!gRTuoK(0773`qQs>olYu56`^6e7e=D1x;( zrnZWlC;=Ea$@gHuh9Hf2z~zk&UYG`;5;CR`%MIW{>O9dBASVv{-b=0SltT?M@=bgr z0?b7|^zc<}c)J;}`~tfG6djz0bR(#v8{Y!IOn?WJI`%?h^Zw1p^%t^qfVV7% z?MZ8iTYueS5T%cB(igIqop%P0FPSN!19OjVLG*ihTVxEUF9|QCCHpT4J#pqs)hGm! zmW-GYXi0lA%n9u%FW-4QcQnf){3{KqI%SYa~`Ndx3hZ*uh3(J(C zf)~+O_i_$zV}lb(MV+SfY??k(%wb-#KP9b{9KXJx->|cO~y0}v^BgU!KjHPs|onB-1n>dEid~W>afmgOR>Z1t2<`0 z*un++MR4HT0Z9k4))lm^4-pglo(~X(4ZcacPZ{YbNk!JHSyto%MDKFf1ri!*$5uyp zKCJn$ToHwZB=AChS1y8+RNMK_F0xQrIfr+-m>!2d;~2o={)m%z!pW zP+ujDo@S1mCXY|cFi{X_73PV)uvL5{pl7WlZ!)Pc-rq--j9JgueL_!lZwl}1{dAQ% zT@>cI3KItSBx;H+WpXt_wx*Lw8Rn<&mWUhlIM|#7_IOSJ3jh&hGev{l1uwPfzuu<$ zyAqoomR)lOqEAwI-7D67`$sC4wU}yeqfw5TYiD)W%I==SV(Mu>6hQq|E8bGj*algi zh_Oj~`hyk)tcbEFO640P(l>d}b88c_!8^&hs{#f!PTq97SVzV%K(%6U8nO$Z@*I>} zS(w$pdP4QrFr=k2gMpDc@t5;;c=pF7iASBTVs!5>`kygKq3GzX=&;(JGEiTQYye z=id8#P0(r>c)bf9F}RVkXWBbN;d~uId3QkPfLCH!e0eUBQ1SgzgZ>NWtHTk#^Sho_ z#LeqrD4$u$g2QV~6SFL?PX?#LtYzVZ%+_C#G$i#VSVJ)GP^%sUItd|eA{%0&t3kJP z`^A)FRCK$jWiVTw+0l@|LN*znl}7UCBSJdN_MUpQA~9sOhTkoG{C{r0_yu& z(vOk`U1p6z9`{*ag&rAt3@p9TdbOCjsn5$QKJ?ixM4~>+=Z!iyc?Li~2Z8LHFJ)WLSM3qMG?)eLzO40h zz7xvXS0_#E$}Dg4hE}nyNdHA2u3x;x drO^K6mM}P0xV$DFHT(a=DaxwJ)JR!G{BJSciA?|i literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 0d13dc75f..bbd419574 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,8 @@ | 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 | | 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 | +![功能图](/img/common/system-feature.png) + ### 工作流程 | | 功能 | 描述 | @@ -174,6 +176,8 @@ | 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 | | 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 | +![功能图](/img/common/bpm-feature.png) + ### 支付系统 | | 功能 | 描述 | @@ -209,6 +213,8 @@ | 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 | | 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 | +![功能图](/img/common/infra-feature.png) + ### 数据报表 | | 功能 | 描述 | From ba541fba8347329387d7ec97ed662f736d937b24 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 27 Mar 2024 21:06:06 +0800 Subject: [PATCH 43/81] =?UTF-8?q?README=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=9B=BE=EF=BC=8C=E6=96=B9=E4=BE=BF=E7=90=86?= =?UTF-8?q?=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bbd419574..4787c4d4f 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ | 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 | | 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 | -![功能图](/img/common/system-feature.png) +![功能图](/.image/common/system-feature.png) ### 工作流程 @@ -176,7 +176,7 @@ | 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 | | 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 | -![功能图](/img/common/bpm-feature.png) +![功能图](/.image/common/bpm-feature.png) ### 支付系统 @@ -213,7 +213,7 @@ | 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 | | 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 | -![功能图](/img/common/infra-feature.png) +![功能图](/.image/common/infra-feature.png) ### 数据报表 From 2ea56b51eba1854345259e9e1c347b355d0324bd Mon Sep 17 00:00:00 2001 From: dhb52 Date: Wed, 27 Mar 2024 14:30:28 +0000 Subject: [PATCH 44/81] =?UTF-8?q?=E3=80=90=E8=BD=BB=E9=87=8F=E7=BA=A7=20PR?= =?UTF-8?q?=E3=80=91=EF=BC=9Afix:=20convertXxxByFlatMap,=20=E5=BD=93map?= =?UTF-8?q?=E5=90=8E=E5=86=85=E5=AE=B9=E4=B8=BAnull=E6=97=B6,=20flatMap?= =?UTF-8?q?=E4=BC=9A=E5=87=BA=E7=8E=B0NPE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: dhb52 --- .../common/util/collection/CollectionUtils.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index cb4ddec34..0d06bc799 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java @@ -78,7 +78,7 @@ public class CollectionUtils { if (CollUtil.isEmpty(from)) { return new ArrayList<>(); } - return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); + return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); } public static List convertListByFlatMap(Collection from, @@ -87,7 +87,7 @@ public class CollectionUtils { if (CollUtil.isEmpty(from)) { return new ArrayList<>(); } - return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); + return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList()); } public static List mergeValuesFromMap(Map> map) { @@ -123,7 +123,7 @@ public class CollectionUtils { if (CollUtil.isEmpty(from)) { return new HashSet<>(); } - return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); + return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); } public static Set convertSetByFlatMap(Collection from, @@ -132,7 +132,7 @@ public class CollectionUtils { if (CollUtil.isEmpty(from)) { return new HashSet<>(); } - return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); + return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet()); } public static Map convertMap(Collection from, Function keyFunc) { @@ -315,4 +315,4 @@ public class CollectionUtils { return list.stream().flatMap(Collection::stream).collect(Collectors.toList()); } -} +} \ No newline at end of file From a80ba4888911f770ed09e3143dead2eba467aa8d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 28 Mar 2024 19:05:16 +0800 Subject: [PATCH 45/81] =?UTF-8?q?bugfix=EF=BC=9A=E5=90=8C=E6=AD=A5=20maste?= =?UTF-8?q?r=20=E4=BF=AE=E6=94=B9=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../receivable/CrmReceivablePlanServiceImpl.java | 6 +----- .../admin/purchase/ErpPurchaseOrderController.java | 14 +++++++------- .../app/spu/AppProductSpuController.java | 2 +- .../controller/app/cart/vo/AppCartAddReqVO.java | 5 +++-- .../order/handler/TradeBrokerageOrderHandler.java | 2 +- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java index 93ecb4746..dd3de19b1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java @@ -20,7 +20,6 @@ import com.mzt.logapi.context.LogRecordContext; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; import jakarta.annotation.Resource; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -46,9 +45,6 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @Resource private CrmReceivablePlanMapper receivablePlanMapper; - @Resource - @Lazy // 延迟加载,避免循环依赖 - private CrmReceivableService receivableService; @Resource private CrmContractService contractService; @Resource @@ -144,7 +140,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { // 2. 删除 receivablePlanMapper.deleteById(id); // 3. 删除数据权限 - permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); + permissionService.deletePermission(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), id); // 4. 记录操作日志上下文 LogRecordContext.putVariable("receivablePlan", receivablePlan); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java index 203d2fec0..bbaf2edf7 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java @@ -61,14 +61,14 @@ public class ErpPurchaseOrderController { @PostMapping("/create") @Operation(summary = "创建采购订单") - @PreAuthorize("@ss.hasPermission('erp:purchase-create:create')") + @PreAuthorize("@ss.hasPermission('erp:purchase-order:create')") public CommonResult createPurchaseOrder(@Valid @RequestBody ErpPurchaseOrderSaveReqVO createReqVO) { return success(purchaseOrderService.createPurchaseOrder(createReqVO)); } @PutMapping("/update") @Operation(summary = "更新采购订单") - @PreAuthorize("@ss.hasPermission('erp:purchase-create:update')") + @PreAuthorize("@ss.hasPermission('erp:purchase-order:update')") public CommonResult updatePurchaseOrder(@Valid @RequestBody ErpPurchaseOrderSaveReqVO updateReqVO) { purchaseOrderService.updatePurchaseOrder(updateReqVO); return success(true); @@ -76,7 +76,7 @@ public class ErpPurchaseOrderController { @PutMapping("/update-status") @Operation(summary = "更新采购订单的状态") - @PreAuthorize("@ss.hasPermission('erp:purchase-create:update-status')") + @PreAuthorize("@ss.hasPermission('erp:purchase-order:update-status')") public CommonResult updatePurchaseOrderStatus(@RequestParam("id") Long id, @RequestParam("status") Integer status) { purchaseOrderService.updatePurchaseOrderStatus(id, status); @@ -86,7 +86,7 @@ public class ErpPurchaseOrderController { @DeleteMapping("/delete") @Operation(summary = "删除采购订单") @Parameter(name = "ids", description = "编号数组", required = true) - @PreAuthorize("@ss.hasPermission('erp:purchase-create:delete')") + @PreAuthorize("@ss.hasPermission('erp:purchase-order:delete')") public CommonResult deletePurchaseOrder(@RequestParam("ids") List ids) { purchaseOrderService.deletePurchaseOrder(ids); return success(true); @@ -95,7 +95,7 @@ public class ErpPurchaseOrderController { @GetMapping("/get") @Operation(summary = "获得采购订单") @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('erp:purchase-create:query')") + @PreAuthorize("@ss.hasPermission('erp:purchase-order:query')") public CommonResult getPurchaseOrder(@RequestParam("id") Long id) { ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.getPurchaseOrder(id); if (purchaseOrder == null) { @@ -115,7 +115,7 @@ public class ErpPurchaseOrderController { @GetMapping("/page") @Operation(summary = "获得采购订单分页") - @PreAuthorize("@ss.hasPermission('erp:purchase-create:query')") + @PreAuthorize("@ss.hasPermission('erp:purchase-order:query')") public CommonResult> getPurchaseOrderPage(@Valid ErpPurchaseOrderPageReqVO pageReqVO) { PageResult pageResult = purchaseOrderService.getPurchaseOrderPage(pageReqVO); return success(buildPurchaseOrderVOPageResult(pageResult)); @@ -123,7 +123,7 @@ public class ErpPurchaseOrderController { @GetMapping("/export-excel") @Operation(summary = "导出采购订单 Excel") - @PreAuthorize("@ss.hasPermission('erp:purchase-create:export')") + @PreAuthorize("@ss.hasPermission('erp:purchase-order:export')") @OperateLog(type = EXPORT) public void exportPurchaseOrderExcel(@Valid ErpPurchaseOrderPageReqVO pageReqVO, HttpServletResponse response) throws IOException { diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java index 57fc47d45..d8d6f29ef 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java @@ -101,7 +101,7 @@ public class AppProductSpuController { throw exception(SPU_NOT_EXISTS); } if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) { - throw exception(SPU_NOT_ENABLE); + throw exception(SPU_NOT_ENABLE, spu.getName()); } // 获得商品 SKU List skus = productSkuService.getSkuListBySpuId(spu.getId()); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartAddReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartAddReqVO.java index 26d774234..0c33b8626 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartAddReqVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppCartAddReqVO.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.trade.controller.app.cart.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - +import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; +import lombok.Data; @Schema(description = "用户 App - 购物车添加购物项 Request VO") @Data @@ -15,6 +15,7 @@ public class AppCartAddReqVO { @Schema(description = "新增商品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "数量不能为空") + @Min(value = 1, message = "商品数量必须大于等于 1") private Integer count; } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java index d0a3afd5f..e1ceaa505 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java @@ -83,7 +83,7 @@ public class TradeBrokerageOrderHandler implements TradeOrderHandler { if (order.getBrokerageUserId() == null) { return; } - cancelBrokerage(order.getBrokerageUserId(), orderItem.getOrderId()); + cancelBrokerage(order.getBrokerageUserId(), orderItem.getId()); } /** From 90f82b157de162ecf07cb904ce2fe632d6bdb6ea Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 30 Mar 2024 09:22:54 +0800 Subject: [PATCH 46/81] =?UTF-8?q?CRM=EF=BC=9A=E4=BC=98=E5=8C=96=E3=80=90?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=BB=9F=E8=AE=A1=E3=80=91=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/enums/DateIntervalEnum.java | 28 +- .../framework/common/util/date/DateUtils.java | 39 -- .../common/util/date/LocalDateTimeUtils.java | 144 +++++- .../module/crm/enums/ErrorCodeConstants.java | 1 - .../CrmStatisticsCustomerController.http | 2 +- .../CrmStatisticsCustomerController.java | 18 +- ...CrmStatisticsCustomerByUserBaseRespVO.java | 2 - ...atisticsCustomerContractSummaryRespVO.java | 31 +- ...atisticsCustomerDealCycleByDateRespVO.java | 2 +- ...atisticsCustomerDealCycleByUserRespVO.java | 4 +- .../customer/CrmStatisticsCustomerReqVO.java | 7 +- ...StatisticsCustomerSummaryByDateRespVO.java | 4 +- ...StatisticsCustomerSummaryByUserRespVO.java | 8 +- ...tatisticsFollowUpSummaryByDateRespVO.java} | 6 +- ...tatisticsFollowUpSummaryByTypeRespVO.java} | 6 +- ...tatisticsFollowUpSummaryByUserRespVO.java} | 6 +- .../vo/rank/CrmStatisticsRankRespVO.java | 1 + .../CrmStatisticsCustomerMapper.java | 24 +- .../CrmStatisticsCustomerService.java | 8 +- .../CrmStatisticsCustomerServiceImpl.java | 454 +++++------------- .../CrmStatisticsCustomerMapper.xml | 288 +++++------ 21 files changed, 483 insertions(+), 600 deletions(-) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/{CrmStatisticsFollowupSummaryByDateRespVO.java => CrmStatisticsFollowUpSummaryByDateRespVO.java} (79%) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/{CrmStatisticsFollowupSummaryByTypeRespVO.java => CrmStatisticsFollowUpSummaryByTypeRespVO.java} (76%) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/{CrmStatisticsFollowupSummaryByUserRespVO.java => CrmStatisticsFollowUpSummaryByUserRespVO.java} (75%) diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java index 9a614ddc9..498b67124 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.common.enums; +import cn.hutool.core.util.ArrayUtil; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; @@ -7,7 +8,7 @@ import lombok.Getter; import java.util.Arrays; /** - * 时间间隔类型枚举 + * 时间间隔的枚举 * * @author dhb52 */ @@ -15,26 +16,19 @@ import java.util.Arrays; @AllArgsConstructor public enum DateIntervalEnum implements IntArrayValuable { - TODAY(1, "今天"), - YESTERDAY(2, "昨天"), - THIS_WEEK(3, "本周"), - LAST_WEEK(4, "上周"), - THIS_MONTH(5, "本月"), - LAST_MONTH(6, "上月"), - THIS_QUARTER(7, "本季度"), - LAST_QUARTER(8, "上季度"), - THIS_YEAR(9, "本年"), - LAST_YEAR(10, "去年"), - CUSTOMER(11, "自定义"), + DAY(1, "天"), + WEEK(2, "周"), + MONTH(3, "月"), + QUARTER(4, "季度"), + YEAR(5, "年") ; - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getType).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray(); /** * 类型 */ - private final Integer type; - + private final Integer interval; /** * 名称 */ @@ -45,4 +39,8 @@ public enum DateIntervalEnum implements IntArrayValuable { return ARRAYS; } + public static DateIntervalEnum valueOf(Integer interval) { + return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values()); + } + } \ No newline at end of file diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java index 60b3f1e1e..b51a838c6 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java @@ -65,19 +65,11 @@ public class DateUtils { return new Date(System.currentTimeMillis() + duration.toMillis()); } - public static boolean isExpired(Date time) { - return System.currentTimeMillis() > time.getTime(); - } - public static boolean isExpired(LocalDateTime time) { LocalDateTime now = LocalDateTime.now(); return now.isAfter(time); } - public static long diff(Date endTime, Date startTime) { - return endTime.getTime() - startTime.getTime(); - } - /** * 创建指定时间 * @@ -134,37 +126,6 @@ public class DateUtils { return a.isAfter(b) ? a : b; } - /** - * 计算当期时间相差的日期 - * - * @param field 日历字段.
eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,
Calendar.HOUR_OF_DAY等. - * @param amount 相差的数值 - * @return 计算后的日志 - */ - public static Date addDate(int field, int amount) { - return addDate(null, field, amount); - } - - /** - * 计算当期时间相差的日期 - * - * @param date 设置时间 - * @param field 日历字段 例如说,{@link Calendar#DAY_OF_MONTH} 等 - * @param amount 相差的数值 - * @return 计算后的日志 - */ - public static Date addDate(Date date, int field, int amount) { - if (amount == 0) { - return date; - } - Calendar c = Calendar.getInstance(); - if (date != null) { - c.setTime(date); - } - c.add(field, amount); - return c.getTime(); - } - /** * 是否今天 * diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java index 87c20798e..cf978d81a 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java @@ -1,13 +1,18 @@ package cn.iocoder.yudao.framework.common.util.date; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; -import java.time.Duration; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; +import java.time.*; +import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAdjusters; +import java.util.ArrayList; +import java.util.List; /** * 时间工具类,用于 {@link java.time.LocalDateTime} @@ -21,6 +26,22 @@ public class LocalDateTimeUtils { */ public static LocalDateTime EMPTY = buildTime(1970, 1, 1); + /** + * 解析时间 + * + * 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说,会尽量去解析,直到成功 + * + * @param time 时间 + * @return 时间字符串 + */ + public static LocalDateTime parse(String time) { + try { + return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN); + } catch (DateTimeParseException e) { + return LocalDateTimeUtil.parse(time); + } + } + public static LocalDateTime addTime(Duration duration) { return LocalDateTime.now().plus(duration); } @@ -54,6 +75,21 @@ public class LocalDateTimeUtils { return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)}; } + /** + * 判指定断时间,是否在该时间范围内 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param time 指定时间 + * @return 是否 + */ + public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) { + if (startTime == null || endTime == null || time == null) { + return false; + } + return LocalDateTimeUtil.isIn(parse(time), startTime, endTime); + } + /** * 判断当前时间是否在该时间范围内 * @@ -122,6 +158,16 @@ public class LocalDateTimeUtils { return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX); } + /** + * 获得指定日期所在季度 + * + * @param date 日期 + * @return 所在季度 + */ + public static int getQuarterOfYear(LocalDateTime date) { + return (date.getMonthValue() - 1) / 3 + 1; + } + /** * 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负 * @@ -168,4 +214,94 @@ public class LocalDateTimeUtils { return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN); } + public static List getDateRangeList(LocalDateTime startTime, + LocalDateTime endTime, + Integer interval) { + // 1.1 找到枚举 + DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval); + Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval); + // 1.2 将时间对齐 + startTime = LocalDateTimeUtil.beginOfDay(startTime); + endTime = LocalDateTimeUtil.endOfDay(endTime); + + // 2. 循环,生成时间范围 + List timeRanges = new ArrayList<>(); + switch (intervalEnum) { + case DateIntervalEnum.DAY: + while (startTime.isBefore(endTime)) { + timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)}); + startTime = startTime.plusDays(1); + } + break; + case DateIntervalEnum.WEEK: + while (startTime.isBefore(endTime)) { + LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1); + timeRanges.add(new LocalDateTime[]{startTime, endOfWeek}); + startTime = endOfWeek.plusNanos(1); + } + break; + case DateIntervalEnum.MONTH: + while (startTime.isBefore(endTime)) { + LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1); + timeRanges.add(new LocalDateTime[]{startTime, endOfMonth}); + startTime = endOfMonth.plusNanos(1); + } + break; + case DateIntervalEnum.QUARTER: + while (startTime.isBefore(endTime)) { + LocalDateTime quarterEnd = startTime.withMonth(getQuarterOfYear(startTime) * 3 + 1) + .withDayOfMonth(1).minusNanos(1); + timeRanges.add(new LocalDateTime[]{startTime, quarterEnd}); + startTime = quarterEnd.plusNanos(1); + } + break; + case DateIntervalEnum.YEAR: + while (startTime.isBefore(endTime)) { + LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1); + timeRanges.add(new LocalDateTime[]{startTime, endOfYear}); + startTime = endOfYear.plusNanos(1); + } + break; + default: + throw new IllegalArgumentException("Invalid interval: " + interval); + } + // 3. 兜底,最后一个时间,需要保持在 endTime 之前 + LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges); + if (lastTimeRange != null) { + lastTimeRange[1] = endTime; + } + return timeRanges; + } + + /** + * 格式化时间范围 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param interval 时间间隔 + * @return 时间范围 + */ + public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) { + // 1. 找到枚举 + DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval); + Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval); + + // 2. 循环,生成时间范围 + switch (intervalEnum) { + case DateIntervalEnum.DAY: + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN); + case DateIntervalEnum.WEEK: + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN) + + StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime)); + case DateIntervalEnum.MONTH: + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN); + case DateIntervalEnum.QUARTER: + return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime)); + case DateIntervalEnum.YEAR: + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN); + default: + throw new IllegalArgumentException("Invalid interval: " + interval); + } + } + } diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java index d49f6068c..27fb6f9ce 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java @@ -104,6 +104,5 @@ public interface ErrorCodeConstants { ErrorCode FOLLOW_UP_RECORD_DELETE_DENIED = new ErrorCode(1_020_013_001, "删除跟进记录失败,原因:没有权限"); // ========== 数据统计 1_020_014_000 ========== - ErrorCode STATISTICS_CUSTOMER_TIMES_NOT_SET = new ErrorCode(1_020_014_000, "自定义时间间隔,必须输入时间区间"); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http index 9d96e159a..b5d35f5c4 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http @@ -1,6 +1,6 @@ # == 1. 客户总量分析 == ### 1.1 客户总量分析(按日) -GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&intervalType=11×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 +GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&interval=1×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index 4a0b2e760..7d45ae217 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -40,25 +40,25 @@ public class CrmStatisticsCustomerController { return success(customerService.getCustomerSummaryByUser(reqVO)); } - @GetMapping("/get-followup-summary-by-date") + @GetMapping("/get-follow-up-summary-by-date") @Operation(summary = "获取客户跟进次数分析(按日期)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getFollowupSummaryByDate(reqVO)); + public CommonResult> getFollowupSummaryByDate(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getFollowUpSummaryByDate(reqVO)); } - @GetMapping("/get-followup-summary-by-user") + @GetMapping("/get-follow-up-summary-by-user") @Operation(summary = "获取客户跟进次数分析(按用户)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getFollowupSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getFollowupSummaryByUser(reqVO)); + public CommonResult> getFollowUpSummaryByUser(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getFollowUpSummaryByUser(reqVO)); } - @GetMapping("/get-followup-summary-by-type") + @GetMapping("/get-follow-up-summary-by-type") @Operation(summary = "获取客户跟进次数分析(按类型)") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getFollowupSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getFollowupSummaryByType(reqVO)); + public CommonResult> getFollowUpSummaryByType(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getFollowUpSummaryByType(reqVO)); } @GetMapping("/get-contract-summary") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java index 41fa9152e..bde0029c6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerByUserBaseRespVO.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; -import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -13,7 +12,6 @@ import lombok.Data; public class CrmStatisticsCustomerByUserBaseRespVO { @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @JsonIgnore private Long ownerUserId; @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java index ec1d27303..fa03d4609 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerContractSummaryRespVO.java @@ -1,18 +1,12 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.math.BigDecimal; -import java.time.LocalDate; import java.time.LocalDateTime; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; - @Schema(description = "管理后台 - CRM 客户转化率分析 VO") @Data public class CrmStatisticsCustomerContractSummaryRespVO { @@ -29,31 +23,19 @@ public class CrmStatisticsCustomerContractSummaryRespVO { @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200.00") private BigDecimal receivablePrice; - @Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @JsonIgnore - private String industryId; + @Schema(description = "客户行业编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer industryId; - @Schema(description = "客户行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "金融") - private String industryName; - - @Schema(description = "客户来源ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @JsonIgnore - private String source; - - @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "外呼") - private String sourceName; + @Schema(description = "客户来源编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer source; @Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @JsonIgnore private Long ownerUserId; - @Schema(description = "负责人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") private String ownerUserName; @Schema(description = "创建人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @JsonIgnore - private String creatorUserId; - + private String creator; @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED, example = "源码") private String creatorUserName; @@ -61,7 +43,6 @@ public class CrmStatisticsCustomerContractSummaryRespVO { private LocalDateTime createTime; @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02 00:00:00") - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT) - private LocalDate orderDate; + private LocalDateTime orderDate; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java index 44b2526e8..62facb053 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByDateRespVO.java @@ -11,6 +11,6 @@ public class CrmStatisticsCustomerDealCycleByDateRespVO { private String time; @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") - private Double customerDealCycle = 0.0; + private Double customerDealCycle; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java index 6c8137983..1c394b8b4 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerDealCycleByUserRespVO.java @@ -8,9 +8,9 @@ import lombok.Data; public class CrmStatisticsCustomerDealCycleByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") - private Double customerDealCycle = 0.0; + private Double customerDealCycle; @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer customerDealCount = 0; + private Integer customerDealCount; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java index 38f15cf03..0564c2679 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java @@ -30,12 +30,12 @@ public class CrmStatisticsCustomerReqVO { * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 * 后续,可能会支持选择部分用户进行查询 */ - @Schema(description = "负责人用户 id 集合", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2") + @Schema(description = "负责人用户 id 集合", hidden = true, example = "2") private List userIds; @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") - private Integer intervalType; + private Integer interval; /** * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间 @@ -49,7 +49,8 @@ public class CrmStatisticsCustomerReqVO { * group by DATE_FORMAT(field, #{dateFormat}) * 非前端传递, 由Service计算后传递给Mapper的参数 */ - @Schema(description = "Group By 日期格式", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "%Y%m") + @Deprecated + @Schema(description = "Group By 日期格式", hidden = true, example = "%Y%m") private String sqlDateFormat; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java index 866a58f1e..7ffcb20ff 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByDateRespVO.java @@ -11,9 +11,9 @@ public class CrmStatisticsCustomerSummaryByDateRespVO { private String time; @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer customerCreateCount = 0; + private Integer customerCreateCount; @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer customerDealCount = 0; + private Integer customerDealCount; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java index bd719a1b8..fa8372b8b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerSummaryByUserRespVO.java @@ -10,15 +10,15 @@ import java.math.BigDecimal; public class CrmStatisticsCustomerSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { @Schema(description = "新建客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer customerCreateCount = 0; + private Integer customerCreateCount; @Schema(description = "成交客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer customerDealCount = 0; + private Integer customerDealCount; @Schema(description = "合同总金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") - private BigDecimal contractPrice = BigDecimal.ZERO; + private BigDecimal contractPrice; @Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") - private BigDecimal receivablePrice = BigDecimal.ZERO; + private BigDecimal receivablePrice; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByDateRespVO.java similarity index 79% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByDateRespVO.java index 6bd6f1d4d..9040c1eab 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByDateRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByDateRespVO.java @@ -5,15 +5,15 @@ import lombok.Data; @Schema(description = "管理后台 - CRM 跟进次数分析(按日期) VO") @Data -public class CrmStatisticsFollowupSummaryByDateRespVO { +public class CrmStatisticsFollowUpSummaryByDateRespVO { @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") private String time; @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer followupRecordCount = 0; + private Integer followUpRecordCount; @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer followupCustomerCount = 0; + private Integer followUpCustomerCount; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByTypeRespVO.java similarity index 76% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByTypeRespVO.java index c722305a5..d39f1cc0d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByTypeRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByTypeRespVO.java @@ -6,12 +6,12 @@ import lombok.Data; @Schema(description = "管理后台 - CRM 跟进次数分析(按类型) VO") @Data -public class CrmStatisticsFollowupSummaryByTypeRespVO { +public class CrmStatisticsFollowUpSummaryByTypeRespVO { @Schema(description = "跟进类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private String followupType; + private Integer followUpType; @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer followupRecordCount = 0; + private Integer followUpRecordCount; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByUserRespVO.java similarity index 75% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByUserRespVO.java index 5cd610c36..065135626 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowupSummaryByUserRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsFollowUpSummaryByUserRespVO.java @@ -5,12 +5,12 @@ import lombok.Data; @Schema(description = "管理后台 - CRM 跟进次数分析(按用户) VO") @Data -public class CrmStatisticsFollowupSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { +public class CrmStatisticsFollowUpSummaryByUserRespVO extends CrmStatisticsCustomerByUserBaseRespVO { @Schema(description = "跟进次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer followupRecordCount = 0; + private Integer followUpRecordCount; @Schema(description = "跟进客户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer followupCustomerCount = 0; + private Integer followUpCustomerCount; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java index 101cd8bcc..261aa893e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java @@ -17,6 +17,7 @@ public class CrmStatisticsRankRespVO { @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private String deptName; + // TODO @芋艿:需要改下,金额是 bigdecimal /** * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义 *

diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 7c8ed7e93..6b535d4c8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -13,32 +13,32 @@ import java.util.List; @Mapper public interface CrmStatisticsCustomerMapper { - List selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review + List selectCustomerCreateCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); - List selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); // 已经 review + List selectCustomerDealCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); - List selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); // 已经 review + List selectCustomerCreateCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); - List selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review + List selectCustomerDealCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); - List selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review + List selectContractPriceGroupByUser(CrmStatisticsCustomerReqVO reqVO); - List selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO crmStatisticsCustomerReqVO); // 已经 review + List selectReceivablePriceGroupByUser(CrmStatisticsCustomerReqVO reqVO); - List selectFollowupRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); + List selectFollowUpRecordCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); - List selectFollowupCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); + List selectFollowUpCustomerCountGroupByDate(CrmStatisticsCustomerReqVO reqVO); - List selectFollowupRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); + List selectFollowUpRecordCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); - List selectFollowupCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); + List selectFollowUpCustomerCountGroupByUser(CrmStatisticsCustomerReqVO reqVO); List selectContractSummary(CrmStatisticsCustomerReqVO reqVO); - List selectFollowupRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO); + List selectFollowUpRecordCountGroupByType(CrmStatisticsCustomerReqVO reqVO); List selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO); - List selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); // TODO } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java index 546124701..06873e4a1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -27,14 +27,13 @@ public interface CrmStatisticsCustomerService { */ List getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO); - /** * 跟进次数分析(按日期) * * @param reqVO 请求参数 * @return 统计数据 */ - List getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO); + List getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO); /** * 跟进次数分析(按用户) @@ -42,7 +41,7 @@ public interface CrmStatisticsCustomerService { * @param reqVO 请求参数 * @return 统计数据 */ - List getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO); + List getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO); /** * 客户跟进次数分析(按类型) @@ -50,8 +49,7 @@ public interface CrmStatisticsCustomerService { * @param reqVO 请求参数 * @return 统计数据 */ - List getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO); - + List getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO); /** * 获取合同摘要信息(客户转化率页面) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index f951e25f5..250560ef2 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -1,19 +1,13 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.DateTime; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; -import cn.iocoder.yudao.module.system.api.dict.DictDataApi; -import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; @@ -27,10 +21,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CUSTOMER_TIMES_NOT_SET; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; /** * CRM 客户分析 Service 实现类 @@ -41,13 +33,6 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.STATISTICS_CU @Validated public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerService { - private static final String SQL_DATE_FORMAT_BY_MONTH = "%Y%m"; - private static final String SQL_DATE_FORMAT_BY_DAY = "%Y%m%d"; - - private static final String TIME_FORMAT_BY_MONTH = "yyyyMM"; - private static final String TIME_FORMAT_BY_DAY = "yyyyMMdd"; - - @Resource private CrmStatisticsCustomerMapper customerMapper; @@ -55,283 +40,211 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; - @Resource - private DictDataApi dictDataApi; @Override public List getCustomerSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { + reqVO.setUserIds(getUserIds(reqVO)); + if (CollUtil.isEmpty(reqVO.getUserIds())) { return Collections.emptyList(); } - reqVO.setUserIds(userIds); - // 2. 获取分项统计数据 - initParams(reqVO); - List customerCreateCountVoList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO); - List customerDealCountVoList = customerMapper.selectCustomerDealCountGroupByDate(reqVO); + // 2. 按天统计,获取分项统计数据 + List customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByDate(reqVO); + List customerDealCountList = customerMapper.selectCustomerDealCountGroupByDate(reqVO); - // 3. 合并数据 - List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); - Map customerCreateCountMap = convertMap(customerCreateCountVoList, - CrmStatisticsCustomerSummaryByDateRespVO::getTime, - CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount); - Map customerDealCountMap = convertMap(customerDealCountVoList, - CrmStatisticsCustomerSummaryByDateRespVO::getTime, - CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount); - List respVoList = convertList(times, - time -> new CrmStatisticsCustomerSummaryByDateRespVO() - .setTime(time) - .setCustomerCreateCount(customerCreateCountMap.getOrDefault(time, 0)) - .setCustomerDealCount(customerDealCountMap.getOrDefault(time, 0))); - - return respVoList; + // 3. 按照日期间隔,合并数据 + List timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); + return convertList(timeRanges, times -> { + Integer customerCreateCount = customerCreateCountList.stream() + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) + .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerCreateCount).sum(); + Integer customerDealCount = customerDealCountList.stream() + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) + .mapToInt(CrmStatisticsCustomerSummaryByDateRespVO::getCustomerDealCount).sum(); + return new CrmStatisticsCustomerSummaryByDateRespVO() + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) + .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount); + }); } @Override public List getCustomerSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { + reqVO.setUserIds(getUserIds(reqVO)); + if (CollUtil.isEmpty(reqVO.getUserIds())) { return Collections.emptyList(); } - reqVO.setUserIds(userIds); - // 2. 获取分项统计数据 - initParams(reqVO); - List customerCreateCount = customerMapper.selectCustomerCreateCountGroupByUser(reqVO); - List customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO); - List contractPrice = customerMapper.selectContractPriceGroupByUser(reqVO); - List receivablePrice = customerMapper.selectReceivablePriceGroupByUser(reqVO); + // 2. 按用户统计,获取分项统计数据 + List customerCreateCountList = customerMapper.selectCustomerCreateCountGroupByUser(reqVO); + List customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO); + List contractPriceList = customerMapper.selectContractPriceGroupByUser(reqVO); + List receivablePriceList = customerMapper.selectReceivablePriceGroupByUser(reqVO); - // 3. 合并统计数据 - Map customerCreateCountMap = convertMap(customerCreateCount, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount); - Map customerDealCountMap = convertMap(customerDealCount, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); - Map contractPriceMap = convertMap(contractPrice, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getContractPrice); - Map receivablePriceMap = convertMap(receivablePrice, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getReceivablePrice); - List respVoList = convertList(userIds, userId -> { - CrmStatisticsCustomerSummaryByUserRespVO vo = new CrmStatisticsCustomerSummaryByUserRespVO(); - // ownerUserId 为基类属性 - vo.setOwnerUserId(userId); - vo.setCustomerCreateCount(customerCreateCountMap.getOrDefault(userId, 0)) - .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)) - .setContractPrice(contractPriceMap.getOrDefault(userId, BigDecimal.ZERO)) - .setReceivablePrice(receivablePriceMap.getOrDefault(userId, BigDecimal.ZERO)); - return vo; + // 3.1 按照用户,合并统计数据 + List summaryList = convertList(reqVO.getUserIds(), userId -> { + Integer customerCreateCount = customerCreateCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) + .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerCreateCount).sum(); + Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) + .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum(); + BigDecimal contractPrice = contractPriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) + .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getContractPrice()), BigDecimal::add); + BigDecimal receivablePrice = receivablePriceList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) + .reduce(BigDecimal.ZERO, (sum, vo) -> sum.add(vo.getReceivablePrice()), BigDecimal::add); + return (CrmStatisticsCustomerSummaryByUserRespVO) new CrmStatisticsCustomerSummaryByUserRespVO() + .setCustomerCreateCount(customerCreateCount).setCustomerDealCount(customerDealCount) + .setContractPrice(contractPrice).setReceivablePrice(receivablePrice).setOwnerUserId(userId); }); - - // 4. 拼接用户信息 - appendUserInfo(respVoList); - - return respVoList; + // 3.2 拼接用户信息 + appendUserInfo(summaryList); + return summaryList; } @Override - public List getFollowupSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { + public List getFollowUpSummaryByDate(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { + reqVO.setUserIds(getUserIds(reqVO)); + if (CollUtil.isEmpty(reqVO.getUserIds())) { return Collections.emptyList(); } - reqVO.setUserIds(userIds); - // 2. 获取分项统计数据 - initParams(reqVO); - List followupRecordCount = customerMapper.selectFollowupRecordCountGroupByDate(reqVO); - List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByDate(reqVO); + // 2. 按天统计,获取分项统计数据 + List followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByDate(reqVO); + List followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByDate(reqVO); - // 3. 合并统计数据 - List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); - Map followupRecordCountMap = convertMap(followupRecordCount, - CrmStatisticsFollowupSummaryByDateRespVO::getTime, - CrmStatisticsFollowupSummaryByDateRespVO::getFollowupRecordCount); - Map followupCustomerCountMap = convertMap(followupCustomerCount, - CrmStatisticsFollowupSummaryByDateRespVO::getTime, - CrmStatisticsFollowupSummaryByDateRespVO::getFollowupCustomerCount); - List respVoList = convertList(times, time -> - new CrmStatisticsFollowupSummaryByDateRespVO().setTime(time) - .setFollowupRecordCount(followupRecordCountMap.getOrDefault(time, 0)) - .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(time, 0)) - ); - - return respVoList; + // 3. 按照时间间隔,合并统计数据 + List timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); + return convertList(timeRanges, times -> { + Integer followUpRecordCount = followUpRecordCountList.stream() + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) + .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpRecordCount).sum(); + Integer followUpCustomerCount = followUpCustomerCountList.stream() + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) + .mapToInt(CrmStatisticsFollowUpSummaryByDateRespVO::getFollowUpCustomerCount).sum(); + return new CrmStatisticsFollowUpSummaryByDateRespVO() + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) + .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount); + }); } @Override - public List getFollowupSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { + public List getFollowUpSummaryByUser(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { + reqVO.setUserIds(getUserIds(reqVO)); + if (CollUtil.isEmpty(reqVO.getUserIds())) { return Collections.emptyList(); } - reqVO.setUserIds(userIds); - // 2. 获取分项统计数据 - initParams(reqVO); - List followupRecordCount = customerMapper.selectFollowupRecordCountGroupByUser(reqVO); - List followupCustomerCount = customerMapper.selectFollowupCustomerCountGroupByUser(reqVO); + // 2. 按用户统计,获取分项统计数据 + List followUpRecordCountList = customerMapper.selectFollowUpRecordCountGroupByUser(reqVO); + List followUpCustomerCountList = customerMapper.selectFollowUpCustomerCountGroupByUser(reqVO); - // 3. 合并统计数据 - Map followupRecordCountMap = convertMap(followupRecordCount, - CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsFollowupSummaryByUserRespVO::getFollowupRecordCount); - Map followupCustomerCountMap = convertMap(followupCustomerCount, - CrmStatisticsFollowupSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsFollowupSummaryByUserRespVO::getFollowupCustomerCount); - List respVoList = convertList(userIds, userId -> { - CrmStatisticsFollowupSummaryByUserRespVO vo = new CrmStatisticsFollowupSummaryByUserRespVO() - .setFollowupRecordCount(followupRecordCountMap.getOrDefault(userId, 0)) - .setFollowupCustomerCount(followupCustomerCountMap.getOrDefault(userId, 0)); - // ownerUserId 为基类属性 - vo.setOwnerUserId(userId); - return vo; + // 3.1 按照用户,合并统计数据 + List summaryList = convertList(reqVO.getUserIds(), userId -> { + Integer followUpRecordCount = followUpRecordCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) + .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpRecordCount).sum(); + Integer followUpCustomerCount = followUpCustomerCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) + .mapToInt(CrmStatisticsFollowUpSummaryByUserRespVO::getFollowUpCustomerCount).sum(); + return (CrmStatisticsFollowUpSummaryByUserRespVO) new CrmStatisticsFollowUpSummaryByUserRespVO() + .setFollowUpCustomerCount(followUpRecordCount).setFollowUpRecordCount(followUpCustomerCount).setOwnerUserId(userId); }); - - // 4. 拼接用户信息 - appendUserInfo(respVoList); - return respVoList; + // 3.2 拼接用户信息 + appendUserInfo(summaryList); + return summaryList; } @Override - public List getFollowupSummaryByType(CrmStatisticsCustomerReqVO reqVO) { + public List getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { + reqVO.setUserIds(getUserIds(reqVO)); + if (CollUtil.isEmpty(reqVO.getUserIds())) { return Collections.emptyList(); } - reqVO.setUserIds(userIds); - // 2. 获得排行数据 - initParams(reqVO); - List respVoList = customerMapper.selectFollowupRecordCountGroupByType(reqVO); - - // 3. 获取字典数据 - List followUpTypes = dictDataApi.getDictDataList(CRM_FOLLOW_UP_TYPE); - Map followUpTypeMap = convertMap(followUpTypes, - DictDataRespDTO::getValue, DictDataRespDTO::getLabel); - respVoList.forEach(vo -> { - vo.setFollowupType(followUpTypeMap.get(vo.getFollowupType())); - }); - - return respVoList; + // 2. 获得跟进数据 + return customerMapper.selectFollowUpRecordCountGroupByType(reqVO); } @Override public List getContractSummary(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { + reqVO.setUserIds(getUserIds(reqVO)); + if (CollUtil.isEmpty(reqVO.getUserIds())) { return Collections.emptyList(); } - reqVO.setUserIds(userIds); - // 2. 获取统计数据 - initParams(reqVO); - List respVoList = customerMapper.selectContractSummary(reqVO); + // 2. 按用户统计,获取统计数据 + List summaryList = customerMapper.selectContractSummary(reqVO); - // 3. 设置 创建人、负责人、行业、来源 - // 3.1 获取客户所属行业 - Map industryMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_INDUSTRY), - DictDataRespDTO::getValue, DictDataRespDTO::getLabel); - // 3.2 获取客户来源 - Map sourceMap = convertMap(dictDataApi.getDictDataList(CRM_CUSTOMER_SOURCE), - DictDataRespDTO::getValue, DictDataRespDTO::getLabel); - // 3.3 获取创建人、负责人列表 - Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(respVoList, - vo -> Stream.of(NumberUtils.parseLong(vo.getCreatorUserId()), vo.getOwnerUserId()))); - // 3.4 设置 创建人、负责人、行业、来源 - respVoList.forEach(vo -> { - MapUtils.findAndThen(industryMap, vo.getIndustryId(), vo::setIndustryName); - MapUtils.findAndThen(sourceMap, vo.getSource(), vo::setSourceName); - MapUtils.findAndThen(userMap, NumberUtils.parseLong(vo.getCreatorUserId()), - user -> vo.setCreatorUserName(user.getNickname())); - MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())); + // 3. 拼接信息 + Map userMap = adminUserApi.getUserMap( + convertSetByFlatMap(summaryList, vo -> Stream.of(NumberUtils.parseLong(vo.getCreator()), vo.getOwnerUserId()))); + summaryList.forEach(vo -> { + findAndThen(userMap, NumberUtils.parseLong(vo.getCreator()), user -> vo.setCreatorUserName(user.getNickname())); + findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())); }); - - return respVoList; + return summaryList; } @Override public List getCustomerDealCycleByDate(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { + reqVO.setUserIds(getUserIds(reqVO)); + if (CollUtil.isEmpty(reqVO.getUserIds())) { return Collections.emptyList(); } - reqVO.setUserIds(userIds); - // 2. 获取分项统计数据 - initParams(reqVO); - List customerDealCycle = customerMapper.selectCustomerDealCycleGroupByDate(reqVO); + // 2. 按天统计,获取分项统计数据 + List customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByDate(reqVO); - // 3. 合并统计数据 - List times = generateTimeSeries(reqVO.getTimes()[0], reqVO.getTimes()[1]); - Map customerDealCycleMap = convertMap(customerDealCycle, - CrmStatisticsCustomerDealCycleByDateRespVO::getTime, - CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle); - List respVoList = convertList(times, time -> - new CrmStatisticsCustomerDealCycleByDateRespVO().setTime(time) - .setCustomerDealCycle(customerDealCycleMap.getOrDefault(time, 0D)) - ); - - return respVoList; + // 3. 按照日期间隔,合并统计数据 + List timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); + return convertList(timeRanges, times -> { + Double customerDealCycle = customerDealCycleList.stream() + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], vo.getTime())) + .mapToDouble(CrmStatisticsCustomerDealCycleByDateRespVO::getCustomerDealCycle).sum(); + return new CrmStatisticsCustomerDealCycleByDateRespVO() + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) + .setCustomerDealCycle(customerDealCycle); + }); } @Override public List getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { + reqVO.setUserIds(getUserIds(reqVO)); + if (CollUtil.isEmpty(reqVO.getUserIds())) { return Collections.emptyList(); } - reqVO.setUserIds(userIds); - // 2. 获取分项统计数据 - initParams(reqVO); - List customerDealCycle = customerMapper.selectCustomerDealCycleGroupByUser(reqVO); - List customerDealCount = customerMapper.selectCustomerDealCountGroupByUser(reqVO); + // 2. 按用户统计,获取分项统计数据 + List customerDealCycleList = customerMapper.selectCustomerDealCycleGroupByUser(reqVO); + List customerDealCountList = customerMapper.selectCustomerDealCountGroupByUser(reqVO); - // 3. 合并统计数据 - Map customerDealCycleMap = convertMap(customerDealCycle, - CrmStatisticsCustomerDealCycleByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle); - Map customerDealCountMap = convertMap(customerDealCount, - CrmStatisticsCustomerSummaryByUserRespVO::getOwnerUserId, - CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount); - List respVoList = convertList(userIds, userId -> { - CrmStatisticsCustomerDealCycleByUserRespVO vo = new CrmStatisticsCustomerDealCycleByUserRespVO() - .setCustomerDealCycle(customerDealCycleMap.getOrDefault(userId, 0.0)) - .setCustomerDealCount(customerDealCountMap.getOrDefault(userId, 0)); - // ownerUserId 为基类属性 - vo.setOwnerUserId(userId); - return vo; + // 3.1 按照用户,合并统计数据 + List summaryList = convertList(reqVO.getUserIds(), userId -> { + Double customerDealCycle = customerDealCycleList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) + .mapToDouble(CrmStatisticsCustomerDealCycleByUserRespVO::getCustomerDealCycle).sum(); + Integer customerDealCount = customerDealCountList.stream().filter(vo -> userId.equals(vo.getOwnerUserId())) + .mapToInt(CrmStatisticsCustomerSummaryByUserRespVO::getCustomerDealCount).sum(); + return (CrmStatisticsCustomerDealCycleByUserRespVO) new CrmStatisticsCustomerDealCycleByUserRespVO() + .setCustomerDealCycle(customerDealCycle).setCustomerDealCount(customerDealCount).setOwnerUserId(userId); }); - - // 4. 拼接用户信息 - appendUserInfo(respVoList); - - return respVoList; + // 3.2 拼接用户信息 + appendUserInfo(summaryList); + return summaryList; } /** * 拼接用户信息(昵称) * - * @param respVoList 统计数据 + * @param voList 统计数据 */ - private void appendUserInfo(List respVoList) { - Map userMap = adminUserApi.getUserMap(convertSet(respVoList, - CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); - respVoList.forEach(vo -> MapUtils.findAndThen(userMap, - vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); + private void appendUserInfo(List voList) { + Map userMap = adminUserApi.getUserMap( + convertSet(voList, CrmStatisticsCustomerByUserBaseRespVO::getOwnerUserId)); + voList.forEach(vo -> findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()))); } /** @@ -347,113 +260,10 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe } // 情况二:选中某个部门 // 2.1 获得部门列表 - Long deptId = reqVO.getDeptId(); - List deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId); - deptIds.add(deptId); + List deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId); + deptIds.add(reqVO.getDeptId()); // 2.2 获得用户编号 return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); } - - /** - * 判断是否按照 月粒度 统计 - * - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return 是, 按月粒度, 否则按天粒度统计。 - */ - private boolean queryByMonth(LocalDateTime startTime, LocalDateTime endTime) { - return LocalDateTimeUtil.between(startTime, endTime).toDays() > 31; - } - - /** - * 生成时间序列 - * - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return 时间序列 - */ - private List generateTimeSeries(LocalDateTime startTime, LocalDateTime endTime) { - boolean byMonth = queryByMonth(startTime, endTime); - List times = CollUtil.newArrayList(); - while (!startTime.isAfter(endTime)) { - times.add(LocalDateTimeUtil.format(startTime, byMonth ? TIME_FORMAT_BY_MONTH : TIME_FORMAT_BY_DAY)); - if (byMonth) - startTime = startTime.plusMonths(1); - else - startTime = startTime.plusDays(1); - } - - return times; - } - - /** - * 获取 SQL 查询 GROUP BY 的时间格式 - * - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return SQL 查询 GROUP BY 的时间格式 - */ - private String getSqlDateFormat(LocalDateTime startTime, LocalDateTime endTime) { - return queryByMonth(startTime, endTime) ? SQL_DATE_FORMAT_BY_MONTH : SQL_DATE_FORMAT_BY_DAY; - } - - private void initParams(CrmStatisticsCustomerReqVO reqVO) { - final Integer intervalType = reqVO.getIntervalType(); - - // 1. 自定义时间间隔,必须输入起始日期-结束日期 - if (DateIntervalEnum.CUSTOMER.getType().equals(intervalType)) { - if (ObjUtil.isEmpty(reqVO.getTimes()) || reqVO.getTimes().length != 2) { - throw exception(STATISTICS_CUSTOMER_TIMES_NOT_SET); - } - // 设置 mapper sqlDateFormat 参数 - reqVO.setSqlDateFormat(getSqlDateFormat(reqVO.getTimes()[0], reqVO.getTimes()[1])); - // 自定义日期无需计算日期参数 - return; - } - - // 2. 根据时间区间类型计算时间段区间日期 - DateTime beginDate = null; - DateTime endDate = null; - if (DateIntervalEnum.TODAY.getType().equals(intervalType)) { - beginDate = DateUtil.beginOfDay(DateUtil.date()); - endDate = DateUtil.endOfDay(DateUtil.date()); - } else if (DateIntervalEnum.YESTERDAY.getType().equals(intervalType)) { - beginDate = DateUtil.offsetDay(DateUtil.date(), -1); - endDate = DateUtil.offsetDay(DateUtil.date(), -1); - } else if (DateIntervalEnum.THIS_WEEK.getType().equals(intervalType)) { - beginDate = DateUtil.beginOfWeek(DateUtil.date()); - endDate = DateUtil.endOfWeek(DateUtil.date()); - } else if (DateIntervalEnum.LAST_WEEK.getType().equals(intervalType)) { - beginDate = DateUtil.beginOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1)); - endDate = DateUtil.endOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1)); - } else if (DateIntervalEnum.THIS_MONTH.getType().equals(intervalType)) { - beginDate = DateUtil.beginOfMonth(DateUtil.date()); - endDate = DateUtil.endOfMonth(DateUtil.date()); - } else if (DateIntervalEnum.LAST_MONTH.getType().equals(intervalType)) { - beginDate = DateUtil.beginOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1)); - endDate = DateUtil.endOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1)); - } else if (DateIntervalEnum.THIS_QUARTER.getType().equals(intervalType)) { - beginDate = DateUtil.beginOfQuarter(DateUtil.date()); - endDate = DateUtil.endOfQuarter(DateUtil.date()); - } else if (DateIntervalEnum.LAST_QUARTER.getType().equals(intervalType)) { - beginDate = DateUtil.beginOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3)); - endDate = DateUtil.endOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3)); - } else if (DateIntervalEnum.THIS_YEAR.getType().equals(intervalType)) { - beginDate = DateUtil.beginOfYear(DateUtil.date()); - endDate = DateUtil.endOfYear(DateUtil.date()); - } else if (DateIntervalEnum.LAST_YEAR.getType().equals(intervalType)) { - beginDate = DateUtil.beginOfYear(DateUtil.offsetMonth(DateUtil.date(), -12)); - endDate = DateUtil.endOfYear(DateUtil.offsetMonth(DateUtil.date(), -12)); - } - - // 3. 计算开始、结束日期时间,并设置reqVo - LocalDateTime[] times = new LocalDateTime[2]; - times[0] = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.of(beginDate)); - times[1] = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.of(endDate)); - // 3.1 设置 mapper 时间区间 参数 - reqVO.setTimes(times); - // 3.2 设置 mapper sqlDateFormat 参数 - reqVO.setSqlDateFormat(getSqlDateFormat(times[0], times[1])); - } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index 32a1d425e..4e1cee0ea 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -5,31 +5,31 @@ @@ -38,13 +38,13 @@ SELECT owner_user_id, COUNT(1) AS customer_create_count - FROM crm_customer - WHERE deleted = 0 - AND owner_user_id in - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + FROM crm_customer + WHERE deleted = 0 + AND owner_user_id in + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -53,16 +53,16 @@ SELECT customer.owner_user_id, COUNT( DISTINCT customer.id ) AS customer_deal_count - FROM crm_customer AS customer - LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id - WHERE customer.deleted = 0 AND contract.deleted = 0 - AND contract.audit_status = 20 - AND customer.owner_user_id IN - - #{userId} - - AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - GROUP BY customer.owner_user_id + FROM crm_customer AS customer + LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id + WHERE customer.deleted = 0 AND contract.deleted = 0 + AND contract.audit_status = 20 + AND customer.owner_user_id IN + + #{userId} + + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + GROUP BY customer.owner_user_id - SELECT - DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, - COUNT(*) AS followup_record_count - FROM crm_follow_up_record - WHERE creator IN - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} - GROUP BY time + DATE_FORMAT( create_time, '%Y-%m-%d' ) AS time, + COUNT(*) AS follow_up_record_count + FROM crm_follow_up_record + WHERE creator IN + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} + GROUP BY time - SELECT - DATE_FORMAT( create_time, #{sqlDateFormat} ) AS time, - COUNT(DISTINCT biz_id) AS followup_customer_count - FROM crm_follow_up_record - WHERE creator IN - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} - GROUP BY time + DATE_FORMAT( create_time, '%Y-%m-%d' ) AS time, + COUNT(DISTINCT biz_id) AS follow_up_customer_count + FROM crm_follow_up_record + WHERE creator IN + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} + GROUP BY time - SELECT creator as owner_user_id, - COUNT(*) AS followup_record_count - FROM crm_follow_up_record - WHERE creator IN - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} - GROUP BY creator + COUNT(*) AS follow_up_record_count + FROM crm_follow_up_record + WHERE creator IN + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} + GROUP BY creator - SELECT creator as owner_user_id, - COUNT(DISTINCT biz_id) AS followup_customer_count - FROM crm_follow_up_record - WHERE creator IN - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} - GROUP BY creator + COUNT(DISTINCT biz_id) AS follow_up_customer_count + FROM crm_follow_up_record + WHERE creator IN + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} + GROUP BY creator - SELECT - type AS followupType, - COUNT(*) AS followup_record_count - FROM crm_follow_up_record - WHERE creator IN - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} - GROUP BY followupType + type AS follow_up_type, + COUNT(*) AS follow_up_record_count + FROM crm_follow_up_record + WHERE creator IN + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type} + GROUP BY follow_up_type From a8bec19196cd63b135734011b1de13d8b728b6a8 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 30 Mar 2024 11:00:15 +0800 Subject: [PATCH 47/81] =?UTF-8?q?CRM=EF=BC=9Acode=20review=E3=80=90?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=BB=9F=E8=AE=A1=E3=80=91=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsCustomerController.java | 6 ++- .../CrmStatisticsCustomerMapper.java | 2 +- .../CrmStatisticsCustomerService.java | 6 ++- .../CrmStatisticsCustomerMapper.xml | 30 +++++------ .../statistics/CrmStatisticsRankMapper.xml | 54 +++++++++---------- 5 files changed, 51 insertions(+), 47 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index 7d45ae217..450a9cd99 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -61,8 +61,10 @@ public class CrmStatisticsCustomerController { return success(customerService.getFollowUpSummaryByType(reqVO)); } + // TODO @dhb52:【客户转化率】里,应该少了一个接口,给上面图标的; + @GetMapping("/get-contract-summary") - @Operation(summary = "获取合同摘要信息(客户转化率页面)") + @Operation(summary = "获取客户的首次合同、回款信息列表", description = "用于【客户转化率】页面") @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") public CommonResult> getContractSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { return success(customerService.getContractSummary(reqVO)); @@ -82,4 +84,6 @@ public class CrmStatisticsCustomerController { return success(customerService.getCustomerDealCycleByUser(reqVO)); } + // TODO dhb52:【成交周期分析】里,有按照员工(已实现)、地区(未实现)、产品(未实现),需要在看看哈;可以把 CustomerDealCycle 拆成 3 个 tab,员工客户成交周期分析、地区客户成交周期分析、产品客户成交周期分析; + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 6b535d4c8..213a07d29 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -39,6 +39,6 @@ public interface CrmStatisticsCustomerMapper { List selectCustomerDealCycleGroupByDate(CrmStatisticsCustomerReqVO reqVO); - List selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); // TODO + List selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java index 06873e4a1..55317f4ae 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -52,16 +52,18 @@ public interface CrmStatisticsCustomerService { List getFollowUpSummaryByType(CrmStatisticsCustomerReqVO reqVO); /** - * 获取合同摘要信息(客户转化率页面) + * 获取客户的首次合同、回款信息列表,用于【客户转化率】页面 * * @param reqVO 请求参数 - * @return 合同摘要列表 + * @return 客户的首次合同、回款信息列表 */ List getContractSummary(CrmStatisticsCustomerReqVO reqVO); /** * 客户成交周期(按日期) * + * 成交的定义:客户 customer 在创建出来,到合同 contract 第一次成交的时间差 + * * @param reqVO 请求参数 * @return 统计数据 */ diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index 4e1cee0ea..0672d629e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -24,7 +24,7 @@ COUNT( DISTINCT customer_id ) AS customerDealCount FROM crm_contract WHERE deleted = 0 - AND audit_status = 20 + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} AND owner_user_id IN #{userId} @@ -56,7 +56,7 @@ FROM crm_customer AS customer LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id WHERE customer.deleted = 0 AND contract.deleted = 0 - AND contract.audit_status = 20 + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} AND customer.owner_user_id IN #{userId} @@ -72,7 +72,7 @@ IFNULL(SUM(total_price), 0) AS contract_price FROM crm_contract WHERE deleted = 0 - AND audit_status = 20 + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} AND owner_user_id in #{userId} @@ -88,7 +88,7 @@ IFNULL(SUM(price), 0) AS receivable_price FROM crm_receivable WHERE deleted = 0 - AND audit_status = 20 + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} AND owner_user_id IN #{userId} @@ -175,28 +175,24 @@ + @@ -222,11 +219,12 @@ FROM crm_customer AS customer LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id WHERE customer.deleted = 0 AND contract.deleted = 0 - AND contract.audit_status = 20 + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} AND customer.owner_user_id IN #{userId} + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} GROUP BY customer.owner_user_id diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml index c82c55412..ae84e1d47 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml @@ -7,11 +7,11 @@ SELECT IFNULL(SUM(total_price), 0) AS count, owner_user_id FROM crm_contract WHERE deleted = 0 - AND audit_status = 20 + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} and owner_user_id in - - #{userId} - + + #{userId} + AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -22,11 +22,11 @@ SELECT IFNULL(SUM(price), 0) AS count, owner_user_id FROM crm_receivable WHERE deleted = 0 - AND audit_status = 20 + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} AND owner_user_id in - - #{userId} - + + #{userId} + AND return_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -37,11 +37,11 @@ SELECT COUNT(1) AS count, owner_user_id FROM crm_contract WHERE deleted = 0 - AND audit_status = 20 + AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} AND owner_user_id in - - #{userId} - + + #{userId} + AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -55,9 +55,9 @@ WHERE deleted = 0 AND audit_status = 20 AND owner_user_id in - - #{userId} - + + #{userId} + AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -69,9 +69,9 @@ FROM crm_customer WHERE deleted = 0 AND owner_user_id in - - #{userId} - + + #{userId} + AND create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -83,9 +83,9 @@ FROM crm_contact WHERE deleted = 0 AND owner_user_id in - - #{userId} - + + #{userId} + AND create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -99,9 +99,9 @@ WHERE cfur.deleted = 0 AND cc.deleted = 0 AND cc.owner_user_id in - - #{userId} - + + #{userId} + AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY cc.owner_user_id @@ -115,9 +115,9 @@ WHERE cfur.deleted = 0 AND cc.deleted = 0 AND cc.owner_user_id in - - #{userId} - + + #{userId} + AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY cc.owner_user_id From 8266fb8f94f4679e37fdaf3444486aa6d14ca922 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 30 Mar 2024 11:49:06 +0800 Subject: [PATCH 48/81] =?UTF-8?q?CRM=EF=BC=9A=E5=AE=8C=E5=96=84=E3=80=90?= =?UTF-8?q?=E6=8E=92=E8=A1=8C=E7=89=88=E3=80=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/rank/CrmStatisticsRankRespVO.java | 9 +++-- .../statistics/CrmStatisticsRankMapper.xml | 38 ++++++++----------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java index 261aa893e..feb2f3f2e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/rank/CrmStatisticsRankRespVO.java @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.rank; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.math.BigDecimal; + @Schema(description = "管理后台 - CRM 排行榜统计 Response VO") @Data @@ -17,14 +19,15 @@ public class CrmStatisticsRankRespVO { @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private String deptName; - // TODO @芋艿:需要改下,金额是 bigdecimal /** * 数量是个特别“抽象”的概念,在不同排行下,代表不同含义 - *

+ * * 1. 金额:合同金额排行、回款金额排行 * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行 + * + * 为什么使用 BigDecimal 的原因: */ @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer count; + private BigDecimal count; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml index ae84e1d47..abd63f27d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsRankMapper.xml @@ -12,8 +12,7 @@ #{userId} - AND order_date between #{times[0],javaType=java.time.LocalDateTime} and - #{times[1],javaType=java.time.LocalDateTime} + AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -27,8 +26,7 @@ #{userId} - AND return_time between #{times[0],javaType=java.time.LocalDateTime} and - #{times[1],javaType=java.time.LocalDateTime} + AND return_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -42,25 +40,23 @@ #{userId} - AND order_date between #{times[0],javaType=java.time.LocalDateTime} and - #{times[1],javaType=java.time.LocalDateTime} + AND order_date between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id - @@ -86,8 +81,7 @@ #{userId} - AND create_time between #{times[0],javaType=java.time.LocalDateTime} and - #{times[1],javaType=java.time.LocalDateTime} + AND create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY owner_user_id @@ -102,8 +96,7 @@ #{userId} - AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and - #{times[1],javaType=java.time.LocalDateTime} + AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY cc.owner_user_id @@ -118,8 +111,7 @@ #{userId} - AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and - #{times[1],javaType=java.time.LocalDateTime} + AND cfur.create_time between #{times[0],javaType=java.time.LocalDateTime} and #{times[1],javaType=java.time.LocalDateTime} GROUP BY cc.owner_user_id From 924580f4a29db98d7add392e516be8505d0ed61b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 30 Mar 2024 13:51:46 +0800 Subject: [PATCH 49/81] =?UTF-8?q?CRM=EF=BC=9A=E9=87=8D=E6=9E=84=E3=80=90?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=94=BB=E5=83=8F=E3=80=91=EF=BC=8C=E4=BB=8E?= =?UTF-8?q?=E3=80=90=E5=AE=A2=E6=88=B7=E7=BB=9F=E8=AE=A1=E3=80=91=E6=8B=86?= =?UTF-8?q?=E5=88=86=E5=87=BA=E5=8E=BB=E5=85=88~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsCustomerController.java | 32 ---- .../CrmStatisticsPortraitController.java | 61 +++++++ .../CrmStatisticsRankController.java | 1 - .../customer/CrmStatisticsCustomerReqVO.java | 8 - .../CrmStatisticCustomerAreaRespVO.java | 2 +- .../CrmStatisticCustomerIndustryRespVO.java | 2 +- .../CrmStatisticCustomerLevelRespVO.java | 2 +- .../CrmStatisticCustomerSourceRespVO.java | 2 +- .../CrmStatisticsCustomerMapper.java | 14 +- .../CrmStatisticsPortraitMapper.java | 28 +++ .../CrmStatisticsCustomerService.java | 36 ---- .../CrmStatisticsCustomerServiceImpl.java | 107 ----------- .../CrmStatisticsPortraitService.java | 50 ++++++ .../CrmStatisticsPortraitServiceImpl.java | 166 ++++++++++++++++++ .../CrmStatisticsCustomerMapper.xml | 85 --------- .../CrmStatisticsPortraitMapper.xml | 90 ++++++++++ 16 files changed, 400 insertions(+), 286 deletions(-) create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/{customer/analyze => portrait}/CrmStatisticCustomerAreaRespVO.java (98%) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/{customer/analyze => portrait}/CrmStatisticCustomerIndustryRespVO.java (98%) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/{customer/analyze => portrait}/CrmStatisticCustomerLevelRespVO.java (98%) rename yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/{customer/analyze => portrait}/CrmStatisticCustomerSourceRespVO.java (98%) create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index 413bae8f8..450a9cd99 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -2,10 +2,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -90,32 +86,4 @@ public class CrmStatisticsCustomerController { // TODO dhb52:【成交周期分析】里,有按照员工(已实现)、地区(未实现)、产品(未实现),需要在看看哈;可以把 CustomerDealCycle 拆成 3 个 tab,员工客户成交周期分析、地区客户成交周期分析、产品客户成交周期分析; - @GetMapping("/get-customer-industry-summary") - @Operation(summary = "获取客户行业统计数据") - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getCustomerIndustry(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getCustomerIndustry(reqVO)); - } - - @GetMapping("/get-customer-source-summary") - @Operation(summary = "获取客户来源统计数据") - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getCustomerSource(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getCustomerSource(reqVO)); - } - - @GetMapping("/get-customer-level-summary") - @Operation(summary = "获取客户级别统计数据") - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getCustomerLevel(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getCustomerLevel(reqVO)); - } - - @GetMapping("/get-customer-area-summary") - @Operation(summary = "获取客户地区统计数据") - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") - public CommonResult> getCustomerArea(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(customerService.getCustomerArea(reqVO)); - } - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java new file mode 100644 index 000000000..90ef26688 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO; +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPortraitService; +import io.swagger.v3.oas.annotations.Operation; +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.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - CRM 客户画像") +@RestController +@RequestMapping("/crm/statistics-portrait") +@Validated +public class CrmStatisticsPortraitController { + + @Resource + private CrmStatisticsPortraitService statisticsPortraitService; + + @GetMapping("/get-customer-area-summary") + @Operation(summary = "获取客户地区统计数据", description = "用于【城市分布分析】页面") + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") + public CommonResult> getCustomerAreaSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(statisticsPortraitService.getCustomerArea(reqVO)); + } + + @GetMapping("/get-customer-industry-summary") + @Operation(summary = "获取客户行业统计数据") + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") + public CommonResult> getCustomerIndustry(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(statisticsPortraitService.getCustomerIndustry(reqVO)); + } + + @GetMapping("/get-customer-level-summary") + @Operation(summary = "获取客户级别统计数据") + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") + public CommonResult> getCustomerLevel(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(statisticsPortraitService.getCustomerLevel(reqVO)); + } + + @GetMapping("/get-customer-source-summary") + @Operation(summary = "获取客户来源统计数据") + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") + public CommonResult> getCustomerSource(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(statisticsPortraitService.getCustomerSource(reqVO)); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java index f3757ea28..974963d33 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java @@ -18,7 +18,6 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - @Tag(name = "管理后台 - CRM 排行榜统计") @RestController @RequestMapping("/crm/statistics-rank") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java index 0564c2679..5c887c6af 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java @@ -45,12 +45,4 @@ public class CrmStatisticsCustomerReqVO { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] times; - /** - * group by DATE_FORMAT(field, #{dateFormat}) - * 非前端传递, 由Service计算后传递给Mapper的参数 - */ - @Deprecated - @Schema(description = "Group By 日期格式", hidden = true, example = "%Y%m") - private String sqlDateFormat; - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerAreaRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java similarity index 98% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerAreaRespVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java index 5f6924d58..05220d47c 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerAreaRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerIndustryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java similarity index 98% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerIndustryRespVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java index 481174092..8d368a306 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerIndustryRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerLevelRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java similarity index 98% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerLevelRespVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java index 74e06918c..5d6e9793d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerLevelRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerSourceRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java similarity index 98% rename from yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerSourceRespVO.java rename to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java index 2edba39ed..60b938139 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerSourceRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index f2e9dc9f3..b7f495b61 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -1,16 +1,12 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; import org.apache.ibatis.annotations.Mapper; import java.util.List; /** - * CRM 数据统计 员工客户分析 Mapper + * CRM 数据统计 Mapper * * @author dhb52 */ @@ -45,12 +41,4 @@ public interface CrmStatisticsCustomerMapper { List selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); - List selectCustomerIndustryListGroupbyIndustryId(CrmStatisticsCustomerReqVO reqVO); - - List selectCustomerSourceListGroupbySource(CrmStatisticsCustomerReqVO reqVO); - - List selectCustomerLevelListGroupbyLevel(CrmStatisticsCustomerReqVO reqVO); - - List selectSummaryListByAreaId(CrmStatisticsCustomerReqVO reqVO); - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java new file mode 100644 index 000000000..25b7cb2d2 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.crm.dal.mysql.statistics; + +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * CRM 数据画像 Mapper + * + * @author dhb52 + */ +@Mapper +public interface CrmStatisticsPortraitMapper { + + List selectCustomerIndustryListGroupbyIndustryId(CrmStatisticsCustomerReqVO reqVO); + + List selectCustomerSourceListGroupbySource(CrmStatisticsCustomerReqVO reqVO); + + List selectCustomerLevelListGroupbyLevel(CrmStatisticsCustomerReqVO reqVO); + + List selectSummaryListByAreaId(CrmStatisticsCustomerReqVO reqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java index a69e1b9a0..55317f4ae 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -1,10 +1,6 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; import java.util.List; @@ -81,36 +77,4 @@ public interface CrmStatisticsCustomerService { */ List getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO); - /** - * 获取客户行业统计数据 - * - * @param reqVO 请求参数 - * @return 统计数据 - */ - List getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO); - - /** - * 获取客户来源统计数据 - * - * @param reqVO 请求参数 - * @return 统计数据 - */ - List getCustomerSource(CrmStatisticsCustomerReqVO reqVO); - - /** - * 获取客户级别统计数据 - * - * @param reqVO 请求参数 - * @return 统计数据 - */ - List getCustomerLevel(CrmStatisticsCustomerReqVO reqVO); - - /** - * 获取客户地区统计数据 - * - * @param reqVO 请求参数 - * @return 统计数据 - */ - List getCustomerArea(CrmStatisticsCustomerReqVO reqVO); - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index e20d3a963..250560ef2 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -4,14 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; -import cn.iocoder.yudao.framework.ip.core.Area; -import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; -import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; @@ -30,8 +23,6 @@ import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 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.module.crm.enums.DictTypeConstants.*; /** * CRM 客户分析 Service 实现类 @@ -245,104 +236,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe return summaryList; } - @Override - public List getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO) { - // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { - return Collections.emptyList(); - } - reqVO.setUserIds(userIds); - // 2. 获取客户行业统计数据 - List industryRespVOList = customerMapper.selectCustomerIndustryListGroupbyIndustryId(reqVO); - if (CollUtil.isEmpty(industryRespVOList)) { - return Collections.emptyList(); - } - - return convertList(industryRespVOList, item -> { - if (ObjUtil.isNull(item.getIndustryId())) { - return item; - } - item.setIndustryName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, item.getIndustryId())); - return item; - }); - } - - @Override - public List getCustomerSource(CrmStatisticsCustomerReqVO reqVO) { - // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { - return Collections.emptyList(); - } - reqVO.setUserIds(userIds); - // 2. 获取客户行业统计数据 - List sourceRespVOList = customerMapper.selectCustomerSourceListGroupbySource(reqVO); - if (CollUtil.isEmpty(sourceRespVOList)) { - return Collections.emptyList(); - } - - return convertList(sourceRespVOList, item -> { - if (ObjUtil.isNull(item.getSource())) { - return item; - } - item.setSourceName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_SOURCE, item.getSource())); - return item; - }); - } - - @Override - public List getCustomerLevel(CrmStatisticsCustomerReqVO reqVO) { - // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { - return Collections.emptyList(); - } - reqVO.setUserIds(userIds); - // 2. 获取客户行业统计数据 - List levelRespVOList = customerMapper.selectCustomerLevelListGroupbyLevel(reqVO); - if (CollUtil.isEmpty(levelRespVOList)) { - return Collections.emptyList(); - } - - return convertList(levelRespVOList, item -> { - if (ObjUtil.isNull(item.getLevel())) { - return item; - } - item.setLevelName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_LEVEL, item.getLevel())); - return item; - }); - } - - @Override - public List getCustomerArea(CrmStatisticsCustomerReqVO reqVO) { - // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { - return Collections.emptyList(); - } - reqVO.setUserIds(userIds); - // 2. 获取客户地区统计数据 - List list = customerMapper.selectSummaryListByAreaId(reqVO); - if (CollUtil.isEmpty(list)) { - return Collections.emptyList(); - } - - // 拼接数据 - List areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area); - areaList.add(new Area().setId(null).setName("未知")); - Map areaMap = convertMap(areaList, Area::getId); - List customerAreaRespVOList = convertList(list, item -> { - Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE); - if (parentId == null) { - return item; - } - findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName())); - return item; - }); - return customerAreaRespVOList; - } - /** * 拼接用户信息(昵称) * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java new file mode 100644 index 000000000..33afe0eca --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.crm.service.statistics; + +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO; + +import java.util.List; + +/** + * CRM 客户画像 Service 接口 + * + * @author HUIHUI + */ +public interface CrmStatisticsPortraitService { + + /** + * 获取客户地区统计数据 + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerArea(CrmStatisticsCustomerReqVO reqVO); + + /** + * 获取客户行业统计数据 + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO); + + /** + * 获取客户级别统计数据 + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerLevel(CrmStatisticsCustomerReqVO reqVO); + + /** + * 获取客户来源统计数据 + * + * @param reqVO 请求参数 + * @return 统计数据 + */ + List getCustomerSource(CrmStatisticsCustomerReqVO reqVO); + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java new file mode 100644 index 000000000..2e63d8786 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java @@ -0,0 +1,166 @@ +package cn.iocoder.yudao.module.crm.service.statistics; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.ip.core.Area; +import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO; +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; + +/** + * CRM 客户画像 Service 实现类 + * + * @author HUIHUI + */ +@Service +public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitService { + + @Resource + private CrmStatisticsPortraitMapper portraitMapper; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + @Resource + private DictDataApi dictDataApi; + + @Override + public List getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + // 2. 获取客户行业统计数据 + List industryRespVOList = portraitMapper.selectCustomerIndustryListGroupbyIndustryId(reqVO); + if (CollUtil.isEmpty(industryRespVOList)) { + return Collections.emptyList(); + } + + return convertList(industryRespVOList, item -> { + if (ObjUtil.isNull(item.getIndustryId())) { + return item; + } + item.setIndustryName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, item.getIndustryId())); + return item; + }); + } + + @Override + public List getCustomerSource(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + // 2. 获取客户行业统计数据 + List sourceRespVOList = portraitMapper.selectCustomerSourceListGroupbySource(reqVO); + if (CollUtil.isEmpty(sourceRespVOList)) { + return Collections.emptyList(); + } + + return convertList(sourceRespVOList, item -> { + if (ObjUtil.isNull(item.getSource())) { + return item; + } + item.setSourceName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_SOURCE, item.getSource())); + return item; + }); + } + + @Override + public List getCustomerLevel(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + // 2. 获取客户行业统计数据 + List levelRespVOList = portraitMapper.selectCustomerLevelListGroupbyLevel(reqVO); + if (CollUtil.isEmpty(levelRespVOList)) { + return Collections.emptyList(); + } + + return convertList(levelRespVOList, item -> { + if (ObjUtil.isNull(item.getLevel())) { + return item; + } + item.setLevelName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_LEVEL, item.getLevel())); + return item; + }); + } + + @Override + public List getCustomerArea(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + // 2. 获取客户地区统计数据 + List list = portraitMapper.selectSummaryListByAreaId(reqVO); + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + + // 拼接数据 + List areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area); + areaList.add(new Area().setId(null).setName("未知")); + Map areaMap = convertMap(areaList, Area::getId); + List customerAreaRespVOList = convertList(list, item -> { + Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE); + if (parentId == null) { + return item; + } + findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName())); + return item; + }); + return customerAreaRespVOList; + } + + /** + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号 + * + * @param reqVO 请求参数 + * @return 用户编号数组 + */ + private List getUserIds(CrmStatisticsCustomerReqVO reqVO) { + // 情况一:选中某个用户 + if (ObjUtil.isNotNull(reqVO.getUserId())) { + return List.of(reqVO.getUserId()); + } + // 情况二:选中某个部门 + // 2.1 获得部门列表 + List deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId); + deptIds.add(reqVO.getDeptId()); + // 2.2 获得用户编号 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index acf354505..0672d629e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -229,89 +229,4 @@ GROUP BY customer.owner_user_id - - - - - diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml new file mode 100644 index 000000000..0364027c7 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml @@ -0,0 +1,90 @@ + + + + + + + + + + From 3f5724e97803557d54a746ee26f4cc6cbf78e3d2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 30 Mar 2024 14:17:28 +0800 Subject: [PATCH 50/81] =?UTF-8?q?CRM=EF=BC=9Acode=20review=E3=80=90?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=94=BB=E5=83=8F=E3=80=91=E7=9A=84=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsPortraitController.java | 22 +++--- .../CrmStatisticCustomerAreaRespVO.java | 2 + .../CrmStatisticCustomerIndustryRespVO.java | 3 + .../CrmStatisticCustomerLevelRespVO.java | 5 +- .../CrmStatisticCustomerSourceRespVO.java | 5 +- .../CrmStatisticsPortraitMapper.java | 4 +- .../CrmStatisticsPortraitService.java | 8 +-- .../CrmStatisticsPortraitServiceImpl.java | 67 ++++++++++--------- .../CrmStatisticsPortraitMapper.xml | 2 + 9 files changed, 69 insertions(+), 49 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java index 90ef26688..0f1e9e635 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java @@ -30,32 +30,34 @@ public class CrmStatisticsPortraitController { @Resource private CrmStatisticsPortraitService statisticsPortraitService; + // TODO @puhui999:搞个属于自己的 CrmStatisticsCustomerReqVO 类哈 + @GetMapping("/get-customer-area-summary") @Operation(summary = "获取客户地区统计数据", description = "用于【城市分布分析】页面") @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") public CommonResult> getCustomerAreaSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(statisticsPortraitService.getCustomerArea(reqVO)); + return success(statisticsPortraitService.getCustomerAreaSummary(reqVO)); } @GetMapping("/get-customer-industry-summary") - @Operation(summary = "获取客户行业统计数据") + @Operation(summary = "获取客户行业统计数据", description = "用于【客户行业分析】页面") @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") - public CommonResult> getCustomerIndustry(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(statisticsPortraitService.getCustomerIndustry(reqVO)); + public CommonResult> getCustomerIndustrySummary(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(statisticsPortraitService.getCustomerIndustrySummary(reqVO)); } @GetMapping("/get-customer-level-summary") - @Operation(summary = "获取客户级别统计数据") + @Operation(summary = "获取客户级别统计数据", description = "用于【客户级别分析】页面") @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") - public CommonResult> getCustomerLevel(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(statisticsPortraitService.getCustomerLevel(reqVO)); + public CommonResult> getCustomerLevelSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(statisticsPortraitService.getCustomerLevelSummary(reqVO)); } @GetMapping("/get-customer-source-summary") - @Operation(summary = "获取客户来源统计数据") + @Operation(summary = "获取客户来源统计数据", description = "用于【客户来源分析】页面") @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") - public CommonResult> getCustomerSource(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(statisticsPortraitService.getCustomerSource(reqVO)); + public CommonResult> getCustomerSourceSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(statisticsPortraitService.getCustomerSourceSummary(reqVO)); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java index 05220d47c..e83563d5b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java @@ -18,6 +18,8 @@ public class CrmStatisticCustomerAreaRespVO { @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer dealCount; + // TODO @puhui999:下面两个的计算,交给前端。后端只返回数据即可。 + @Schema(description = "省份占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Double areaPortion; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java index 8d368a306..4029a42b0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java @@ -9,6 +9,7 @@ public class CrmStatisticCustomerIndustryRespVO { @Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Integer industryId; + // TODO @puhui999:这个前端字典翻译哈 @Schema(description = "客户行业名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private String industryName; @@ -18,6 +19,8 @@ public class CrmStatisticCustomerIndustryRespVO { @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer dealCount; + // TODO @puhui999:下面两个的计算,交给前端。后端只返回数据即可。 + @Schema(description = "行业占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Double industryPortion; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java index 5d6e9793d..c438ee21e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java @@ -7,8 +7,9 @@ import lombok.Data; @Data public class CrmStatisticCustomerLevelRespVO { - @Schema(description = "客户级别ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @Schema(description = "客户级别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Integer level; + // TODO @puhui999:这个前端字典翻译哈 @Schema(description = "客户级别名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private String levelName; @@ -18,6 +19,8 @@ public class CrmStatisticCustomerLevelRespVO { @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer dealCount; + // TODO @puhui999:下面两个的计算,交给前端。后端只返回数据即可。 + @Schema(description = "级别占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Double levelPortion; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java index 60b938139..e66a5c634 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java @@ -7,8 +7,9 @@ import lombok.Data; @Data public class CrmStatisticCustomerSourceRespVO { - @Schema(description = "客户来源ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @Schema(description = "客户来源编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Integer source; + // TODO @puhui999:这个前端字典翻译哈 @Schema(description = "客户来源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private String sourceName; @@ -18,6 +19,8 @@ public class CrmStatisticCustomerSourceRespVO { @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer dealCount; + // TODO @puhui999:下面两个的计算,交给前端。后端只返回数据即可。 + @Schema(description = "来源占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Double sourcePortion; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java index 25b7cb2d2..193cc9307 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java @@ -12,11 +12,13 @@ import java.util.List; /** * CRM 数据画像 Mapper * - * @author dhb52 + * @author HUIHUI */ @Mapper public interface CrmStatisticsPortraitMapper { + // TODO @puuhui999:GroupBy + List selectCustomerIndustryListGroupbyIndustryId(CrmStatisticsCustomerReqVO reqVO); List selectCustomerSourceListGroupbySource(CrmStatisticsCustomerReqVO reqVO); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java index 33afe0eca..dd1f3e3da 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java @@ -21,7 +21,7 @@ public interface CrmStatisticsPortraitService { * @param reqVO 请求参数 * @return 统计数据 */ - List getCustomerArea(CrmStatisticsCustomerReqVO reqVO); + List getCustomerAreaSummary(CrmStatisticsCustomerReqVO reqVO); /** * 获取客户行业统计数据 @@ -29,7 +29,7 @@ public interface CrmStatisticsPortraitService { * @param reqVO 请求参数 * @return 统计数据 */ - List getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO); + List getCustomerIndustrySummary(CrmStatisticsCustomerReqVO reqVO); /** * 获取客户级别统计数据 @@ -37,7 +37,7 @@ public interface CrmStatisticsPortraitService { * @param reqVO 请求参数 * @return 统计数据 */ - List getCustomerLevel(CrmStatisticsCustomerReqVO reqVO); + List getCustomerLevelSummary(CrmStatisticsCustomerReqVO reqVO); /** * 获取客户来源统计数据 @@ -45,6 +45,6 @@ public interface CrmStatisticsPortraitService { * @param reqVO 请求参数 * @return 统计数据 */ - List getCustomerSource(CrmStatisticsCustomerReqVO reqVO); + List getCustomerSourceSummary(CrmStatisticsCustomerReqVO reqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java index 2e63d8786..e38a702bc 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java @@ -28,6 +28,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; +// TODO @puhui999:参考 CrmStatisticsCustomerServiceImpl 代码风格,优化下这个类哈;包括命名、空行、注释等; /** * CRM 客户画像 Service 实现类 * @@ -47,7 +48,37 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe private DictDataApi dictDataApi; @Override - public List getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO) { + public List getCustomerAreaSummary(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组 + List userIds = getUserIds(reqVO); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + reqVO.setUserIds(userIds); + // 2. 获取客户地区统计数据 + List list = portraitMapper.selectSummaryListByAreaId(reqVO); + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + + // 拼接数据 + List areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area); + areaList.add(new Area().setId(null).setName("未知")); + Map areaMap = convertMap(areaList, Area::getId); + List customerAreaRespVOList = convertList(list, item -> { + Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE); + // TODO @puhui999:找不到,可以归到未知哈; + if (parentId == null) { + return item; + } + findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName())); + return item; + }); + return customerAreaRespVOList; + } + + @Override + public List getCustomerIndustrySummary(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { @@ -70,13 +101,14 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe } @Override - public List getCustomerSource(CrmStatisticsCustomerReqVO reqVO) { + public List getCustomerSourceSummary(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); + // 2. 获取客户行业统计数据 List sourceRespVOList = portraitMapper.selectCustomerSourceListGroupbySource(reqVO); if (CollUtil.isEmpty(sourceRespVOList)) { @@ -93,7 +125,7 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe } @Override - public List getCustomerLevel(CrmStatisticsCustomerReqVO reqVO) { + public List getCustomerLevelSummary(CrmStatisticsCustomerReqVO reqVO) { // 1. 获得用户编号数组 List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { @@ -115,35 +147,6 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe }); } - @Override - public List getCustomerArea(CrmStatisticsCustomerReqVO reqVO) { - // 1. 获得用户编号数组 - List userIds = getUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { - return Collections.emptyList(); - } - reqVO.setUserIds(userIds); - // 2. 获取客户地区统计数据 - List list = portraitMapper.selectSummaryListByAreaId(reqVO); - if (CollUtil.isEmpty(list)) { - return Collections.emptyList(); - } - - // 拼接数据 - List areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area); - areaList.add(new Area().setId(null).setName("未知")); - Map areaMap = convertMap(areaList, Area::getId); - List customerAreaRespVOList = convertList(list, item -> { - Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE); - if (parentId == null) { - return item; - } - findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName())); - return item; - }); - return customerAreaRespVOList; - } - /** * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号 * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml index 0364027c7..946bd292b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml @@ -2,6 +2,8 @@ + + - SELECT - DATE_FORMAT(create_time, '%Y-%m-%d') AS time, - COUNT(*) AS customerCreateCount - FROM crm_customer - WHERE deleted = 0 - AND owner_user_id IN - - #{userId} - - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - GROUP BY time + SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time, + COUNT(*) AS customerCreateCount + FROM crm_customer + WHERE deleted = 0 + AND owner_user_id IN + + #{userId} + + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + GROUP BY time From a9a8efc808cb037692c253147833c24badcea50b Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 3 Apr 2024 10:09:46 +0800 Subject: [PATCH 64/81] =?UTF-8?q?CRM:=20=E5=AE=8C=E5=96=84=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=94=BB=E5=83=8F=E6=95=B0=E6=8D=AE=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsPortraitController.java | 20 ++- .../CrmStatisticCustomerAreaRespVO.java | 8 -- .../CrmStatisticCustomerIndustryRespVO.java | 11 -- .../CrmStatisticCustomerLevelRespVO.java | 11 -- .../CrmStatisticCustomerSourceRespVO.java | 11 -- .../portrait/CrmStatisticsPortraitReqVO.java | 42 ++++++ .../CrmStatisticsPortraitMapper.java | 16 +-- .../CrmStatisticsPortraitService.java | 14 +- .../CrmStatisticsPortraitServiceImpl.java | 74 +++------- .../CrmStatisticsPortraitMapper.xml | 129 +++++++----------- 10 files changed, 132 insertions(+), 204 deletions(-) create mode 100644 yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticsPortraitReqVO.java diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java index 0f1e9e635..986e2268c 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticsPortraitReqVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO; @@ -30,34 +30,32 @@ public class CrmStatisticsPortraitController { @Resource private CrmStatisticsPortraitService statisticsPortraitService; - // TODO @puhui999:搞个属于自己的 CrmStatisticsCustomerReqVO 类哈 - @GetMapping("/get-customer-area-summary") @Operation(summary = "获取客户地区统计数据", description = "用于【城市分布分析】页面") @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") - public CommonResult> getCustomerAreaSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(statisticsPortraitService.getCustomerAreaSummary(reqVO)); + public CommonResult> getCustomerAreaSummary(@Valid CrmStatisticsPortraitReqVO reqVO) { + return success(statisticsPortraitService.getCustomerSummaryByArea(reqVO)); } @GetMapping("/get-customer-industry-summary") @Operation(summary = "获取客户行业统计数据", description = "用于【客户行业分析】页面") @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") - public CommonResult> getCustomerIndustrySummary(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(statisticsPortraitService.getCustomerIndustrySummary(reqVO)); + public CommonResult> getCustomerIndustrySummary(@Valid CrmStatisticsPortraitReqVO reqVO) { + return success(statisticsPortraitService.getCustomerSummaryByIndustry(reqVO)); } @GetMapping("/get-customer-level-summary") @Operation(summary = "获取客户级别统计数据", description = "用于【客户级别分析】页面") @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") - public CommonResult> getCustomerLevelSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(statisticsPortraitService.getCustomerLevelSummary(reqVO)); + public CommonResult> getCustomerLevelSummary(@Valid CrmStatisticsPortraitReqVO reqVO) { + return success(statisticsPortraitService.getCustomerSummaryByLevel(reqVO)); } @GetMapping("/get-customer-source-summary") @Operation(summary = "获取客户来源统计数据", description = "用于【客户来源分析】页面") @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')") - public CommonResult> getCustomerSourceSummary(@Valid CrmStatisticsCustomerReqVO reqVO) { - return success(statisticsPortraitService.getCustomerSourceSummary(reqVO)); + public CommonResult> getCustomerSourceSummary(@Valid CrmStatisticsPortraitReqVO reqVO) { + return success(statisticsPortraitService.getCustomerSummaryBySource(reqVO)); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java index e83563d5b..3420e7e5b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java @@ -18,12 +18,4 @@ public class CrmStatisticCustomerAreaRespVO { @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer dealCount; - // TODO @puhui999:下面两个的计算,交给前端。后端只返回数据即可。 - - @Schema(description = "省份占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Double areaPortion; - - @Schema(description = "成交占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Double dealPortion; - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java index 4029a42b0..84b8de70f 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java @@ -9,9 +9,6 @@ public class CrmStatisticCustomerIndustryRespVO { @Schema(description = "客户行业ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Integer industryId; - // TODO @puhui999:这个前端字典翻译哈 - @Schema(description = "客户行业名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private String industryName; @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer customerCount; @@ -19,12 +16,4 @@ public class CrmStatisticCustomerIndustryRespVO { @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer dealCount; - // TODO @puhui999:下面两个的计算,交给前端。后端只返回数据即可。 - - @Schema(description = "行业占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Double industryPortion; - - @Schema(description = "成交占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Double dealPortion; - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java index c438ee21e..dea4eeb0c 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java @@ -9,9 +9,6 @@ public class CrmStatisticCustomerLevelRespVO { @Schema(description = "客户级别编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Integer level; - // TODO @puhui999:这个前端字典翻译哈 - @Schema(description = "客户级别名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private String levelName; @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer customerCount; @@ -19,12 +16,4 @@ public class CrmStatisticCustomerLevelRespVO { @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer dealCount; - // TODO @puhui999:下面两个的计算,交给前端。后端只返回数据即可。 - - @Schema(description = "级别占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Double levelPortion; - - @Schema(description = "成交占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Double dealPortion; - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java index e66a5c634..61b9688ff 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java @@ -9,9 +9,6 @@ public class CrmStatisticCustomerSourceRespVO { @Schema(description = "客户来源编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Integer source; - // TODO @puhui999:这个前端字典翻译哈 - @Schema(description = "客户来源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private String sourceName; @Schema(description = "客户个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer customerCount; @@ -19,12 +16,4 @@ public class CrmStatisticCustomerSourceRespVO { @Schema(description = "成交个数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer dealCount; - // TODO @puhui999:下面两个的计算,交给前端。后端只返回数据即可。 - - @Schema(description = "来源占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Double sourcePortion; - - @Schema(description = "成交占比(%)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Double dealPortion; - } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticsPortraitReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticsPortraitReqVO.java new file mode 100644 index 000000000..c284e7457 --- /dev/null +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticsPortraitReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - CRM 客户画像 Request VO") +@Data +public class CrmStatisticsPortraitReqVO { + + @Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "部门 id 不能为空") + private Long deptId; + + /** + * 负责人用户 id, 当用户为空, 则计算部门下用户 + */ + @Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1") + private Long userId; + + /** + * userIds 目前不用前端传递,目前是方便后端通过 deptId 读取编号后,设置回来 + * 后续,可能会支持选择部分用户进行查询 + */ + @Schema(description = "负责人用户 id 集合", hidden = true, example = "2") + private List userIds; + + /** + * 前端如果选择自定义时间, 那么前端传递起始-终止时间, 如果选择其他时间间隔类型, 则由后台计算起始-终止时间 + * 并作为参数传递给Mapper + */ + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] times; + +} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java index 193cc9307..a7c942752 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java @@ -1,10 +1,6 @@ package cn.iocoder.yudao.module.crm.dal.mysql.statistics; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -17,14 +13,12 @@ import java.util.List; @Mapper public interface CrmStatisticsPortraitMapper { - // TODO @puuhui999:GroupBy + List selectSummaryListGroupByAreaId(CrmStatisticsPortraitReqVO reqVO); - List selectCustomerIndustryListGroupbyIndustryId(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerIndustryListGroupByIndustryId(CrmStatisticsPortraitReqVO reqVO); - List selectCustomerSourceListGroupbySource(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerSourceListGroupBySource(CrmStatisticsPortraitReqVO reqVO); - List selectCustomerLevelListGroupbyLevel(CrmStatisticsCustomerReqVO reqVO); - - List selectSummaryListByAreaId(CrmStatisticsCustomerReqVO reqVO); + List selectCustomerLevelListGroupByLevel(CrmStatisticsPortraitReqVO reqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java index dd1f3e3da..c568d3b4e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java @@ -1,10 +1,6 @@ package cn.iocoder.yudao.module.crm.service.statistics; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*; import java.util.List; @@ -21,7 +17,7 @@ public interface CrmStatisticsPortraitService { * @param reqVO 请求参数 * @return 统计数据 */ - List getCustomerAreaSummary(CrmStatisticsCustomerReqVO reqVO); + List getCustomerSummaryByArea(CrmStatisticsPortraitReqVO reqVO); /** * 获取客户行业统计数据 @@ -29,7 +25,7 @@ public interface CrmStatisticsPortraitService { * @param reqVO 请求参数 * @return 统计数据 */ - List getCustomerIndustrySummary(CrmStatisticsCustomerReqVO reqVO); + List getCustomerSummaryByIndustry(CrmStatisticsPortraitReqVO reqVO); /** * 获取客户级别统计数据 @@ -37,7 +33,7 @@ public interface CrmStatisticsPortraitService { * @param reqVO 请求参数 * @return 统计数据 */ - List getCustomerLevelSummary(CrmStatisticsCustomerReqVO reqVO); + List getCustomerSummaryByLevel(CrmStatisticsPortraitReqVO reqVO); /** * 获取客户来源统计数据 @@ -45,6 +41,6 @@ public interface CrmStatisticsPortraitService { * @param reqVO 请求参数 * @return 统计数据 */ - List getCustomerSourceSummary(CrmStatisticsCustomerReqVO reqVO); + List getCustomerSummaryBySource(CrmStatisticsPortraitReqVO reqVO); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java index e38a702bc..9863f62fc 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java @@ -5,15 +5,10 @@ import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.ip.core.Area; import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.*; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; -import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; @@ -26,9 +21,7 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; -import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; -// TODO @puhui999:参考 CrmStatisticsCustomerServiceImpl 代码风格,优化下这个类哈;包括命名、空行、注释等; /** * CRM 客户画像 Service 实现类 * @@ -44,64 +37,54 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; - @Resource - private DictDataApi dictDataApi; @Override - public List getCustomerAreaSummary(CrmStatisticsCustomerReqVO reqVO) { - // 1. 获得用户编号数组 + public List getCustomerSummaryByArea(CrmStatisticsPortraitReqVO reqVO) { + // 1.1 获得用户编号数组 List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); - // 2. 获取客户地区统计数据 - List list = portraitMapper.selectSummaryListByAreaId(reqVO); + // 1.2 获取客户地区统计数据 + List list = portraitMapper.selectSummaryListGroupByAreaId(reqVO); if (CollUtil.isEmpty(list)) { return Collections.emptyList(); } - // 拼接数据 + // 2. 拼接数据 List areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area); areaList.add(new Area().setId(null).setName("未知")); Map areaMap = convertMap(areaList, Area::getId); - List customerAreaRespVOList = convertList(list, item -> { + return convertList(list, item -> { Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE); - // TODO @puhui999:找不到,可以归到未知哈; - if (parentId == null) { - return item; + if (parentId == null) { // 找不到,归到未知 + return item.setAreaId(null).setAreaName("未知"); } findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName())); return item; }); - return customerAreaRespVOList; } @Override - public List getCustomerIndustrySummary(CrmStatisticsCustomerReqVO reqVO) { + public List getCustomerSummaryByIndustry(CrmStatisticsPortraitReqVO reqVO) { // 1. 获得用户编号数组 List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); + // 2. 获取客户行业统计数据 - List industryRespVOList = portraitMapper.selectCustomerIndustryListGroupbyIndustryId(reqVO); + List industryRespVOList = portraitMapper.selectCustomerIndustryListGroupByIndustryId(reqVO); if (CollUtil.isEmpty(industryRespVOList)) { return Collections.emptyList(); } - - return convertList(industryRespVOList, item -> { - if (ObjUtil.isNull(item.getIndustryId())) { - return item; - } - item.setIndustryName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, item.getIndustryId())); - return item; - }); + return industryRespVOList; } @Override - public List getCustomerSourceSummary(CrmStatisticsCustomerReqVO reqVO) { + public List getCustomerSummaryBySource(CrmStatisticsPortraitReqVO reqVO) { // 1. 获得用户编号数组 List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { @@ -110,41 +93,28 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe reqVO.setUserIds(userIds); // 2. 获取客户行业统计数据 - List sourceRespVOList = portraitMapper.selectCustomerSourceListGroupbySource(reqVO); + List sourceRespVOList = portraitMapper.selectCustomerSourceListGroupBySource(reqVO); if (CollUtil.isEmpty(sourceRespVOList)) { return Collections.emptyList(); } - - return convertList(sourceRespVOList, item -> { - if (ObjUtil.isNull(item.getSource())) { - return item; - } - item.setSourceName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_SOURCE, item.getSource())); - return item; - }); + return sourceRespVOList; } @Override - public List getCustomerLevelSummary(CrmStatisticsCustomerReqVO reqVO) { + public List getCustomerSummaryByLevel(CrmStatisticsPortraitReqVO reqVO) { // 1. 获得用户编号数组 List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); - // 2. 获取客户行业统计数据 - List levelRespVOList = portraitMapper.selectCustomerLevelListGroupbyLevel(reqVO); + + // 2. 获取客户级别统计数据 + List levelRespVOList = portraitMapper.selectCustomerLevelListGroupByLevel(reqVO); if (CollUtil.isEmpty(levelRespVOList)) { return Collections.emptyList(); } - - return convertList(levelRespVOList, item -> { - if (ObjUtil.isNull(item.getLevel())) { - return item; - } - item.setLevelName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_LEVEL, item.getLevel())); - return item; - }); + return levelRespVOList; } /** @@ -153,7 +123,7 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe * @param reqVO 请求参数 * @return 用户编号数组 */ - private List getUserIds(CrmStatisticsCustomerReqVO reqVO) { + private List getUserIds(CrmStatisticsPortraitReqVO reqVO) { // 情况一:选中某个用户 if (ObjUtil.isNotNull(reqVO.getUserId())) { return List.of(reqVO.getUserId()); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml index 946bd292b..42056a48b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml @@ -2,91 +2,60 @@ - - - - - - + + + + + + From 132c1cc828334fa303ba53df5bc34ea82d8e5995 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 3 Apr 2024 19:52:52 +0800 Subject: [PATCH 65/81] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=AE=BF=E9=97=AE=E6=97=A5=E5=BF=97=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=98=AF=E5=90=A6=E8=AE=B0=E5=BD=95=E3=80=81?= =?UTF-8?q?=E8=84=B1=E6=95=8F=E3=80=81=E6=93=8D=E4=BD=9C=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E7=AD=89=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/mysql/ruoyi-vue-pro.sql | 30 +-- .../common/util/spring/SpringAopUtils.java | 46 ----- .../common/util/spring/SpringUtils.java | 24 +++ .../config/YudaoApiLogAutoConfiguration.java | 15 +- .../apilog/core/annotations/ApiAccessLog.java | 65 ++++++ .../apilog/core/enums/OperateTypeEnum.java | 51 +++++ .../core/filter/ApiAccessLogFilter.java | 192 +++++++++++++++--- .../interceptor/ApiAccessLogInterceptor.java | 67 ++++++ .../apilog/core/service/ApiAccessLog.java | 85 -------- .../service/ApiAccessLogFrameworkService.java | 7 +- .../ApiAccessLogFrameworkServiceImpl.java | 4 +- .../apilog/core/service/ApiErrorLog.java | 107 ---------- .../service/ApiErrorLogFrameworkService.java | 7 +- .../ApiErrorLogFrameworkServiceImpl.java | 4 +- .../core/handler/GlobalExceptionHandler.java | 9 +- .../logger/dto/ApiAccessLogCreateReqDTO.java | 20 +- .../vo/apiaccesslog/ApiAccessLogRespVO.java | 17 ++ .../dal/dataobject/logger/ApiAccessLogDO.java | 21 ++ .../src/test/resources/sql/create_tables.sql | 6 +- .../admin/notify/NotifyMessageController.java | 2 + 20 files changed, 483 insertions(+), 296 deletions(-) delete mode 100644 yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringAopUtils.java create mode 100644 yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringUtils.java create mode 100644 yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/annotations/ApiAccessLog.java create mode 100644 yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/enums/OperateTypeEnum.java create mode 100644 yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index 95923d4eb..f7b7c292c 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -11,7 +11,7 @@ Target Server Version : 80200 (8.2.0) File Encoding : 65001 - Date: 30/03/2024 20:42:06 + Date: 03/04/2024 19:07:31 */ SET NAMES utf8mb4; @@ -327,9 +327,13 @@ CREATE TABLE `infra_api_access_log` ( `application_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名', `request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求方法名', `request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求地址', - `request_params` varchar(8000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求参数', + `request_params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '请求参数', + `response_body` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '响应结果', `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户 IP', `user_agent` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '浏览器 UA', + `operate_module` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作模块', + `operate_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作名', + `operate_type` tinyint NULL DEFAULT 0 COMMENT '操作分类', `begin_time` datetime NOT NULL COMMENT '开始请求时间', `end_time` datetime NOT NULL COMMENT '结束请求时间', `duration` int NOT NULL COMMENT '执行时长', @@ -343,7 +347,7 @@ CREATE TABLE `infra_api_access_log` ( `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE, INDEX `idx_create_time`(`create_time` ASC) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 35832 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表'; +) ENGINE = InnoDB AUTO_INCREMENT = 35920 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表'; -- ---------------------------- -- Records of infra_api_access_log @@ -385,7 +389,7 @@ CREATE TABLE `infra_api_error_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 16429 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; +) ENGINE = InnoDB AUTO_INCREMENT = 16462 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; -- ---------------------------- -- Records of infra_api_error_log @@ -494,7 +498,7 @@ CREATE TABLE `infra_config` ( -- Records of infra_config -- ---------------------------- BEGIN; -INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'sys.user.init-password', '123456', b'0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-02-28 22:54:14', b'0'); +INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'sys.user.init-password', '123456', b'0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-04-03 17:22:28', b'0'); INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:33:38', b'0'); INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:57:03', b'0'); INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:52:07', b'0'); @@ -690,7 +694,7 @@ CREATE TABLE `infra_file` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1301 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; +) ENGINE = InnoDB AUTO_INCREMENT = 1302 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; -- ---------------------------- -- Records of infra_file @@ -1416,7 +1420,7 @@ CREATE TABLE `system_login_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 3054 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; +) ENGINE = InnoDB AUTO_INCREMENT = 3066 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; -- ---------------------------- -- Records of system_login_log @@ -2453,7 +2457,7 @@ CREATE TABLE `system_oauth2_access_token` ( PRIMARY KEY (`id`) USING BTREE, INDEX `idx_access_token`(`access_token` ASC) USING BTREE, INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 6332 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 6366 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; -- ---------------------------- -- Records of system_oauth2_access_token @@ -2575,7 +2579,7 @@ CREATE TABLE `system_oauth2_refresh_token` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1430 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 1441 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; -- ---------------------------- -- Records of system_oauth2_refresh_token @@ -2615,7 +2619,7 @@ CREATE TABLE `system_operate_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 11964 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录'; +) ENGINE = InnoDB AUTO_INCREMENT = 12000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录'; -- ---------------------------- -- Records of system_operate_log @@ -5305,7 +5309,7 @@ CREATE TABLE `system_sms_log` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 946 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; +) ENGINE = InnoDB AUTO_INCREMENT = 947 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; -- ---------------------------- -- Records of system_sms_log @@ -5475,7 +5479,7 @@ INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `c INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (151, '大租户', 126, '土豆大', NULL, 0, 'https://tudou.iocoder.cn', 111, '2023-12-08 00:00:00', 10, '1', '2023-12-02 23:35:05', '1', '2023-12-08 23:39:56', b'0'); INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (152, '新租户', 127, '土豆', NULL, 0, 'http://xx.iocoder.cn', 111, '2025-12-31 00:00:00', 50, '1', '2023-12-30 11:43:17', '1', '2023-12-30 11:43:17', b'0'); INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (153, '小明的租户', 128, 'xiaoming', '15601691301', 0, 'xiaoming.iocoder.cn', 111, '2025-12-01 00:00:00', 100, '1', '2024-02-27 21:58:25', '1', '2024-02-28 22:53:54', b'0'); -INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (154, 'hh', 129, 'hh', NULL, 0, 'http://hh.iocoder.cn', 111, '2024-04-30 00:00:00', 123, '1', '2024-03-30 17:52:59', '1', '2024-03-30 17:52:59', b'0'); +INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (154, 'hh', 129, 'hh', NULL, 0, 'http://hh.iocoder.cn', 111, '2024-04-30 00:00:00', 123, '1', '2024-03-30 17:52:59', '1', '2024-04-03 15:06:42', b'0'); COMMIT; -- ---------------------------- @@ -5619,7 +5623,7 @@ CREATE TABLE `system_users` ( -- Records of system_users -- ---------------------------- BEGIN; -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png', 0, '0:0:0:0:0:0:0:1', '2024-03-30 17:18:34', 'admin', '2021-01-05 17:03:47', NULL, '2024-03-30 17:18:34', b'0', 1); +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png', 0, '127.0.0.1', '2024-04-03 17:31:00', 'admin', '2021-01-05 17:03:47', NULL, '2024-04-03 17:31:00', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-03-18 21:09:04', '', '2021-01-13 23:50:35', NULL, '2024-03-18 21:09:04', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$KhExCYl7lx6eWWZYKsibKOZ8IBJRyuNuCcEOLQ11RYhJKgHmlSwK.', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-03-26 07:11:35', '', '2021-01-21 02:13:53', NULL, '2024-03-26 07:11:35', b'0', 1); diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringAopUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringAopUtils.java deleted file mode 100644 index b71342cb3..000000000 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringAopUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.framework.common.util.spring; - -import cn.hutool.core.bean.BeanUtil; -import org.springframework.aop.framework.AdvisedSupport; -import org.springframework.aop.framework.AopProxy; -import org.springframework.aop.support.AopUtils; - -/** - * Spring AOP 工具类 - * - * 参考波克尔 http://www.bubuko.com/infodetail-3471885.html 实现 - */ -public class SpringAopUtils { - - /** - * 获取代理的目标对象 - * - * @param proxy 代理对象 - * @return 目标对象 - */ - public static Object getTarget(Object proxy) throws Exception { - // 不是代理对象 - if (!AopUtils.isAopProxy(proxy)) { - return proxy; - } - // Jdk 代理 - if (AopUtils.isJdkDynamicProxy(proxy)) { - return getJdkDynamicProxyTargetObject(proxy); - } - // Cglib 代理 - return getCglibProxyTargetObject(proxy); - } - - private static Object getCglibProxyTargetObject(Object proxy) throws Exception { - Object dynamicAdvisedInterceptor = BeanUtil.getFieldValue(proxy, "CGLIB$CALLBACK_0"); - AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(dynamicAdvisedInterceptor, "advised"); - return advisedSupport.getTargetSource().getTarget(); - } - - private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { - AopProxy aopProxy = (AopProxy) BeanUtil.getFieldValue(proxy, "h"); - AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(aopProxy, "advised"); - return advisedSupport.getTargetSource().getTarget(); - } - -} diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringUtils.java new file mode 100644 index 000000000..a501a7116 --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringUtils.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.framework.common.util.spring; + +import cn.hutool.extra.spring.SpringUtil; + +import java.util.Objects; + +/** + * Spring 工具类 + * + * @author 芋道源码 + */ +public class SpringUtils extends SpringUtil { + + /** + * 是否为生产环境 + * + * @return 是否生产环境 + */ + public static boolean isProd() { + String activeProfile = getActiveProfile(); + return Objects.equals("prod", activeProfile); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogAutoConfiguration.java index 172ca3f13..d1f7453b6 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogAutoConfiguration.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.framework.apilog.config; import cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter; +import cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor; import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService; import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkServiceImpl; import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService; @@ -10,23 +11,26 @@ import cn.iocoder.yudao.framework.web.config.WebProperties; import cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration; import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi; import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi; +import jakarta.servlet.Filter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; - -import jakarta.servlet.Filter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @AutoConfiguration(after = YudaoWebAutoConfiguration.class) -public class YudaoApiLogAutoConfiguration { +public class YudaoApiLogAutoConfiguration implements WebMvcConfigurer { @Bean + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") public ApiAccessLogFrameworkService apiAccessLogFrameworkService(ApiAccessLogApi apiAccessLogApi) { return new ApiAccessLogFrameworkServiceImpl(apiAccessLogApi); } @Bean + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") public ApiErrorLogFrameworkService apiErrorLogFrameworkService(ApiErrorLogApi apiErrorLogApi) { return new ApiErrorLogFrameworkServiceImpl(apiErrorLogApi); } @@ -49,4 +53,9 @@ public class YudaoApiLogAutoConfiguration { return bean; } + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new ApiAccessLogInterceptor()); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/annotations/ApiAccessLog.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/annotations/ApiAccessLog.java new file mode 100644 index 000000000..096c3bef2 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/annotations/ApiAccessLog.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.framework.apilog.core.annotations; + +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 访问日志注解 + * + * @author 芋道源码 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ApiAccessLog { + + // ========== 开关字段 ========== + + /** + * 是否记录访问日志 + */ + boolean enable() default true; + /** + * 是否记录请求参数 + * + * 默认记录,主要考虑请求数据一般不大。可手动设置为 false 进行关闭 + */ + boolean requestEnable() default true; + /** + * 是否记录响应结果 + * + * 默认不记录,主要考虑响应数据可能比较大。可手动设置为 true 进行打开 + */ + boolean responseEnable() default false; + /** + * 敏感参数数组 + * + * 添加后,请求参数、响应结果不会记录该参数 + */ + String[] sanitizeKeys() default {}; + + // ========== 模块字段 ========== + + /** + * 操作模块 + * + * 为空时,会尝试读取 {@link io.swagger.v3.oas.annotations.tags.Tag#name()} 属性 + */ + String operateModule() default ""; + /** + * 操作名 + * + * 为空时,会尝试读取 {@link io.swagger.v3.oas.annotations.Operation#summary()} 属性 + */ + String operateName() default ""; + /** + * 操作分类 + * + * 实际并不是数组,因为枚举不能设置 null 作为默认值 + */ + OperateTypeEnum[] operateType() default {}; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/enums/OperateTypeEnum.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/enums/OperateTypeEnum.java new file mode 100644 index 000000000..a7f00558e --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/enums/OperateTypeEnum.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.framework.apilog.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 操作日志的操作类型 + * + * @author ruoyi + */ +@Getter +@AllArgsConstructor +public enum OperateTypeEnum { + + /** + * 查询 + */ + GET(1), + /** + * 新增 + */ + CREATE(2), + /** + * 修改 + */ + UPDATE(3), + /** + * 删除 + */ + DELETE(4), + /** + * 导出 + */ + EXPORT(5), + /** + * 导入 + */ + IMPORT(6), + /** + * 其它 + * + * 在无法归类时,可以选择使用其它。因为还有操作名可以进一步标识 + */ + OTHER(0); + + /** + * 类型 + */ + private final Integer type; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java index ee219de39..654db7a6e 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java @@ -1,38 +1,56 @@ package cn.iocoder.yudao.framework.apilog.core.filter; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLog; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService; import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.web.config.WebProperties; import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; -import lombok.extern.slf4j.Slf4j; - +import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.method.HandlerMethod; + import java.io.IOException; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import java.util.Iterator; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor.*; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; /** * API 访问日志 Filter * + * 目的:记录 API 访问日志到数据库中 + * * @author 芋道源码 */ @Slf4j public class ApiAccessLogFilter extends ApiRequestFilter { + private static final String[] SANITIZE_KEYS = new String[]{"password", "token", "accessToken", "refreshToken"}; + private final String applicationName; private final ApiAccessLogFrameworkService apiAccessLogFrameworkService; @@ -44,6 +62,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter { } @Override + @SuppressWarnings("NullableProblems") protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 获得开始时间 @@ -66,45 +85,166 @@ public class ApiAccessLogFilter extends ApiRequestFilter { private void createApiAccessLog(HttpServletRequest request, LocalDateTime beginTime, Map queryString, String requestBody, Exception ex) { - ApiAccessLog accessLog = new ApiAccessLog(); + ApiAccessLogCreateReqDTO accessLog = new ApiAccessLogCreateReqDTO(); try { - this.buildApiAccessLogDTO(accessLog, request, beginTime, queryString, requestBody, ex); + boolean enable = buildApiAccessLog(accessLog, request, beginTime, queryString, requestBody, ex); + if (!enable) { + return; + } apiAccessLogFrameworkService.createApiAccessLog(accessLog); } catch (Throwable th) { log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th); } } - private void buildApiAccessLogDTO(ApiAccessLog accessLog, HttpServletRequest request, LocalDateTime beginTime, + private boolean buildApiAccessLog(ApiAccessLogCreateReqDTO accessLog, HttpServletRequest request, LocalDateTime beginTime, Map queryString, String requestBody, Exception ex) { + // 判断:是否要记录操作日志 + HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(ATTRIBUTE_HANDLER_METHOD); + ApiAccessLog accessLogAnnotation = null; + if (handlerMethod != null) { + accessLogAnnotation = handlerMethod.getMethodAnnotation(ApiAccessLog.class); + if (accessLogAnnotation != null && BooleanUtil.isFalse(accessLogAnnotation.enable())) { + return false; + } + } + // 处理用户信息 - accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request)); - accessLog.setUserType(WebFrameworkUtils.getLoginUserType(request)); + accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request)) + .setUserType(WebFrameworkUtils.getLoginUserType(request)); // 设置访问结果 CommonResult result = WebFrameworkUtils.getCommonResult(request); if (result != null) { - accessLog.setResultCode(result.getCode()); - accessLog.setResultMsg(result.getMsg()); + accessLog.setResultCode(result.getCode()).setResultMsg(result.getMsg()); } else if (ex != null) { - accessLog.setResultCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode()); - accessLog.setResultMsg(ExceptionUtil.getRootCauseMessage(ex)); + accessLog.setResultCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode()) + .setResultMsg(ExceptionUtil.getRootCauseMessage(ex)); } else { - accessLog.setResultCode(0); - accessLog.setResultMsg(""); + accessLog.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()).setResultMsg(""); + } + // 设置请求字段 + accessLog.setTraceId(TracerUtils.getTraceId()).setApplicationName(applicationName) + .setRequestUrl(request.getRequestURI()).setRequestMethod(request.getMethod()) + .setUserAgent(ServletUtils.getUserAgent(request)).setUserIp(ServletUtils.getClientIP(request)); + String[] sanitizeKeys = accessLogAnnotation != null ? accessLogAnnotation.sanitizeKeys() : null; + Boolean requestEnable = accessLogAnnotation != null ? accessLogAnnotation.requestEnable() : Boolean.TRUE; + if (!BooleanUtil.isFalse(requestEnable)) { // 默认记录,所以判断 !false + Map requestParams = MapUtil.builder() + .put("query", sanitizeMap(queryString, sanitizeKeys)) + .put("body", sanitizeJson(requestBody, sanitizeKeys)).build(); + accessLog.setRequestParams(toJsonString(requestParams)); + } + Boolean responseEnable = accessLogAnnotation != null ? accessLogAnnotation.responseEnable() : Boolean.FALSE; + if (BooleanUtil.isTrue(responseEnable)) { // 默认不记录,默认强制要求 true + accessLog.setResponseBody(sanitizeJson(result, sanitizeKeys)); } - // 设置其它字段 - accessLog.setTraceId(TracerUtils.getTraceId()); - accessLog.setApplicationName(applicationName); - accessLog.setRequestUrl(request.getRequestURI()); - Map requestParams = MapUtil.builder().put("query", queryString).put("body", requestBody).build(); - accessLog.setRequestParams(toJsonString(requestParams)); - accessLog.setRequestMethod(request.getMethod()); - accessLog.setUserAgent(ServletUtils.getUserAgent(request)); - accessLog.setUserIp(ServletUtils.getClientIP(request)); // 持续时间 - accessLog.setBeginTime(beginTime); - accessLog.setEndTime(LocalDateTime.now()); - accessLog.setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.MILLIS)); + accessLog.setBeginTime(beginTime).setEndTime(LocalDateTime.now()) + .setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.MILLIS)); + + // 操作模块 + if (handlerMethod != null) { + Tag tagAnnotation = handlerMethod.getBeanType().getAnnotation(Tag.class); + Operation operationAnnotation = handlerMethod.getMethodAnnotation(Operation.class); + String operateModule = accessLogAnnotation != null ? accessLogAnnotation.operateModule() : + tagAnnotation != null ? StrUtil.nullToDefault(tagAnnotation.name(), tagAnnotation.description()) : null; + String operateName = accessLogAnnotation != null ? accessLogAnnotation.operateName() : + operationAnnotation != null ? operationAnnotation.summary() : null; + OperateTypeEnum operateType = accessLogAnnotation != null && accessLogAnnotation.operateType().length > 0 ? + accessLogAnnotation.operateType()[0] : parseOperateLogType(request); + accessLog.setOperateModule(operateModule).setOperateName(operateName).setOperateType(operateType.getType()); + } + return true; + } + + // ========== 解析 @ApiAccessLog、@Swagger 注解 ========== + + private static OperateTypeEnum parseOperateLogType(HttpServletRequest request) { + RequestMethod requestMethod = RequestMethod.resolve(request.getMethod()); + if (requestMethod == null) { + return OperateTypeEnum.OTHER; + } + switch (requestMethod) { + case GET: + return OperateTypeEnum.GET; + case POST: + return OperateTypeEnum.CREATE; + case PUT: + return OperateTypeEnum.UPDATE; + case DELETE: + return OperateTypeEnum.DELETE; + default: + return OperateTypeEnum.OTHER; + } + } + + // ========== 请求和响应的脱敏逻辑,移除类似 password、token 等敏感字段 ========== + + private static String sanitizeMap(Map map, String[] sanitizeKeys) { + if (CollUtil.isNotEmpty(map)) { + return null; + } + if (sanitizeKeys != null) { + MapUtil.removeAny(map, sanitizeKeys); + } + MapUtil.removeAny(map, SANITIZE_KEYS); + return JsonUtils.toJsonString(map); + } + + private static String sanitizeJson(String jsonString, String[] sanitizeKeys) { + if (StrUtil.isEmpty(jsonString)) { + return null; + } + try { + JsonNode rootNode = JsonUtils.parseTree(jsonString); + sanitizeJson(rootNode, sanitizeKeys); + return JsonUtils.toJsonString(rootNode); + } catch (Exception e) { + // 脱敏失败的情况下,直接忽略异常,避免影响用户请求 + log.error("[sanitizeJson][脱敏({}) 发生异常]", jsonString, e); + return jsonString; + } + } + + private static String sanitizeJson(CommonResult commonResult, String[] sanitizeKeys) { + if (commonResult == null) { + return null; + } + String jsonString = toJsonString(commonResult); + try { + JsonNode rootNode = JsonUtils.parseTree(jsonString); + sanitizeJson(rootNode.get("data"), sanitizeKeys); // 只处理 data 字段,不处理 code、msg 字段,避免错误被脱敏掉 + return JsonUtils.toJsonString(rootNode); + } catch (Exception e) { + // 脱敏失败的情况下,直接忽略异常,避免影响用户请求 + log.error("[sanitizeJson][脱敏({}) 发生异常]", jsonString, e); + return jsonString; + } + } + + private static void sanitizeJson(JsonNode node, String[] sanitizeKeys) { + // 情况一:数组,遍历处理 + if (node.isArray()) { + for (JsonNode childNode : node) { + sanitizeJson(childNode, sanitizeKeys); + } + return; + } + // 情况二:非 Object,只是某个值,直接返回 + if (!node.isObject()) { + return; + } + // 情况三:Object,遍历处理 + Iterator> iterator = node.properties().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (ArrayUtil.contains(sanitizeKeys, entry.getKey()) + || ArrayUtil.contains(SANITIZE_KEYS, entry.getKey())) { + iterator.remove(); + continue; + } + sanitizeJson(entry.getValue(), sanitizeKeys); + } } } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java new file mode 100644 index 000000000..431c201d8 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.framework.apilog.core.interceptor; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StopWatch; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.Map; + +/** + * API 访问日志 Interceptor + * + * 目的:在非 prod 环境时,打印 request 和 response 两条日志到日志文件(控制台)中。 + * + * @author 芋道源码 + */ +@Slf4j +public class ApiAccessLogInterceptor implements HandlerInterceptor { + + public static String ATTRIBUTE_HANDLER_METHOD = "HANDLER_METHOD"; + + private static String ATTRIBUTE_STOP_WATCH = "ApiAccessLogInterceptor.StopWatch"; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + // 记录 HandlerMethod,提供给 ApiAccessLogFilter 使用 + HandlerMethod handlerMethod = handler instanceof HandlerMethod ? (HandlerMethod) handler : null; + if (handlerMethod != null) { + request.setAttribute(ATTRIBUTE_HANDLER_METHOD, handlerMethod); + } + + // 打印 request 日志 + if (!SpringUtils.isProd()) { + Map queryString = ServletUtils.getParamMap(request); + String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null; + if (CollUtil.isEmpty(queryString) && StrUtil.isEmpty(requestBody)) { + log.info("[preHandle][开始请求 URL({}) 无参数]", request.getRequestURI()); + } else { + log.info("[preHandle][开始请求 URL({}) 参数({})]", request.getRequestURI(), + StrUtil.nullToDefault(requestBody, queryString.toString())); + } + // 计时 + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch); + } + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + // 打印 response 日志 + if (!SpringUtils.isProd()) { + StopWatch stopWatch = (StopWatch) request.getAttribute(ATTRIBUTE_STOP_WATCH); + stopWatch.stop(); + log.info("[afterCompletion][完成请求 URL({}) 耗时({} ms)]", + request.getRequestURI(), stopWatch.getTotalTimeMillis()); + } + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java deleted file mode 100644 index 457603008..000000000 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java +++ /dev/null @@ -1,85 +0,0 @@ -package cn.iocoder.yudao.framework.apilog.core.service; - -import lombok.Data; - -import jakarta.validation.constraints.NotNull; -import java.time.LocalDateTime; - -/** - * API 访问日志 - * - * @author 芋道源码 - */ -@Data -public class ApiAccessLog { - - /** - * 链路追踪编号 - */ - private String traceId; - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - */ - private Integer userType; - /** - * 应用名 - */ - @NotNull(message = "应用名不能为空") - private String applicationName; - - /** - * 请求方法名 - */ - @NotNull(message = "http 请求方法不能为空") - private String requestMethod; - /** - * 访问地址 - */ - @NotNull(message = "访问地址不能为空") - private String requestUrl; - /** - * 请求参数 - */ - @NotNull(message = "请求参数不能为空") - private String requestParams; - /** - * 用户 IP - */ - @NotNull(message = "ip 不能为空") - private String userIp; - /** - * 浏览器 UA - */ - @NotNull(message = "User-Agent 不能为空") - private String userAgent; - - /** - * 开始请求时间 - */ - @NotNull(message = "开始请求时间不能为空") - private LocalDateTime beginTime; - /** - * 结束请求时间 - */ - @NotNull(message = "结束请求时间不能为空") - private LocalDateTime endTime; - /** - * 执行时长,单位:毫秒 - */ - @NotNull(message = "执行时长不能为空") - private Integer duration; - /** - * 结果码 - */ - @NotNull(message = "错误码不能为空") - private Integer resultCode; - /** - * 结果提示 - */ - private String resultMsg; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkService.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkService.java index b8f21883b..2f3c78f60 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkService.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkService.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.framework.apilog.core.service; +import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; + /** * API 访问日志 Framework Service 接口 * @@ -10,7 +12,8 @@ public interface ApiAccessLogFrameworkService { /** * 创建 API 访问日志 * - * @param apiAccessLog API 访问日志 + * @param reqDTO API 访问日志 */ - void createApiAccessLog(ApiAccessLog apiAccessLog); + void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO); + } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java index 83162f164..8f8e34306 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.framework.apilog.core.service; -import cn.hutool.core.bean.BeanUtil; import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi; import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; import lombok.RequiredArgsConstructor; @@ -20,8 +19,7 @@ public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkSe @Override @Async - public void createApiAccessLog(ApiAccessLog apiAccessLog) { - ApiAccessLogCreateReqDTO reqDTO = BeanUtil.copyProperties(apiAccessLog, ApiAccessLogCreateReqDTO.class); + public void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO) { apiAccessLogApi.createApiAccessLog(reqDTO); } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java deleted file mode 100644 index 2956d204c..000000000 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java +++ /dev/null @@ -1,107 +0,0 @@ -package cn.iocoder.yudao.framework.apilog.core.service; - -import lombok.Data; - -import jakarta.validation.constraints.NotNull; -import java.time.LocalDateTime; - -/** - * API 错误日志 - * - * @author 芋道源码 - */ -@Data -public class ApiErrorLog { - - /** - * 链路编号 - */ - private String traceId; - /** - * 账号编号 - */ - private Long userId; - /** - * 用户类型 - */ - private Integer userType; - /** - * 应用名 - */ - @NotNull(message = "应用名不能为空") - private String applicationName; - - /** - * 请求方法名 - */ - @NotNull(message = "http 请求方法不能为空") - private String requestMethod; - /** - * 访问地址 - */ - @NotNull(message = "访问地址不能为空") - private String requestUrl; - /** - * 请求参数 - */ - @NotNull(message = "请求参数不能为空") - private String requestParams; - /** - * 用户 IP - */ - @NotNull(message = "ip 不能为空") - private String userIp; - /** - * 浏览器 UA - */ - @NotNull(message = "User-Agent 不能为空") - private String userAgent; - - /** - * 异常时间 - */ - @NotNull(message = "异常时间不能为空") - private LocalDateTime exceptionTime; - /** - * 异常名 - */ - @NotNull(message = "异常名不能为空") - private String exceptionName; - /** - * 异常发生的类全名 - */ - @NotNull(message = "异常发生的类全名不能为空") - private String exceptionClassName; - /** - * 异常发生的类文件 - */ - @NotNull(message = "异常发生的类文件不能为空") - private String exceptionFileName; - /** - * 异常发生的方法名 - */ - @NotNull(message = "异常发生的方法名不能为空") - private String exceptionMethodName; - /** - * 异常发生的方法所在行 - */ - @NotNull(message = "异常发生的方法所在行不能为空") - private Integer exceptionLineNumber; - /** - * 异常的栈轨迹异常的栈轨迹 - */ - @NotNull(message = "异常的栈轨迹不能为空") - private String exceptionStackTrace; - /** - * 异常导致的根消息 - */ - @NotNull(message = "异常导致的根消息不能为空") - private String exceptionRootCauseMessage; - /** - * 异常导致的消息 - */ - @NotNull(message = "异常导致的消息不能为空") - private String exceptionMessage; - - -} diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkService.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkService.java index dfc71cb1f..33bebb711 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkService.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkService.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.framework.apilog.core.service; +import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; + /** * API 错误日志 Framework Service 接口 * @@ -10,7 +12,8 @@ public interface ApiErrorLogFrameworkService { /** * 创建 API 错误日志 * - * @param apiErrorLog API 错误日志 + * @param reqDTO API 错误日志 */ - void createApiErrorLog(ApiErrorLog apiErrorLog); + void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO); + } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java index cb5abe3c2..32e4f8043 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.framework.apilog.core.service; -import cn.hutool.core.bean.BeanUtil; import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi; import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; import lombok.RequiredArgsConstructor; @@ -20,8 +19,7 @@ public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkServ @Override @Async - public void createApiErrorLog(ApiErrorLog apiErrorLog) { - ApiErrorLogCreateReqDTO reqDTO = BeanUtil.copyProperties(apiErrorLog, ApiErrorLogCreateReqDTO.class); + public void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO) { apiErrorLogApi.createApiErrorLog(reqDTO); } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java index 5af13b789..243f949f2 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.web.core.handler; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLog; import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -11,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; +import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -46,6 +46,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC @Slf4j public class GlobalExceptionHandler { + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") private final String applicationName; private final ApiErrorLogFrameworkService apiErrorLogFrameworkService; @@ -237,10 +238,10 @@ public class GlobalExceptionHandler { private void createExceptionLog(HttpServletRequest req, Throwable e) { // 插入错误日志 - ApiErrorLog errorLog = new ApiErrorLog(); + ApiErrorLogCreateReqDTO errorLog = new ApiErrorLogCreateReqDTO(); try { // 初始化 errorLog - initExceptionLog(errorLog, req, e); + buildExceptionLog(errorLog, req, e); // 执行插入 errorLog apiErrorLogFrameworkService.createApiErrorLog(errorLog); } catch (Throwable th) { @@ -248,7 +249,7 @@ public class GlobalExceptionHandler { } } - private void initExceptionLog(ApiErrorLog errorLog, HttpServletRequest request, Throwable e) { + private void buildExceptionLog(ApiErrorLogCreateReqDTO errorLog, HttpServletRequest request, Throwable e) { // 处理用户信息 errorLog.setUserId(WebFrameworkUtils.getLoginUserId(request)); errorLog.setUserType(WebFrameworkUtils.getLoginUserType(request)); diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiAccessLogCreateReqDTO.java b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiAccessLogCreateReqDTO.java index cbab51a54..88add1ab6 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiAccessLogCreateReqDTO.java +++ b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiAccessLogCreateReqDTO.java @@ -44,8 +44,11 @@ public class ApiAccessLogCreateReqDTO { /** * 请求参数 */ - @NotNull(message = "请求参数不能为空") private String requestParams; + /** + * 响应结果 + */ + private String responseBody; /** * 用户 IP */ @@ -57,6 +60,21 @@ public class ApiAccessLogCreateReqDTO { @NotNull(message = "User-Agent 不能为空") private String userAgent; + /** + * 操作模块 + */ + private String operateModule; + /** + * 操作名 + */ + private String operateName; + /** + * 操作分类 + * + * 枚举,参见 OperateTypeEnum 类 + */ + private Integer operateType; + /** * 开始请求时间 */ diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java index 5e3f3c976..ff0fed7a3 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java @@ -48,6 +48,10 @@ public class ApiAccessLogRespVO { @ExcelProperty("请求参数") private String requestParams; + @Schema(description = "响应结果") + @ExcelProperty("响应结果") + private String responseBody; + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") @ExcelProperty("用户 IP") private String userIp; @@ -56,6 +60,19 @@ public class ApiAccessLogRespVO { @ExcelProperty("浏览器 UA") private String userAgent; + @Schema(description = "操作模块", requiredMode = Schema.RequiredMode.REQUIRED, example = "商品模块") + @ExcelProperty("操作模块") + private String operateModule; + + @Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建商品") + @ExcelProperty("操作名") + private String operateName; + + @Schema(description = "操作分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "操作分类", converter = DictConvert.class) + @DictFormat(DictTypeConstants.OPERATE_TYPE) + private Integer operateType; + @Schema(description = "开始请求时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("开始请求时间") private LocalDateTime beginTime; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java index ab4292bf9..d4850fcc8 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.logger; +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; @@ -70,6 +71,10 @@ public class ApiAccessLogDO extends BaseDO { * body: Quest Body */ private String requestParams; + /** + * 响应结果 + */ + private String responseBody; /** * 用户 IP */ @@ -81,6 +86,21 @@ public class ApiAccessLogDO extends BaseDO { // ========== 执行相关字段 ========== + /** + * 操作模块 + */ + private String operateModule; + /** + * 操作名 + */ + private String operateName; + /** + * 操作分类 + * + * 枚举 {@link OperateTypeEnum} + */ + private Integer operateType; + /** * 开始请求时间 */ @@ -93,6 +113,7 @@ public class ApiAccessLogDO extends BaseDO { * 执行时长,单位:毫秒 */ private Integer duration; + /** * 结果码 * diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql index a5f896ce1..9f383ac5a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql @@ -94,8 +94,12 @@ CREATE TABLE IF NOT EXISTS "infra_api_access_log" ( "request_method" varchar(16) not null default '', "request_url" varchar(255) not null default '', "request_params" varchar(8000) not null default '', + "response_body" varchar(8000) not null default '', "user_ip" varchar(50) not null, "user_agent" varchar(512) not null, + `operate_module` varchar(50) NOT NULL, + `operate_name` varchar(50) NOT NULL, + `operate_type` bigint(4) NOT NULL DEFAULT '0', "begin_time" timestamp not null, "end_time" timestamp not null, "duration" integer not null, @@ -108,7 +112,7 @@ CREATE TABLE IF NOT EXISTS "infra_api_access_log" ( "deleted" bit not null default false, "tenant_id" bigint not null default '0', primary key ("id") - ) COMMENT 'API 访问日志表'; +) COMMENT 'API 访问日志表'; CREATE TABLE IF NOT EXISTS "infra_api_error_log" ( "id" bigint not null GENERATED BY DEFAULT AS IDENTITY, diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java index ecf06ad37..190904677 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.system.controller.admin.notify; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -88,6 +89,7 @@ public class NotifyMessageController { @GetMapping("/get-unread-count") @Operation(summary = "获得当前用户的未读站内信数量") + @ApiAccessLog(enable = false) // 由于前端会不断轮询该接口,记录日志没有意义 public CommonResult getUnreadNotifyMessageCount() { return success(notifyMessageService.getUnreadNotifyMessageCount( getLoginUserId(), UserTypeEnum.ADMIN.getValue())); From 22a170ee62ae8dfb1375a0963692c2098bb2228a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 3 Apr 2024 19:55:14 +0800 Subject: [PATCH 66/81] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=AE=BF=E9=97=AE=E6=97=A5=E5=BF=97=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=98=AF=E5=90=A6=E8=AE=B0=E5=BD=95=E3=80=81?= =?UTF-8?q?=E8=84=B1=E6=95=8F=E3=80=81=E6=93=8D=E4=BD=9C=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E7=AD=89=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apilog/core/interceptor/ApiAccessLogInterceptor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java index 431c201d8..1cd43916f 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java @@ -23,9 +23,9 @@ import java.util.Map; @Slf4j public class ApiAccessLogInterceptor implements HandlerInterceptor { - public static String ATTRIBUTE_HANDLER_METHOD = "HANDLER_METHOD"; + public static final String ATTRIBUTE_HANDLER_METHOD = "HANDLER_METHOD"; - private static String ATTRIBUTE_STOP_WATCH = "ApiAccessLogInterceptor.StopWatch"; + private static final String ATTRIBUTE_STOP_WATCH = "ApiAccessLogInterceptor.StopWatch"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { From 000b6cf47fd8f14827a28b43c4f2c410b372163c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 4 Apr 2024 01:21:08 +0800 Subject: [PATCH 67/81] =?UTF-8?q?=E3=80=90=E9=87=8D=E6=9E=84=E3=80=91V2=20?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97=E8=BD=AC=E6=AD=A3=EF=BC=8C?= =?UTF-8?q?=E5=9F=BA=E4=BA=8E=E6=B3=A8=E8=A7=A3=E7=9A=84=E5=8F=AF=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E5=8F=98=E9=87=8F=E3=80=81=E5=8F=AF=E4=BB=A5=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=87=BD=E6=95=B0=E7=9A=84=E9=80=9A=E7=94=A8?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/mysql/ruoyi-vue-pro.sql | 56 +-- .../YudaoOperateLogAutoConfiguration.java | 23 -- .../operatelog/core/aop/OperateLogAspect.java | 375 ------------------ .../operatelog/core/package-info.java | 1 - .../operatelog/core/service/OperateLog.java | 110 ----- .../service/OperateLogFrameworkService.java | 17 - .../OperateLogFrameworkServiceImpl.java | 28 -- .../operatelog/core/util/OperateLogUtils.java | 21 - .../framework/operatelog/package-info.java | 6 - ...ot.autoconfigure.AutoConfiguration.imports | 1 - .../core/service/LogRecordServiceImpl.java | 14 +- .../operatelog/CrmOperateLogController.java | 6 +- .../service/codegen/inner/CodegenEngine.java | 6 +- .../codegen/java/controller/controller.vm | 4 +- .../system/api/logger/OperateLogApi.java | 14 +- .../logger/dto/OperateLogCreateReqDTO.java | 95 ++--- ...eReqDTO.java => OperateLogPageReqDTO.java} | 4 +- ...gV2RespDTO.java => OperateLogRespDTO.java} | 2 +- .../logger/dto/OperateLogV2CreateReqDTO.java | 84 ---- .../system/api/logger/OperateLogApiImpl.java | 24 +- .../vo/operatelog/OperateLogPageReqVO.java | 21 +- .../vo/operatelog/OperateLogRespVO.java | 53 +-- .../dal/dataobject/logger/OperateLogDO.java | 83 +--- .../dal/dataobject/logger/OperateLogV2DO.java | 87 ---- .../dal/mysql/logger/OperateLogMapper.java | 34 +- .../dal/mysql/logger/OperateLogV2Mapper.java | 21 - .../service/logger/OperateLogService.java | 17 +- .../service/logger/OperateLogServiceImpl.java | 46 +-- .../logger/OperateLogServiceImplTest.java | 103 ++--- .../src/test/resources/sql/create_tables.sql | 17 +- 30 files changed, 185 insertions(+), 1188 deletions(-) delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogAutoConfiguration.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/package-info.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkService.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkServiceImpl.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/util/OperateLogUtils.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/package-info.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/{OperateLogV2PageReqDTO.java => OperateLogPageReqDTO.java} (81%) rename yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/{OperateLogV2RespDTO.java => OperateLogRespDTO.java} (96%) delete mode 100644 yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2CreateReqDTO.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index f7b7c292c..2770b0333 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -11,7 +11,7 @@ Target Server Version : 80200 (8.2.0) File Encoding : 65001 - Date: 03/04/2024 19:07:31 + Date: 04/04/2024 01:17:25 */ SET NAMES utf8mb4; @@ -347,7 +347,7 @@ CREATE TABLE `infra_api_access_log` ( `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE, INDEX `idx_create_time`(`create_time` ASC) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 35920 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表'; +) ENGINE = InnoDB AUTO_INCREMENT = 35925 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表'; -- ---------------------------- -- Records of infra_api_access_log @@ -726,7 +726,7 @@ CREATE TABLE `infra_file_config` ( -- ---------------------------- BEGIN; INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库', 1, '我是数据库', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2024-02-28 22:54:07', b'0'); -INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, '七牛存储器', 20, '', b'1', '{\"@class\":\"cn.iocoder.yudao.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3.cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\"}', '1', '2024-01-13 22:11:12', '1', '2024-01-13 22:24:06', b'0'); +INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, '七牛存储器', 20, '', b'1', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3.cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\"}', '1', '2024-01-13 22:11:12', '1', '2024-04-03 19:38:34', b'0'); COMMIT; -- ---------------------------- @@ -1420,7 +1420,7 @@ CREATE TABLE `system_login_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 3066 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; +) ENGINE = InnoDB AUTO_INCREMENT = 3067 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; -- ---------------------------- -- Records of system_login_log @@ -2457,7 +2457,7 @@ CREATE TABLE `system_oauth2_access_token` ( PRIMARY KEY (`id`) USING BTREE, INDEX `idx_access_token`(`access_token` ASC) USING BTREE, INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 6366 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 6372 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; -- ---------------------------- -- Records of system_oauth2_access_token @@ -2579,7 +2579,7 @@ CREATE TABLE `system_oauth2_refresh_token` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1441 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 1442 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; -- ---------------------------- -- Records of system_oauth2_refresh_token @@ -2592,46 +2592,6 @@ COMMIT; -- ---------------------------- DROP TABLE IF EXISTS `system_operate_log`; CREATE TABLE `system_operate_log` ( - `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键', - `trace_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '链路追踪编号', - `user_id` bigint NOT NULL COMMENT '用户编号', - `user_type` tinyint NOT NULL DEFAULT 0 COMMENT '用户类型', - `module` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模块标题', - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作名', - `type` bigint NOT NULL DEFAULT 0 COMMENT '操作分类', - `content` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '操作内容', - `exts` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '拓展字段', - `request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '请求方法名', - `request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '请求地址', - `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户 IP', - `user_agent` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '浏览器 UA', - `java_method` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT 'Java 方法名', - `java_method_args` varchar(8000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT 'Java 方法的参数', - `start_time` datetime NOT NULL COMMENT '操作时间', - `duration` int NOT NULL COMMENT '执行时长', - `result_code` int NOT NULL DEFAULT 0 COMMENT '结果码', - `result_msg` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '结果提示', - `result_data` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '结果数据', - `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', - `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 12000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录'; - --- ---------------------------- --- Records of system_operate_log --- ---------------------------- -BEGIN; -COMMIT; - --- ---------------------------- --- Table structure for system_operate_log_v2 --- ---------------------------- -DROP TABLE IF EXISTS `system_operate_log_v2`; -CREATE TABLE `system_operate_log_v2` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键', `trace_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '链路追踪编号', `user_id` bigint NOT NULL COMMENT '用户编号', @@ -2655,7 +2615,7 @@ CREATE TABLE `system_operate_log_v2` ( ) ENGINE = InnoDB AUTO_INCREMENT = 9019 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本'; -- ---------------------------- --- Records of system_operate_log_v2 +-- Records of system_operate_log -- ---------------------------- BEGIN; COMMIT; @@ -5623,7 +5583,7 @@ CREATE TABLE `system_users` ( -- Records of system_users -- ---------------------------- BEGIN; -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png', 0, '127.0.0.1', '2024-04-03 17:31:00', 'admin', '2021-01-05 17:03:47', NULL, '2024-04-03 17:31:00', b'0', 1); +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png', 0, '127.0.0.1', '2024-04-03 20:01:18', 'admin', '2021-01-05 17:03:47', NULL, '2024-04-03 20:01:18', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-03-18 21:09:04', '', '2021-01-13 23:50:35', NULL, '2024-03-18 21:09:04', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$KhExCYl7lx6eWWZYKsibKOZ8IBJRyuNuCcEOLQ11RYhJKgHmlSwK.', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-03-26 07:11:35', '', '2021-01-21 02:13:53', NULL, '2024-03-26 07:11:35', b'0', 1); diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogAutoConfiguration.java deleted file mode 100644 index 441ec6bbd..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogAutoConfiguration.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.framework.operatelog.config; - -import cn.iocoder.yudao.framework.operatelog.core.aop.OperateLogAspect; -import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkService; -import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkServiceImpl; -import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.context.annotation.Bean; - -@AutoConfiguration -public class YudaoOperateLogAutoConfiguration { - - @Bean - public OperateLogAspect operateLogAspect() { - return new OperateLogAspect(); - } - - @Bean - public OperateLogFrameworkService operateLogFrameworkService(OperateLogApi operateLogApi) { - return new OperateLogFrameworkServiceImpl(operateLogApi); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java deleted file mode 100644 index 4371fa883..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java +++ /dev/null @@ -1,375 +0,0 @@ -package cn.iocoder.yudao.framework.operatelog.core.aop; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; -import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; -import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum; -import cn.iocoder.yudao.framework.operatelog.core.service.OperateLog; -import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkService; -import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; -import com.google.common.collect.Maps; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Operation; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.multipart.MultipartFile; - -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.lang.annotation.Annotation; -import java.lang.reflect.Array; -import java.time.LocalDateTime; -import java.util.*; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.SUCCESS; - -/** - * 拦截使用 @OperateLog 注解,如果满足条件,则生成操作日志。 - * 满足如下任一条件,则会进行记录: - * 1. 使用 @ApiOperation + 非 @GetMapping - * 2. 使用 @OperateLog 注解 - *

- * 但是,如果声明 @OperateLog 注解时,将 enable 属性设置为 false 时,强制不记录。 - * - * @author 芋道源码 - */ -@Aspect -@Slf4j -public class OperateLogAspect { - - /** - * 用于记录操作内容的上下文 - * - * @see OperateLog#getContent() - */ - private static final ThreadLocal CONTENT = new ThreadLocal<>(); - /** - * 用于记录拓展字段的上下文 - * - * @see OperateLog#getExts() - */ - private static final ThreadLocal> EXTS = new ThreadLocal<>(); - - @Resource - private OperateLogFrameworkService operateLogFrameworkService; - - @Around("@annotation(operation)") - public Object around(ProceedingJoinPoint joinPoint, Operation operation) throws Throwable { - // 可能也添加了 @ApiOperation 注解 - cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog = getMethodAnnotation(joinPoint, - cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog.class); - return around0(joinPoint, operateLog, operation); - } - - @Around("!@annotation(io.swagger.v3.oas.annotations.Operation) && @annotation(operateLog)") - // 兼容处理,只添加 @OperateLog 注解的情况 - public Object around(ProceedingJoinPoint joinPoint, - cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog) throws Throwable { - return around0(joinPoint, operateLog, null); - } - - private Object around0(ProceedingJoinPoint joinPoint, - cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog, - Operation operation) throws Throwable { - // 目前,只有管理员,才记录操作日志!所以非管理员,直接调用,不进行记录 - Integer userType = WebFrameworkUtils.getLoginUserType(); - if (!Objects.equals(userType, UserTypeEnum.ADMIN.getValue())) { - return joinPoint.proceed(); - } - - // 记录开始时间 - LocalDateTime startTime = LocalDateTime.now(); - try { - // 执行原有方法 - Object result = joinPoint.proceed(); - // 记录正常执行时的操作日志 - this.log(joinPoint, operateLog, operation, startTime, result, null); - return result; - } catch (Throwable exception) { - this.log(joinPoint, operateLog, operation, startTime, null, exception); - throw exception; - } finally { - clearThreadLocal(); - } - } - - public static void setContent(String content) { - CONTENT.set(content); - } - - public static void addExt(String key, Object value) { - if (EXTS.get() == null) { - EXTS.set(new HashMap<>()); - } - EXTS.get().put(key, value); - } - - private static void clearThreadLocal() { - CONTENT.remove(); - EXTS.remove(); - } - - private void log(ProceedingJoinPoint joinPoint, - cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog, - Operation operation, - LocalDateTime startTime, Object result, Throwable exception) { - try { - // 判断不记录的情况 - if (!isLogEnable(joinPoint, operateLog)) { - return; - } - // 真正记录操作日志 - this.log0(joinPoint, operateLog, operation, startTime, result, exception); - } catch (Throwable ex) { - log.error("[log][记录操作日志时,发生异常,其中参数是 joinPoint({}) operateLog({}) apiOperation({}) result({}) exception({}) ]", - joinPoint, operateLog, operation, result, exception, ex); - } - } - - private void log0(ProceedingJoinPoint joinPoint, - cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog, - Operation operation, - LocalDateTime startTime, Object result, Throwable exception) { - OperateLog operateLogObj = new OperateLog(); - // 补全通用字段 - operateLogObj.setTraceId(TracerUtils.getTraceId()); - operateLogObj.setStartTime(startTime); - // 补充用户信息 - fillUserFields(operateLogObj); - // 补全模块信息 - fillModuleFields(operateLogObj, joinPoint, operateLog, operation); - // 补全请求信息 - fillRequestFields(operateLogObj); - // 补全方法信息 - fillMethodFields(operateLogObj, joinPoint, operateLog, startTime, result, exception); - - // 异步记录日志 - operateLogFrameworkService.createOperateLog(operateLogObj); - } - - private static void fillUserFields(OperateLog operateLogObj) { - operateLogObj.setUserId(WebFrameworkUtils.getLoginUserId()); - operateLogObj.setUserType(WebFrameworkUtils.getLoginUserType()); - } - - private static void fillModuleFields(OperateLog operateLogObj, - ProceedingJoinPoint joinPoint, - cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog, - Operation operation) { - // module 属性 - if (operateLog != null) { - operateLogObj.setModule(operateLog.module()); - } - if (StrUtil.isEmpty(operateLogObj.getModule())) { - Tag tag = getClassAnnotation(joinPoint, Tag.class); - if (tag != null) { - // 优先读取 @Tag 的 name 属性 - if (StrUtil.isNotEmpty(tag.name())) { - operateLogObj.setModule(tag.name()); - } - // 没有的话,读取 @API 的 description 属性 - if (StrUtil.isEmpty(operateLogObj.getModule()) && ArrayUtil.isNotEmpty(tag.description())) { - operateLogObj.setModule(tag.description()); - } - } - } - // name 属性 - if (operateLog != null) { - operateLogObj.setName(operateLog.name()); - } - if (StrUtil.isEmpty(operateLogObj.getName()) && operation != null) { - operateLogObj.setName(operation.summary()); - } - // type 属性 - if (operateLog != null && ArrayUtil.isNotEmpty(operateLog.type())) { - operateLogObj.setType(operateLog.type()[0].getType()); - } - if (operateLogObj.getType() == null) { - RequestMethod requestMethod = obtainFirstMatchRequestMethod(obtainRequestMethod(joinPoint)); - OperateTypeEnum operateLogType = convertOperateLogType(requestMethod); - operateLogObj.setType(operateLogType != null ? operateLogType.getType() : null); - } - // content 和 exts 属性 - operateLogObj.setContent(CONTENT.get()); - operateLogObj.setExts(EXTS.get()); - } - - private static void fillRequestFields(OperateLog operateLogObj) { - // 获得 Request 对象 - HttpServletRequest request = ServletUtils.getRequest(); - if (request == null) { - return; - } - // 补全请求信息 - operateLogObj.setRequestMethod(request.getMethod()); - operateLogObj.setRequestUrl(request.getRequestURI()); - operateLogObj.setUserIp(ServletUtils.getClientIP(request)); - operateLogObj.setUserAgent(ServletUtils.getUserAgent(request)); - } - - private static void fillMethodFields(OperateLog operateLogObj, - ProceedingJoinPoint joinPoint, - cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog, - LocalDateTime startTime, Object result, Throwable exception) { - MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); - operateLogObj.setJavaMethod(methodSignature.toString()); - if (operateLog == null || operateLog.logArgs()) { - operateLogObj.setJavaMethodArgs(obtainMethodArgs(joinPoint)); - } - if (operateLog == null || operateLog.logResultData()) { - operateLogObj.setResultData(obtainResultData(result)); - } - operateLogObj.setDuration((int) (LocalDateTimeUtil.between(startTime, LocalDateTime.now()).toMillis())); - // (正常)处理 resultCode 和 resultMsg 字段 - if (result instanceof CommonResult) { - CommonResult commonResult = (CommonResult) result; - operateLogObj.setResultCode(commonResult.getCode()); - operateLogObj.setResultMsg(commonResult.getMsg()); - } else { - operateLogObj.setResultCode(SUCCESS.getCode()); - } - // (异常)处理 resultCode 和 resultMsg 字段 - if (exception != null) { - operateLogObj.setResultCode(INTERNAL_SERVER_ERROR.getCode()); - operateLogObj.setResultMsg(ExceptionUtil.getRootCauseMessage(exception)); - } - } - - private static boolean isLogEnable(ProceedingJoinPoint joinPoint, - cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog) { - // 有 @OperateLog 注解的情况下 - if (operateLog != null) { - return operateLog.enable(); - } - // 没有 @ApiOperation 注解的情况下,只记录 POST、PUT、DELETE 的情况 - return obtainFirstLogRequestMethod(obtainRequestMethod(joinPoint)) != null; - } - - private static RequestMethod obtainFirstLogRequestMethod(RequestMethod[] requestMethods) { - if (ArrayUtil.isEmpty(requestMethods)) { - return null; - } - return Arrays.stream(requestMethods).filter(requestMethod -> - requestMethod == RequestMethod.POST - || requestMethod == RequestMethod.PUT - || requestMethod == RequestMethod.DELETE) - .findFirst().orElse(null); - } - - private static RequestMethod obtainFirstMatchRequestMethod(RequestMethod[] requestMethods) { - if (ArrayUtil.isEmpty(requestMethods)) { - return null; - } - // 优先,匹配最优的 POST、PUT、DELETE - RequestMethod result = obtainFirstLogRequestMethod(requestMethods); - if (result != null) { - return result; - } - // 然后,匹配次优的 GET - result = Arrays.stream(requestMethods).filter(requestMethod -> requestMethod == RequestMethod.GET) - .findFirst().orElse(null); - if (result != null) { - return result; - } - // 兜底,获得第一个 - return requestMethods[0]; - } - - private static OperateTypeEnum convertOperateLogType(RequestMethod requestMethod) { - if (requestMethod == null) { - return null; - } - switch (requestMethod) { - case GET: - return OperateTypeEnum.GET; - case POST: - return OperateTypeEnum.CREATE; - case PUT: - return OperateTypeEnum.UPDATE; - case DELETE: - return OperateTypeEnum.DELETE; - default: - return OperateTypeEnum.OTHER; - } - } - - private static RequestMethod[] obtainRequestMethod(ProceedingJoinPoint joinPoint) { - RequestMapping requestMapping = AnnotationUtils.getAnnotation( // 使用 Spring 的工具类,可以处理 @RequestMapping 别名注解 - ((MethodSignature) joinPoint.getSignature()).getMethod(), RequestMapping.class); - return requestMapping != null ? requestMapping.method() : new RequestMethod[]{}; - } - - @SuppressWarnings("SameParameterValue") - private static T getMethodAnnotation(ProceedingJoinPoint joinPoint, Class annotationClass) { - return ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(annotationClass); - } - - @SuppressWarnings("SameParameterValue") - private static T getClassAnnotation(ProceedingJoinPoint joinPoint, Class annotationClass) { - return ((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaringClass().getAnnotation(annotationClass); - } - - private static String obtainMethodArgs(ProceedingJoinPoint joinPoint) { - // TODO 提升:参数脱敏和忽略 - MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); - String[] argNames = methodSignature.getParameterNames(); - Object[] argValues = joinPoint.getArgs(); - // 拼接参数 - Map args = Maps.newHashMapWithExpectedSize(argValues.length); - for (int i = 0; i < argNames.length; i++) { - String argName = argNames[i]; - Object argValue = argValues[i]; - // 被忽略时,标记为 ignore 字符串,避免和 null 混在一起 - args.put(argName, !isIgnoreArgs(argValue) ? argValue : "[ignore]"); - } - return JsonUtils.toJsonString(args); - } - - private static String obtainResultData(Object result) { - // TODO 提升:结果脱敏和忽略 - if (result instanceof CommonResult) { - result = ((CommonResult) result).getData(); - } - return JsonUtils.toJsonString(result); - } - - private static boolean isIgnoreArgs(Object object) { - Class clazz = object.getClass(); - // 处理数组的情况 - if (clazz.isArray()) { - return IntStream.range(0, Array.getLength(object)) - .anyMatch(index -> isIgnoreArgs(Array.get(object, index))); - } - // 递归,处理数组、Collection、Map 的情况 - if (Collection.class.isAssignableFrom(clazz)) { - return ((Collection) object).stream() - .anyMatch((Predicate) OperateLogAspect::isIgnoreArgs); - } - if (Map.class.isAssignableFrom(clazz)) { - return isIgnoreArgs(((Map) object).values()); - } - // obj - return object instanceof MultipartFile - || object instanceof HttpServletRequest - || object instanceof HttpServletResponse - || object instanceof BindingResult; - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/package-info.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/package-info.java deleted file mode 100644 index 58aa3d59e..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.framework.operatelog.core; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java deleted file mode 100644 index 1e3b8c8a8..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java +++ /dev/null @@ -1,110 +0,0 @@ -package cn.iocoder.yudao.framework.operatelog.core.service; - -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.Map; - -/** - * 操作日志 - * - * @author 芋道源码 - */ -@Data -public class OperateLog { - - /** - * 链路追踪编号 - */ - private String traceId; - - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - */ - private Integer userType; - - /** - * 操作模块 - */ - private String module; - - /** - * 操作名 - */ - private String name; - - /** - * 操作分类 - */ - private Integer type; - - /** - * 操作明细 - */ - private String content; - - /** - * 拓展字段 - */ - private Map exts; - - /** - * 请求方法名 - */ - private String requestMethod; - - /** - * 请求地址 - */ - private String requestUrl; - - /** - * 用户 IP - */ - private String userIp; - - /** - * 浏览器 UserAgent - */ - private String userAgent; - - /** - * Java 方法名 - */ - private String javaMethod; - - /** - * Java 方法的参数 - */ - private String javaMethodArgs; - - /** - * 开始时间 - */ - private LocalDateTime startTime; - - /** - * 执行时长,单位:毫秒 - */ - private Integer duration; - - /** - * 结果码 - */ - private Integer resultCode; - - /** - * 结果提示 - */ - private String resultMsg; - - /** - * 结果数据 - */ - private String resultData; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkService.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkService.java deleted file mode 100644 index 235616244..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkService.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.framework.operatelog.core.service; - -/** - * 操作日志 Framework Service 接口 - * - * @author 芋道源码 - */ -public interface OperateLogFrameworkService { - - /** - * 记录操作日志 - * - * @param operateLog 操作日志请求 - */ - void createOperateLog(OperateLog operateLog); - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkServiceImpl.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkServiceImpl.java deleted file mode 100644 index 495193f7c..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLogFrameworkServiceImpl.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.framework.operatelog.core.service; - -import cn.hutool.core.bean.BeanUtil; -import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; -import lombok.RequiredArgsConstructor; -import org.springframework.scheduling.annotation.Async; - -/** - * 操作日志 Framework Service 实现类 - * - * 基于 {@link OperateLogApi} 实现,记录操作日志 - * - * @author 芋道源码 - */ -@RequiredArgsConstructor -public class OperateLogFrameworkServiceImpl implements OperateLogFrameworkService { - - private final OperateLogApi operateLogApi; - - @Override - @Async - public void createOperateLog(OperateLog operateLog) { - OperateLogCreateReqDTO reqDTO = BeanUtil.toBean(operateLog, OperateLogCreateReqDTO.class); - operateLogApi.createOperateLog(reqDTO); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/util/OperateLogUtils.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/util/OperateLogUtils.java deleted file mode 100644 index a9801e50e..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/util/OperateLogUtils.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.framework.operatelog.core.util; - -import cn.iocoder.yudao.framework.operatelog.core.aop.OperateLogAspect; - -/** - * 操作日志工具类 - * 目前主要的作用,是提供给业务代码,记录操作明细和拓展字段 - * - * @author 芋道源码 - */ -public class OperateLogUtils { - - public static void setContent(String content) { - OperateLogAspect.setContent(content); - } - - public static void addExt(String key, Object value) { - OperateLogAspect.addExt(key, value); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/package-info.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/package-info.java deleted file mode 100644 index d272b53b4..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 用户操作日志:记录用户的操作,用于对用户的操作的审计与追溯,永久保存。 - * - * @author 芋道源码 - */ -package cn.iocoder.yudao.framework.operatelog; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 04ccab1cd..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -cn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogAutoConfiguration \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/LogRecordServiceImpl.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/LogRecordServiceImpl.java index 5f0ba9b6d..d6aeb3bf0 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/LogRecordServiceImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/LogRecordServiceImpl.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; import com.mzt.logapi.beans.LogRecord; import com.mzt.logapi.service.ILogRecordService; import jakarta.annotation.Resource; @@ -30,7 +30,7 @@ public class LogRecordServiceImpl implements ILogRecordService { @Override public void record(LogRecord logRecord) { // 1. 补全通用字段 - OperateLogV2CreateReqDTO reqDTO = new OperateLogV2CreateReqDTO(); + OperateLogCreateReqDTO reqDTO = new OperateLogCreateReqDTO(); reqDTO.setTraceId(TracerUtils.getTraceId()); // 补充用户信息 fillUserFields(reqDTO); @@ -40,12 +40,10 @@ public class LogRecordServiceImpl implements ILogRecordService { fillRequestFields(reqDTO); // 2. 异步记录日志 - operateLogApi.createOperateLogV2(reqDTO); - // TODO 测试结束删除或搞个开关 - log.info("操作日志 ===> {}", reqDTO); + operateLogApi.createOperateLog(reqDTO); } - private static void fillUserFields(OperateLogV2CreateReqDTO reqDTO) { + private static void fillUserFields(OperateLogCreateReqDTO reqDTO) { // 使用 SecurityFrameworkUtils。因为要考虑,rpc、mq、job,它其实不是 web; LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); if (loginUser == null) { @@ -55,7 +53,7 @@ public class LogRecordServiceImpl implements ILogRecordService { reqDTO.setUserType(loginUser.getUserType()); } - public static void fillModuleFields(OperateLogV2CreateReqDTO reqDTO, LogRecord logRecord) { + public static void fillModuleFields(OperateLogCreateReqDTO reqDTO, LogRecord logRecord) { reqDTO.setType(logRecord.getType()); // 大模块类型,例如:CRM 客户 reqDTO.setSubType(logRecord.getSubType());// 操作名称,例如:转移客户 reqDTO.setBizId(Long.parseLong(logRecord.getBizNo())); // 业务编号,例如:客户编号 @@ -63,7 +61,7 @@ public class LogRecordServiceImpl implements ILogRecordService { reqDTO.setExtra(logRecord.getExtra()); // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"} } - private static void fillRequestFields(OperateLogV2CreateReqDTO reqDTO) { + private static void fillRequestFields(OperateLogCreateReqDTO reqDTO) { // 获得 Request 对象 HttpServletRequest request = ServletUtils.getRequest(); if (request == null) { diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java index a94a32cf5..5b03191d8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogR import cn.iocoder.yudao.module.crm.enums.LogRecordConstants; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -55,9 +55,9 @@ public class CrmOperateLogController { @Operation(summary = "获得操作日志") @PreAuthorize("@ss.hasPermission('crm:operate-log:query')") public CommonResult> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) { - OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO(); + OperateLogPageReqDTO reqDTO = new OperateLogPageReqDTO(); reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释 - reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId()); + reqDTO.setType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId()); return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO), CrmOperateLogRespVO.class)); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java index d70452e22..650df057a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java @@ -8,6 +8,8 @@ import cn.hutool.extra.template.TemplateConfig; import cn.hutool.extra.template.TemplateEngine; import cn.hutool.extra.template.engine.velocity.VelocityEngine; import cn.hutool.system.SystemUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; @@ -24,8 +26,6 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum; import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; @@ -211,7 +211,7 @@ public class CodegenEngine { globalBindingMap.put("LocalDateTimeUtilsClassName", LocalDateTimeUtils.class.getName()); globalBindingMap.put("ObjectUtilsClassName", ObjectUtils.class.getName()); globalBindingMap.put("DictConvertClassName", DictConvert.class.getName()); - globalBindingMap.put("OperateLogClassName", OperateLog.class.getName()); + globalBindingMap.put("ApiAccessLogClassName", ApiAccessLog.class.getName()); globalBindingMap.put("OperateTypeEnumClassName", OperateTypeEnum.class.getName()); globalBindingMap.put("BeanUtils", BeanUtils.class.getName()); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm index 32d00c7a5..a358d7cc0 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm @@ -23,7 +23,7 @@ import static ${CommonResultClassName}.success; import ${ExcelUtilsClassName}; -import ${OperateLogClassName}; +import ${ApiAccessLogClassName}; import static ${OperateTypeEnumClassName}.*; import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*; @@ -114,7 +114,7 @@ public class ${sceneEnum.prefixClass}${table.className}Controller { #if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:export')") #end - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) #if ( $table.templateType != 2 ) public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO, HttpServletResponse response) throws IOException { diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java index 509d3f0a1..2cf1d6eb0 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java @@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.system.api.logger; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO; import jakarta.validation.Valid; /** @@ -21,19 +20,12 @@ public interface OperateLogApi { */ void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO); - /** - * 创建操作日志 - * - * @param createReqDTO 请求 - */ - void createOperateLogV2(@Valid OperateLogV2CreateReqDTO createReqDTO); - /** * 获取指定模块的指定数据的操作日志分页 * * @param pageReqVO 请求 * @return 操作日志分页 */ - PageResult getOperateLogPage(OperateLogV2PageReqDTO pageReqVO); + PageResult getOperateLogPage(OperateLogPageReqDTO pageReqVO); } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogCreateReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogCreateReqDTO.java index 6c2d199e8..87c0254b7 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogCreateReqDTO.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogCreateReqDTO.java @@ -1,123 +1,84 @@ package cn.iocoder.yudao.module.system.api.logger.dto; -import lombok.Data; - +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; -import java.time.LocalDateTime; -import java.util.Map; +import lombok.Data; /** - * 操作日志创建 Request DTO + * 系统操作日志 Create Request DTO + * + * @author HUIHUI */ @Data public class OperateLogCreateReqDTO { /** * 链路追踪编号 + * + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。 */ private String traceId; - /** * 用户编号 + * + * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性 */ @NotNull(message = "用户编号不能为空") private Long userId; /** * 用户类型 + * + * 关联 {@link UserTypeEnum} */ @NotNull(message = "用户类型不能为空") private Integer userType; - /** - * 操作模块 + * 操作模块类型 */ - @NotEmpty(message = "操作模块不能为空") - private String module; - + @NotEmpty(message = "操作模块类型不能为空") + private String type; /** * 操作名 */ - @NotEmpty(message = "操作名") - private String name; - + @NotEmpty(message = "操作名不能为空") + private String subType; /** - * 操作分类 + * 操作模块业务编号 */ - @NotNull(message = "操作分类不能为空") - private Integer type; - + @NotNull(message = "操作模块业务编号不能为空") + private Long bizId; /** - * 操作明细 + * 操作内容,记录整个操作的明细 + * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 */ - private String content; - + @NotEmpty(message = "操作内容不能为空") + private String action; /** - * 拓展字段 + * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) + * 例如说,记录订单编号,{ orderId: "1"} */ - private Map exts; + private String extra; /** * 请求方法名 */ @NotEmpty(message = "请求方法名不能为空") private String requestMethod; - /** * 请求地址 */ @NotEmpty(message = "请求地址不能为空") private String requestUrl; - /** * 用户 IP */ @NotEmpty(message = "用户 IP 不能为空") private String userIp; - /** - * 浏览器 UserAgent + * 浏览器 UA */ - @NotEmpty(message = "浏览器 UserAgent 不能为空") + @NotEmpty(message = "浏览器 UA 不能为空") private String userAgent; - /** - * Java 方法名 - */ - @NotEmpty(message = "Java 方法名不能为空") - private String javaMethod; - - /** - * Java 方法的参数 - */ - private String javaMethodArgs; - - /** - * 开始时间 - */ - @NotNull(message = "开始时间不能为空") - private LocalDateTime startTime; - - /** - * 执行时长,单位:毫秒 - */ - @NotNull(message = "执行时长不能为空") - private Integer duration; - - /** - * 结果码 - */ - @NotNull(message = "结果码不能为空") - private Integer resultCode; - - /** - * 结果提示 - */ - private String resultMsg; - - /** - * 结果数据 - */ - private String resultData; - } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2PageReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogPageReqDTO.java similarity index 81% rename from yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2PageReqDTO.java rename to yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogPageReqDTO.java index aa54c3f5c..80e52cd5d 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2PageReqDTO.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogPageReqDTO.java @@ -9,12 +9,12 @@ import lombok.Data; * @author HUIHUI */ @Data -public class OperateLogV2PageReqDTO extends PageParam { +public class OperateLogPageReqDTO extends PageParam { /** * 模块类型 */ - private String bizType; + private String type; /** * 模块数据编号 */ diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogRespDTO.java similarity index 96% rename from yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java rename to yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogRespDTO.java index 2e3997cd0..34a7526f9 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogRespDTO.java @@ -13,7 +13,7 @@ import java.time.LocalDateTime; * @author HUIHUI */ @Data -public class OperateLogV2RespDTO implements VO { +public class OperateLogRespDTO implements VO { /** * 日志编号 diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2CreateReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2CreateReqDTO.java deleted file mode 100644 index eed1df133..000000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2CreateReqDTO.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.system.api.logger.dto; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -/** - * 系统操作日志 Create Request DTO - * - * @author HUIHUI - */ -@Data -public class OperateLogV2CreateReqDTO { - - /** - * 链路追踪编号 - * - * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。 - */ - private String traceId; - /** - * 用户编号 - * - * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性 - */ - @NotNull(message = "用户编号不能为空") - private Long userId; - /** - * 用户类型 - * - * 关联 {@link UserTypeEnum} - */ - @NotNull(message = "用户类型不能为空") - private Integer userType; - /** - * 操作模块类型 - */ - @NotEmpty(message = "操作模块类型不能为空") - private String type; - /** - * 操作名 - */ - @NotEmpty(message = "操作名不能为空") - private String subType; - /** - * 操作模块业务编号 - */ - @NotNull(message = "操作模块业务编号不能为空") - private Long bizId; - /** - * 操作内容,记录整个操作的明细 - * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 - */ - @NotEmpty(message = "操作内容不能为空") - private String action; - /** - * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) - * 例如说,记录订单编号,{ orderId: "1"} - */ - private String extra; - - /** - * 请求方法名 - */ - @NotEmpty(message = "请求方法名不能为空") - private String requestMethod; - /** - * 请求地址 - */ - @NotEmpty(message = "请求地址不能为空") - private String requestUrl; - /** - * 用户 IP - */ - @NotEmpty(message = "用户 IP 不能为空") - private String userIp; - /** - * 浏览器 UA - */ - @NotEmpty(message = "浏览器 UA 不能为空") - private String userAgent; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java index 3f7b66d27..5705ea180 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java @@ -1,13 +1,11 @@ package cn.iocoder.yudao.module.system.api.logger; -import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; import cn.iocoder.yudao.module.system.service.logger.OperateLogService; import com.fhs.core.trans.anno.TransMethodResult; import jakarta.annotation.Resource; @@ -28,24 +26,16 @@ public class OperateLogApiImpl implements OperateLogApi { private OperateLogService operateLogService; @Override + @Async public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { operateLogService.createOperateLog(createReqDTO); } - @Override - @Async - public void createOperateLogV2(OperateLogV2CreateReqDTO createReqDTO) { - operateLogService.createOperateLogV2(createReqDTO); - } - @Override @TransMethodResult - public PageResult getOperateLogPage(OperateLogV2PageReqDTO pageReqVO) { - PageResult operateLogPage = operateLogService.getOperateLogPage(pageReqVO); - if (CollUtil.isEmpty(operateLogPage.getList())) { - return PageResult.empty(); - } - return BeanUtils.toBean(operateLogPage, OperateLogV2RespDTO.class); + public PageResult getOperateLogPage(OperateLogPageReqDTO pageReqVO) { + PageResult operateLogPage = operateLogService.getOperateLogPage(pageReqVO); + return BeanUtils.toBean(operateLogPage, OperateLogRespDTO.class); } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java index 5c1742e58..1061f6d05 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java @@ -13,20 +13,23 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @Data public class OperateLogPageReqVO extends PageParam { + @Schema(description = "用户编号", example = "芋道") + private Long userId; + + @Schema(description = "操作模块业务编号", example = "1") + private Long bizId; + @Schema(description = "操作模块,模拟匹配", example = "订单") - private String module; + private String type; - @Schema(description = "用户昵称,模拟匹配", example = "芋道") - private String userNickname; + @Schema(description = "操作名,模拟匹配", example = "创建订单") + private String subType; - @Schema(description = "操作分类,参见 OperateLogTypeEnum 枚举类", example = "1") - private Integer type; - - @Schema(description = "操作状态", example = "true") - private Boolean success; + @Schema(description = "操作明细,模拟匹配", example = "修改编号为 1 的用户信息") + private String action; @Schema(description = "开始时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] startTime; + private LocalDateTime[] createTime; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java index 575dc417b..e017ba7cb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java @@ -1,9 +1,6 @@ package cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.fhs.core.trans.anno.Trans; @@ -14,7 +11,6 @@ import jakarta.validation.constraints.NotEmpty; import lombok.Data; import java.time.LocalDateTime; -import java.util.Map; @Schema(description = "管理后台 - 操作日志 Response VO") @Data @@ -29,31 +25,29 @@ public class OperateLogRespVO implements VO { private String traceId; @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @Trans(type = TransType.SIMPLE, target = AdminUserDO.class, fields = "nickname", ref = "userNickname") + @Trans(type = TransType.SIMPLE, target = AdminUserDO.class, fields = "nickname", ref = "userName") private Long userId; - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") @ExcelProperty("操作人") - private String userNickname; + private String userName; - @Schema(description = "操作模块", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单") - @ExcelProperty("操作模块") - private String module; + @Schema(description = "操作模块类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单") + @ExcelProperty("操作模块类型") + private String type; @Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建订单") @ExcelProperty("操作名") - private String name; + private String subType; - @Schema(description = "操作分类,参见 OperateLogTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "操作类型", converter = DictConvert.class) - @DictFormat(DictTypeConstants.OPERATE_TYPE) - private Integer type; + @Schema(description = "操作模块业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("操作模块业务编号") + private Long bizId; @Schema(description = "操作明细", example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。") - private String content; + private String action; @Schema(description = "拓展字段", example = "{'orderId': 1}") - private Map exts; + private String extra; @Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET") @NotEmpty(message = "请求方法名不能为空") @@ -68,28 +62,7 @@ public class OperateLogRespVO implements VO { @Schema(description = "浏览器 UserAgent", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0") private String userAgent; - @Schema(description = "Java 方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "cn.iocoder.yudao.adminserver.UserController.save(...)") - private String javaMethod; - - @Schema(description = "Java 方法的参数") - private String javaMethodArgs; - - @Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("操作日志") - private LocalDateTime startTime; - - @Schema(description = "执行时长,单位:毫秒", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("执行时长") - private Integer duration; - - @Schema(description = "结果码", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty(value = "结果码") - private Integer resultCode; - - @Schema(description = "结果提示") - private String resultMsg; - - @Schema(description = "结果数据") - private String resultData; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java index d4d4df497..3c7d17128 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java @@ -1,19 +1,11 @@ package cn.iocoder.yudao.module.system.dal.dataobject.logger; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.time.LocalDateTime; -import java.util.Map; /** * 操作日志表 @@ -23,19 +15,8 @@ import java.util.Map; @TableName(value = "system_operate_log", autoResultMap = true) @KeySequence("system_operate_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) public class OperateLogDO extends BaseDO { - /** - * {@link #javaMethodArgs} 的最大长度 - */ - public static final Integer JAVA_METHOD_ARGS_MAX_LENGTH = 8000; - - /** - * {@link #resultData} 的最大长度 - */ - public static final Integer RESULT_MAX_LENGTH = 4000; - /** * 日志主键 */ @@ -60,30 +41,29 @@ public class OperateLogDO extends BaseDO { */ private Integer userType; /** - * 操作模块 + * 操作模块类型 */ - private String module; + private String type; /** * 操作名 */ - private String name; + private String subType; /** - * 操作分类 - * - * 枚举 {@link OperateTypeEnum} + * 操作模块业务编号 */ - private Integer type; + private Long bizId; /** - * 操作内容,记录整个操作的明细 + * 日志内容,记录整个操作的明细 + * * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 */ - private String content; + private String action; /** - * 拓展字段,有些复杂的业务,需要记录一些字段 - * 例如说,记录订单编号,则可以添加 key 为 "orderId",value 为订单编号 + * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) + * + * 例如说,记录订单编号,{ orderId: "1"} */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Map exts; + private String extra; /** * 请求方法名 @@ -102,43 +82,4 @@ public class OperateLogDO extends BaseDO { */ private String userAgent; - /** - * Java 方法名 - */ - private String javaMethod; - /** - * Java 方法的参数 - * - * 实际格式为 Map - * 不使用 @TableField(typeHandler = FastjsonTypeHandler.class) 注解的原因是,数据库存储有长度限制,会进行裁剪,会导致 JSON 反序列化失败 - * 其中,key 为参数名,value 为参数值 - */ - private String javaMethodArgs; - /** - * 开始时间 - */ - private LocalDateTime startTime; - /** - * 执行时长,单位:毫秒 - */ - private Integer duration; - /** - * 结果码 - * - * 目前使用的 {@link CommonResult#getCode()} 属性 - */ - private Integer resultCode; - /** - * 结果提示 - * - * 目前使用的 {@link CommonResult#getMsg()} 属性 - */ - private String resultMsg; - /** - * 结果数据 - * - * 如果是对象,则使用 JSON 格式化 - */ - private String resultData; - } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java deleted file mode 100644 index 2b3741660..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java +++ /dev/null @@ -1,87 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.logger; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 操作日志表 V2 - * - * @author 芋道源码 - */ -@TableName(value = "system_operate_log_v2", autoResultMap = true) -@KeySequence("system_operate_log_seq_v2") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class OperateLogV2DO extends BaseDO { - - /** - * 日志主键 - */ - @TableId - private Long id; - /** - * 链路追踪编号 - * - * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。 - */ - private String traceId; - /** - * 用户编号 - * - * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性 - */ - private Long userId; - /** - * 用户类型 - * - * 关联 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 操作模块类型 - */ - private String type; - /** - * 操作名 - */ - private String subType; - /** - * 操作模块业务编号 - */ - private Long bizId; - /** - * 日志内容,记录整个操作的明细 - * - * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 - */ - private String action; - /** - * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) - * - * 例如说,记录订单编号,{ orderId: "1"} - */ - private String extra; - - /** - * 请求方法名 - */ - private String requestMethod; - /** - * 请求地址 - */ - private String requestUrl; - /** - * 用户 IP - */ - private String userIp; - /** - * 浏览器 UA - */ - private String userAgent; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java index 0bed6b8cc..14e308b56 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java @@ -1,31 +1,33 @@ package cn.iocoder.yudao.module.system.dal.mysql.logger; -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; import org.apache.ibatis.annotations.Mapper; -import java.util.Collection; - @Mapper public interface OperateLogMapper extends BaseMapperX { - default PageResult selectPage(OperateLogPageReqVO reqVO, Collection userIds) { - LambdaQueryWrapperX query = new LambdaQueryWrapperX() - .likeIfPresent(OperateLogDO::getModule, reqVO.getModule()) - .inIfPresent(OperateLogDO::getUserId, userIds) - .eqIfPresent(OperateLogDO::getType, reqVO.getType()) - .betweenIfPresent(OperateLogDO::getStartTime, reqVO.getStartTime()); - if (Boolean.TRUE.equals(reqVO.getSuccess())) { - query.eq(OperateLogDO::getResultCode, GlobalErrorCodeConstants.SUCCESS.getCode()); - } else if (Boolean.FALSE.equals(reqVO.getSuccess())) { - query.gt(OperateLogDO::getResultCode, GlobalErrorCodeConstants.SUCCESS.getCode()); - } - query.orderByDesc(OperateLogDO::getId); // 降序 - return selectPage(reqVO, query); + default PageResult selectPage(OperateLogPageReqVO pageReqDTO) { + return selectPage(pageReqDTO, new LambdaQueryWrapperX() + .eqIfPresent(OperateLogDO::getUserId, pageReqDTO.getUserId()) + .eqIfPresent(OperateLogDO::getBizId, pageReqDTO.getBizId()) + .likeIfPresent(OperateLogDO::getType, pageReqDTO.getType()) + .likeIfPresent(OperateLogDO::getSubType, pageReqDTO.getSubType()) + .likeIfPresent(OperateLogDO::getAction, pageReqDTO.getAction()) + .betweenIfPresent(OperateLogDO::getCreateTime, pageReqDTO.getCreateTime()) + .orderByDesc(OperateLogDO::getId)); + } + + default PageResult selectPage(OperateLogPageReqDTO pageReqDTO) { + return selectPage(pageReqDTO, new LambdaQueryWrapperX() + .eqIfPresent(OperateLogDO::getType, pageReqDTO.getType()) + .eqIfPresent(OperateLogDO::getBizId, pageReqDTO.getBizId()) + .eqIfPresent(OperateLogDO::getUserId, pageReqDTO.getUserId()) + .orderByDesc(OperateLogDO::getId)); } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java deleted file mode 100644 index 117c368e4..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface OperateLogV2Mapper extends BaseMapperX { - - default PageResult selectPage(OperateLogV2PageReqDTO pageReqDTO) { - return selectPage(pageReqDTO, new LambdaQueryWrapperX() - .eqIfPresent(OperateLogV2DO::getType, pageReqDTO.getBizType()) - .eqIfPresent(OperateLogV2DO::getBizId, pageReqDTO.getBizId()) - .eqIfPresent(OperateLogV2DO::getUserId, pageReqDTO.getUserId()) - .orderByDesc(OperateLogV2DO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java index 6843f2fb1..56f713092 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java @@ -2,11 +2,9 @@ package cn.iocoder.yudao.module.system.service.logger; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO; /** * 操作日志 Service 接口 @@ -18,7 +16,7 @@ public interface OperateLogService { /** * 记录操作日志 * - * @param createReqDTO 操作日志请求 + * @param createReqDTO 创建请求 */ void createOperateLog(OperateLogCreateReqDTO createReqDTO); @@ -30,21 +28,12 @@ public interface OperateLogService { */ PageResult getOperateLogPage(OperateLogPageReqVO pageReqVO); - // ======================= LOG V2 ======================= - - /** - * 记录操作日志 V2 - * - * @param createReqDTO 创建请求 - */ - void createOperateLogV2(OperateLogV2CreateReqDTO createReqDTO); - /** * 获得操作日志分页列表 * * @param pageReqVO 分页条件 * @return 操作日志分页列表 */ - PageResult getOperateLogPage(OperateLogV2PageReqDTO pageReqVO); + PageResult getOperateLogPage(OperateLogPageReqDTO pageReqVO); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java index 0f31665fd..6c341d5a1 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java @@ -1,31 +1,17 @@ package cn.iocoder.yudao.module.system.service.logger; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; -import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogV2Mapper; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import java.util.Collection; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO.JAVA_METHOD_ARGS_MAX_LENGTH; -import static cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO.RESULT_MAX_LENGTH; - /** * 操作日志 Service 实现类 * @@ -38,45 +24,21 @@ public class OperateLogServiceImpl implements OperateLogService { @Resource private OperateLogMapper operateLogMapper; - @Resource - private OperateLogV2Mapper operateLogV2Mapper; - - @Resource - private AdminUserService userService; @Override public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { OperateLogDO log = BeanUtils.toBean(createReqDTO, OperateLogDO.class); - log.setJavaMethodArgs(StrUtils.maxLength(log.getJavaMethodArgs(), JAVA_METHOD_ARGS_MAX_LENGTH)); - log.setResultData(StrUtils.maxLength(log.getResultData(), RESULT_MAX_LENGTH)); operateLogMapper.insert(log); } @Override public PageResult getOperateLogPage(OperateLogPageReqVO pageReqVO) { - // 处理基于用户昵称的查询 - Collection userIds = null; - if (StrUtil.isNotEmpty(pageReqVO.getUserNickname())) { - userIds = convertSet(userService.getUserListByNickname(pageReqVO.getUserNickname()), AdminUserDO::getId); - if (CollUtil.isEmpty(userIds)) { - return PageResult.empty(); - } - } - // 查询分页 - return operateLogMapper.selectPage(pageReqVO, userIds); - } - - // ======================= LOG V2 ======================= - - @Override - public void createOperateLogV2(OperateLogV2CreateReqDTO createReqDTO) { - OperateLogV2DO log = BeanUtils.toBean(createReqDTO, OperateLogV2DO.class); - operateLogV2Mapper.insert(log); + return operateLogMapper.selectPage(pageReqVO); } @Override - public PageResult getOperateLogPage(OperateLogV2PageReqDTO pageReqDTO) { - return operateLogV2Mapper.selectPage(pageReqDTO); + public PageResult getOperateLogPage(OperateLogPageReqDTO pageReqDTO) { + return operateLogMapper.selectPage(pageReqDTO); } } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java index a01c6644f..90ba203b5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java @@ -1,35 +1,22 @@ package cn.iocoder.yudao.module.system.service.logger; -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.util.RandomUtils; import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; -import jakarta.annotation.Resource; -import java.util.Collections; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; @Import({OperateLogServiceImpl.class}) public class OperateLogServiceImplTest extends BaseDbUnitTest { @@ -40,13 +27,9 @@ public class OperateLogServiceImplTest extends BaseDbUnitTest { @Resource private OperateLogMapper operateLogMapper; - @MockBean - private AdminUserService userService; - @Test public void testCreateOperateLog() { - OperateLogCreateReqDTO reqVO = RandomUtils.randomPojo(OperateLogCreateReqDTO.class, - o -> o.setExts(MapUtil.builder("orderId", randomLongId()).build())); + OperateLogCreateReqDTO reqVO = RandomUtils.randomPojo(OperateLogCreateReqDTO.class); // 调研 operateLogServiceImpl.createOperateLog(reqVO); @@ -56,44 +39,38 @@ public class OperateLogServiceImplTest extends BaseDbUnitTest { } @Test - public void testGetOperateLogPage() { - // mock(用户信息) - AdminUserDO user = RandomUtils.randomPojo(AdminUserDO.class, o -> { - o.setNickname("wang"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - when(userService.getUserListByNickname("wang")).thenReturn(Collections.singletonList(user)); - Long userId = user.getId(); - + public void testGetOperateLogPage_vo() { // 构造操作日志 OperateLogDO operateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> { - o.setUserId(userId); - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); - o.setModule("order"); - o.setType(OperateTypeEnum.CREATE.getType()); - o.setStartTime(buildTime(2021, 3, 6)); - o.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()); - o.setExts(MapUtil.builder("orderId", randomLongId()).build()); + o.setUserId(2048L); + o.setBizId(999L); + o.setType("订单"); + o.setSubType("创建订单"); + o.setAction("修改编号为 1 的用户信息"); + o.setCreateTime(buildTime(2021, 3, 6)); }); operateLogMapper.insert(operateLogDO); // 测试 userId 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(userId + 1))); - // 测试 module 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setModule("user"))); + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(1024L))); + // 测试 bizId 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setBizId(888L))); // 测试 type 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType(OperateTypeEnum.IMPORT.getType()))); + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType("退款"))); + // 测试 subType 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setSubType("创建退款"))); + // 测试 action 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setAction("修改编号为 1 退款信息"))); // 测试 createTime 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setStartTime(buildTime(2021, 2, 6)))); - // 测试 resultCode 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setResultCode(BAD_REQUEST.getCode()))); + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setCreateTime(buildTime(2021, 2, 6)))); // 构造调用参数 OperateLogPageReqVO reqVO = new OperateLogPageReqVO(); - reqVO.setUserNickname("wang"); - reqVO.setModule("order"); - reqVO.setType(OperateTypeEnum.CREATE.getType()); - reqVO.setStartTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7)); - reqVO.setSuccess(true); + reqVO.setUserId(2048L); + reqVO.setBizId(999L); + reqVO.setType("订"); + reqVO.setSubType("订单"); + reqVO.setAction("用户信息"); + reqVO.setCreateTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7)); // 调用 PageResult pageResult = operateLogServiceImpl.getOperateLogPage(reqVO); @@ -103,4 +80,34 @@ public class OperateLogServiceImplTest extends BaseDbUnitTest { assertPojoEquals(operateLogDO, pageResult.getList().get(0)); } + @Test + public void testGetOperateLogPage_dto() { + // 构造操作日志 + OperateLogDO operateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> { + o.setUserId(2048L); + o.setBizId(999L); + o.setType("订单"); + }); + operateLogMapper.insert(operateLogDO); + // 测试 userId 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(1024L))); + // 测试 bizId 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setBizId(888L))); + // 测试 type 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType("退款"))); + + // 构造调用参数 + OperateLogPageReqDTO reqDTO = new OperateLogPageReqDTO(); + reqDTO.setUserId(2048L); + reqDTO.setBizId(999L); + reqDTO.setType("订单"); + + // 调用 + PageResult pageResult = operateLogServiceImpl.getOperateLogPage(reqDTO); + // 断言,只查到了一条符合条件的 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(operateLogDO, pageResult.getList().get(0)); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql index 78d4e83dd..7b4de5615 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql @@ -202,22 +202,15 @@ CREATE TABLE IF NOT EXISTS `system_operate_log` ( `trace_id` varchar(64) NOT NULL DEFAULT '', `user_id` bigint(20) NOT NULL, "user_type" tinyint not null default '0', - `module` varchar(50) NOT NULL, - `name` varchar(50) NOT NULL, - `type` bigint(4) NOT NULL DEFAULT '0', - `content` varchar(2000) NOT NULL DEFAULT '', - `exts` varchar(512) NOT NULL DEFAULT '', + `type` varchar(50) NOT NULL, + `sub_type` varchar(50) NOT NULL, + `biz_id` bigint(20) NOT NULL, + `action` varchar(2000) NOT NULL DEFAULT '', + `extra` varchar(512) NOT NULL DEFAULT '', `request_method` varchar(16) DEFAULT '', `request_url` varchar(255) DEFAULT '', `user_ip` varchar(50) DEFAULT NULL, `user_agent` varchar(200) DEFAULT NULL, - `java_method` varchar(512) NOT NULL DEFAULT '', - `java_method_args` varchar(8000) DEFAULT '', - `start_time` datetime NOT NULL, - `duration` int(11) NOT NULL, - `result_code` int(11) NOT NULL DEFAULT '0', - `result_msg` varchar(512) DEFAULT '', - `result_data` varchar(4000) DEFAULT '', `creator` varchar(64) DEFAULT '', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `updater` varchar(64) DEFAULT '', From 20583417a4c6a17655104284ef9b231ff3c3a4d1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 4 Apr 2024 01:49:49 +0800 Subject: [PATCH 68/81] =?UTF-8?q?=E3=80=90=E9=87=8D=E6=9E=84=E3=80=91V2=20?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97=E8=BD=AC=E6=AD=A3=EF=BC=8C?= =?UTF-8?q?=E5=9F=BA=E4=BA=8E=E6=B3=A8=E8=A7=A3=E7=9A=84=E5=8F=AF=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E5=8F=98=E9=87=8F=E3=80=81=E5=8F=AF=E4=BB=A5=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=87=BD=E6=95=B0=E7=9A=84=E9=80=9A=E7=94=A8?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 5 -- yudao-framework/pom.xml | 1 - .../pom.xml | 52 ----------------- .../core/annotations/OperateLog.java | 57 ------------------- .../core/enums/OperateTypeEnum.java | 55 ------------------ yudao-module-bpm/yudao-module-bpm-biz/pom.xml | 4 -- yudao-module-crm/yudao-module-crm-biz/pom.xml | 4 -- .../admin/business/CrmBusinessController.java | 6 +- .../admin/clue/CrmClueController.java | 6 +- .../admin/contact/CrmContactController.java | 7 +-- .../admin/contract/CrmContractController.java | 6 +- .../admin/customer/CrmCustomerController.java | 6 +- .../admin/product/CrmProductController.java | 6 +- .../receivable/CrmReceivableController.java | 9 +-- .../CrmReceivablePlanController.java | 6 +- yudao-module-erp/yudao-module-erp-biz/pom.xml | 4 -- .../admin/finance/ErpAccountController.java | 6 +- .../finance/ErpFinancePaymentController.java | 6 +- .../finance/ErpFinanceReceiptController.java | 6 +- .../product/ErpProductCategoryController.java | 6 +- .../admin/product/ErpProductController.java | 6 +- .../product/ErpProductUnitController.java | 6 +- .../purchase/ErpPurchaseInController.java | 6 +- .../purchase/ErpPurchaseOrderController.java | 6 +- .../purchase/ErpPurchaseReturnController.java | 6 +- .../admin/purchase/ErpSupplierController.java | 6 +- .../admin/sale/ErpCustomerController.java | 6 +- .../admin/sale/ErpSaleOrderController.java | 7 +-- .../admin/sale/ErpSaleOutController.java | 6 +- .../admin/sale/ErpSaleReturnController.java | 6 +- .../admin/stock/ErpStockCheckController.java | 6 +- .../admin/stock/ErpStockController.java | 6 +- .../admin/stock/ErpStockInController.java | 8 +-- .../admin/stock/ErpStockMoveController.java | 6 +- .../admin/stock/ErpStockOutController.java | 6 +- .../admin/stock/ErpStockRecordController.java | 6 +- .../admin/stock/ErpWarehouseController.java | 8 +-- .../yudao-module-infra-biz/pom.xml | 4 -- .../admin/config/ConfigController.java | 6 +- .../demo/demo01/Demo01ContactController.java | 12 ++-- .../demo/demo02/Demo02CategoryController.java | 12 ++-- .../demo/demo03/Demo03StudentController.java | 12 ++-- .../controller/admin/file/FileController.java | 2 - .../controller/admin/job/JobController.java | 12 ++-- .../admin/job/JobLogController.java | 12 ++-- .../admin/logger/ApiAccessLogController.java | 6 +- .../admin/logger/ApiErrorLogController.java | 6 +- .../yudao-module-product-biz/pom.xml | 4 -- .../admin/spu/ProductSpuController.java | 6 +- .../yudao-module-promotion-biz/pom.xml | 4 -- .../yudao-module-statistics-biz/pom.xml | 4 -- .../yudao-module-trade-biz/pom.xml | 4 -- .../admin/aftersale/AfterSaleController.java | 2 - .../delivery/DeliveryExpressController.java | 12 ++-- .../yudao-module-member-biz/pom.xml | 4 -- .../app/auth/AppAuthController.java | 2 - yudao-module-mp/yudao-module-mp-biz/pom.xml | 4 -- .../admin/open/MpOpenController.java | 2 - yudao-module-pay/yudao-module-pay-biz/pom.xml | 4 -- .../admin/demo/PayDemoOrderController.java | 3 - .../admin/demo/PayDemoTransferController.java | 2 - .../admin/notify/PayNotifyController.java | 3 - .../admin/order/PayOrderController.java | 12 ++-- .../admin/refund/PayRefundController.java | 12 ++-- .../wallet/PayWalletRechargeController.java | 3 - .../yudao-module-report-biz/pom.xml | 4 -- .../admin/goview/GoViewDataController.java | 3 - .../yudao-module-system-biz/pom.xml | 4 -- .../controller/admin/auth/AuthController.java | 7 --- .../admin/captcha/CaptchaController.java | 3 - .../controller/admin/dept/PostController.java | 14 ++--- .../admin/dict/DictDataController.java | 12 ++-- .../admin/dict/DictTypeController.java | 12 ++-- .../admin/errorcode/ErrorCodeController.java | 20 ++++--- .../admin/logger/LoginLogController.java | 12 ++-- .../admin/logger/OperateLogController.java | 6 +- .../admin/oauth2/OAuth2OpenController.java | 5 -- .../admin/permission/RoleController.java | 12 ++-- .../SensitiveWordController.java | 12 ++-- .../admin/sms/SmsCallbackController.java | 3 - .../admin/sms/SmsLogController.java | 12 ++-- .../admin/sms/SmsTemplateController.java | 6 +- .../admin/tenant/TenantController.java | 12 ++-- .../controller/admin/user/UserController.java | 12 ++-- 84 files changed, 223 insertions(+), 484 deletions(-) delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/annotations/OperateLog.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/enums/OperateTypeEnum.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 0d512d63e..f5a03a90b 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -90,11 +90,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - ${revision} - io.github.mouzt bizlog-sdk diff --git a/yudao-framework/pom.xml b/yudao-framework/pom.xml index 820fc4f2b..76e674099 100644 --- a/yudao-framework/pom.xml +++ b/yudao-framework/pom.xml @@ -25,7 +25,6 @@ yudao-spring-boot-starter-excel yudao-spring-boot-starter-test - yudao-spring-boot-starter-biz-operatelog yudao-spring-boot-starter-biz-tenant yudao-spring-boot-starter-biz-data-permission yudao-spring-boot-starter-biz-ip diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml deleted file mode 100644 index c52d8fc10..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - cn.iocoder.boot - yudao-framework - ${revision} - - 4.0.0 - yudao-spring-boot-starter-biz-operatelog - jar - - ${project.artifactId} - 操作日志 - https://github.com/YunaiV/ruoyi-vue-pro - - - - cn.iocoder.boot - yudao-common - - - - - org.springframework.boot - spring-boot-starter-aop - - - - - cn.iocoder.boot - yudao-spring-boot-starter-web - provided - - - - - cn.iocoder.boot - yudao-module-system-api - ${revision} - - - - - com.google.guava - guava - - - - - diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/annotations/OperateLog.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/annotations/OperateLog.java deleted file mode 100644 index adac32736..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/annotations/OperateLog.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.framework.operatelog.core.annotations; - -import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 操作日志注解 - * - * @author 芋道源码 - */ -@Target({ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface OperateLog { - - // ========== 模块字段 ========== - - /** - * 操作模块 - * - * 为空时,会尝试读取 {@link Tag#name()} 属性 - */ - String module() default ""; - /** - * 操作名 - * - * 为空时,会尝试读取 {@link Operation#summary()} 属性 - */ - String name() default ""; - /** - * 操作分类 - * - * 实际并不是数组,因为枚举不能设置 null 作为默认值 - */ - OperateTypeEnum[] type() default {}; - - // ========== 开关字段 ========== - - /** - * 是否记录操作日志 - */ - boolean enable() default true; - /** - * 是否记录方法参数 - */ - boolean logArgs() default true; - /** - * 是否记录方法结果的数据 - */ - boolean logResultData() default true; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/enums/OperateTypeEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/enums/OperateTypeEnum.java deleted file mode 100644 index 65ee26d71..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/enums/OperateTypeEnum.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.framework.operatelog.core.enums; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 操作日志的操作类型 - * - * @author ruoyi - */ -@Getter -@AllArgsConstructor -public enum OperateTypeEnum { - - /** - * 查询 - * - * 绝大多数情况下,不会记录查询动作,因为过于大量显得没有意义。 - * 在有需要的时候,通过声明 {@link OperateLog} 注解来记录 - */ - GET(1), - /** - * 新增 - */ - CREATE(2), - /** - * 修改 - */ - UPDATE(3), - /** - * 删除 - */ - DELETE(4), - /** - * 导出 - */ - EXPORT(5), - /** - * 导入 - */ - IMPORT(6), - /** - * 其它 - * - * 在无法归类时,可以选择使用其它。因为还有操作名可以进一步标识 - */ - OTHER(0); - - /** - * 类型 - */ - private final Integer type; - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/pom.xml b/yudao-module-bpm/yudao-module-bpm-biz/pom.xml index 4ba9929e7..207b166ba 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/pom.xml +++ b/yudao-module-bpm/yudao-module-bpm-biz/pom.xml @@ -28,10 +28,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-data-permission diff --git a/yudao-module-crm/yudao-module-crm-biz/pom.xml b/yudao-module-crm/yudao-module-crm-biz/pom.xml index 04e048da7..0af42a825 100644 --- a/yudao-module-crm/yudao-module-crm-biz/pom.xml +++ b/yudao-module-crm/yudao-module-crm-biz/pom.xml @@ -34,10 +34,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-ip diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java index 505c0ec46..62aacf506 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.crm.controller.admin.business; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; @@ -39,11 +39,11 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS; @@ -167,7 +167,7 @@ public class CrmBusinessController { @GetMapping("/export-excel") @Operation(summary = "导出商机 Excel") @PreAuthorize("@ss.hasPermission('crm:business:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportBusinessExcel(@Valid CrmBusinessPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PAGE_SIZE_NONE); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java index ecfef7d01..f060e07d9 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; @@ -8,7 +9,6 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueRespVO; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; @@ -37,11 +37,11 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static java.util.Collections.singletonList; @@ -112,7 +112,7 @@ public class CrmClueController { @GetMapping("/export-excel") @Operation(summary = "导出线索 Excel") @PreAuthorize("@ss.hasPermission('crm:clue:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportClueExcel(@Valid CrmCluePageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PAGE_SIZE_NONE); List list = clueService.getCluePage(pageReqVO, getLoginUserId()).getList(); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java index 5c7413159..8d23f5635 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.contact; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; @@ -9,7 +10,6 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*; import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; @@ -37,11 +37,11 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static java.util.Collections.singletonList; @@ -73,7 +73,6 @@ public class CrmContactController { @PutMapping("/update") @Operation(summary = "更新联系人") - @OperateLog(enable = false) @PreAuthorize("@ss.hasPermission('crm:contact:update')") public CommonResult updateContact(@Valid @RequestBody CrmContactSaveReqVO updateReqVO) { contactService.updateContact(updateReqVO); @@ -142,7 +141,7 @@ public class CrmContactController { @GetMapping("/export-excel") @Operation(summary = "导出联系人 Excel") @PreAuthorize("@ss.hasPermission('crm:contact:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportContactExcel(@Valid CrmContactPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageNo(PAGE_SIZE_NONE); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java index d0f30db23..94b90607a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -9,7 +10,6 @@ import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO; @@ -47,10 +47,10 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static java.util.Collections.singletonList; @@ -154,7 +154,7 @@ public class CrmContractController { @GetMapping("/export-excel") @Operation(summary = "导出合同 Excel") @PreAuthorize("@ss.hasPermission('crm:contract:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportContractExcel(@Valid CrmContractPageReqVO exportReqVO, HttpServletResponse response) throws IOException { PageResult pageResult = contractService.getContractPage(exportReqVO, getLoginUserId()); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java index a7d86d9cf..e9d5c63a2 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -14,7 +15,6 @@ import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.ip.core.Area; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; @@ -44,10 +44,10 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*; import static java.util.Collections.singletonList; @@ -242,7 +242,7 @@ public class CrmCustomerController { @GetMapping("/export-excel") @Operation(summary = "导出客户 Excel") @PreAuthorize("@ss.hasPermission('crm:customer:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportCustomerExcel(@Valid CrmCustomerPageReqVO pageVO, HttpServletResponse response) throws IOException { pageVO.setPageSize(PAGE_SIZE_NONE); // 不分页 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java index b8c1ed208..c91e67ff8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.crm.controller.admin.product; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.translate.core.TranslateUtils; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductRespVO; @@ -27,9 +27,9 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - CRM 产品") @RestController @@ -94,7 +94,7 @@ public class CrmProductController { @GetMapping("/export-excel") @Operation(summary = "导出产品 Excel") @PreAuthorize("@ss.hasPermission('crm:product:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportProductExcel(@Valid CrmProductPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java index 37cdbb769..d53cfcf4d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java @@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; @@ -39,11 +39,12 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - CRM 回款") @@ -123,7 +124,7 @@ public class CrmReceivableController { @GetMapping("/export-excel") @Operation(summary = "导出回款 Excel") @PreAuthorize("@ss.hasPermission('crm:receivable:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PAGE_SIZE_NONE); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java index 73b5b5292..dfb4114af 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java @@ -2,12 +2,12 @@ package cn.iocoder.yudao.module.crm.controller.admin.receivable; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO; import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanSaveReqVO; @@ -39,11 +39,11 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - CRM 回款计划") @@ -123,7 +123,7 @@ public class CrmReceivablePlanController { @GetMapping("/export-excel") @Operation(summary = "导出回款计划 Excel") @PreAuthorize("@ss.hasPermission('crm:receivable-plan:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/pom.xml b/yudao-module-erp/yudao-module-erp-biz/pom.xml index 53c934950..f1a3f7a39 100644 --- a/yudao-module-erp/yudao-module-erp-biz/pom.xml +++ b/yudao-module-erp/yudao-module-erp-biz/pom.xml @@ -29,10 +29,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java index 4c8c98058..273d40dd6 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.erp.controller.admin.finance; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountRespVO; import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.account.ErpAccountSaveReqVO; @@ -26,9 +26,9 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 结算账户") @RestController @@ -103,7 +103,7 @@ public class ErpAccountController { @GetMapping("/export-excel") @Operation(summary = "导出结算账户 Excel") @PreAuthorize("@ss.hasPermission('erp:account:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportAccountExcel(@Valid ErpAccountPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java index b1f028a28..29b71e5db 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinancePaymentController.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.finance; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -8,7 +9,6 @@ import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentRespVO; import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO; @@ -36,9 +36,9 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 付款单") @RestController @@ -114,7 +114,7 @@ public class ErpFinancePaymentController { @GetMapping("/export-excel") @Operation(summary = "导出付款单 Excel") @PreAuthorize("@ss.hasPermission('erp:finance-payment:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportFinancePaymentExcel(@Valid ErpFinancePaymentPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinanceReceiptController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinanceReceiptController.java index d36ab3990..e374b3c29 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinanceReceiptController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpFinanceReceiptController.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.erp.controller.admin.finance; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -8,7 +9,6 @@ import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptRespVO; import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.receipt.ErpFinanceReceiptSaveReqVO; @@ -36,9 +36,9 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 收款单") @RestController @@ -114,7 +114,7 @@ public class ErpFinanceReceiptController { @GetMapping("/export-excel") @Operation(summary = "导出收款单 Excel") @PreAuthorize("@ss.hasPermission('erp:finance-receipt:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportFinanceReceiptExcel(@Valid ErpFinanceReceiptPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java index 85f51c1c6..d56b9d890 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.module.erp.controller.admin.product; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryRespVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategorySaveReqVO; @@ -23,9 +23,9 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 产品分类") @RestController @@ -89,7 +89,7 @@ public class ErpProductCategoryController { @GetMapping("/export-excel") @Operation(summary = "导出产品分类 Excel") @PreAuthorize("@ss.hasPermission('erp:product-category:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportProductCategoryExcel(@Valid ErpProductCategoryListReqVO listReqVO, HttpServletResponse response) throws IOException { List list = productCategoryService.getProductCategoryList(listReqVO); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java index cde7bd704..40ed30a20 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.erp.controller.admin.product; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO; @@ -25,9 +25,9 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 产品") @RestController @@ -92,7 +92,7 @@ public class ErpProductController { @GetMapping("/export-excel") @Operation(summary = "导出产品 Excel") @PreAuthorize("@ss.hasPermission('erp:product:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportProductExcel(@Valid ErpProductPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductUnitController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductUnitController.java index 0be3db01c..c1f9d1acd 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductUnitController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductUnitController.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.erp.controller.admin.product; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitRespVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO; @@ -25,9 +25,9 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 产品单位") @RestController @@ -89,7 +89,7 @@ public class ErpProductUnitController { @GetMapping("/export-excel") @Operation(summary = "导出产品单位 Excel") @PreAuthorize("@ss.hasPermission('erp:product-unit:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportProductUnitExcel(@Valid ErpProductUnitPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java index d33c7ae4d..754c3548f 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.purchase; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInRespVO; @@ -37,10 +37,10 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 采购入库") @RestController @@ -125,7 +125,7 @@ public class ErpPurchaseInController { @GetMapping("/export-excel") @Operation(summary = "导出采购入库 Excel") @PreAuthorize("@ss.hasPermission('erp:purchase-in:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportPurchaseInExcel(@Valid ErpPurchaseInPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java index 203d2fec0..a60fda222 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.purchase; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderRespVO; @@ -36,10 +36,10 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 采购订单") @RestController @@ -124,7 +124,7 @@ public class ErpPurchaseOrderController { @GetMapping("/export-excel") @Operation(summary = "导出采购订单 Excel") @PreAuthorize("@ss.hasPermission('erp:purchase-create:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportPurchaseOrderExcel(@Valid ErpPurchaseOrderPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseReturnController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseReturnController.java index 0df31bcf1..4afb083ea 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseReturnController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseReturnController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.purchase; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnRespVO; @@ -37,10 +37,10 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 采购退货") @RestController @@ -125,7 +125,7 @@ public class ErpPurchaseReturnController { @GetMapping("/export-excel") @Operation(summary = "导出采购退货 Excel") @PreAuthorize("@ss.hasPermission('erp:purchase-return:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportPurchaseReturnExcel(@Valid ErpPurchaseReturnPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java index 88253286d..3337b3801 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.erp.controller.admin.purchase; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierRespVO; import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.supplier.ErpSupplierSaveReqVO; @@ -25,9 +25,9 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 供应商") @RestController @@ -89,7 +89,7 @@ public class ErpSupplierController { @GetMapping("/export-excel") @Operation(summary = "导出供应商 Excel") @PreAuthorize("@ss.hasPermission('erp:supplier:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportSupplierExcel(@Valid ErpSupplierPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpCustomerController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpCustomerController.java index 2c2886460..88d050ac6 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpCustomerController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpCustomerController.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerRespVO; import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.customer.ErpCustomerSaveReqVO; @@ -25,9 +25,9 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 客户") @RestController @@ -89,7 +89,7 @@ public class ErpCustomerController { @GetMapping("/export-excel") @Operation(summary = "导出客户 Excel") @PreAuthorize("@ss.hasPermission('erp:customer:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportCustomerExcel(@Valid ErpCustomerPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java index 0ca56a45e..acb49e763 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderRespVO; @@ -36,11 +36,10 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; - @Tag(name = "管理后台 - ERP 销售订单") @RestController @@ -125,7 +124,7 @@ public class ErpSaleOrderController { @GetMapping("/export-excel") @Operation(summary = "导出销售订单 Excel") @PreAuthorize("@ss.hasPermission('erp:sale-out:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java index 5875ea39f..02c556148 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutRespVO; @@ -37,10 +37,10 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 销售出库") @RestController @@ -125,7 +125,7 @@ public class ErpSaleOutController { @GetMapping("/export-excel") @Operation(summary = "导出销售出库 Excel") @PreAuthorize("@ss.hasPermission('erp:sale-out:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportSaleOutExcel(@Valid ErpSaleOutPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleReturnController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleReturnController.java index 0dfba67e9..2a3a83411 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleReturnController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleReturnController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.returns.ErpSaleReturnRespVO; @@ -37,10 +37,10 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 销售退货") @RestController @@ -125,7 +125,7 @@ public class ErpSaleReturnController { @GetMapping("/export-excel") @Operation(summary = "导出销售退货 Excel") @PreAuthorize("@ss.hasPermission('erp:sale-return:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportSaleReturnExcel(@Valid ErpSaleReturnPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java index 298ed54fa..5eda9617d 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockCheckController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.check.ErpStockCheckRespVO; @@ -32,10 +32,10 @@ import java.io.IOException; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 库存调拨单") @RestController @@ -113,7 +113,7 @@ public class ErpStockCheckController { @GetMapping("/export-excel") @Operation(summary = "导出库存调拨单 Excel") @PreAuthorize("@ss.hasPermission('erp:stock-check:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportStockCheckExcel(@Valid ErpStockCheckPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockController.java index 912f59731..f439272fa 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockRespVO; @@ -35,9 +35,9 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 产品库存") @RestController @@ -85,7 +85,7 @@ public class ErpStockController { @GetMapping("/export-excel") @Operation(summary = "导出产品库存 Excel") @PreAuthorize("@ss.hasPermission('erp:stock:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportStockExcel(@Valid ErpStockPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java index 8813da89a..6f9e8b314 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockInController.java @@ -1,21 +1,21 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.in.ErpStockInSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInDO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockInItemDO; -import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; import cn.iocoder.yudao.module.erp.service.product.ErpProductService; import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; import cn.iocoder.yudao.module.erp.service.stock.ErpStockInService; @@ -37,10 +37,10 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 其它入库单") @RestController @@ -125,7 +125,7 @@ public class ErpStockInController { @GetMapping("/export-excel") @Operation(summary = "导出其它入库单 Excel") @PreAuthorize("@ss.hasPermission('erp:stock-in:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportStockInExcel(@Valid ErpStockInPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java index 1df3fd7fc..df0ffc81d 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockMoveController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMovePageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.move.ErpStockMoveRespVO; @@ -35,10 +35,10 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 库存调拨单") @RestController @@ -121,7 +121,7 @@ public class ErpStockMoveController { @GetMapping("/export-excel") @Operation(summary = "导出库存调拨单 Excel") @PreAuthorize("@ss.hasPermission('erp:stock-move:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportStockMoveExcel(@Valid ErpStockMovePageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockOutController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockOutController.java index 9ad592f1a..10e7d47ab 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockOutController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockOutController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.out.ErpStockOutRespVO; @@ -37,10 +37,10 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 其它出库单") @RestController @@ -125,7 +125,7 @@ public class ErpStockOutController { @GetMapping("/export-excel") @Operation(summary = "导出其它出库单 Excel") @PreAuthorize("@ss.hasPermission('erp:stock-out:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportStockOutExcel(@Valid ErpStockOutPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockRecordController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockRecordController.java index 6ed453894..e1b0b36f6 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockRecordController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpStockRecordController.java @@ -1,13 +1,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordRespVO; @@ -35,9 +35,9 @@ import java.io.IOException; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 产品库存明细") @RestController @@ -75,7 +75,7 @@ public class ErpStockRecordController { @GetMapping("/export-excel") @Operation(summary = "导出产品库存明细 Excel") @PreAuthorize("@ss.hasPermission('erp:stock-record:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportStockRecordExcel(@Valid ErpStockRecordPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java index 6b970bcb9..46901a2af 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/stock/ErpWarehouseController.java @@ -1,15 +1,15 @@ package cn.iocoder.yudao.module.erp.controller.admin.stock; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSaveReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehousePageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.warehouse.ErpWarehouseSaveReqVO; import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpWarehouseDO; import cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService; import io.swagger.v3.oas.annotations.Operation; @@ -26,9 +26,9 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - ERP 仓库") @RestController @@ -103,7 +103,7 @@ public class ErpWarehouseController { @GetMapping("/export-excel") @Operation(summary = "导出仓库 Excel") @PreAuthorize("@ss.hasPermission('erp:warehouse:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportWarehouseExcel(@Valid ErpWarehousePageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-infra/yudao-module-infra-biz/pom.xml b/yudao-module-infra/yudao-module-infra-biz/pom.xml index d03084663..44f3aef67 100644 --- a/yudao-module-infra/yudao-module-infra-biz/pom.xml +++ b/yudao-module-infra/yudao-module-infra-biz/pom.xml @@ -31,10 +31,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java index 480bff8bb..edcb22254 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.module.infra.controller.admin.config; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.infra.controller.admin.config.vo.*; import cn.iocoder.yudao.module.infra.convert.config.ConfigConvert; import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO; @@ -23,9 +23,9 @@ import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 参数配置") @RestController @@ -93,7 +93,7 @@ public class ConfigController { @GetMapping("/export") @Operation(summary = "导出参数配置") @PreAuthorize("@ss.hasPermission('infra:config:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportConfig(@Valid ConfigPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java index 522aafafd..2cbf98ffe 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactRespVO; import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactSaveReqVO; @@ -14,18 +14,18 @@ import cn.iocoder.yudao.module.infra.service.demo.demo01.Demo01ContactService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 示例联系人") @RestController @@ -80,7 +80,7 @@ public class Demo01ContactController { @GetMapping("/export-excel") @Operation(summary = "导出示例联系人 Excel") @PreAuthorize("@ss.hasPermission('infra:demo01-contact:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportDemo01ContactExcel(@Valid Demo01ContactPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java index 70cf1bb7e..62228f5c2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO; import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryRespVO; import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO; @@ -12,18 +12,18 @@ import cn.iocoder.yudao.module.infra.service.demo.demo02.Demo02CategoryService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 示例分类") @RestController @@ -78,7 +78,7 @@ public class Demo02CategoryController { @GetMapping("/export-excel") @Operation(summary = "导出示例分类 Excel") @PreAuthorize("@ss.hasPermission('infra:demo02-category:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportDemo02CategoryExcel(@Valid Demo02CategoryListReqVO listReqVO, HttpServletResponse response) throws IOException { List list = demo02CategoryService.getDemo02CategoryList(listReqVO); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java index 7ffd502b7..624de732f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentRespVO; import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentSaveReqVO; @@ -16,18 +16,18 @@ import cn.iocoder.yudao.module.infra.service.demo.demo03.Demo03StudentService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 学生") @RestController @@ -82,7 +82,7 @@ public class Demo03StudentController { @GetMapping("/export-excel") @Operation(summary = "导出学生 Excel") @PreAuthorize("@ss.hasPermission('infra:demo03-student:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportDemo03StudentExcel(@Valid Demo03StudentPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java index 7e1dea2e8..bbd29dc29 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java @@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; import cn.iocoder.yudao.module.infra.service.file.FileService; @@ -40,7 +39,6 @@ public class FileController { @PostMapping("/upload") @Operation(summary = "上传文件", description = "模式一:后端上传文件") - @OperateLog(logArgs = false) // 上传文件,没有记录操作日志的必要 public CommonResult uploadFile(FileUploadReqVO uploadReqVO) throws Exception { MultipartFile file = uploadReqVO.getFile(); String path = uploadReqVO.getPath(); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.java index 4a74731dc..ed9f75df0 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.infra.controller.admin.job; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.quartz.core.util.CronUtils; import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobRespVO; @@ -16,21 +16,21 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.quartz.SchedulerException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 定时任务") @RestController @@ -110,7 +110,7 @@ public class JobController { @GetMapping("/export-excel") @Operation(summary = "导出定时任务 Excel") @PreAuthorize("@ss.hasPermission('infra:job:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportJobExcel(@Valid JobPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java index dd861b749..f1a230eea 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.infra.controller.admin.job; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogRespVO; import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO; @@ -13,6 +13,9 @@ import cn.iocoder.yudao.module.infra.service.job.JobLogService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -20,14 +23,11 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 定时任务日志") @RestController @@ -58,7 +58,7 @@ public class JobLogController { @GetMapping("/export-excel") @Operation(summary = "导出定时任务日志 Excel") @PreAuthorize("@ss.hasPermission('infra:job:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportJobLogExcel(@Valid JobLogPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiAccessLogController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiAccessLogController.java index acaef5290..d170933df 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiAccessLogController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiAccessLogController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.infra.controller.admin.logger; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogRespVO; import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO; @@ -24,8 +24,8 @@ import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - API 访问日志") @RestController @@ -47,7 +47,7 @@ public class ApiAccessLogController { @GetMapping("/export-excel") @Operation(summary = "导出API 访问日志 Excel") @PreAuthorize("@ss.hasPermission('infra:api-access-log:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportApiAccessLogExcel(@Valid ApiAccessLogPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiErrorLogController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiErrorLogController.java index 772303a9c..a5b7a455b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiErrorLogController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiErrorLogController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.infra.controller.admin.logger; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogRespVO; import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO; @@ -24,8 +24,8 @@ import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - API 错误日志") @@ -61,7 +61,7 @@ public class ApiErrorLogController { @GetMapping("/export-excel") @Operation(summary = "导出 API 错误日志 Excel") @PreAuthorize("@ss.hasPermission('infra:api-error-log:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportApiErrorLogExcel(@Valid ApiErrorLogPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-mall/yudao-module-product-biz/pom.xml b/yudao-module-mall/yudao-module-product-biz/pom.xml index 7cf336432..c8bad4605 100644 --- a/yudao-module-mall/yudao-module-product-biz/pom.xml +++ b/yudao-module-mall/yudao-module-product-biz/pom.xml @@ -30,10 +30,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java index ac1a900a1..31d36ba6d 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.module.product.controller.admin.spu; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; @@ -28,9 +28,9 @@ import java.util.Comparator; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 商品 SPU") @RestController @@ -127,7 +127,7 @@ public class ProductSpuController { @GetMapping("/export") @Operation(summary = "导出商品") @PreAuthorize("@ss.hasPermission('product:spu:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportSpuList(@Validated ProductSpuPageReqVO reqVO, HttpServletResponse response) throws IOException { reqVO.setPageSize(PAGE_SIZE_NONE); diff --git a/yudao-module-mall/yudao-module-promotion-biz/pom.xml b/yudao-module-mall/yudao-module-promotion-biz/pom.xml index f3d1340ba..a3bf64965 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/pom.xml +++ b/yudao-module-mall/yudao-module-promotion-biz/pom.xml @@ -41,10 +41,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant diff --git a/yudao-module-mall/yudao-module-statistics-biz/pom.xml b/yudao-module-mall/yudao-module-statistics-biz/pom.xml index 77c7b1bff..fc1545560 100644 --- a/yudao-module-mall/yudao-module-statistics-biz/pom.xml +++ b/yudao-module-mall/yudao-module-statistics-biz/pom.xml @@ -50,10 +50,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant diff --git a/yudao-module-mall/yudao-module-trade-biz/pom.xml b/yudao-module-mall/yudao-module-trade-biz/pom.xml index 0ecadd7d3..fb046ee40 100644 --- a/yudao-module-mall/yudao-module-trade-biz/pom.xml +++ b/yudao-module-mall/yudao-module-trade-biz/pom.xml @@ -50,10 +50,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/AfterSaleController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/AfterSaleController.java index 6d20ba15a..d6429b44f 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/AfterSaleController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/AfterSaleController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.trade.controller.admin.aftersale; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.member.api.user.MemberUserApi; import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; @@ -135,7 +134,6 @@ public class AfterSaleController { @PostMapping("/update-refunded") @Operation(summary = "更新售后订单为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 - @OperateLog(enable = false) // 禁用操作日志,因为没有操作人 public CommonResult updateAfterRefund(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { // 目前业务逻辑,不需要做任何事情 // 当然,退款会有小概率会失败的情况,可以监控失败状态,进行告警 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressController.java index 4b7f87279..5484075c2 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressController.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.module.trade.controller.admin.delivery; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.*; import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressConvert; import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; @@ -12,18 +12,18 @@ import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 快递公司") @RestController @@ -85,7 +85,7 @@ public class DeliveryExpressController { @GetMapping("/export-excel") @Operation(summary = "导出快递公司 Excel") @PreAuthorize("@ss.hasPermission('trade:delivery:express:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportDeliveryExpressExcel(@Valid DeliveryExpressExportReqVO exportReqVO, HttpServletResponse response) throws IOException { List list = deliveryExpressService.getDeliveryExpressList(exportReqVO); diff --git a/yudao-module-member/yudao-module-member-biz/pom.xml b/yudao-module-member/yudao-module-member-biz/pom.xml index 249c061ae..3c9b81e65 100644 --- a/yudao-module-member/yudao-module-member-biz/pom.xml +++ b/yudao-module-member/yudao-module-member-biz/pom.xml @@ -35,10 +35,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java index 16c7db207..d0a75b044 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.member.controller.app.auth; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.security.config.SecurityProperties; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; @@ -64,7 +63,6 @@ public class AppAuthController { @PostMapping("/refresh-token") @Operation(summary = "刷新令牌") @Parameter(name = "refreshToken", description = "刷新令牌", required = true) - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult refreshToken(@RequestParam("refreshToken") String refreshToken) { return success(authService.refreshToken(refreshToken)); } diff --git a/yudao-module-mp/yudao-module-mp-biz/pom.xml b/yudao-module-mp/yudao-module-mp-biz/pom.xml index c7d1b4973..5e3fefbad 100644 --- a/yudao-module-mp/yudao-module-mp-biz/pom.xml +++ b/yudao-module-mp/yudao-module-mp-biz/pom.xml @@ -35,10 +35,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java index 5cfe6b839..22477b22f 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.mp.controller.admin.open; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenCheckSignatureReqVO; import cn.iocoder.yudao.module.mp.controller.admin.open.vo.MpOpenHandleMessageReqVO; @@ -63,7 +62,6 @@ public class MpOpenController { */ @Operation(summary = "处理消息") @PostMapping(value = "/{appId}", produces = "application/xml; charset=UTF-8") - @OperateLog(enable = false) // 回调地址,无需记录操作日志 public String handleMessage(@PathVariable("appId") String appId, @RequestBody String content, MpOpenHandleMessageReqVO reqVO) { diff --git a/yudao-module-pay/yudao-module-pay-biz/pom.xml b/yudao-module-pay/yudao-module-pay-biz/pom.xml index de7afb173..738b133c1 100644 --- a/yudao-module-pay/yudao-module-pay-biz/pom.xml +++ b/yudao-module-pay/yudao-module-pay-biz/pom.xml @@ -30,10 +30,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-pay diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoOrderController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoOrderController.java index 615be4969..538fdbc22 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoOrderController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoOrderController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.pay.controller.admin.demo; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO; @@ -50,7 +49,6 @@ public class PayDemoOrderController { @PostMapping("/update-paid") @Operation(summary = "更新示例订单为已支付") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 - @OperateLog(enable = false) // 禁用操作日志,因为没有操作人 public CommonResult updateDemoOrderPaid(@RequestBody PayOrderNotifyReqDTO notifyReqDTO) { payDemoOrderService.updateDemoOrderPaid(Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayOrderId()); @@ -68,7 +66,6 @@ public class PayDemoOrderController { @PostMapping("/update-refunded") @Operation(summary = "更新示例订单为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 - @OperateLog(enable = false) // 禁用操作日志,因为没有操作人 public CommonResult updateDemoOrderRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { payDemoOrderService.updateDemoOrderRefunded(Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId()); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoTransferController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoTransferController.java index f35f9190f..3d399181b 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoTransferController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoTransferController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.pay.controller.admin.demo; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.pay.api.notify.dto.PayTransferNotifyReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferRespVO; @@ -45,7 +44,6 @@ public class PayDemoTransferController { @PostMapping("/update-status") @Operation(summary = "更新示例转账订单的转账状态") // 由 pay-module 转账服务,进行回调 @PermitAll // 无需登录,安全由 PayDemoTransferService 内部校验实现 - @OperateLog(enable = false) // 禁用操作日志,因为没有操作人 public CommonResult updateDemoTransferStatus(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) { demoTransferService.updateDemoTransferStatus(Long.valueOf(notifyReqDTO.getMerchantTransferId()), notifyReqDTO.getPayTransferId()); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java index 65c8c43c5..631cd7562 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.pay.controller.admin.notify; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; @@ -59,7 +58,6 @@ public class PayNotifyController { @PostMapping(value = "/order/{channelId}") @Operation(summary = "支付渠道的统一【支付】回调") @PermitAll - @OperateLog(enable = false) // 回调地址,无需记录操作日志 public String notifyOrder(@PathVariable("channelId") Long channelId, @RequestParam(required = false) Map params, @RequestBody(required = false) String body) { @@ -80,7 +78,6 @@ public class PayNotifyController { @PostMapping(value = "/refund/{channelId}") @Operation(summary = "支付渠道的统一【退款】回调") @PermitAll - @OperateLog(enable = false) // 回调地址,无需记录操作日志 public String notifyRefund(@PathVariable("channelId") Long channelId, @RequestParam(required = false) Map params, @RequestBody(required = false) String body) { diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java index 4c9dd84a8..68cdf2e01 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.module.pay.controller.admin.order; import cn.hutool.core.collection.CollectionUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*; import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert; @@ -18,23 +18,23 @@ import com.google.common.collect.Maps; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType; @@ -107,7 +107,7 @@ public class PayOrderController { @GetMapping("/export-excel") @Operation(summary = "导出支付订单 Excel") @PreAuthorize("@ss.hasPermission('pay:order:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportOrderExcel(@Valid PayOrderExportReqVO exportReqVO, HttpServletResponse response) throws IOException { List list = orderService.getOrderList(exportReqVO); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/PayRefundController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/PayRefundController.java index 2723d8a64..81255443a 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/PayRefundController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/PayRefundController.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.module.pay.controller.admin.refund; import cn.hutool.core.collection.CollectionUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.*; import cn.iocoder.yudao.module.pay.convert.refund.PayRefundConvert; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; @@ -14,6 +14,9 @@ import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -21,17 +24,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 退款订单") @RestController @@ -76,7 +76,7 @@ public class PayRefundController { @GetMapping("/export-excel") @Operation(summary = "导出退款订单 Excel") @PreAuthorize("@ss.hasPermission('pay:refund:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportRefundExcel(@Valid PayRefundExportReqVO exportReqVO, HttpServletResponse response) throws IOException { List list = refundService.getRefundList(exportReqVO); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/PayWalletRechargeController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/PayWalletRechargeController.java index 63133d2c8..bfa7917a0 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/PayWalletRechargeController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/PayWalletRechargeController.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.pay.controller.admin.wallet; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; import cn.iocoder.yudao.module.pay.service.wallet.PayWalletRechargeService; @@ -32,7 +31,6 @@ public class PayWalletRechargeController { @PostMapping("/update-paid") @Operation(summary = "更新钱包充值为已充值") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob @PermitAll // 无需登录, 内部校验实现 - @OperateLog(enable = false) // 禁用操作日志,因为没有操作人 public CommonResult updateWalletRechargerPaid(@Valid @RequestBody PayOrderNotifyReqDTO notifyReqDTO) { walletRechargeService.updateWalletRechargerPaid(Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayOrderId()); @@ -51,7 +49,6 @@ public class PayWalletRechargeController { @PostMapping("/update-refunded") @Operation(summary = "更新钱包充值为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob @PermitAll // 无需登录, 内部校验实现 - @OperateLog(enable = false) // 禁用操作日志,因为没有操作人 public CommonResult updateWalletRechargeRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { walletRechargeService.updateWalletRechargeRefunded( Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId()); diff --git a/yudao-module-report/yudao-module-report-biz/pom.xml b/yudao-module-report/yudao-module-report-biz/pom.xml index ddc90b348..965cb01c8 100644 --- a/yudao-module-report/yudao-module-report-biz/pom.xml +++ b/yudao-module-report/yudao-module-report-biz/pom.xml @@ -31,10 +31,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant diff --git a/yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/controller/admin/goview/GoViewDataController.java b/yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/controller/admin/goview/GoViewDataController.java index cc41dc814..199341ab1 100644 --- a/yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/controller/admin/goview/GoViewDataController.java +++ b/yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/controller/admin/goview/GoViewDataController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.report.controller.admin.goview; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.RandomUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.report.controller.admin.goview.vo.data.GoViewDataGetBySqlReqVO; import cn.iocoder.yudao.module.report.controller.admin.goview.vo.data.GoViewDataRespVO; import cn.iocoder.yudao.module.report.service.goview.GoViewDataService; @@ -35,7 +34,6 @@ public class GoViewDataController { @RequestMapping("/get-by-sql") @Operation(summary = "使用 SQL 查询数据") @PreAuthorize("@ss.hasPermission('report:go-view-data:get-by-sql')") - @OperateLog(enable = false) // 不记录操作日志,因为不需要 public CommonResult getDataBySQL(@Valid @RequestBody GoViewDataGetBySqlReqVO reqVO) { return success(goViewDataService.getDataBySQL(reqVO.getSql())); } @@ -43,7 +41,6 @@ public class GoViewDataController { @RequestMapping("/get-by-http") @Operation(summary = "使用 HTTP 查询数据", description = "这个只是示例接口,实际应该每个查询,都要写一个接口") @PreAuthorize("@ss.hasPermission('report:go-view-data:get-by-http')") - @OperateLog(enable = false) // 不记录操作日志,因为不需要 public CommonResult getDataByHttp( @RequestParam(required = false) Map params, @RequestBody(required = false) String body) { // params、body 按照需要去接收,这里仅仅是示例 diff --git a/yudao-module-system/yudao-module-system-biz/pom.xml b/yudao-module-system/yudao-module-system-biz/pom.xml index 3c041c420..6de1a58a1 100644 --- a/yudao-module-system/yudao-module-system-biz/pom.xml +++ b/yudao-module-system/yudao-module-system-biz/pom.xml @@ -30,10 +30,6 @@ - - cn.iocoder.boot - yudao-spring-boot-starter-biz-operatelog - cn.iocoder.boot yudao-spring-boot-starter-biz-data-permission diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index f24db16a9..cb002d3a2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -5,7 +5,6 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.security.config.SecurityProperties; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; @@ -66,7 +65,6 @@ public class AuthController { @PostMapping("/login") @PermitAll @Operation(summary = "使用账号密码登录") - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult login(@RequestBody @Valid AuthLoginReqVO reqVO) { return success(authService.login(reqVO)); } @@ -74,7 +72,6 @@ public class AuthController { @PostMapping("/logout") @PermitAll @Operation(summary = "登出系统") - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult logout(HttpServletRequest request) { String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader(), securityProperties.getTokenParameter()); @@ -88,7 +85,6 @@ public class AuthController { @PermitAll @Operation(summary = "刷新令牌") @Parameter(name = "refreshToken", description = "刷新令牌", required = true) - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult refreshToken(@RequestParam("refreshToken") String refreshToken) { return success(authService.refreshToken(refreshToken)); } @@ -124,7 +120,6 @@ public class AuthController { @PostMapping("/sms-login") @PermitAll @Operation(summary = "使用短信验证码登录") - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) { return success(authService.smsLogin(reqVO)); } @@ -132,7 +127,6 @@ public class AuthController { @PostMapping("/send-sms-code") @PermitAll @Operation(summary = "发送手机验证码") - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult sendLoginSmsCode(@RequestBody @Valid AuthSmsSendReqVO reqVO) { authService.sendSmsCode(reqVO); return success(true); @@ -156,7 +150,6 @@ public class AuthController { @PostMapping("/social-login") @PermitAll @Operation(summary = "社交快捷登录,使用 code 授权码", description = "适合未登录的用户,但是社交账号已绑定用户") - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) { return success(authService.socialLogin(reqVO)); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java index ad8953562..174c560c4 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.controller.admin.captcha; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import com.xingyuv.captcha.model.common.ResponseModel; import com.xingyuv.captcha.model.vo.CaptchaVO; import com.xingyuv.captcha.service.CaptchaService; @@ -28,7 +27,6 @@ public class CaptchaController { @PostMapping({"/get"}) @Operation(summary = "获得验证码") @PermitAll - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) { assert request.getRemoteHost() != null; data.setBrowserInfo(getRemoteId(request)); @@ -38,7 +36,6 @@ public class CaptchaController { @PostMapping("/check") @Operation(summary = "校验验证码") @PermitAll - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) { data.setBrowserInfo(getRemoteId(request)); return captchaService.check(data); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java index 4d586ce7f..cf44cd5ef 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java @@ -1,35 +1,35 @@ package cn.iocoder.yudao.module.system.controller.admin.dept; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostRespVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSimpleRespVO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; import cn.iocoder.yudao.module.system.service.dept.PostService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 岗位") @RestController @@ -94,7 +94,7 @@ public class PostController { @GetMapping("/export") @Operation(summary = "岗位管理") @PreAuthorize("@ss.hasPermission('system:post:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void export(HttpServletResponse response, @Validated PostPageReqVO reqVO) throws IOException { reqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = postService.getPostPage(reqVO).getList(); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java index 41b90307b..59b03f006 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.system.controller.admin.dict; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataRespVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; @@ -16,18 +16,18 @@ import cn.iocoder.yudao.module.system.service.dict.DictDataService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 字典数据") @RestController @@ -92,7 +92,7 @@ public class DictDataController { @GetMapping("/export") @Operation(summary = "导出字典数据") @PreAuthorize("@ss.hasPermission('system:dict:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void export(HttpServletResponse response, @Valid DictDataPageReqVO exportReqVO) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = dictDataService.getDictDataPage(exportReqVO).getList(); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java index b0e112764..8873ca4d8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.system.controller.admin.dict; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeRespVO; import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; @@ -15,18 +15,18 @@ import cn.iocoder.yudao.module.system.service.dict.DictTypeService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 字典类型") @RestController @@ -90,7 +90,7 @@ public class DictTypeController { @Operation(summary = "导出数据类型") @GetMapping("/export") @PreAuthorize("@ss.hasPermission('system:dict:query')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void export(HttpServletResponse response, @Valid DictTypePageReqVO exportReqVO) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = dictTypeService.getDictTypePage(exportReqVO).getList(); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/ErrorCodeController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/ErrorCodeController.java index 6288bb3d5..9a28f8d72 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/ErrorCodeController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/errorcode/ErrorCodeController.java @@ -1,29 +1,31 @@ package cn.iocoder.yudao.module.system.controller.admin.errorcode; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.*; +import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeRespVO; +import cn.iocoder.yudao.module.system.controller.admin.errorcode.vo.ErrorCodeSaveReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.errorcode.ErrorCodeDO; import cn.iocoder.yudao.module.system.service.errorcode.ErrorCodeService; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 错误码") @RestController @@ -78,7 +80,7 @@ public class ErrorCodeController { @GetMapping("/export-excel") @Operation(summary = "导出错误码 Excel") @PreAuthorize("@ss.hasPermission('system:error-code:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportErrorCodeExcel(@Valid ErrorCodePageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java index aba876d91..1903ab6e5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java @@ -1,31 +1,31 @@ package cn.iocoder.yudao.module.system.controller.admin.logger; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogRespVO; import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; import cn.iocoder.yudao.module.system.service.logger.LoginLogService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 登录日志") @RestController @@ -47,7 +47,7 @@ public class LoginLogController { @GetMapping("/export") @Operation(summary = "导出登录日志 Excel") @PreAuthorize("@ss.hasPermission('system:login-log:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportLoginLog(HttpServletResponse response, @Valid LoginLogPageReqVO exportReqVO) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = loginLogService.getLoginLogPage(exportReqVO).getList(); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java index 2933eef29..a7036c8f6 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.system.controller.admin.logger; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.translate.core.TranslateUtils; import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogRespVO; @@ -25,8 +25,8 @@ import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 操作日志") @RestController @@ -48,7 +48,7 @@ public class OperateLogController { @Operation(summary = "导出操作日志") @GetMapping("/export") @PreAuthorize("@ss.hasPermission('system:operate-log:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportOperateLog(HttpServletResponse response, @Valid OperateLogPageReqVO exportReqVO) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = operateLogService.getOperateLogPage(exportReqVO).getList(); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java index e5d37ad3d..114a156ab 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java @@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; @@ -95,7 +94,6 @@ public class OAuth2OpenController { @Parameter(name = "scope", example = "user_info"), @Parameter(name = "refresh_token", example = "123424233"), }) - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult postAccessToken(HttpServletRequest request, @RequestParam("grant_type") String grantType, @RequestParam(value = "code", required = false) String code, // 授权码模式 @@ -146,7 +144,6 @@ public class OAuth2OpenController { @PermitAll @Operation(summary = "删除访问令牌") @Parameter(name = "token", required = true, description = "访问令牌", example = "biu") - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult revokeToken(HttpServletRequest request, @RequestParam("token") String token) { // 校验客户端 @@ -165,7 +162,6 @@ public class OAuth2OpenController { @PermitAll @Operation(summary = "校验访问令牌") @Parameter(name = "token", required = true, description = "访问令牌", example = "biu") - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult checkToken(HttpServletRequest request, @RequestParam("token") String token) { // 校验客户端 @@ -216,7 +212,6 @@ public class OAuth2OpenController { @Parameter(name = "auto_approve", required = true, description = "用户是否接受", example = "true"), @Parameter(name = "state", example = "1") }) - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult approveOrDeny(@RequestParam("response_type") String responseType, @RequestParam("client_id") String clientId, @RequestParam(value = "scope", required = false) String scope, diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java index b9b96b6e7..e29abe16d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java @@ -1,31 +1,31 @@ package cn.iocoder.yudao.module.system.controller.admin.permission; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.*; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.service.permission.RoleService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.Comparator; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static java.util.Collections.singleton; @Tag(name = "管理后台 - 角色") @@ -95,7 +95,7 @@ public class RoleController { @GetMapping("/export-excel") @Operation(summary = "导出角色 Excel") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) @PreAuthorize("@ss.hasPermission('system:role:export')") public void export(HttpServletResponse response, @Validated RolePageReqVO exportReqVO) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/SensitiveWordController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/SensitiveWordController.java index 0b9b0049c..16b49f299 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/SensitiveWordController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sensitiveword/SensitiveWordController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.system.controller.admin.sensitiveword; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordRespVO; import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordSaveVO; @@ -14,19 +14,19 @@ import cn.iocoder.yudao.module.system.service.sensitiveword.SensitiveWordService import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; import java.util.Set; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 敏感词") @RestController @@ -81,7 +81,7 @@ public class SensitiveWordController { @GetMapping("/export-excel") @Operation(summary = "导出敏感词 Excel") @PreAuthorize("@ss.hasPermission('system:sensitive-word:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportSensitiveWordExcel(@Valid SensitiveWordPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java index f392ac759..06c4ba66f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.controller.admin.sms; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; import cn.iocoder.yudao.module.system.service.sms.SmsSendService; import io.swagger.v3.oas.annotations.Operation; @@ -28,7 +27,6 @@ public class SmsCallbackController { @PostMapping("/aliyun") @PermitAll @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/document_detail/120998.html 文档") - @OperateLog(enable = false) public CommonResult receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable { String text = ServletUtils.getBody(request); smsSendService.receiveSmsStatus(SmsChannelEnum.ALIYUN.getCode(), text); @@ -38,7 +36,6 @@ public class SmsCallbackController { @PostMapping("/tencent") @PermitAll @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/52077 文档") - @OperateLog(enable = false) public CommonResult receiveTencentSmsStatus(HttpServletRequest request) throws Throwable { String text = ServletUtils.getBody(request); smsSendService.receiveSmsStatus(SmsChannelEnum.TENCENT.getCode(), text); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java index 7496da2f2..1e468f6e0 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java @@ -1,31 +1,31 @@ package cn.iocoder.yudao.module.system.controller.admin.sms; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogRespVO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; import cn.iocoder.yudao.module.system.service.sms.SmsLogService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 短信日志") @RestController @@ -47,7 +47,7 @@ public class SmsLogController { @GetMapping("/export-excel") @Operation(summary = "导出短信日志 Excel") @PreAuthorize("@ss.hasPermission('system:sms-log:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportSmsLogExcel(@Valid SmsLogPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java index 8ac579388..481b6b033 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.system.controller.admin.sms; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.*; @@ -9,7 +10,6 @@ import cn.iocoder.yudao.module.system.service.sms.SmsSendService; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Operation; @@ -22,8 +22,8 @@ import jakarta.validation.Valid; import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 短信模板") @RestController @@ -79,7 +79,7 @@ public class SmsTemplateController { @GetMapping("/export-excel") @Operation(summary = "导出短信模板 Excel") @PreAuthorize("@ss.hasPermission('system:sms-template:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportSmsTemplateExcel(@Valid SmsTemplatePageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java index 73112330d..51aab02d6 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.system.controller.admin.tenant; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO; import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; @@ -15,18 +15,18 @@ import cn.iocoder.yudao.module.system.service.tenant.TenantService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - import jakarta.annotation.Resource; import jakarta.annotation.security.PermitAll; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 租户") @RestController @@ -98,7 +98,7 @@ public class TenantController { @GetMapping("/export-excel") @Operation(summary = "导出租户 Excel") @PreAuthorize("@ss.hasPermission('system:tenant:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportTenantExcel(@Valid TenantPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java index 9efb49ce6..8f1a376f8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.system.controller.admin.user; import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*; import cn.iocoder.yudao.module.system.convert.user.UserConvert; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; @@ -18,22 +18,22 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 用户") @RestController @@ -127,7 +127,7 @@ public class UserController { @GetMapping("/export") @Operation(summary = "导出用户") @PreAuthorize("@ss.hasPermission('system:user:export')") - @OperateLog(type = EXPORT) + @ApiAccessLog(operateType = EXPORT) public void exportUserList(@Validated UserPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); From 87c7b3fac2c291b45c3537d972d0ac3e690d4342 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 4 Apr 2024 09:42:03 +0800 Subject: [PATCH 69/81] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91syste?= =?UTF-8?q?m=20user=20=E5=92=8C=20role=20=E6=8E=A5=E5=85=A5=E6=96=B0?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/DataPermissionUtils.java | 20 +++++ .../system/enums/LogRecordConstants.java | 33 ++++++++ .../admin/permission/RoleController.java | 12 +-- .../permission/vo/role/RoleSaveReqVO.java | 11 ++- .../vo/role/RoleUpdateStatusReqVO.java | 23 ------ .../admin/user/vo/user/UserSaveReqVO.java | 17 +++- .../core/AdminUserParseFunction.java | 9 ++- .../operatelog/core/AreaParseFunction.java | 3 +- .../operatelog/core/DeptParseFunction.java | 11 +-- .../operatelog/core/PostParseFunction.java | 46 +++++++++++ .../framework/operatelog/package-info.java | 3 + .../service/permission/RoleService.java | 8 -- .../service/permission/RoleServiceImpl.java | 67 +++++++++------- .../service/user/AdminUserServiceImpl.java | 77 +++++++++++++------ 14 files changed, 232 insertions(+), 108 deletions(-) create mode 100644 yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java delete mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java index c154bd5f5..583c482b2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder; import lombok.SneakyThrows; +import java.util.concurrent.Callable; + /** * 数据权限 Util * @@ -40,4 +42,22 @@ public class DataPermissionUtils { } } + /** + * 忽略数据权限,执行对应的逻辑 + * + * @param callable 逻辑 + * @return 执行结果 + */ + @SneakyThrows + public static T executeIgnore(Callable callable) { + DataPermission dataPermission = getDisableDataPermissionDisable(); + DataPermissionContextHolder.add(dataPermission); + try { + // 执行 callable + return callable.call(); + } finally { + DataPermissionContextHolder.remove(); + } + } + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java new file mode 100644 index 000000000..4ee384073 --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.enums; + +/** + * System 操作日志枚举 + * 目的:统一管理,也减少 Service 里各种“复杂”字符串 + * + * @author 芋道源码 + */ +public interface LogRecordConstants { + + // ======================= SYSTEM_USER 用户 ======================= + + String SYSTEM_USER_TYPE = "SYSTEM 用户"; + String SYSTEM_USER_CREATE_SUB_TYPE = "创建用户"; + String SYSTEM_USER_CREATE_SUCCESS = "创建了用户【{{#user.nickname}}】"; + String SYSTEM_USER_UPDATE_SUB_TYPE = "更新用户"; + String SYSTEM_USER_UPDATE_SUCCESS = "更新了用户【{{#user.nickname}}】: {_DIFF{#updateReqVO}}"; + String SYSTEM_USER_DELETE_SUB_TYPE = "删除用户"; + String SYSTEM_USER_DELETE_SUCCESS = "删除了用户【{{#user.nickname}}】"; + String SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE = "重置用户密码"; + String SYSTEM_USER_UPDATE_PASSWORD_SUCCESS = "将用户【{{#user.nickname}}】的密码从【{{#user.password}}】重置为【{{#newPassword}}】"; + + // ======================= SYSTEM_ROLE 角色 ======================= + + String SYSTEM_ROLE_TYPE = "SYSTEM 角色"; + String SYSTEM_ROLE_CREATE_SUB_TYPE = "创建角色"; + String SYSTEM_ROLE_CREATE_SUCCESS = "创建了角色【{{#role.name}}】"; + String SYSTEM_ROLE_UPDATE_SUB_TYPE = "更新角色"; + String SYSTEM_ROLE_UPDATE_SUCCESS = "更新了角色【{{#role.name}}】: {_DIFF{#updateReqVO}}"; + String SYSTEM_ROLE_DELETE_SUB_TYPE = "删除角色"; + String SYSTEM_ROLE_DELETE_SUCCESS = "删除了角色【{{#role.name}}】"; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java index e29abe16d..ee6ae9fbb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java @@ -52,14 +52,6 @@ public class RoleController { return success(true); } - @PutMapping("/update-status") - @Operation(summary = "修改角色状态") - @PreAuthorize("@ss.hasPermission('system:role:update')") - public CommonResult updateRoleStatus(@Valid @RequestBody RoleUpdateStatusReqVO reqVO) { - roleService.updateRoleStatus(reqVO.getId(), reqVO.getStatus()); - return success(true); - } - @DeleteMapping("/delete") @Operation(summary = "删除角色") @Parameter(name = "id", description = "角色编号", required = true, example = "1024") @@ -87,10 +79,10 @@ public class RoleController { @GetMapping({"/list-all-simple", "/simple-list"}) @Operation(summary = "获取角色精简信息列表", description = "只包含被开启的角色,主要用于前端的下拉选项") - public CommonResult> getSimpleRoleList() { + public CommonResult> getSimpleRoleList() { List list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus())); list.sort(Comparator.comparing(RoleDO::getSort)); - return success(BeanUtils.toBean(list, RoleSimpleRespVO.class)); + return success(BeanUtils.toBean(list, RoleRespVO.class)); } @GetMapping("/export-excel") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java index c6508685b..dc97bc276 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; +import com.mzt.logapi.starter.annotation.DiffLogField; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -7,7 +8,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -@Schema(description = "管理后台 - 角色创建 Request VO") +@Schema(description = "管理后台 - 角色创建/更新 Request VO") @Data public class RoleSaveReqVO { @@ -16,19 +17,23 @@ public class RoleSaveReqVO { @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "管理员") @NotBlank(message = "角色名称不能为空") - @Size(max = 30, message = "角色名称长度不能超过30个字符") + @Size(max = 30, message = "角色名称长度不能超过 30 个字符") + @DiffLogField(name = "角色名称") private String name; @NotBlank(message = "角色标志不能为空") - @Size(max = 100, message = "角色标志长度不能超过100个字符") + @Size(max = 100, message = "角色标志长度不能超过 100 个字符") @Schema(description = "角色编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ADMIN") + @DiffLogField(name = "角色标志") private String code; @Schema(description = "显示顺序不能为空", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @NotNull(message = "显示顺序不能为空") + @DiffLogField(name = "显示顺序") private Integer sort; @Schema(description = "备注", example = "我是一个角色") + @DiffLogField(name = "备注") private String remark; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java deleted file mode 100644 index 98fb0514b..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 角色更新状态 Request VO") -@Data -public class RoleUpdateStatusReqVO { - - @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "角色编号不能为空") - private Long id; - - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java index 4777ee198..f196e651d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java @@ -2,12 +2,16 @@ package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.framework.operatelog.core.DeptParseFunction; +import cn.iocoder.yudao.module.system.framework.operatelog.core.PostParseFunction; +import cn.iocoder.yudao.module.system.framework.operatelog.core.SexParseFunction; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.mzt.logapi.starter.annotation.DiffLogField; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.*; import lombok.Data; import org.hibernate.validator.constraints.Length; -import jakarta.validation.constraints.*; import java.util.Set; @Schema(description = "管理后台 - 用户创建/修改 Request VO") @@ -21,34 +25,43 @@ public class UserSaveReqVO { @NotBlank(message = "用户账号不能为空") @Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成") @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符") + @DiffLogField(name = "用户账号") private String username; @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") @Size(max = 30, message = "用户昵称长度不能超过30个字符") + @DiffLogField(name = "用户昵称") private String nickname; @Schema(description = "备注", example = "我是一个用户") + @DiffLogField(name = "备注") private String remark; - @Schema(description = "部门ID", example = "我是一个用户") + @Schema(description = "部门编号", example = "我是一个用户") + @DiffLogField(name = "部门", function = DeptParseFunction.NAME) private Long deptId; @Schema(description = "岗位编号数组", example = "1") + @DiffLogField(name = "岗位", function = PostParseFunction.NAME) private Set postIds; @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") @Email(message = "邮箱格式不正确") @Size(max = 50, message = "邮箱长度不能超过 50 个字符") + @DiffLogField(name = "用户邮箱") private String email; @Schema(description = "手机号码", example = "15601691300") @Mobile + @DiffLogField(name = "手机号码") private String mobile; @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") + @DiffLogField(name = "用户性别", function = SexParseFunction.NAME) private Integer sex; @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") + @DiffLogField(name = "用户头像") private String avatar; // ========== 仅【创建】时,需要传递的字段 ========== diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java index 8111d4adc..f07125128 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java @@ -1,8 +1,9 @@ package cn.iocoder.yudao.module.system.framework.operatelog.core; +import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; import com.mzt.logapi.service.IParseFunction; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -20,7 +21,7 @@ public class AdminUserParseFunction implements IParseFunction { public static final String NAME = "getAdminUserById"; @Resource - private AdminUserApi adminUserApi; + private AdminUserService adminUserService; @Override public String functionName() { @@ -34,7 +35,7 @@ public class AdminUserParseFunction implements IParseFunction { } // 获取用户信息 - AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString())); + AdminUserDO user = adminUserService.getUser(Convert.toLong(value)); if (user == null) { log.warn("[apply][获取用户{{}}为空", value); return ""; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java index e22ab5cf2..f2b908901 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.system.framework.operatelog.core; +import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; import com.mzt.logapi.service.IParseFunction; @@ -32,7 +33,7 @@ public class AreaParseFunction implements IParseFunction { if (StrUtil.isEmptyIfStr(value)) { return ""; } - return AreaUtils.format(Integer.parseInt(value.toString())); + return AreaUtils.format(Convert.toInt(value)); } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java index 1a7ba9d8f..125c32483 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java @@ -1,15 +1,16 @@ package cn.iocoder.yudao.module.system.framework.operatelog.core; +import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.system.api.dept.DeptApi; -import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.service.dept.DeptService; import com.mzt.logapi.service.IParseFunction; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** - * 管理员名字的 {@link IParseFunction} 实现类 + * 部门名字的 {@link IParseFunction} 实现类 * * @author HUIHUI */ @@ -20,7 +21,7 @@ public class DeptParseFunction implements IParseFunction { public static final String NAME = "getDeptById"; @Resource - private DeptApi deptApi; + private DeptService deptService; @Override public String functionName() { @@ -34,7 +35,7 @@ public class DeptParseFunction implements IParseFunction { } // 获取部门信息 - DeptRespDTO dept = deptApi.getDept(Long.parseLong(value.toString())); + DeptDO dept = deptService.getDept(Convert.toLong(value)); if (dept == null) { log.warn("[apply][获取部门{{}}为空", value); return ""; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java new file mode 100644 index 000000000..e246d1234 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.framework.operatelog.core; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.service.dept.PostService; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 岗位名字的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Slf4j +@Component +public class PostParseFunction implements IParseFunction { + + public static final String NAME = "getPostById"; + + @Resource + private PostService postService; + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + + // 获取岗位信息 + PostDO post = postService.getPost(Convert.toLong(value)); + if (post == null) { + log.warn("[apply][获取岗位{{}}为空", value); + return ""; + } + return post.getName(); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java index 978444e17..c7f124832 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java @@ -1 +1,4 @@ +/** + * 占位文件,避免文件夹缩进 + */ package cn.iocoder.yudao.module.system.framework.operatelog; \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java index 21e588730..6de8b515b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java @@ -40,14 +40,6 @@ public interface RoleService { */ void deleteRole(Long id); - /** - * 更新角色状态 - * - * @param id 角色编号 - * @param status 状态 - */ - void updateRoleStatus(Long id, Integer status); - /** * 设置角色的数据权限 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java index fcef74f5c..3f3c17a53 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java @@ -17,6 +17,8 @@ import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; import com.google.common.annotations.VisibleForTesting; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.starter.annotation.LogRecord; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @@ -30,6 +32,7 @@ import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*; /** * 角色 Service 实现类 @@ -48,41 +51,40 @@ public class RoleServiceImpl implements RoleService { @Override @Transactional(rollbackFor = Exception.class) + @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_CREATE_SUB_TYPE, bizNo = "{{#role.id}}", + success = SYSTEM_ROLE_CREATE_SUCCESS) public Long createRole(RoleSaveReqVO createReqVO, Integer type) { - // 校验角色 + // 1. 校验角色 validateRoleDuplicate(createReqVO.getName(), createReqVO.getCode(), null); - // 插入到数据库 - RoleDO role = BeanUtils.toBean(createReqVO, RoleDO.class); - role.setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType())); - role.setStatus(CommonStatusEnum.ENABLE.getStatus()); - role.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限 + + // 2. 插入到数据库 + RoleDO role = BeanUtils.toBean(createReqVO, RoleDO.class) + .setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType())) + .setStatus(CommonStatusEnum.ENABLE.getStatus()) + .setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限 roleMapper.insert(role); - // 返回 + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("role", role); return role.getId(); } @Override @CacheEvict(value = RedisKeyConstants.ROLE, key = "#updateReqVO.id") + @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = SYSTEM_ROLE_UPDATE_SUCCESS) public void updateRole(RoleSaveReqVO updateReqVO) { - // 校验是否可以更新 - validateRoleForUpdate(updateReqVO.getId()); - // 校验角色的唯一字段是否重复 + // 1.1 校验是否可以更新 + RoleDO role = validateRoleForUpdate(updateReqVO.getId()); + // 1.2 校验角色的唯一字段是否重复 validateRoleDuplicate(updateReqVO.getName(), updateReqVO.getCode(), updateReqVO.getId()); - // 更新到数据库 + // 2. 更新到数据库 RoleDO updateObj = BeanUtils.toBean(updateReqVO, RoleDO.class); roleMapper.updateById(updateObj); - } - @Override - @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") - public void updateRoleStatus(Long id, Integer status) { - // 校验是否可以更新 - validateRoleForUpdate(id); - - // 更新状态 - RoleDO updateObj = new RoleDO().setId(id).setStatus(status); - roleMapper.updateById(updateObj); + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("role", role); } @Override @@ -102,13 +104,19 @@ public class RoleServiceImpl implements RoleService { @Override @Transactional(rollbackFor = Exception.class) @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") + @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = SYSTEM_ROLE_DELETE_SUCCESS) public void deleteRole(Long id) { - // 校验是否可以更新 - validateRoleForUpdate(id); - // 标记删除 + // 1. 校验是否可以更新 + RoleDO role = validateRoleForUpdate(id); + + // 2.1 标记删除 roleMapper.deleteById(id); - // 删除相关数据 + // 2.2 删除相关数据 permissionService.processRoleDeleted(id); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("role", role); } /** @@ -149,15 +157,16 @@ public class RoleServiceImpl implements RoleService { * @param id 角色编号 */ @VisibleForTesting - void validateRoleForUpdate(Long id) { - RoleDO roleDO = roleMapper.selectById(id); - if (roleDO == null) { + RoleDO validateRoleForUpdate(Long id) { + RoleDO role = roleMapper.selectById(id); + if (role == null) { throw exception(ROLE_NOT_EXISTS); } // 内置角色,不允许删除 - if (RoleTypeEnum.SYSTEM.getType().equals(roleDO.getType())) { + if (RoleTypeEnum.SYSTEM.getType().equals(role.getType())) { throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); } + return role; } @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java index a8ed7620a..efae327f8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java @@ -27,6 +27,10 @@ import cn.iocoder.yudao.module.system.service.dept.PostService; import cn.iocoder.yudao.module.system.service.permission.PermissionService; import cn.iocoder.yudao.module.system.service.tenant.TenantService; import com.google.common.annotations.VisibleForTesting; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; @@ -34,7 +38,6 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import jakarta.annotation.Resource; import java.io.InputStream; import java.time.LocalDateTime; import java.util.*; @@ -43,6 +46,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*; /** * 后台用户 Service 实现类 @@ -79,42 +83,54 @@ public class AdminUserServiceImpl implements AdminUserService { @Override @Transactional(rollbackFor = Exception.class) + @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}", + success = SYSTEM_USER_CREATE_SUCCESS) public Long createUser(UserSaveReqVO createReqVO) { - // 校验账户配合 + // 1.1 校验账户配合 tenantService.handleTenantInfo(tenant -> { long count = userMapper.selectCount(); if (count >= tenant.getAccountCount()) { throw exception(USER_COUNT_MAX, tenant.getAccountCount()); } }); - // 校验正确性 + // 1.2 校验正确性 validateUserForCreateOrUpdate(null, createReqVO.getUsername(), createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptId(), createReqVO.getPostIds()); - // 插入用户 + // 2.1 插入用户 AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class); user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码 userMapper.insert(user); - // 插入关联岗位 + // 2.2 插入关联岗位 if (CollectionUtil.isNotEmpty(user.getPostIds())) { userPostMapper.insertBatch(convertList(user.getPostIds(), postId -> new UserPostDO().setUserId(user.getId()).setPostId(postId))); } + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("user", user); return user.getId(); } @Override @Transactional(rollbackFor = Exception.class) + @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = SYSTEM_USER_UPDATE_SUCCESS) public void updateUser(UserSaveReqVO updateReqVO) { updateReqVO.setPassword(null); // 特殊:此处不更新密码 - // 校验正确性 - validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(), + // 1. 校验正确性 + AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(), updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds()); - // 更新用户 + + // 2.1 更新用户 AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class); userMapper.updateById(updateObj); - // 更新岗位 + // 2.2 更新岗位 updateUserPost(updateReqVO, updateObj); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldUser, UserSaveReqVO.class)); + LogRecordContext.putVariable("user", oldUser); } private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) { @@ -124,7 +140,7 @@ public class AdminUserServiceImpl implements AdminUserService { Set postIds = CollUtil.emptyIfNull(updateObj.getPostIds()); Collection createPostIds = CollUtil.subtract(postIds, dbPostIds); Collection deletePostIds = CollUtil.subtract(dbPostIds, postIds); - // 执行新增和删除。对于已经授权的菜单,不用做任何处理 + // 执行新增和删除。对于已经授权的岗位,不用做任何处理 if (!CollectionUtil.isEmpty(createPostIds)) { userPostMapper.insertBatch(convertList(createPostIds, postId -> new UserPostDO().setUserId(userId).setPostId(postId))); @@ -173,14 +189,21 @@ public class AdminUserServiceImpl implements AdminUserService { } @Override + @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE, bizNo = "{{#id}}", + success = SYSTEM_USER_UPDATE_PASSWORD_SUCCESS) public void updateUserPassword(Long id, String password) { - // 校验用户存在 - validateUserExists(id); - // 更新密码 + // 1. 校验用户存在 + AdminUserDO user = validateUserExists(id); + + // 2. 更新密码 AdminUserDO updateObj = new AdminUserDO(); updateObj.setId(id); updateObj.setPassword(encodePassword(password)); // 加密密码 userMapper.updateById(updateObj); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("user", user); + LogRecordContext.putVariable("newPassword", updateObj.getPassword()); } @Override @@ -196,15 +219,21 @@ public class AdminUserServiceImpl implements AdminUserService { @Override @Transactional(rollbackFor = Exception.class) + @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = SYSTEM_USER_DELETE_SUCCESS) public void deleteUser(Long id) { - // 校验用户存在 - validateUserExists(id); - // 删除用户 + // 1. 校验用户存在 + AdminUserDO user = validateUserExists(id); + + // 2.1 删除用户 userMapper.deleteById(id); - // 删除用户关联数据 + // 2.2 删除用户关联数据 permissionService.processUserDeleted(id); - // 删除用户岗位 + // 2.2 删除用户岗位 userPostMapper.deleteByUserId(id); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("user", user); } @Override @@ -294,12 +323,12 @@ public class AdminUserServiceImpl implements AdminUserService { return deptIds; } - private void validateUserForCreateOrUpdate(Long id, String username, String mobile, String email, + private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email, Long deptId, Set postIds) { // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确 - DataPermissionUtils.executeIgnore(() -> { + return DataPermissionUtils.executeIgnore(() -> { // 校验用户存在 - validateUserExists(id); + AdminUserDO user = validateUserExists(id); // 校验用户名唯一 validateUsernameUnique(id, username); // 校验手机号唯一 @@ -310,18 +339,20 @@ public class AdminUserServiceImpl implements AdminUserService { deptService.validateDeptList(CollectionUtils.singleton(deptId)); // 校验岗位处于开启状态 postService.validatePostList(postIds); + return user; }); } @VisibleForTesting - void validateUserExists(Long id) { + AdminUserDO validateUserExists(Long id) { if (id == null) { - return; + return null; } AdminUserDO user = userMapper.selectById(id); if (user == null) { throw exception(USER_NOT_EXISTS); } + return user; } @VisibleForTesting From f869a5718df38236c7c637e286ae4e13097c55e7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 4 Apr 2024 09:57:06 +0800 Subject: [PATCH 70/81] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91syste?= =?UTF-8?q?m=20user=20=E5=92=8C=20role=20=E6=8E=A5=E5=85=A5=E6=96=B0?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/infra/enums/DictTypeConstants.java | 4 ++-- .../vo/apiaccesslog/ApiAccessLogRespVO.java | 2 +- .../module/system/enums/DictTypeConstants.java | 2 -- .../service/permission/RoleServiceImplTest.java | 17 ----------------- 4 files changed, 3 insertions(+), 22 deletions(-) diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/DictTypeConstants.java b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/DictTypeConstants.java index 53ab807cb..36ad63d56 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/DictTypeConstants.java +++ b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/DictTypeConstants.java @@ -7,8 +7,6 @@ package cn.iocoder.yudao.module.infra.enums; */ public interface DictTypeConstants { - String REDIS_TIMEOUT_TYPE = "infra_redis_timeout_type"; // Redis 超时类型 - String JOB_STATUS = "infra_job_status"; // 定时任务状态的枚举 String JOB_LOG_STATUS = "infra_job_log_status"; // 定时任务日志状态的枚举 @@ -17,4 +15,6 @@ public interface DictTypeConstants { String CONFIG_TYPE = "infra_config_type"; // 参数配置类型 String BOOLEAN_STRING = "infra_boolean_string"; // Boolean 是否类型 + String OPERATE_TYPE = "infra_operate_type"; // 操作类型 + } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java index ff0fed7a3..e991450a4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java @@ -70,7 +70,7 @@ public class ApiAccessLogRespVO { @Schema(description = "操作分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @ExcelProperty(value = "操作分类", converter = DictConvert.class) - @DictFormat(DictTypeConstants.OPERATE_TYPE) + @DictFormat(cn.iocoder.yudao.module.infra.enums.DictTypeConstants.OPERATE_TYPE) private Integer operateType; @Schema(description = "开始请求时间", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java index c0e342383..d7967fe28 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java @@ -14,8 +14,6 @@ public interface DictTypeConstants { String USER_SEX = "system_user_sex"; // 用户性别 - String OPERATE_TYPE = "system_operate_type"; // 操作类型 - String LOGIN_TYPE = "system_login_type"; // 登录日志的类型 String LOGIN_RESULT = "system_login_result"; // 登录结果 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java index d2c400719..941b7bca1 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java @@ -79,23 +79,6 @@ public class RoleServiceImplTest extends BaseDbUnitTest { assertPojoEquals(reqVO, newRoleDO); } - @Test - public void testUpdateRoleStatus() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) - .setType(RoleTypeEnum.CUSTOM.getType())); - roleMapper.insert(roleDO); - - // 准备参数 - Long roleId = roleDO.getId(); - - // 调用 - roleService.updateRoleStatus(roleId, CommonStatusEnum.DISABLE.getStatus()); - // 断言 - RoleDO dbRoleDO = roleMapper.selectById(roleId); - assertEquals(CommonStatusEnum.DISABLE.getStatus(), dbRoleDO.getStatus()); - } - @Test public void testUpdateRoleDataScope() { // mock 数据 From 05af77b786b0eb370bd721382647646dcc629f58 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 5 Apr 2024 14:44:00 +0800 Subject: [PATCH 71/81] =?UTF-8?q?crm=EF=BC=9Acode=20review=20=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=94=BB=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmStatisticsPortraitServiceImpl.java | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java index 9863f62fc..eae012866 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java @@ -40,21 +40,22 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe @Override public List getCustomerSummaryByArea(CrmStatisticsPortraitReqVO reqVO) { - // 1.1 获得用户编号数组 + // 1. 获得用户编号数组 List userIds = getUserIds(reqVO); if (CollUtil.isEmpty(userIds)) { return Collections.emptyList(); } reqVO.setUserIds(userIds); - // 1.2 获取客户地区统计数据 + + // 2. 获取客户地区统计数据 List list = portraitMapper.selectSummaryListGroupByAreaId(reqVO); if (CollUtil.isEmpty(list)) { return Collections.emptyList(); } - // 2. 拼接数据 + // 3. 拼接数据 List areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area); - areaList.add(new Area().setId(null).setName("未知")); + areaList.add(new Area().setId(null).setName("未知")); // TODO @puhui999:是不是 65 find 的逻辑改下;不用 findAndThen,直接从 areaMap 拿;拿到就设置,不拿到就设置 null 和 未知;这样,58 本行可以删除掉完事了;这样代码更简单和一致 Map areaMap = convertMap(areaList, Area::getId); return convertList(list, item -> { Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE); @@ -76,11 +77,7 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe reqVO.setUserIds(userIds); // 2. 获取客户行业统计数据 - List industryRespVOList = portraitMapper.selectCustomerIndustryListGroupByIndustryId(reqVO); - if (CollUtil.isEmpty(industryRespVOList)) { - return Collections.emptyList(); - } - return industryRespVOList; + return portraitMapper.selectCustomerIndustryListGroupByIndustryId(reqVO); } @Override @@ -93,11 +90,7 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe reqVO.setUserIds(userIds); // 2. 获取客户行业统计数据 - List sourceRespVOList = portraitMapper.selectCustomerSourceListGroupBySource(reqVO); - if (CollUtil.isEmpty(sourceRespVOList)) { - return Collections.emptyList(); - } - return sourceRespVOList; + return portraitMapper.selectCustomerSourceListGroupBySource(reqVO); } @Override @@ -110,11 +103,7 @@ public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitSe reqVO.setUserIds(userIds); // 2. 获取客户级别统计数据 - List levelRespVOList = portraitMapper.selectCustomerLevelListGroupByLevel(reqVO); - if (CollUtil.isEmpty(levelRespVOList)) { - return Collections.emptyList(); - } - return levelRespVOList; + return portraitMapper.selectCustomerLevelListGroupByLevel(reqVO); } /** From e18d26d8a6c004394c5c3fd7cb184d3fde402a2f Mon Sep 17 00:00:00 2001 From: dhb52 Date: Fri, 5 Apr 2024 14:59:00 +0800 Subject: [PATCH 72/81] =?UTF-8?q?fix:=20[CRM-=E5=AE=A2=E6=88=B7=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1]=E6=8C=89=E5=AE=A2=E6=88=B7=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=AD=9B=E9=80=89,=E5=85=B3=E8=81=94?= =?UTF-8?q?=E5=90=88=E5=90=8C=E6=98=AF=E5=90=A6=E6=9C=89=E6=88=90=E4=BA=A4?= =?UTF-8?q?=E8=AE=B0=E5=BD=95,=E6=9C=89=E5=88=99=E8=A7=86=E4=B8=BA[?= =?UTF-8?q?=E6=88=90=E4=BA=A4=E5=AE=A2=E6=88=B7]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/CrmStatisticsCustomerMapper.xml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index b767e2092..7f8b36ba4 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -18,17 +18,18 @@ - SELECT DATE_FORMAT(order_date, '%Y-%m-%d') AS time, - COUNT(DISTINCT customer_id) AS customerDealCount - FROM crm_contract - WHERE deleted = 0 - AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} - AND owner_user_id IN + SELECT DATE_FORMAT(customer.create_time, '%Y-%m-%d') AS time, + COUNT(DISTINCT customer.id) AS customer_deal_count + FROM crm_customer AS customer + LEFT JOIN crm_contract AS contract ON contract.customer_id = customer.id + WHERE customer.deleted = 0 AND contract.deleted = 0 + AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status} + AND customer.owner_user_id IN #{userId} - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - GROUP BY time + AND contract.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} + GROUP BY time +