Line data Source code
1 : // Package profile は、ユーザープロフィールに関するユースケース(アプリケーションロジック)を提供します。
2 : // ドメイン層のエンティティやリポジトリを操作し、アプリケーション全体で再利用可能なビジネスフローを実装します。
3 : package profile
4 :
5 : import (
6 : "context"
7 :
8 : "resume/internal/domain/entity"
9 : "resume/internal/shared/apperr"
10 : "resume/internal/shared/valerr"
11 : )
12 :
13 : // UpdateUserAddress は 認証済みユーザーの住所情報を更新します。
14 : func (uc *Interactor) UpdateUserAddress(
15 : ctx context.Context,
16 : in UpdateUserAddressInput,
17 0 : ) (*UpdateUserAddressOutput, error) {
18 0 : var updated *entity.UserAddress
19 0 :
20 0 : err := uc.tx.Do(ctx, func(txCtx context.Context) error {
21 0 :
22 0 : // ------------------------------------------------------
23 0 : // ① 対象レコード取得 → 自分の住所かチェック
24 0 : // ------------------------------------------------------
25 0 : current, err := uc.uaRepo.FindByID(txCtx, in.AddressID)
26 0 : if err != nil {
27 0 : return err
28 0 : }
29 :
30 0 : if current == nil {
31 0 : ve := valerr.New()
32 0 : ve.Add(
33 0 : "domain.user_address.id",
34 0 : "validation.rules.invalid",
35 0 : map[string]any{
36 0 : "field": "domain.user_address.id",
37 0 : },
38 0 : )
39 0 : return apperr.New(
40 0 : apperr.CodeUnprocessable,
41 0 : "The request contains semantically invalid data.",
42 0 : ve.ToDetails(),
43 0 : )
44 0 : }
45 :
46 0 : if current.UserID != in.UserID {
47 0 : ve := valerr.New()
48 0 : ve.Add(
49 0 : "domain.user_address.id",
50 0 : "validation.rules.invalid",
51 0 : map[string]any{
52 0 : "field": "domain.user_address.id",
53 0 : },
54 0 : )
55 0 : return apperr.New(
56 0 : apperr.CodeUnprocessable,
57 0 : "The request contains semantically invalid data.",
58 0 : ve.ToDetails(),
59 0 : )
60 0 : }
61 : // ------------------------------------------------------
62 : // ② 現住所(Home) の重複チェック
63 : // ※ 自分自身のレコードは除外したいので ExceptID 版を使う
64 : // ------------------------------------------------------
65 0 : if in.PurposeID == entity.AddressPurposeIDHome {
66 0 : exists, err := uc.uaRepo.ExistsByUserIDAndPurposeIDExceptID(
67 0 : txCtx,
68 0 : in.UserID,
69 0 : in.PurposeID,
70 0 : in.AddressID,
71 0 : )
72 0 : if err != nil {
73 0 : return err
74 0 : }
75 0 : if exists {
76 0 : ve := valerr.New()
77 0 : ve.Add(
78 0 : "domain.user_address.purpose_id",
79 0 : "validation.rules.distinct",
80 0 : map[string]any{
81 0 : "field": "domain.user_address.purpose_id.label",
82 0 : },
83 0 : )
84 0 :
85 0 : // 既存のバリデーションエラーと同じ Code/Message/Details で返す
86 0 : return apperr.New(
87 0 : apperr.CodeUnprocessable,
88 0 : "The request contains semantically invalid data.",
89 0 : ve.ToDetails(),
90 0 : )
91 0 : }
92 : }
93 :
94 : // ------------------------------------------------------
95 : // ③ current に新しい値をマージして更新
96 : // (新しく entity を作るより current を上書きするほうが自然)
97 : // ------------------------------------------------------
98 0 : current.PurposeID = in.PurposeID
99 0 : current.IsPrimary = in.IsPrimary
100 0 : current.CountryCode = in.CountryCode
101 0 : current.AdministrativeArea = in.AdministrativeArea
102 0 : current.Locality = in.Locality
103 0 : current.DependentLocality = in.DependentLocality
104 0 : current.PostalCode = in.PostalCode
105 0 : current.SortingCode = in.SortingCode
106 0 : current.AddressLine1 = in.AddressLine1
107 0 : current.AddressLine2 = in.AddressLine2
108 0 : current.AddressLine3 = in.AddressLine3
109 0 : current.Latitude = in.Latitude
110 0 : current.Longitude = in.Longitude
111 0 :
112 0 : updated, err = uc.uaRepo.Update(txCtx, current)
113 0 : return err
114 : })
115 0 : if err != nil {
116 0 : return nil, err
117 0 : }
118 0 : return &UpdateUserAddressOutput{
119 0 : UserAddress: updated,
120 0 : }, nil
121 : }
|