刘帅
2026-09-21 7a8af8c19b40b8028e25ee0b669463d06f723a30
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
package com.dsh.guns.modular.system.controller.system;
 
import com.dsh.course.feignClient.account.AppUserClient;
import com.dsh.guns.config.UserExt;
import com.dsh.guns.core.common.exception.BizExceptionEnum;
import com.dsh.guns.core.exception.GunsException;
import com.dsh.guns.modular.system.audit.ManagementAuditWriter;
import com.dsh.guns.modular.system.model.SiteBooking;
import com.dsh.guns.modular.system.service.ISiteBookingService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.dsh.guns.modular.system.model.User;
import com.dsh.guns.modular.system.service.IUserService;
 
import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
 
/** Deliberately narrow, auditable unmasking capability for the admin account. */
@RestController
@RequestMapping("/system/sensitive")
public class SensitiveDataController {
    @Resource private AppUserClient appUserClient;
    @Resource private ManagementAuditWriter auditWriter;
    @Resource private IUserService userService;
    @Resource private ISiteBookingService siteBookingService;
 
    @GetMapping("/app-user/{id}")
    public Map<String, Object> appUser(@PathVariable Integer id) {
        assertAdmin();
        Map<String, Object> result = appUserClient.sensitiveAppUser(id);
        writeAudit("app-user", id, userName(result), result == null ? "NOT_FOUND" : "SUCCESS");
        return result;
    }
 
    @GetMapping("/employee/{id}")
    public Map<String, Object> employee(@PathVariable Integer id) {
        assertAdmin();
        User employee = userService.getById(id);
        Map<String, Object> result = null;
        if (employee != null) {
            result = new HashMap<String, Object>();
            result.put("id", employee.getId());
            result.put("name", employee.getName());
            result.put("phone", employee.getPhone());
            result.put("idCard", employee.getIdCard());
        }
        writeAudit("employee", id, employee == null ? null : employee.getName(), result == null ? "NOT_FOUND" : "SUCCESS");
        return result;
    }
 
    @GetMapping("/student/{id}")
    public Map<String, Object> student(@PathVariable Integer id) {
        assertAdmin();
        Map<String, Object> result = appUserClient.sensitiveStudent(id);
        writeAudit("student", id, userName(result), result == null ? "NOT_FOUND" : "SUCCESS");
        return result;
    }
 
    /**
     * 预约管理查看关联预约人的敏感信息。
     *
     * <p>仅通过预约记录定位用户,避免前端以任意用户 ID 查询;该接口保留在
     * /system/sensitive 路径下,以使用全局的脱敏豁免和网关放行策略。</p>
     */
    @GetMapping("/booking/{bookingId}")
    public Map<String, Object> booking(@PathVariable Integer bookingId) {
        assertAdmin();
        SiteBooking booking = siteBookingService.getById(bookingId);
        if (booking == null || booking.getAppUserId() == null) {
            writeBookingAudit(bookingId, booking, null, "NOT_FOUND");
            return null;
        }
 
        Map<String, Object> result = appUserClient.sensitiveAppUser(booking.getAppUserId());
        writeBookingAudit(bookingId, booking, result, result == null ? "NOT_FOUND" : "SUCCESS");
        return result;
    }
 
    private void assertAdmin() {
        if (UserExt.getUser() == null || !"admin".equalsIgnoreCase(UserExt.getUser().getAccount())) {
            throw new GunsException(BizExceptionEnum.NO_PERMITION);
        }
    }
 
    private void writeAudit(String resource, Integer targetUserId, String targetUserName, String result) {
        Map<String, Object> audit = new HashMap<String, Object>();
        audit.put("traceId", UUID.randomUUID().toString().replace("-", ""));
        audit.put("sourceService", "mb-cloud-management");
        audit.put("sourceType", "HTTP");
        audit.put("operationType", "VIEW_SENSITIVE");
        audit.put("riskLevel", "SENSITIVE");
        audit.put("operationName", "超级管理员查看敏感信息");
        audit.put("requestMethod", "GET");
        audit.put("requestUri", "/system/sensitive/" + resource + "/" + targetUserId);
        // 敏感信息访问需同时记录对象名称,避免日志中只有无法识别的 ID。
        audit.put("requestParams", "targetUserId=" + targetUserId + ", targetUserName="
                + (targetUserName == null ? "-" : targetUserName));
        audit.put("userId", UserExt.getUser().getId());
        audit.put("userAccount", "admin");
        audit.put("result", result);
        audit.put("createdAt", new Date());
        auditWriter.append(audit);
    }
 
    private void writeBookingAudit(Integer bookingId, SiteBooking booking,
                                   Map<String, Object> user, String result) {
        Map<String, Object> audit = new HashMap<String, Object>();
        audit.put("traceId", UUID.randomUUID().toString().replace("-", ""));
        audit.put("sourceService", "mb-cloud-management");
        audit.put("sourceType", "HTTP");
        audit.put("operationType", "VIEW_SENSITIVE");
        audit.put("riskLevel", "SENSITIVE");
        audit.put("operationName", "超级管理员从预约管理查看敏感信息");
        audit.put("requestMethod", "GET");
        audit.put("requestUri", "/system/sensitive/booking/" + bookingId);
        // 审计记录只存预约、用户标识和查询结果,绝不写入手机号、身份证号等明文。
        audit.put("requestParams", "bookingId=" + bookingId + ", appUserId="
                + (booking == null || booking.getAppUserId() == null ? "-" : booking.getAppUserId())
                + ", targetUserName=" + (userName(user) == null ? "-" : userName(user)));
        audit.put("userId", UserExt.getUser().getId());
        audit.put("userAccount", "admin");
        audit.put("result", result);
        audit.put("createdAt", new Date());
        auditWriter.append(audit);
    }
 
    private String userName(Map<String, Object> user) {
        if (user == null || user.get("name") == null) return null;
        return String.valueOf(user.get("name"));
    }
}