@mamindom/contracts 1.0.130 → 1.0.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/events/auth/index.d.ts +1 -0
- package/dist/events/auth/index.js +1 -0
- package/dist/events/auth/user-authenticated.interface.d.ts +5 -0
- package/dist/events/auth/user-authenticated.interface.js +2 -0
- package/dist/gen/notification.d.ts +239 -0
- package/dist/gen/notification.js +50 -1
- package/dist/gen/order.d.ts +18 -0
- package/dist/gen/order.js +1 -0
- package/dist/gen/users.d.ts +15 -0
- package/dist/gen/users.js +1 -0
- package/dist/proto/notification.proto +232 -0
- package/dist/proto/order.proto +20 -0
- package/dist/proto/users.proto +16 -0
- package/events/auth/index.ts +1 -0
- package/events/auth/user-authenticated.interface.ts +5 -0
- package/gen/notification.ts +348 -0
- package/gen/order.ts +28 -0
- package/gen/users.ts +26 -0
- package/package.json +1 -1
- package/proto/notification.proto +232 -0
- package/proto/order.proto +20 -0
- package/proto/users.proto +16 -0
package/gen/order.ts
CHANGED
|
@@ -10,6 +10,23 @@ import { Observable } from "rxjs";
|
|
|
10
10
|
|
|
11
11
|
export const protobufPackage = "order.v1";
|
|
12
12
|
|
|
13
|
+
export interface AggregateByCustomersRequest {
|
|
14
|
+
userIds: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CustomerOrderAggregate {
|
|
18
|
+
userId: string;
|
|
19
|
+
ordersCount: number;
|
|
20
|
+
/** Decimal у вигляді рядка ('1234.56') — щоб не втратити точність на gRPC border. */
|
|
21
|
+
totalSum: string;
|
|
22
|
+
/** ISO-рядок, відсутній якщо клієнт ще не робив замовлень. */
|
|
23
|
+
lastOrderAt?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AggregateByCustomersResponse {
|
|
27
|
+
items: CustomerOrderAggregate[];
|
|
28
|
+
}
|
|
29
|
+
|
|
13
30
|
export interface OrderItem {
|
|
14
31
|
id: string;
|
|
15
32
|
productId: string;
|
|
@@ -325,6 +342,10 @@ export interface OrderServiceClient {
|
|
|
325
342
|
reorderOrder(request: ReorderOrderRequest): Observable<ReorderResponse>;
|
|
326
343
|
|
|
327
344
|
listHistory(request: ListHistoryRequest): Observable<ListHistoryResponse>;
|
|
345
|
+
|
|
346
|
+
/** CRM-агрегація для експорту клієнтів — повертає count/sum/last для кожного userId. */
|
|
347
|
+
|
|
348
|
+
aggregateByCustomers(request: AggregateByCustomersRequest): Observable<AggregateByCustomersResponse>;
|
|
328
349
|
}
|
|
329
350
|
|
|
330
351
|
export interface OrderServiceController {
|
|
@@ -371,6 +392,12 @@ export interface OrderServiceController {
|
|
|
371
392
|
listHistory(
|
|
372
393
|
request: ListHistoryRequest,
|
|
373
394
|
): Promise<ListHistoryResponse> | Observable<ListHistoryResponse> | ListHistoryResponse;
|
|
395
|
+
|
|
396
|
+
/** CRM-агрегація для експорту клієнтів — повертає count/sum/last для кожного userId. */
|
|
397
|
+
|
|
398
|
+
aggregateByCustomers(
|
|
399
|
+
request: AggregateByCustomersRequest,
|
|
400
|
+
): Promise<AggregateByCustomersResponse> | Observable<AggregateByCustomersResponse> | AggregateByCustomersResponse;
|
|
374
401
|
}
|
|
375
402
|
|
|
376
403
|
export function OrderServiceControllerMethods() {
|
|
@@ -392,6 +419,7 @@ export function OrderServiceControllerMethods() {
|
|
|
392
419
|
"setTrackingNumber",
|
|
393
420
|
"reorderOrder",
|
|
394
421
|
"listHistory",
|
|
422
|
+
"aggregateByCustomers",
|
|
395
423
|
];
|
|
396
424
|
for (const method of grpcMethods) {
|
|
397
425
|
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
|
package/gen/users.ts
CHANGED
|
@@ -62,6 +62,21 @@ export interface User {
|
|
|
62
62
|
bonuses: number;
|
|
63
63
|
email?: string | undefined;
|
|
64
64
|
phone?: string | undefined;
|
|
65
|
+
internalNote?: string | undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AdminUpdateCustomerRequest {
|
|
69
|
+
id: string;
|
|
70
|
+
firstName?: string | undefined;
|
|
71
|
+
lastName?:
|
|
72
|
+
| string
|
|
73
|
+
| undefined;
|
|
74
|
+
/** internal_note редагується тут окремо, бо це CRM-поле — не видиме клієнту. */
|
|
75
|
+
internalNote?: string | undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface AdminUpdateCustomerResponse {
|
|
79
|
+
user: User | undefined;
|
|
65
80
|
}
|
|
66
81
|
|
|
67
82
|
export interface NotificationSettings {
|
|
@@ -378,6 +393,10 @@ export interface UsersServiceClient {
|
|
|
378
393
|
clearCart(request: ClearCartRequest): Observable<ClearCartResponse>;
|
|
379
394
|
|
|
380
395
|
syncCart(request: SyncCartRequest): Observable<GetCartResponse>;
|
|
396
|
+
|
|
397
|
+
/** Admin CRM — редагування клієнта менеджером (customers.edit на gateway). */
|
|
398
|
+
|
|
399
|
+
adminUpdateCustomer(request: AdminUpdateCustomerRequest): Observable<AdminUpdateCustomerResponse>;
|
|
381
400
|
}
|
|
382
401
|
|
|
383
402
|
export interface UsersServiceController {
|
|
@@ -481,6 +500,12 @@ export interface UsersServiceController {
|
|
|
481
500
|
clearCart(request: ClearCartRequest): Promise<ClearCartResponse> | Observable<ClearCartResponse> | ClearCartResponse;
|
|
482
501
|
|
|
483
502
|
syncCart(request: SyncCartRequest): Promise<GetCartResponse> | Observable<GetCartResponse> | GetCartResponse;
|
|
503
|
+
|
|
504
|
+
/** Admin CRM — редагування клієнта менеджером (customers.edit на gateway). */
|
|
505
|
+
|
|
506
|
+
adminUpdateCustomer(
|
|
507
|
+
request: AdminUpdateCustomerRequest,
|
|
508
|
+
): Promise<AdminUpdateCustomerResponse> | Observable<AdminUpdateCustomerResponse> | AdminUpdateCustomerResponse;
|
|
484
509
|
}
|
|
485
510
|
|
|
486
511
|
export function UsersServiceControllerMethods() {
|
|
@@ -513,6 +538,7 @@ export function UsersServiceControllerMethods() {
|
|
|
513
538
|
"removeCartItem",
|
|
514
539
|
"clearCart",
|
|
515
540
|
"syncCart",
|
|
541
|
+
"adminUpdateCustomer",
|
|
516
542
|
];
|
|
517
543
|
for (const method of grpcMethods) {
|
|
518
544
|
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
|
package/package.json
CHANGED
package/proto/notification.proto
CHANGED
|
@@ -16,6 +16,42 @@ service NotificationTemplateService {
|
|
|
16
16
|
rpc RestoreVersion (RestoreVersionRequest) returns (TemplateResponse);
|
|
17
17
|
rpc RenderPreview (RenderPreviewRequest) returns (RenderPreviewResponse);
|
|
18
18
|
rpc SendTest (SendTestRequest) returns (SendTestResponse);
|
|
19
|
+
// Реальна відправка клієнту з адмінки — або по шаблону, або ad-hoc body.
|
|
20
|
+
rpc SendToCustomer (SendToCustomerRequest) returns (SendToCustomerResponse);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// SendToCustomer — викликається з gateway після того, як клієнтський контакт
|
|
24
|
+
// (email/phone) вже резолвнений і опційно згенерований персональний купон.
|
|
25
|
+
message SendToCustomerRequest {
|
|
26
|
+
// userId з auth-service — пишемо в NotificationLog.user_id для зведень.
|
|
27
|
+
string user_id = 1;
|
|
28
|
+
NotificationChannel channel = 2;
|
|
29
|
+
|
|
30
|
+
// Recipient — email або phone, заздалегідь визначені gateway з акаунта.
|
|
31
|
+
string recipient = 3;
|
|
32
|
+
|
|
33
|
+
// Шлях 1: templateId — рендеримо існуючий шаблон з variables.
|
|
34
|
+
optional string template_id = 4;
|
|
35
|
+
|
|
36
|
+
// Шлях 2: ad-hoc контент. Якщо template_id порожній — використовуємо ці поля.
|
|
37
|
+
// body_html — для EMAIL (буде обгорнутий брендингом).
|
|
38
|
+
// body_text — для SMS (plain text) або текстова версія email.
|
|
39
|
+
optional string custom_subject = 5;
|
|
40
|
+
optional string custom_body_html = 6;
|
|
41
|
+
optional string custom_body_text = 7;
|
|
42
|
+
|
|
43
|
+
// JSON з placeholders для Handlebars: { "coupon_code": "...", ... }
|
|
44
|
+
string variables_json = 8;
|
|
45
|
+
|
|
46
|
+
// Locale для пошуку версії шаблону + надсилання branding-обгортки.
|
|
47
|
+
optional NotificationLocale locale = 9;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
message SendToCustomerResponse {
|
|
51
|
+
bool ok = 1;
|
|
52
|
+
string error_message = 2;
|
|
53
|
+
// id запису в NotificationLog (для трекінгу в /notifications/logs).
|
|
54
|
+
string log_id = 3;
|
|
19
55
|
}
|
|
20
56
|
|
|
21
57
|
service NotificationLogsService {
|
|
@@ -29,6 +65,202 @@ service TelegramChatsService {
|
|
|
29
65
|
rpc TestSendTelegram (TestSendTelegramRequest) returns (TestSendTelegramResponse);
|
|
30
66
|
}
|
|
31
67
|
|
|
68
|
+
// Маркетингові кампанії: створення, запуск, статистика.
|
|
69
|
+
service CampaignsService {
|
|
70
|
+
rpc ListCampaigns (ListCampaignsRequest) returns (ListCampaignsResponse);
|
|
71
|
+
rpc GetCampaign (GetCampaignRequest) returns (CampaignResponse);
|
|
72
|
+
rpc CreateCampaign (CreateCampaignRequest) returns (CampaignResponse);
|
|
73
|
+
rpc UpdateCampaign (UpdateCampaignRequest) returns (CampaignResponse);
|
|
74
|
+
rpc DeleteCampaign (DeleteCampaignRequest) returns (DeleteCampaignResponse);
|
|
75
|
+
rpc StartCampaign (StartCampaignRequest) returns (CampaignResponse);
|
|
76
|
+
rpc CancelCampaign (CancelCampaignRequest) returns (CampaignResponse);
|
|
77
|
+
// Превью сегменту — повертає кількість + перші N клієнтів, БЕЗ створення кампанії.
|
|
78
|
+
rpc PreviewSegment (PreviewSegmentRequest) returns (PreviewSegmentResponse);
|
|
79
|
+
// Список отримувачів конкретної кампанії з їхніми статусами.
|
|
80
|
+
rpc ListRecipients (ListRecipientsRequest) returns (ListRecipientsResponse);
|
|
81
|
+
// Викликається з gateway після того, як сегмент резолвнено клієнтами —
|
|
82
|
+
// передається список {userId, recipient}, нотифікація розкладає у чергу.
|
|
83
|
+
rpc EnqueueRecipients(EnqueueRecipientsRequest) returns (EnqueueRecipientsResponse);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
message EnqueueRecipientsRequest {
|
|
87
|
+
string campaign_id = 1;
|
|
88
|
+
repeated CampaignResolvedRecipient recipients = 2;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
message CampaignResolvedRecipient {
|
|
92
|
+
string user_id = 1;
|
|
93
|
+
string recipient = 2; // email або phone
|
|
94
|
+
// true → у клієнта promotions=false. dispatcher позначить SKIPPED_OPT_OUT
|
|
95
|
+
// якщо respect_opt_out=true для кампанії.
|
|
96
|
+
bool opt_out = 3;
|
|
97
|
+
// Унікальний код, згенерований gateway через CouponsService.CreateCoupon.
|
|
98
|
+
// Підставляється у шаблон як {{coupon_code}}.
|
|
99
|
+
optional string coupon_code = 4;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
message EnqueueRecipientsResponse {
|
|
103
|
+
int32 enqueued = 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
message ListCampaignsRequest {
|
|
107
|
+
optional CampaignStatusEnum status = 1;
|
|
108
|
+
optional int32 page = 2;
|
|
109
|
+
optional int32 limit = 3;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
message ListCampaignsResponse {
|
|
113
|
+
repeated Campaign items = 1;
|
|
114
|
+
int32 total = 2;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
message GetCampaignRequest {
|
|
118
|
+
string id = 1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
message DeleteCampaignRequest {
|
|
122
|
+
string id = 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
message DeleteCampaignResponse {
|
|
126
|
+
bool ok = 1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
message StartCampaignRequest {
|
|
130
|
+
string id = 1;
|
|
131
|
+
// ISO date; якщо порожньо → запустити одразу.
|
|
132
|
+
optional string scheduled_at = 2;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
message CancelCampaignRequest {
|
|
136
|
+
string id = 1;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
message CreateCampaignRequest {
|
|
140
|
+
string name = 1;
|
|
141
|
+
NotificationChannel channel = 2;
|
|
142
|
+
optional string template_id = 3;
|
|
143
|
+
optional string custom_subject = 4;
|
|
144
|
+
optional string custom_body_html = 5;
|
|
145
|
+
optional string custom_body_text = 6;
|
|
146
|
+
// JSON segment spec.
|
|
147
|
+
string segment_json = 7;
|
|
148
|
+
// JSON coupon spec — null/empty для кампаній без купонів.
|
|
149
|
+
optional string coupon_spec_json = 8;
|
|
150
|
+
bool respect_opt_out = 9;
|
|
151
|
+
optional NotificationLocale locale = 10;
|
|
152
|
+
string created_by_id = 11;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
message UpdateCampaignRequest {
|
|
156
|
+
string id = 1;
|
|
157
|
+
// Можна оновлювати тільки DRAFT — гілки нижче опційні.
|
|
158
|
+
optional string name = 2;
|
|
159
|
+
optional NotificationChannel channel = 3;
|
|
160
|
+
optional string template_id = 4;
|
|
161
|
+
optional string custom_subject = 5;
|
|
162
|
+
optional string custom_body_html = 6;
|
|
163
|
+
optional string custom_body_text = 7;
|
|
164
|
+
optional string segment_json = 8;
|
|
165
|
+
optional string coupon_spec_json = 9;
|
|
166
|
+
optional bool respect_opt_out = 10;
|
|
167
|
+
optional NotificationLocale locale = 11;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
message CampaignResponse {
|
|
171
|
+
bool ok = 1;
|
|
172
|
+
string error_message = 2;
|
|
173
|
+
Campaign campaign = 3;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
message Campaign {
|
|
177
|
+
string id = 1;
|
|
178
|
+
string name = 2;
|
|
179
|
+
NotificationChannel channel = 3;
|
|
180
|
+
optional string template_id = 4;
|
|
181
|
+
optional string custom_subject = 5;
|
|
182
|
+
optional string custom_body_html = 6;
|
|
183
|
+
optional string custom_body_text = 7;
|
|
184
|
+
NotificationLocale locale = 8;
|
|
185
|
+
string segment_json = 9;
|
|
186
|
+
optional string coupon_spec_json = 10;
|
|
187
|
+
bool respect_opt_out = 11;
|
|
188
|
+
CampaignStatusEnum status = 12;
|
|
189
|
+
optional string scheduled_at = 13;
|
|
190
|
+
optional string started_at = 14;
|
|
191
|
+
optional string completed_at = 15;
|
|
192
|
+
int32 total_recipients = 16;
|
|
193
|
+
int32 sent_count = 17;
|
|
194
|
+
int32 failed_count = 18;
|
|
195
|
+
int32 skipped_opt_out_count = 19;
|
|
196
|
+
string created_by_id = 20;
|
|
197
|
+
string created_at = 21;
|
|
198
|
+
string updated_at = 22;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
enum CampaignStatusEnum {
|
|
202
|
+
CAMPAIGN_STATUS_UNSPECIFIED = 0;
|
|
203
|
+
DRAFT = 1;
|
|
204
|
+
SCHEDULED = 2;
|
|
205
|
+
RUNNING = 3;
|
|
206
|
+
COMPLETED = 4;
|
|
207
|
+
CANCELLED = 5;
|
|
208
|
+
CAMPAIGN_FAILED = 6;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
message PreviewSegmentRequest {
|
|
212
|
+
string segment_json = 1;
|
|
213
|
+
NotificationChannel channel = 2; // потрібно для перевірки наявності email/phone
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
message PreviewSegmentResponse {
|
|
217
|
+
int32 total_count = 1;
|
|
218
|
+
// Перші 10 для UI; решта — за необхідності окремим запитом.
|
|
219
|
+
repeated PreviewSegmentSample sample = 2;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
message PreviewSegmentSample {
|
|
223
|
+
string user_id = 1;
|
|
224
|
+
string first_name = 2;
|
|
225
|
+
string last_name = 3;
|
|
226
|
+
string email = 4;
|
|
227
|
+
string phone = 5;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
message ListRecipientsRequest {
|
|
231
|
+
string campaign_id = 1;
|
|
232
|
+
optional CampaignRecipientStatusEnum status = 2;
|
|
233
|
+
optional int32 page = 3;
|
|
234
|
+
optional int32 limit = 4;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
message ListRecipientsResponse {
|
|
238
|
+
repeated CampaignRecipient items = 1;
|
|
239
|
+
int32 total = 2;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
message CampaignRecipient {
|
|
243
|
+
string id = 1;
|
|
244
|
+
string campaign_id = 2;
|
|
245
|
+
string user_id = 3;
|
|
246
|
+
string recipient = 4;
|
|
247
|
+
CampaignRecipientStatusEnum status = 5;
|
|
248
|
+
optional string coupon_code = 6;
|
|
249
|
+
optional string notification_log_id = 7;
|
|
250
|
+
optional string error_message = 8;
|
|
251
|
+
optional string sent_at = 9;
|
|
252
|
+
string created_at = 10;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
enum CampaignRecipientStatusEnum {
|
|
256
|
+
CAMPAIGN_RECIPIENT_UNSPECIFIED = 0;
|
|
257
|
+
RECIPIENT_PENDING = 1;
|
|
258
|
+
RECIPIENT_SENT = 2;
|
|
259
|
+
RECIPIENT_FAILED = 3;
|
|
260
|
+
RECIPIENT_SKIPPED_OPT_OUT = 4;
|
|
261
|
+
RECIPIENT_SKIPPED_NO_CONTACT = 5;
|
|
262
|
+
}
|
|
263
|
+
|
|
32
264
|
// Брендинг email-шаблонів — singleton редагується адміном.
|
|
33
265
|
service BrandingService {
|
|
34
266
|
rpc GetBranding (BrandingEmpty) returns (BrandingProfile);
|
package/proto/order.proto
CHANGED
|
@@ -25,6 +25,26 @@ service OrderService {
|
|
|
25
25
|
rpc ReorderOrder(ReorderOrderRequest) returns (ReorderResponse);
|
|
26
26
|
|
|
27
27
|
rpc ListHistory(ListHistoryRequest) returns (ListHistoryResponse);
|
|
28
|
+
|
|
29
|
+
// CRM-агрегація для експорту клієнтів — повертає count/sum/last для кожного userId.
|
|
30
|
+
rpc AggregateByCustomers(AggregateByCustomersRequest) returns (AggregateByCustomersResponse);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
message AggregateByCustomersRequest {
|
|
34
|
+
repeated string user_ids = 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
message CustomerOrderAggregate {
|
|
38
|
+
string user_id = 1;
|
|
39
|
+
int32 orders_count = 2;
|
|
40
|
+
// Decimal у вигляді рядка ('1234.56') — щоб не втратити точність на gRPC border.
|
|
41
|
+
string total_sum = 3;
|
|
42
|
+
// ISO-рядок, відсутній якщо клієнт ще не робив замовлень.
|
|
43
|
+
optional string last_order_at = 4;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
message AggregateByCustomersResponse {
|
|
47
|
+
repeated CustomerOrderAggregate items = 1;
|
|
28
48
|
}
|
|
29
49
|
|
|
30
50
|
// ─── Enums (string у proto3, для зворотної сумісності) ─────────
|
package/proto/users.proto
CHANGED
|
@@ -47,6 +47,9 @@ service UsersService {
|
|
|
47
47
|
rpc RemoveCartItem(RemoveCartItemRequest) returns (GetCartResponse);
|
|
48
48
|
rpc ClearCart(ClearCartRequest) returns (ClearCartResponse);
|
|
49
49
|
rpc SyncCart(SyncCartRequest) returns (GetCartResponse);
|
|
50
|
+
|
|
51
|
+
// Admin CRM — редагування клієнта менеджером (customers.edit на gateway).
|
|
52
|
+
rpc AdminUpdateCustomer(AdminUpdateCustomerRequest) returns (AdminUpdateCustomerResponse);
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
message GetMeRequest {
|
|
@@ -87,6 +90,19 @@ message User {
|
|
|
87
90
|
int32 bonuses = 4;
|
|
88
91
|
optional string email = 5;
|
|
89
92
|
optional string phone = 6;
|
|
93
|
+
optional string internal_note = 7;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
message AdminUpdateCustomerRequest {
|
|
97
|
+
string id = 1;
|
|
98
|
+
optional string first_name = 2;
|
|
99
|
+
optional string last_name = 3;
|
|
100
|
+
// internal_note редагується тут окремо, бо це CRM-поле — не видиме клієнту.
|
|
101
|
+
optional string internal_note = 4;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
message AdminUpdateCustomerResponse {
|
|
105
|
+
User user = 1;
|
|
90
106
|
}
|
|
91
107
|
|
|
92
108
|
message NotificationSettings {
|