| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- using Domain.Entities.Store.ValueObject;
- namespace Domain.Entities.Store;
- /// <summary>
- /// 배송 정보. Order 1:1 (Physical 상품 포함 주문 시점에 생성).
- /// 주문 시점의 회원 주소 스냅샷을 보존 (회원이 추후 주소 변경해도 영향 없음). 암호화는 MemberAddress 와 동일.
- /// </summary>
- public class Shipment
- {
- [ForeignKey(nameof(OrderID))]
- public virtual Order? Order { get; private set; }
- [Key]
- public int ID { get; private set; }
- public int OrderID { get; private set; }
- // 주소 스냅샷 (암호화)
- public string RecipientName_Encrypted { 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!;
- public int KeyVersion { get; private set; }
- public ShipmentStatus Status { get; private set; } = ShipmentStatus.Preparing;
- public string? Carrier { get; private set; }
- public string? TrackingNumber { get; private set; }
- /// <summary>배송비 (원) — 관리자 수동 입력. 기본 0.</summary>
- public int ShippingFee { get; private set; }
- /// <summary>관리자 메모 (운영용 내부 메모, 최대 500자)</summary>
- public string? AdminMemo { get; private set; }
- public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
- public DateTime? ShippedAt { get; private set; }
- public DateTime? DeliveredAt { get; private set; }
- private Shipment() { }
- public static Shipment Create(
- int orderID,
- string recipientNameEncrypted,
- string phoneEncrypted,
- string zipCode,
- string address1Encrypted,
- string address2Encrypted,
- int keyVersion
- ) {
- if (orderID <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(orderID));
- }
- if (string.IsNullOrWhiteSpace(recipientNameEncrypted))
- {
- throw new ArgumentException("RecipientName_Encrypted is required.", nameof(recipientNameEncrypted));
- }
- 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));
- }
- return new Shipment
- {
- OrderID = orderID,
- RecipientName_Encrypted = recipientNameEncrypted,
- Phone_Encrypted = phoneEncrypted,
- ZipCode = zipCode,
- Address1_Encrypted = address1Encrypted,
- Address2_Encrypted = address2Encrypted,
- KeyVersion = keyVersion
- };
- }
- public void RegisterTracking(string carrier, string trackingNumber)
- {
- if (string.IsNullOrWhiteSpace(carrier))
- {
- throw new ArgumentException("Carrier is required.", nameof(carrier));
- }
- if (carrier.Length > 50)
- {
- throw new ArgumentOutOfRangeException(nameof(carrier));
- }
- if (string.IsNullOrWhiteSpace(trackingNumber))
- {
- throw new ArgumentException("TrackingNumber is required.", nameof(trackingNumber));
- }
- if (trackingNumber.Length > 50)
- {
- throw new ArgumentOutOfRangeException(nameof(trackingNumber));
- }
- Carrier = carrier;
- TrackingNumber = trackingNumber;
- if (Status == ShipmentStatus.Preparing)
- {
- Status = ShipmentStatus.Pickup;
- }
- }
- public void MarkInTransit()
- {
- if (Status is not ShipmentStatus.Pickup and not ShipmentStatus.Preparing)
- {
- throw new InvalidOperationException($"Cannot mark in transit from status {Status}.");
- }
- Status = ShipmentStatus.InTransit;
- ShippedAt = DateTime.UtcNow;
- }
- public void MarkDelivered()
- {
- if (Status != ShipmentStatus.InTransit)
- {
- throw new InvalidOperationException($"Cannot mark delivered from status {Status}.");
- }
- Status = ShipmentStatus.Delivered;
- DeliveredAt = DateTime.UtcNow;
- }
- public void MarkFailed()
- {
- Status = ShipmentStatus.Failed;
- }
- /// <summary>배송비 + 관리자 메모 동시 갱신 (배송 상세 form 에서 호출)</summary>
- public void UpdateBillingInfo(int shippingFee, string? adminMemo)
- {
- if (shippingFee < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(shippingFee), "ShippingFee must be >= 0.");
- }
- if (adminMemo is { Length: > 500 })
- {
- throw new ArgumentOutOfRangeException(nameof(adminMemo), "AdminMemo max length is 500.");
- }
- ShippingFee = shippingFee;
- AdminMemo = string.IsNullOrWhiteSpace(adminMemo) ? null : adminMemo.Trim();
- }
- }
|