| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- using Domain.Entities.Members;
- namespace Domain.Entities.Store;
- /// <summary>
- /// 회원 배송 주소. AES-GCM 으로 PII 필드 암호화 (수령인명/연락처/주소). ZipCode 만 평문.
- /// 키 회전 지원: 각 row 의 KeyVersion 컬럼으로 사용한 키 추적.
- /// 수령인명 검색은 RecipientName_Hash (SHA-256 hex) 로 수행.
- /// 1차 정책: 회원당 최대 5개 주소, IsDefault=true 는 정확히 1개.
- /// </summary>
- public class MemberAddress
- {
- [ForeignKey(nameof(MemberID))]
- public virtual Member? Member { get; private set; }
- [Key]
- public int ID { get; private set; }
- public int MemberID { get; private set; }
- public bool IsDefault { get; private set; }
- // AES-GCM 암호화 컬럼 (Base64)
- public string RecipientName_Encrypted { get; private set; } = default!;
- /// <summary>수령인명 SHA-256 hex (검색용, 64자 대문자). 키 회전과 무관.</summary>
- public string RecipientName_Hash { get; private set; } = default!;
- public string Phone_Encrypted { get; private set; } = default!;
- public string ZipCode { get; private set; } = default!;
- public string Address1_Encrypted { get; private set; } = default!;
- public string Address2_Encrypted { get; private set; } = default!;
- /// <summary>암호화 시 사용한 키 버전 (IFieldEncryptor.CurrentKeyVersion 스냅샷).</summary>
- public int KeyVersion { get; private set; }
- public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
- public DateTime? UpdatedAt { get; private set; }
- private MemberAddress() { }
- public static MemberAddress Create(
- int memberID,
- string recipientNameEncrypted,
- string recipientNameHash,
- string phoneEncrypted,
- string zipCode,
- string address1Encrypted,
- string address2Encrypted,
- int keyVersion,
- bool isDefault = false
- ) {
- if (memberID <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(memberID));
- }
- ValidateEncryptedFields(recipientNameEncrypted, recipientNameHash, phoneEncrypted, zipCode, address1Encrypted, address2Encrypted, keyVersion);
- return new MemberAddress
- {
- MemberID = memberID,
- RecipientName_Encrypted = recipientNameEncrypted,
- RecipientName_Hash = recipientNameHash,
- Phone_Encrypted = phoneEncrypted,
- ZipCode = zipCode,
- Address1_Encrypted = address1Encrypted,
- Address2_Encrypted = address2Encrypted,
- KeyVersion = keyVersion,
- IsDefault = isDefault
- };
- }
- public void Update(
- string recipientNameEncrypted,
- string recipientNameHash,
- string phoneEncrypted,
- string zipCode,
- string address1Encrypted,
- string address2Encrypted,
- int keyVersion
- ) {
- ValidateEncryptedFields(recipientNameEncrypted, recipientNameHash, phoneEncrypted, zipCode, address1Encrypted, address2Encrypted, keyVersion);
- RecipientName_Encrypted = recipientNameEncrypted;
- RecipientName_Hash = recipientNameHash;
- Phone_Encrypted = phoneEncrypted;
- ZipCode = zipCode;
- Address1_Encrypted = address1Encrypted;
- Address2_Encrypted = address2Encrypted;
- KeyVersion = keyVersion;
- UpdatedAt = DateTime.UtcNow;
- }
- public void SetAsDefault()
- {
- IsDefault = true;
- UpdatedAt = DateTime.UtcNow;
- }
- public void UnsetDefault()
- {
- IsDefault = false;
- UpdatedAt = DateTime.UtcNow;
- }
- private static void ValidateEncryptedFields(
- string recipientNameEncrypted,
- string recipientNameHash,
- string phoneEncrypted,
- string zipCode,
- string address1Encrypted,
- string address2Encrypted,
- int keyVersion
- ) {
- if (string.IsNullOrWhiteSpace(recipientNameEncrypted))
- {
- throw new ArgumentException("RecipientName_Encrypted is required.", nameof(recipientNameEncrypted));
- }
- if (string.IsNullOrWhiteSpace(recipientNameHash))
- {
- throw new ArgumentException("RecipientName_Hash is required.", nameof(recipientNameHash));
- }
- if (recipientNameHash.Length != 64)
- {
- throw new ArgumentOutOfRangeException(nameof(recipientNameHash), "Hash must be 64 hex chars (SHA-256).");
- }
- if (string.IsNullOrWhiteSpace(phoneEncrypted))
- {
- throw new ArgumentException("Phone_Encrypted is required.", nameof(phoneEncrypted));
- }
- if (string.IsNullOrWhiteSpace(zipCode))
- {
- throw new ArgumentException("ZipCode is required.", nameof(zipCode));
- }
- if (zipCode.Length > 10)
- {
- throw new ArgumentOutOfRangeException(nameof(zipCode));
- }
- if (string.IsNullOrWhiteSpace(address1Encrypted))
- {
- throw new ArgumentException("Address1_Encrypted is required.", nameof(address1Encrypted));
- }
- if (string.IsNullOrWhiteSpace(address2Encrypted))
- {
- throw new ArgumentException("Address2_Encrypted is required.", nameof(address2Encrypted));
- }
- if (keyVersion <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(keyVersion));
- }
- }
- }
|