pii_struct.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package helper
  2. import (
  3. "fwh-toolbox-server/domain"
  4. "fwh-toolbox-server/dto"
  5. )
  6. // sanitizeStrong 尝试对已知的强类型做源头脱敏(优化 A)。
  7. //
  8. // 返回:
  9. // - 脱敏后的数据副本 + true:命中强类型,已完成脱敏。
  10. // - nil + false:未命中强类型,调用方应改走流式脱敏(优化 B)。
  11. //
  12. // 设计约束:所有脱敏均作用于输入数据的副本,绝不修改上游对象(这些对象
  13. // 可能来自 Mongo 缓存或被其他调用方共享)。规则与掩码函数复用 pii_rules.go,
  14. // 确保 A 与 B 的行为一致。
  15. func sanitizeStrong(data any) (any, bool) {
  16. switch v := data.(type) {
  17. case *dto.SiteInfoDTO:
  18. if v == nil {
  19. return v, true
  20. }
  21. cp := *v
  22. sanitizeSiteInfoDTO(&cp)
  23. return &cp, true
  24. case dto.SiteInfoDTO:
  25. sanitizeSiteInfoDTO(&v)
  26. return v, true
  27. case *dto.ResPage[dto.SiteInfoDTO]:
  28. if v == nil {
  29. return v, true
  30. }
  31. cp := *v
  32. cp.Data = make([]dto.SiteInfoDTO, len(v.Data))
  33. copy(cp.Data, v.Data)
  34. for i := range cp.Data {
  35. sanitizeSiteInfoDTO(&cp.Data[i])
  36. }
  37. return &cp, true
  38. case *domain.SimCardInfo:
  39. if v == nil {
  40. return v, true
  41. }
  42. cp := *v
  43. sanitizeDomainSimCardInfo(&cp)
  44. return &cp, true
  45. case *dto.SimCardInfo:
  46. if v == nil {
  47. return v, true
  48. }
  49. cp := *v
  50. sanitizeDTOSimCardInfo(&cp)
  51. return &cp, true
  52. case *dto.ResPage[dto.SimCardInfo]:
  53. if v == nil {
  54. return v, true
  55. }
  56. cp := *v
  57. cp.Data = make([]dto.SimCardInfo, len(v.Data))
  58. copy(cp.Data, v.Data)
  59. for i := range cp.Data {
  60. sanitizeDTOSimCardInfo(&cp.Data[i])
  61. }
  62. return &cp, true
  63. case *dto.ResPage[domain.SimCardInfo]:
  64. if v == nil {
  65. return v, true
  66. }
  67. cp := *v
  68. cp.Data = make([]domain.SimCardInfo, len(v.Data))
  69. copy(cp.Data, v.Data)
  70. for i := range cp.Data {
  71. sanitizeDomainSimCardInfo(&cp.Data[i])
  72. }
  73. return &cp, true
  74. case *domain.SimCardMonthlyData:
  75. if v == nil {
  76. return v, true
  77. }
  78. cp := *v
  79. sanitizeSimCardMonthlyData(&cp)
  80. return &cp, true
  81. default:
  82. return nil, false
  83. }
  84. }
  85. // maskStrPtr 对 *string 字段就地脱敏(作用于副本的指针,需重新分配以免污染原值)。
  86. func maskStrPtr(p **string, kind MaskKind) {
  87. if p == nil || *p == nil {
  88. return
  89. }
  90. masked := applyMaskString(kind, **p)
  91. *p = &masked
  92. }
  93. func maskStr(s *string, kind MaskKind) {
  94. if s == nil {
  95. return
  96. }
  97. *s = applyMaskString(kind, *s)
  98. }
  99. // sanitizeSiteInfoDTO 对站点信息做字段级脱敏。
  100. // 保留:city / province / country(行政区)、iccId(业务标识)、sn、各类版本与状态字段。
  101. func sanitizeSiteInfoDTO(s *dto.SiteInfoDTO) {
  102. // 姓名
  103. maskStrPtr(&s.UserName, MaskName)
  104. maskStrPtr(&s.FirstName, MaskName)
  105. maskStrPtr(&s.LastName, MaskName)
  106. maskStrPtr(&s.AccountName, MaskName)
  107. maskStrPtr(&s.FinancierName, MaskName)
  108. // 手机号 —— 完全脱敏
  109. maskStrPtr(&s.ContactNumber, MaskFull)
  110. maskStrPtr(&s.FinancierContractNumber, MaskFull)
  111. // 完整地址串 —— 完全脱敏
  112. maskStrPtr(&s.Address, MaskFull)
  113. maskStrPtr(&s.DeviceAddress, MaskFull)
  114. maskStrPtr(&s.AddressSecondLast, MaskFull)
  115. // 经纬度 —— 完全脱敏(string 类型)
  116. s.Longitude = maskFull()
  117. s.Latitude = maskFull()
  118. // 账号
  119. maskStrPtr(&s.Account, MaskAccount)
  120. maskStrPtr(&s.InstallerAccount, MaskAccount)
  121. // 设备/网络标识 —— 完全脱敏(保留 ICCID)
  122. maskStrPtr(&s.Mac, MaskFull)
  123. maskStrPtr(&s.DeviceIp, MaskFull)
  124. maskStrPtr(&s.GatewayIp, MaskFull)
  125. s.IMEI = maskFull()
  126. // s.ICCID 保留
  127. }
  128. // sanitizeDomainSimCardInfo 对 domain.SimCardInfo 脱敏(保留 GICCID)。
  129. func sanitizeDomainSimCardInfo(s *domain.SimCardInfo) {
  130. s.IMEI = maskFull()
  131. s.IMSI = maskFull()
  132. s.MSISDN = maskFull()
  133. // s.GICCID 保留
  134. }
  135. // sanitizeDTOSimCardInfo 对 dto.SimCardInfo 脱敏(保留 GICCID、SN 列表)。
  136. func sanitizeDTOSimCardInfo(s *dto.SimCardInfo) {
  137. s.IMEI = maskFull()
  138. s.IMSI = maskFull()
  139. s.MSISDN = maskFull()
  140. // s.GICCID / s.SN 保留
  141. }
  142. // sanitizeSimCardMonthlyData 月用量数据本身不含 IMEI/IMSI/MSISDN,仅含 GICCID(simId) 与用量,
  143. // 无需脱敏;保留以显式声明该类型已被审阅。
  144. func sanitizeSimCardMonthlyData(_ *domain.SimCardMonthlyData) {}