| | |
| | | package com.sinata.appointment.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.sinata.appointment.dto.OpLogListVo; |
| | | import com.sinata.appointment.entity.BizOpLog; |
| | | import com.sinata.appointment.entity.MpUser; |
| | | import com.sinata.appointment.entity.SysUser; |
| | | import com.sinata.appointment.mapper.BizOpLogMapper; |
| | | import com.sinata.appointment.mapper.MpUserMapper; |
| | | import com.sinata.appointment.mapper.SysUserMapper; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.slf4j.MDC; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | @Service |
| | |
| | | public class OpLogApplicationService { |
| | | |
| | | public static final String MDC_TRACE = "traceId"; |
| | | public static final String SOURCE_ADMIN = "ADMIN"; |
| | | public static final String SOURCE_MINI_EMPLOYEE = "MINI_EMPLOYEE"; |
| | | |
| | | private final BizOpLogMapper bizOpLogMapper; |
| | | private final SysUserMapper sysUserMapper; |
| | | private final MpUserMapper mpUserMapper; |
| | | |
| | | @Async |
| | | public IPage<OpLogListVo> page(long page, long size, String sourceType) { |
| | | Page<BizOpLog> pager = new Page<>(page, size); |
| | | LambdaQueryWrapper<BizOpLog> query = |
| | | new LambdaQueryWrapper<BizOpLog>() |
| | | .eq(BizOpLog::getDelFlag, 0) |
| | | .orderByDesc(BizOpLog::getOpTime) |
| | | .orderByDesc(BizOpLog::getId); |
| | | String normalizedSourceType = normalizeSourceType(sourceType); |
| | | if (normalizedSourceType != null) { |
| | | query.eq(BizOpLog::getSourceType, normalizedSourceType); |
| | | } |
| | | IPage<BizOpLog> logPage = bizOpLogMapper.selectPage(pager, query); |
| | | Page<OpLogListVo> result = |
| | | new Page<>(logPage.getCurrent(), logPage.getSize(), logPage.getTotal()); |
| | | List<BizOpLog> records = logPage.getRecords(); |
| | | result.setRecords(toListVos(records)); |
| | | return result; |
| | | } |
| | | |
| | | @Async("applicationTaskExecutor") |
| | | public void recordAsync( |
| | | String sourceType, |
| | | Long operatorUserId, |
| | | String module, |
| | | String action, |
| | | String resourceType, |
| | | String resourceId, |
| | | String detailJson) { |
| | | String normalizedSourceType = normalizeSourceType(sourceType); |
| | | if (!StringUtils.hasText(normalizedSourceType)) { |
| | | return; |
| | | } |
| | | if (SOURCE_MINI_EMPLOYEE.equals(normalizedSourceType) && !isEmployee(operatorUserId)) { |
| | | return; |
| | | } |
| | | BizOpLog log = new BizOpLog(); |
| | | log.setSourceType(normalizedSourceType); |
| | | log.setOperatorUserId(operatorUserId); |
| | | log.setModule(module); |
| | | log.setAction(action); |
| | |
| | | log.setTraceId(MDC.get(MDC_TRACE)); |
| | | bizOpLogMapper.insert(log); |
| | | } |
| | | |
| | | private List<OpLogListVo> toListVos(List<BizOpLog> records) { |
| | | if (records == null || records.isEmpty()) { |
| | | return List.of(); |
| | | } |
| | | List<Long> adminUserIds = |
| | | records.stream() |
| | | .filter(item -> SOURCE_ADMIN.equals(item.getSourceType())) |
| | | .map(BizOpLog::getOperatorUserId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .toList(); |
| | | List<Long> mpUserIds = |
| | | records.stream() |
| | | .filter(item -> SOURCE_MINI_EMPLOYEE.equals(item.getSourceType())) |
| | | .map(BizOpLog::getOperatorUserId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .toList(); |
| | | Map<Long, String> adminNameMap = loadAdminNameMap(adminUserIds); |
| | | Map<Long, String> mpNameMap = loadMpNameMap(mpUserIds); |
| | | return records.stream() |
| | | .map(item -> toListVo(item, adminNameMap, mpNameMap)) |
| | | .toList(); |
| | | } |
| | | |
| | | private OpLogListVo toListVo( |
| | | BizOpLog item, Map<Long, String> adminNameMap, Map<Long, String> mpNameMap) { |
| | | OpLogListVo vo = new OpLogListVo(); |
| | | vo.setId(item.getId()); |
| | | vo.setSourceType(item.getSourceType()); |
| | | vo.setSourceTypeText(resolveSourceTypeText(item.getSourceType())); |
| | | vo.setOperatorUserId(item.getOperatorUserId()); |
| | | vo.setModule(item.getModule()); |
| | | vo.setAction(item.getAction()); |
| | | vo.setResourceType(item.getResourceType()); |
| | | vo.setResourceId(item.getResourceId()); |
| | | vo.setDetailJson(item.getDetailJson()); |
| | | vo.setTraceId(item.getTraceId()); |
| | | vo.setOpTime(item.getOpTime()); |
| | | if (SOURCE_MINI_EMPLOYEE.equals(item.getSourceType())) { |
| | | vo.setOperatorName(mpNameMap.getOrDefault(item.getOperatorUserId(), "—")); |
| | | } else { |
| | | vo.setOperatorName(adminNameMap.getOrDefault(item.getOperatorUserId(), "—")); |
| | | } |
| | | return vo; |
| | | } |
| | | |
| | | private Map<Long, String> loadAdminNameMap(List<Long> userIds) { |
| | | if (userIds == null || userIds.isEmpty()) { |
| | | return Map.of(); |
| | | } |
| | | return sysUserMapper.selectBatchIds(userIds).stream() |
| | | .collect( |
| | | Collectors.toMap( |
| | | SysUser::getId, |
| | | user -> firstNonBlank(user.getRealName(), user.getUsername(), "—"), |
| | | (a, _b) -> a)); |
| | | } |
| | | |
| | | private Map<Long, String> loadMpNameMap(List<Long> userIds) { |
| | | if (userIds == null || userIds.isEmpty()) { |
| | | return Map.of(); |
| | | } |
| | | return mpUserMapper.selectBatchIds(userIds).stream() |
| | | .filter(user -> "EMPLOYEE".equals(user.getUserType())) |
| | | .collect( |
| | | Collectors.toMap( |
| | | MpUser::getId, |
| | | user -> firstNonBlank(user.getNickName(), user.getMobile(), "—"), |
| | | (a, _b) -> a)); |
| | | } |
| | | |
| | | private boolean isEmployee(Long operatorUserId) { |
| | | if (operatorUserId == null) { |
| | | return false; |
| | | } |
| | | MpUser user = mpUserMapper.selectById(operatorUserId); |
| | | return user != null && "EMPLOYEE".equals(user.getUserType()); |
| | | } |
| | | |
| | | private String normalizeSourceType(String sourceType) { |
| | | if (!StringUtils.hasText(sourceType)) { |
| | | return null; |
| | | } |
| | | String normalized = sourceType.trim().toUpperCase(); |
| | | if (SOURCE_ADMIN.equals(normalized)) { |
| | | return SOURCE_ADMIN; |
| | | } |
| | | if (SOURCE_MINI_EMPLOYEE.equals(normalized)) { |
| | | return SOURCE_MINI_EMPLOYEE; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private String resolveSourceTypeText(String sourceType) { |
| | | if (SOURCE_MINI_EMPLOYEE.equals(sourceType)) { |
| | | return "员工端"; |
| | | } |
| | | return "管理后台"; |
| | | } |
| | | |
| | | private String firstNonBlank(String... values) { |
| | | for (String value : values) { |
| | | if (StringUtils.hasText(value)) { |
| | | return value.trim(); |
| | | } |
| | | } |
| | | return "—"; |
| | | } |
| | | } |