longz
2026-04-27 25df88d0ab9d7444ed1d162b9efa1259ac2f6f66
backend/src/main/java/com/sinata/appointment/service/OpLogApplicationService.java
@@ -1,11 +1,24 @@
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
@@ -13,18 +26,50 @@
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);
@@ -35,4 +80,114 @@
        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 "—";
    }
}