1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
|
/* eslint-disable @typescript-eslint/no-explicit-any */
import { NextRequest } from "next/server";
import db from "@/db/db";
import {
ORGANIZATION_MASTER_HRHMTB_CCTR,
ORGANIZATION_MASTER_HRHMTB_CCTR_TEXT,
ORGANIZATION_MASTER_HRHMTB_PCTR,
ORGANIZATION_MASTER_HRHMTB_ZBUKRS,
ORGANIZATION_MASTER_HRHMTB_ZEKGRP,
ORGANIZATION_MASTER_HRHMTB_ZEKORG,
ORGANIZATION_MASTER_HRHMTB_ZGSBER,
ORGANIZATION_MASTER_HRHMTB_ZGSBER_TEXT,
ORGANIZATION_MASTER_HRHMTB_ZLGORT,
ORGANIZATION_MASTER_HRHMTB_ZSPART,
ORGANIZATION_MASTER_HRHMTB_ZVKBUR,
ORGANIZATION_MASTER_HRHMTB_ZVKGRP,
ORGANIZATION_MASTER_HRHMTB_ZVKORG,
ORGANIZATION_MASTER_HRHMTB_ZVSTEL,
ORGANIZATION_MASTER_HRHMTB_ZVTWEG,
ORGANIZATION_MASTER_HRHMTB_ZWERKS
} from "@/db/schema/MDG/mdg";
import {
ToXMLFields,
serveWsdl,
createXMLParser,
extractRequestData,
convertXMLToDBData,
processNestedArray,
createErrorResponse,
createSuccessResponse,
replaceSubTableData,
withSoapLogging
} from "@/lib/soap/mdg/utils";
// 스키마에서 직접 타입 추론
type CctrData = typeof ORGANIZATION_MASTER_HRHMTB_CCTR.$inferInsert;
type CctrTextData = typeof ORGANIZATION_MASTER_HRHMTB_CCTR_TEXT.$inferInsert;
type PctrData = typeof ORGANIZATION_MASTER_HRHMTB_PCTR.$inferInsert;
type ZbukrsData = typeof ORGANIZATION_MASTER_HRHMTB_ZBUKRS.$inferInsert;
type ZekgrpData = typeof ORGANIZATION_MASTER_HRHMTB_ZEKGRP.$inferInsert;
type ZekorgData = typeof ORGANIZATION_MASTER_HRHMTB_ZEKORG.$inferInsert;
type ZgsberData = typeof ORGANIZATION_MASTER_HRHMTB_ZGSBER.$inferInsert;
type ZgsberTextData = typeof ORGANIZATION_MASTER_HRHMTB_ZGSBER_TEXT.$inferInsert;
type ZlgortData = typeof ORGANIZATION_MASTER_HRHMTB_ZLGORT.$inferInsert;
type ZspartData = typeof ORGANIZATION_MASTER_HRHMTB_ZSPART.$inferInsert;
type ZvkburData = typeof ORGANIZATION_MASTER_HRHMTB_ZVKBUR.$inferInsert;
type ZvkgrpData = typeof ORGANIZATION_MASTER_HRHMTB_ZVKGRP.$inferInsert;
type ZvkorgData = typeof ORGANIZATION_MASTER_HRHMTB_ZVKORG.$inferInsert;
type ZvstelData = typeof ORGANIZATION_MASTER_HRHMTB_ZVSTEL.$inferInsert;
type ZvtwegData = typeof ORGANIZATION_MASTER_HRHMTB_ZVTWEG.$inferInsert;
type ZwerksData = typeof ORGANIZATION_MASTER_HRHMTB_ZWERKS.$inferInsert;
// XML에서 받는 데이터 구조
type CctrXML = ToXMLFields<Omit<CctrData, 'id' | 'createdAt' | 'updatedAt'>> & {
TEXT?: TextXML[];
};
type TextXML = ToXMLFields<Omit<CctrTextData, 'id' | 'createdAt' | 'updatedAt'>>;
type PctrXML = ToXMLFields<Omit<PctrData, 'id' | 'createdAt' | 'updatedAt'>> & {
TEXT?: TextXML[];
};
type ZbukrsXML = ToXMLFields<Omit<ZbukrsData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZekgrpXML = ToXMLFields<Omit<ZekgrpData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZekorgXML = ToXMLFields<Omit<ZekorgData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZgsberXML = ToXMLFields<Omit<ZgsberData, 'id' | 'createdAt' | 'updatedAt'>> & {
TEXT?: ZgsberTextXML[];
};
type ZgsberTextXML = ToXMLFields<Omit<ZgsberTextData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZlgortXML = ToXMLFields<Omit<ZlgortData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZspartXML = ToXMLFields<Omit<ZspartData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZvkburXML = ToXMLFields<Omit<ZvkburData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZvkgrpXML = ToXMLFields<Omit<ZvkgrpData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZvkorgXML = ToXMLFields<Omit<ZvkorgData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZvstelXML = ToXMLFields<Omit<ZvstelData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZvtwegXML = ToXMLFields<Omit<ZvtwegData, 'id' | 'createdAt' | 'updatedAt'>>;
type ZwerksXML = ToXMLFields<Omit<ZwerksData, 'id' | 'createdAt' | 'updatedAt'>>;
// 처리된 데이터 구조
interface ProcessedOrganizationData {
cctrItems: Array<{ cctr: CctrData; texts: CctrTextData[] }>;
pctrItems: Array<{ pctr: PctrData; texts: CctrTextData[] }>;
zbukrsItems: ZbukrsData[];
zekgrpItems: ZekgrpData[];
zekorgItems: ZekorgData[];
zgsberItems: Array<{ zgsber: ZgsberData; texts: ZgsberTextData[] }>;
zlgortItems: ZlgortData[];
zspartItems: ZspartData[];
zvkburItems: ZvkburData[];
zvkgrpItems: ZvkgrpData[];
zvkorgItems: ZvkorgData[];
zvstelItems: ZvstelData[];
zvtwegItems: ZvtwegData[];
zwerksItems: ZwerksData[];
}
export async function GET(request: NextRequest) {
const url = new URL(request.url);
if (url.searchParams.has('wsdl')) {
return serveWsdl('IF_MDZ_EVCP_ORGANIZATION_MASTER.wsdl');
}
return new Response('Method Not Allowed', { status: 405 });
}
export async function POST(request: NextRequest) {
const url = new URL(request.url);
if (url.searchParams.has('wsdl')) {
return serveWsdl('IF_MDZ_EVCP_ORGANIZATION_MASTER.wsdl');
}
const body = await request.text();
return withSoapLogging(
'INBOUND',
'S-ERP',
'IF_MDZ_EVCP_ORGANIZATION_MASTER',
body,
async () => {
console.log('🚀 ORGANIZATION_MASTER 수신 시작, 데이터 길이:', body.length);
const parser = createXMLParser([
'HRHMTB_CCTR', 'HRHMTB_PCTR', 'HRHMTB_ZBUKRS', 'HRHMTB_ZEKGRP',
'HRHMTB_ZEKORG', 'HRHMTB_ZGSBER', 'HRHMTB_ZLGORT', 'HRHMTB_ZSPART',
'HRHMTB_ZVKBUR', 'HRHMTB_ZVKGRP', 'HRHMTB_ZVKORG', 'HRHMTB_ZVSTEL',
'HRHMTB_ZVTWEG', 'HRHMTB_ZWERKS', 'TEXT'
]);
const parsedData = parser.parse(body);
console.log('XML root keys:', Object.keys(parsedData));
// IF_MDZ_EVCP_ORGANIZATION_MASTER 또는 IF_MDZ_EVCP_ORGANIZATION_MASTERReq 패턴 시도
let requestData = extractRequestData(parsedData, 'IF_MDZ_EVCP_ORGANIZATION_MASTERReq');
if (!requestData) {
requestData = extractRequestData(parsedData, 'IF_MDZ_EVCP_ORGANIZATION_MASTER');
}
if (!requestData) {
console.error('Could not find valid request data in the received payload');
console.error('Received XML structure:', JSON.stringify(parsedData, null, 2));
throw new Error('Missing request data - could not find IF_MDZ_EVCP_ORGANIZATION_MASTERReq data');
}
console.log('Validating request data structure:',
Object.keys(requestData).map(key => `${key}: ${requestData[key] ? 'found' : 'not found'}`).join(', ')
);
// XML 데이터를 DB 삽입 가능한 형태로 변환
const processedOrganizations = transformOrganizationData(requestData);
// 데이터베이스 저장
await saveToDatabase(processedOrganizations);
console.log('🎉 처리 완료: 조직 마스터 데이터');
return createSuccessResponse('http://60.101.108.100/api/IF_MDZ_EVCP_ORGANIZATION_MASTER/');
}
).catch(error => {
return createErrorResponse(error);
});
}
/**
* ORGANIZATION 마스터 데이터 변환 함수
*
* 데이터 처리 아키텍처:
* - 독립적인 여러 조직 테이블들을 처리 (CCTR, PCTR, ZBUKRS, ZEKGRP, ZEKORG, ZGSBER 등)
* - 각 테이블은 고유한 unique 필드를 가짐
* - 일부 테이블은 하위 TEXT 테이블을 가짐 (CCTR, PCTR, ZGSBER)
* - 전체 데이터셋 기반 upsert 처리
*
* @param requestData XML에서 파싱된 ORGANIZATION 데이터
* @returns 처리된 ORGANIZATION 데이터 구조
*/
function transformOrganizationData(requestData: any): ProcessedOrganizationData {
const result: ProcessedOrganizationData = {
cctrItems: [],
pctrItems: [],
zbukrsItems: [],
zekgrpItems: [],
zekorgItems: [],
zgsberItems: [],
zlgortItems: [],
zspartItems: [],
zvkburItems: [],
zvkgrpItems: [],
zvkorgItems: [],
zvstelItems: [],
zvtwegItems: [],
zwerksItems: []
};
// HRHMTB_CCTR 처리 (unique 필드: CCTR, 하위 테이블: TEXT)
if (requestData.items1 && Array.isArray(requestData.items1)) {
result.cctrItems = requestData.items1.map((item: CctrXML) => {
const cctrKey = item.CCTR || '';
const fkData = { CCTR: cctrKey };
const cctr = convertXMLToDBData<CctrData>(
item as Record<string, string | undefined>,
fkData
);
const texts = processNestedArray(
item.TEXT,
(text) => convertXMLToDBData<CctrTextData>(text as Record<string, string | undefined>, fkData),
fkData
);
return { cctr, texts };
});
}
// HRHMTB_PCTR 처리 (unique 필드: PCTR, 하위 테이블: TEXT)
if (requestData.items2 && Array.isArray(requestData.items2)) {
result.pctrItems = requestData.items2.map((item: PctrXML) => {
const pctrKey = item.PCTR || '';
const fkData = { CCTR: pctrKey }; // TEXT 테이블은 CCTR 필드를 사용
const pctr = convertXMLToDBData<PctrData>(
item as Record<string, string | undefined>,
fkData
);
const texts = processNestedArray(
item.TEXT,
(text) => convertXMLToDBData<CctrTextData>(text as Record<string, string | undefined>, fkData),
fkData
);
return { pctr, texts };
});
}
// HRHMTB_ZBUKRS 처리 (unique 필드: ZBUKRS)
if (requestData.items3 && Array.isArray(requestData.items3)) {
result.zbukrsItems = requestData.items3.map((item: ZbukrsXML) =>
convertXMLToDBData<ZbukrsData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZEKGRP 처리 (unique 필드: ZEKGRP)
if (requestData.items4 && Array.isArray(requestData.items4)) {
result.zekgrpItems = requestData.items4.map((item: ZekgrpXML) =>
convertXMLToDBData<ZekgrpData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZEKORG 처리 (unique 필드: ZEKORG)
if (requestData.items5 && Array.isArray(requestData.items5)) {
result.zekorgItems = requestData.items5.map((item: ZekorgXML) =>
convertXMLToDBData<ZekorgData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZGSBER 처리 (unique 필드: ZGSBER, 하위 테이블: TEXT)
if (requestData.items6 && Array.isArray(requestData.items6)) {
result.zgsberItems = requestData.items6.map((item: ZgsberXML) => {
const zgsberKey = item.ZGSBER || '';
const fkData = { ZGSBER: zgsberKey };
const zgsber = convertXMLToDBData<ZgsberData>(
item as Record<string, string | undefined>,
fkData
);
const texts = processNestedArray(
item.TEXT,
(text) => convertXMLToDBData<ZgsberTextData>(text as Record<string, string | undefined>, fkData),
fkData
);
return { zgsber, texts };
});
}
// HRHMTB_ZLGORT 처리 (unique 필드: ZLGORT, ZWERKS)
if (requestData.items7 && Array.isArray(requestData.items7)) {
result.zlgortItems = requestData.items7.map((item: ZlgortXML) =>
convertXMLToDBData<ZlgortData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZSPART 처리 (unique 필드: ZSPART)
if (requestData.items8 && Array.isArray(requestData.items8)) {
result.zspartItems = requestData.items8.map((item: ZspartXML) =>
convertXMLToDBData<ZspartData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZVKBUR 처리 (unique 필드: ZVKBUR)
if (requestData.items9 && Array.isArray(requestData.items9)) {
result.zvkburItems = requestData.items9.map((item: ZvkburXML) =>
convertXMLToDBData<ZvkburData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZVKGRP 처리 (unique 필드: ZVKGRP)
if (requestData.items10 && Array.isArray(requestData.items10)) {
result.zvkgrpItems = requestData.items10.map((item: ZvkgrpXML) =>
convertXMLToDBData<ZvkgrpData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZVKORG 처리 (unique 필드: ZVKORG)
if (requestData.items11 && Array.isArray(requestData.items11)) {
result.zvkorgItems = requestData.items11.map((item: ZvkorgXML) =>
convertXMLToDBData<ZvkorgData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZVSTEL 처리 (unique 필드: ZVSTEL)
if (requestData.items12 && Array.isArray(requestData.items12)) {
result.zvstelItems = requestData.items12.map((item: ZvstelXML) =>
convertXMLToDBData<ZvstelData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZVTWEG 처리 (unique 필드: ZVTWEG)
if (requestData.items13 && Array.isArray(requestData.items13)) {
result.zvtwegItems = requestData.items13.map((item: ZvtwegXML) =>
convertXMLToDBData<ZvtwegData>(
item as Record<string, string | undefined>
)
);
}
// HRHMTB_ZWERKS 처리 (unique 필드: ZWERKS)
if (requestData.items14 && Array.isArray(requestData.items14)) {
result.zwerksItems = requestData.items14.map((item: ZwerksXML) =>
convertXMLToDBData<ZwerksData>(
item as Record<string, string | undefined>
)
);
}
return result;
}
// 데이터베이스 저장 함수
/**
* 처리된 ORGANIZATION 데이터를 데이터베이스에 저장
*
* 저장 전략:
* 1. 각 조직 테이블별로 고유 필드 기준 upsert 처리
* 2. 하위 TEXT 테이블들: FK 기준 전체 삭제 후 재삽입
* - CCTR_TEXT, ZGSBER_TEXT 포함
* 3. 14개의 독립적인 조직 테이블 처리
*
* @param processedOrganizations 변환된 ORGANIZATION 데이터
*/
async function saveToDatabase(processedOrganizations: ProcessedOrganizationData) {
console.log('데이터베이스 저장 시작: 조직 마스터 데이터');
try {
await db.transaction(async (tx) => {
// CCTR 테이블 처리 (unique 필드: CCTR)
for (const { cctr, texts } of processedOrganizations.cctrItems) {
if (!cctr.CCTR) continue;
await tx.insert(ORGANIZATION_MASTER_HRHMTB_CCTR)
.values(cctr)
.onConflictDoUpdate({
target: ORGANIZATION_MASTER_HRHMTB_CCTR.CCTR,
set: { ...cctr, updatedAt: new Date() }
});
await replaceSubTableData(tx, ORGANIZATION_MASTER_HRHMTB_CCTR_TEXT, texts, 'CCTR', cctr.CCTR);
}
// PCTR 테이블 처리 (unique 필드: PCTR)
for (const { pctr, texts } of processedOrganizations.pctrItems) {
if (!pctr.PCTR) continue;
await tx.insert(ORGANIZATION_MASTER_HRHMTB_PCTR)
.values(pctr)
.onConflictDoUpdate({
target: ORGANIZATION_MASTER_HRHMTB_PCTR.PCTR,
set: { ...pctr, updatedAt: new Date() }
});
// PCTR의 TEXT는 CCTR_TEXT 테이블을 사용하므로 처리하지 않음
}
// 나머지 단일 테이블들 처리
const tableProcessors = [
{ items: processedOrganizations.zbukrsItems, table: ORGANIZATION_MASTER_HRHMTB_ZBUKRS, key: 'ZBUKRS' },
{ items: processedOrganizations.zekgrpItems, table: ORGANIZATION_MASTER_HRHMTB_ZEKGRP, key: 'ZEKGRP' },
{ items: processedOrganizations.zekorgItems, table: ORGANIZATION_MASTER_HRHMTB_ZEKORG, key: 'ZEKORG' },
{ items: processedOrganizations.zlgortItems, table: ORGANIZATION_MASTER_HRHMTB_ZLGORT, key: 'ZLGORT' },
{ items: processedOrganizations.zspartItems, table: ORGANIZATION_MASTER_HRHMTB_ZSPART, key: 'ZSPART' },
{ items: processedOrganizations.zvkburItems, table: ORGANIZATION_MASTER_HRHMTB_ZVKBUR, key: 'ZVKBUR' },
{ items: processedOrganizations.zvkgrpItems, table: ORGANIZATION_MASTER_HRHMTB_ZVKGRP, key: 'ZVKGRP' },
{ items: processedOrganizations.zvkorgItems, table: ORGANIZATION_MASTER_HRHMTB_ZVKORG, key: 'ZVKORG' },
{ items: processedOrganizations.zvstelItems, table: ORGANIZATION_MASTER_HRHMTB_ZVSTEL, key: 'ZVSTEL' },
{ items: processedOrganizations.zvtwegItems, table: ORGANIZATION_MASTER_HRHMTB_ZVTWEG, key: 'ZVTWEG' },
{ items: processedOrganizations.zwerksItems, table: ORGANIZATION_MASTER_HRHMTB_ZWERKS, key: 'ZWERKS' }
];
for (const { items, table, key } of tableProcessors) {
for (const item of items) {
if (!(item as any)[key]) continue;
await tx.insert(table)
.values(item)
.onConflictDoUpdate({
target: (table as any)[key],
set: { ...item, updatedAt: new Date() }
});
}
}
// ZGSBER 테이블 처리 (TEXT 포함)
for (const { zgsber, texts } of processedOrganizations.zgsberItems) {
if (!zgsber.ZGSBER) continue;
await tx.insert(ORGANIZATION_MASTER_HRHMTB_ZGSBER)
.values(zgsber)
.onConflictDoUpdate({
target: ORGANIZATION_MASTER_HRHMTB_ZGSBER.ZGSBER,
set: { ...zgsber, updatedAt: new Date() }
});
await replaceSubTableData(tx, ORGANIZATION_MASTER_HRHMTB_ZGSBER_TEXT, texts, 'ZGSBER', zgsber.ZGSBER);
}
});
console.log('조직 마스터 데이터 처리 완료.');
return true;
} catch (error) {
console.error('데이터베이스 저장 중 오류 발생:', error);
throw error;
}
}
|