1.0
longz
2026-06-24 e45e74e5ae207f7642be9a88ed99b593de89fb3b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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
@RequiredArgsConstructor
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;
 
    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.setResourceType(resourceType);
        log.setResourceId(resourceId);
        log.setDetailJson(detailJson);
        log.setOpTime(LocalDateTime.now());
        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 "—";
    }
}