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 6ce57abf1..8386e80ed 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 @@ -11,6 +11,8 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 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.logger.OperateLogApi; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import io.swagger.v3.oas.annotations.Operation; @@ -36,6 +38,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; 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.LogRecordConstants.CRM_CUSTOMER; @Tag(name = "管理后台 - CRM 客户") @RestController @@ -50,6 +53,8 @@ public class CrmCustomerController { private DeptApi deptApi; @Resource private AdminUserApi adminUserApi; + @Resource + private OperateLogApi operateLogApi; @PostMapping("/create") @Operation(summary = "创建客户") @@ -130,6 +135,21 @@ public class CrmCustomerController { return success(true); } + @GetMapping("/operate-log") + @Operation(summary = "获得客户操作日志") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('crm:customer:query')") + public CommonResult> getOperateLog(@RequestParam("id") Long id) { + // 1. 获取客户 + CrmCustomerDO customer = customerService.getCustomer(id); + if (customer == null) { + return success(null); + } + + // 2. 获取操作日志 + return success(operateLogApi.getOperateLogByModuleAndBizId(CRM_CUSTOMER, id)); + } + // TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了; @PutMapping("/lock") @Operation(summary = "锁定/解锁客户") diff --git a/yudao-module-system/yudao-module-system-api/pom.xml b/yudao-module-system/yudao-module-system-api/pom.xml index 416413cd6..3c91bd974 100644 --- a/yudao-module-system/yudao-module-system-api/pom.xml +++ b/yudao-module-system/yudao-module-system-api/pom.xml @@ -29,6 +29,16 @@ bizlog-sdk + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-core + + org.springframework.boot 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 6e28c7d78..ad42c87f7 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 @@ -1,9 +1,11 @@ package cn.iocoder.yudao.module.system.api.logger; import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; - +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; import jakarta.validation.Valid; +import java.util.List; + /** * 操作日志 API 接口 * @@ -18,4 +20,13 @@ public interface OperateLogApi { */ void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO); + /** + * 获取指定模块的指定数据的操作日志 + * + * @param module 操作模块 + * @param bizId 操作模块编号 + * @return 操作日志 + */ + List getOperateLogByModuleAndBizId(String module, Long bizId); + } 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/OperateLogV2RespDTO.java new file mode 100644 index 000000000..9a918abe3 --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.system.api.logger.dto; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; + +/** + * 系统操作日志 Resp DTO + * + * @author HUIHUI + */ +@Data +public class OperateLogV2RespDTO { + + /** + * 链路追踪编号 + * + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。 + */ + private String traceId; + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性 + */ + private Long userId; + /** + * 用户类型 + * + * 关联 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 操作模块 + */ + private String module; + /** + * 操作名 + */ + private String name; + /** + * 操作模块业务编号 + */ + private Long bizId; + /** + * 操作内容,记录整个操作的明细 + * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 + */ + private String content; + /** + * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) + * 例如说,记录订单编号,{ orderId: "1"} + */ + private String extra; + + /** + * 请求方法名 + */ + private String requestMethod; + /** + * 请求地址 + */ + private String requestUrl; + /** + * 用户 IP + */ + private String userIp; + /** + * 浏览器 UA + */ + private String userAgent; + + /** + * 创建时间 + */ + // TODO puhui999: 木得效果怎么肥事 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) + private LocalDateTime createTime; + /** + * 创建者,关联 AdminUserDO#getId + */ + private String creator; + /** + * 创建者名称 + */ + private String creatorName; + +} 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 20aa2635d..d748bcc23 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,11 +1,23 @@ package cn.iocoder.yudao.module.system.api.logger; +import cn.hutool.core.collection.CollUtil; +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.OperateLogV2RespDTO; +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.service.logger.OperateLogService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import jakarta.annotation.Resource; 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.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; /** * 操作日志 API 实现类 @@ -18,10 +30,31 @@ public class OperateLogApiImpl implements OperateLogApi { @Resource private OperateLogService operateLogService; + @Resource + private AdminUserService adminUserService; @Override public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { operateLogService.createOperateLog(createReqDTO); } + @Override + public List getOperateLogByModuleAndBizId(String module, Long bizId) { + List logList = operateLogService.getOperateLogByModuleAndBizId(module, bizId); + if (CollUtil.isEmpty(logList)) { + return Collections.emptyList(); + } + + // 获取用户 + List userList = adminUserService.getUserList(convertSet(logList, item -> Long.parseLong(item.getCreator()))); + Map userMap = convertMap(userList, AdminUserDO::getId); + return convertList(logList, item -> { + OperateLogV2RespDTO bean = BeanUtils.toBean(item, OperateLogV2RespDTO.class); + findAndThen(userMap, Long.parseLong(item.getCreator()), user -> { + bean.setCreatorName(user.getNickname()); + }); + return bean; + }); + } + } 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 index 246f45612..1b7b1eaab 100644 --- 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 @@ -59,7 +59,11 @@ public class OperateLogV2DO extends BaseDO { * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 */ private String content; - + /** + * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) + * 例如说,记录订单编号,{ orderId: "1"} + */ + private String extra; /** * 请求方法名 */ 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 index e08686914..362f0d2c4 100644 --- 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 @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO; import org.apache.ibatis.annotations.Mapper; import java.util.Collection; +import java.util.List; @Mapper public interface OperateLogV2Mapper extends BaseMapperX { @@ -20,4 +21,11 @@ public interface OperateLogV2Mapper extends BaseMapperX { return selectPage(reqVO, query); } + default List selectListByModuleAndBizId(String module, Long bizId) { + return selectList(new LambdaQueryWrapperX() + .eq(OperateLogV2DO::getModule, module) + .eq(OperateLogV2DO::getBizId, bizId) + .orderByDesc(OperateLogV2DO::getCreateTime)); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java index 161d4eca6..a81280f99 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.system.framework.bizlog.service; 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.system.api.logger.OperateLogApi; +import cn.iocoder.yudao.module.system.service.logger.OperateLogService; import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO; import com.mzt.logapi.beans.LogRecord; import com.mzt.logapi.service.ILogRecordService; @@ -18,7 +18,7 @@ import java.util.List; /** * 操作日志 ILogRecordService 实现类 * - * 基于 {@link OperateLogApi} 实现,记录操作日志 + * 基于 {@link OperateLogService} 实现,记录操作日志 * * @author HUIHUI */ @@ -27,7 +27,7 @@ import java.util.List; public class ILogRecordServiceImpl implements ILogRecordService { @Resource - private OperateLogApi operateLogApi; + private OperateLogService operateLogService; @Override public void record(LogRecord logRecord) { @@ -41,6 +41,7 @@ public class ILogRecordServiceImpl implements ILogRecordService { // 补全请求信息 fillRequestFields(reqBO); // 异步记录日志 + operateLogService.createOperateLogV2(reqBO); log.info("操作日志 ===> {}", reqBO); } @@ -54,6 +55,7 @@ public class ILogRecordServiceImpl implements ILogRecordService { reqBO.setName(logRecord.getSubType());// 操作名称如 转移客户 reqBO.setBizId(Long.parseLong(logRecord.getBizNo())); // 操作模块业务编号 reqBO.setContent(logRecord.getAction());// 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 + reqBO.setExtra(logRecord.getExtra()); // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"} } private static void fillRequestFields(OperateLogV2CreateReqBO reqBO) { 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 9aada4999..30a1a1a7c 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 @@ -4,6 +4,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; 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.service.logger.bo.OperateLogV2CreateReqBO; + +import java.util.List; /** * 操作日志 Service 接口 @@ -19,13 +23,6 @@ public interface OperateLogService { */ void createOperateLog(OperateLogCreateReqDTO createReqDTO); - /** - * 记录操作日志 V2 - * - * @param createReqDTO 操作日志请求 - */ - void createOperateLogV2(OperateLogCreateReqDTO createReqDTO); - /** * 获得操作日志分页列表 * @@ -34,4 +31,22 @@ public interface OperateLogService { */ PageResult getOperateLogPage(OperateLogPageReqVO pageReqVO); + //======================= LOG V2 ======================= + + /** + * 记录操作日志 V2 + * + * @param createReqBO 创建请求 + */ + void createOperateLogV2(OperateLogV2CreateReqBO createReqBO); + + /** + * 获取指定模块的指定数据的操作日志 + * + * @param module 操作模块 + * @param bizId 操作模块编号 + * @return 操作日志 + */ + List getOperateLogByModuleAndBizId(String module, Long bizId); + } 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 840913f62..894cc48c8 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 @@ -12,6 +12,7 @@ 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.logger.bo.OperateLogV2CreateReqBO; import cn.iocoder.yudao.module.system.service.user.AdminUserService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -19,6 +20,7 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.util.Collection; +import java.util.List; 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; @@ -50,12 +52,6 @@ public class OperateLogServiceImpl implements OperateLogService { operateLogMapper.insert(log); } - @Override - public void createOperateLogV2(OperateLogCreateReqDTO createReqDTO) { - OperateLogV2DO log = BeanUtils.toBean(createReqDTO, OperateLogV2DO.class); - operateLogV2Mapper.insert(log); - } - @Override public PageResult getOperateLogPage(OperateLogPageReqVO pageReqVO) { // 处理基于用户昵称的查询 @@ -70,4 +66,17 @@ public class OperateLogServiceImpl implements OperateLogService { return operateLogMapper.selectPage(pageReqVO, userIds); } + //======================= LOG V2 ======================= + + @Override + public void createOperateLogV2(OperateLogV2CreateReqBO createReqBO) { + OperateLogV2DO log = BeanUtils.toBean(createReqBO, OperateLogV2DO.class); + operateLogV2Mapper.insert(log); + } + + @Override + public List getOperateLogByModuleAndBizId(String module, Long bizId) { + return operateLogV2Mapper.selectListByModuleAndBizId(module, bizId); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java index 8c1675879..d6c44604c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java @@ -53,6 +53,11 @@ public class OperateLogV2CreateReqBO { */ @NotEmpty(message = "操作内容不能为空") private String content; + /** + * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) + * 例如说,记录订单编号,{ orderId: "1"} + */ + private String extra; /** * 请求方法名