geek-custom-api-core 0.0.46 → 0.0.48
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/.history/package_20260219173525.json +97 -0
- package/.history/package_20260221164010.json +97 -0
- package/.history/package_20260221164247.json +98 -0
- package/.history/sql/deposit_20260221172413.sql +136 -0
- package/.history/sql/deposit_20260221172422.sql +136 -0
- package/.history/sql/deposit_20260221172436.sql +136 -0
- package/.history/sql/deposit_20260221172455.sql +136 -0
- package/.history/sql/deposit_20260221172502.sql +136 -0
- package/.history/sql/deposit_20260221172519.sql +136 -0
- package/.history/sql/deposit_20260221172816.sql +141 -0
- package/.history/sql/deposit_20260221172832.sql +141 -0
- package/.history/sql/deposit_20260221172911.sql +140 -0
- package/.history/sql/deposit_20260221175833.sql +141 -0
- package/.history/sql/deposit_20260221223728.sql +141 -0
- package/dist/api/app/deposit/deposit.admin.controller.d.ts +33 -2
- package/dist/api/app/deposit/deposit.admin.controller.js +135 -36
- package/dist/api/app/deposit/deposit.admin.controller.js.map +1 -1
- package/dist/api/app/deposit/deposit.service.d.ts +37 -3
- package/dist/api/app/deposit/deposit.service.js +311 -148
- package/dist/api/app/deposit/deposit.service.js.map +1 -1
- package/dist/api/app/deposit/dto/depositJobSearch.dto.d.ts +8 -0
- package/dist/api/app/deposit/dto/depositJobSearch.dto.js +53 -0
- package/dist/api/app/deposit/dto/depositJobSearch.dto.js.map +1 -0
- package/dist/api/app/deposit/entity/depositJobResult.entity.d.ts +12 -0
- package/dist/api/app/deposit/entity/depositJobResult.entity.js +72 -0
- package/dist/api/app/deposit/entity/depositJobResult.entity.js.map +1 -0
- package/dist/common/config/orm.config.js +1 -1
- package/dist/common/config/orm.config.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -2
- package/sql/deposit.sql +24 -8
|
@@ -41,6 +41,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
41
41
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
42
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
43
|
};
|
|
44
|
+
var DepositService_1;
|
|
44
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
46
|
exports.DepositService = void 0;
|
|
46
47
|
const common_1 = require("@nestjs/common");
|
|
@@ -48,6 +49,7 @@ const default_service_1 = require("../../../common/service/default.service");
|
|
|
48
49
|
const depositJob_entity_1 = require("./entity/depositJob.entity");
|
|
49
50
|
const enum_1 = require("../../../common/enum/enum");
|
|
50
51
|
const depositJobDetail_entity_1 = require("./entity/depositJobDetail.entity");
|
|
52
|
+
const depositJobResult_entity_1 = require("./entity/depositJobResult.entity");
|
|
51
53
|
const XLSX = __importStar(require("xlsx"));
|
|
52
54
|
const depositManager_entity_1 = require("./entity/depositManager.entity");
|
|
53
55
|
const gsUtil_1 = require("../../../common/util/gsUtil");
|
|
@@ -59,70 +61,136 @@ const partner_service_1 = require("../../admin/partner/partner.service");
|
|
|
59
61
|
const core_1 = require("@nestjs/core");
|
|
60
62
|
const date_fns_1 = require("date-fns");
|
|
61
63
|
const depositOrder_entity_1 = require("./entity/depositOrder.entity");
|
|
62
|
-
let DepositService = class DepositService extends default_service_1.DefaultService {
|
|
64
|
+
let DepositService = DepositService_1 = class DepositService extends default_service_1.DefaultService {
|
|
63
65
|
constructor(partnerService, moduleRef) {
|
|
64
66
|
super(moduleRef);
|
|
65
67
|
this.partnerService = partnerService;
|
|
66
68
|
this.moduleRef = moduleRef;
|
|
69
|
+
this.logger = new common_1.Logger(DepositService_1.name);
|
|
67
70
|
}
|
|
68
71
|
async issueSingle(dto) {
|
|
69
|
-
|
|
70
|
-
throw new common_1.HttpException('예치금 금액은 0보다 큰 값이어야 합니다.', common_1.HttpStatus.BAD_REQUEST);
|
|
71
|
-
}
|
|
72
|
-
if (dto.expiredDt && dto.expiredDt < new Date()) {
|
|
73
|
-
throw new common_1.HttpException('만료일시는 현재 시간 이후여야 합니다.', common_1.HttpStatus.BAD_REQUEST);
|
|
74
|
-
}
|
|
72
|
+
const targetAmount = Number(dto.amount);
|
|
75
73
|
const entityManager = await this.getEntityManager();
|
|
76
|
-
entityManager.transaction(async (manager) => {
|
|
77
|
-
|
|
74
|
+
return await entityManager.transaction(async (manager) => {
|
|
75
|
+
const partner = await this.partnerService.findOne(dto.partnerSno);
|
|
76
|
+
const member = await (0, gsUtil_1.shopByServerApi)('/profile', enum_1.HTTP_METHOD.GET, partner.systemKey, partner.longLivedToken, { memberNo: dto.memberNo });
|
|
77
|
+
if (!member) {
|
|
78
|
+
throw new Error('존재하지 않는 회원');
|
|
79
|
+
}
|
|
80
|
+
const memberId = member.memberId;
|
|
81
|
+
const memberName = member.memberName;
|
|
78
82
|
const job = await manager.save(depositJob_entity_1.DepositJob, {
|
|
79
83
|
managerSno: dto.managerSno,
|
|
80
84
|
jobType: enum_1.DEPOSIT_JOB_TYPE.SINGLE,
|
|
85
|
+
jobName: targetAmount > 0 ? '단건 지급' : '단건 차감',
|
|
81
86
|
totalCount: 1,
|
|
82
|
-
successCount:
|
|
87
|
+
successCount: 1,
|
|
83
88
|
failCount: 0,
|
|
84
89
|
status: enum_1.DEPOSIT_STATUS.DONE,
|
|
85
90
|
});
|
|
86
|
-
|
|
87
|
-
jobSno: job.sno,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
memberId:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
ORDER BY sno DESC
|
|
104
|
-
LIMIT 1
|
|
105
|
-
FOR UPDATE
|
|
106
|
-
`, [dto.partnerSno, dto.memberNo]);
|
|
107
|
-
const prevBalance = (_a = lastLedger === null || lastLedger === void 0 ? void 0 : lastLedger.balanceAfter) !== null && _a !== void 0 ? _a : 0;
|
|
108
|
-
await manager.query(`
|
|
109
|
-
INSERT INTO gs_depositManager
|
|
110
|
-
(partnerSno, memberNo, memberId, memberName, depositType, source, managerSno,
|
|
111
|
-
refType, refSno, depositAmount, balanceAfter, reason)
|
|
112
|
-
VALUES (?, ?, ?, ?, 'ISSUE', 'ADMIN', ?, 'JOB_DETAIL', ?, ?, ?, ?)
|
|
113
|
-
`, [
|
|
114
|
-
dto.partnerSno,
|
|
115
|
-
dto.memberNo,
|
|
116
|
-
dto.memberId,
|
|
117
|
-
dto.memberName,
|
|
118
|
-
dto.managerSno,
|
|
119
|
-
jobDetail.sno,
|
|
120
|
-
dto.amount,
|
|
121
|
-
prevBalance + dto.amount,
|
|
122
|
-
dto.reason,
|
|
123
|
-
]);
|
|
124
|
-
return { success: true };
|
|
91
|
+
if (targetAmount > 0) {
|
|
92
|
+
return await this.processIssue(manager, Object.assign(Object.assign({}, dto), { memberId: member.memberId, memberName: member.memberName, jobSno: job.sno, amount: targetAmount }));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
return await this.executeFifoDeduct(manager, Object.assign(Object.assign({}, dto), { memberId: member.memberId, memberName: member.memberName, amount: Math.abs(targetAmount), orderNo: `DEDUCT_SINGLE_${Date.now()}` }));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async executeFifoDeduct(manager, data) {
|
|
100
|
+
const deductOrder = await manager.save(depositOrder_entity_1.DepositOrder, {
|
|
101
|
+
partnerSno: data.partnerSno,
|
|
102
|
+
memberNo: data.memberNo,
|
|
103
|
+
orderNo: data.orderNo,
|
|
104
|
+
memberId: data.memberId,
|
|
105
|
+
memberName: data.memberName,
|
|
106
|
+
usedAmount: data.amount,
|
|
107
|
+
orderStatus: 'DONE',
|
|
125
108
|
});
|
|
109
|
+
const availableIssues = await manager.query(`
|
|
110
|
+
SELECT jd.sno AS issueDetailSno, jd.depositAmount, jd.expiredDt,
|
|
111
|
+
(jd.depositAmount - IFNULL(usedSub.netUsed, 0)) AS remainAmount
|
|
112
|
+
FROM gs_depositJobDetail jd
|
|
113
|
+
LEFT JOIN (
|
|
114
|
+
SELECT issueDetailSno, SUM(usedDepositAmount - refundAmount) AS netUsed
|
|
115
|
+
FROM gs_depositOrderDetail GROUP BY issueDetailSno
|
|
116
|
+
) usedSub ON jd.sno = usedSub.issueDetailSno
|
|
117
|
+
WHERE jd.partnerSno = ? AND jd.memberNo = ? AND jd.expireFl = 'N'
|
|
118
|
+
AND (jd.expiredDt IS NULL OR jd.expiredDt >= NOW())
|
|
119
|
+
HAVING remainAmount > 0
|
|
120
|
+
ORDER BY CASE WHEN jd.expiredDt IS NULL THEN 1 ELSE 0 END ASC, jd.expiredDt ASC, jd.sno ASC
|
|
121
|
+
FOR UPDATE
|
|
122
|
+
`, [data.partnerSno, data.memberNo]);
|
|
123
|
+
let remainToDeduct = data.amount;
|
|
124
|
+
const details = [];
|
|
125
|
+
for (const issue of availableIssues) {
|
|
126
|
+
if (remainToDeduct <= 0)
|
|
127
|
+
break;
|
|
128
|
+
const take = Math.min(Number(issue.remainAmount), remainToDeduct);
|
|
129
|
+
details.push(manager.create(depositOrderDetail_entity_1.DepositOrderDetail, {
|
|
130
|
+
orderSno: deductOrder.sno,
|
|
131
|
+
partnerSno: data.partnerSno,
|
|
132
|
+
memberNo: data.memberNo,
|
|
133
|
+
orderProductOptionNo: 'DEDUCT',
|
|
134
|
+
mallProductNo: 'DEDUCT',
|
|
135
|
+
orderProductName: `[관리자차감] ${data.reason}`,
|
|
136
|
+
usedDepositAmount: take,
|
|
137
|
+
issueDetailSno: issue.issueDetailSno,
|
|
138
|
+
refundYn: enum_1.YN.N,
|
|
139
|
+
}));
|
|
140
|
+
remainToDeduct -= take;
|
|
141
|
+
}
|
|
142
|
+
if (remainToDeduct > 0)
|
|
143
|
+
throw new Error(`잔액 부족 (부족분: ${remainToDeduct})`);
|
|
144
|
+
await manager.save(depositOrderDetail_entity_1.DepositOrderDetail, details);
|
|
145
|
+
const [lastLedger] = await manager.query(`SELECT balanceAfter FROM gs_depositManager WHERE partnerSno = ? AND memberNo = ? ORDER BY sno DESC LIMIT 1 FOR UPDATE`, [data.partnerSno, data.memberNo]);
|
|
146
|
+
const prevBalance = Number((lastLedger === null || lastLedger === void 0 ? void 0 : lastLedger.balanceAfter) || 0);
|
|
147
|
+
await manager.save(depositManager_entity_1.DepositManager, {
|
|
148
|
+
partnerSno: data.partnerSno,
|
|
149
|
+
memberNo: data.memberNo,
|
|
150
|
+
memberId: data.memberId,
|
|
151
|
+
memberName: data.memberName,
|
|
152
|
+
depositType: enum_1.DEPOSIT_TYPE.ADJUST,
|
|
153
|
+
source: enum_1.DEPOSIT_SOURCE.ADMIN,
|
|
154
|
+
refType: enum_1.DEPOSIT_REF_TYPE.ORDER,
|
|
155
|
+
refSno: deductOrder.sno,
|
|
156
|
+
depositAmount: -data.amount,
|
|
157
|
+
balanceAfter: prevBalance - data.amount,
|
|
158
|
+
reason: data.reason,
|
|
159
|
+
managerSno: data.managerSno,
|
|
160
|
+
});
|
|
161
|
+
return { success: true };
|
|
162
|
+
}
|
|
163
|
+
async processIssue(manager, data) {
|
|
164
|
+
var _a;
|
|
165
|
+
const jobDetail = await manager.save(depositJobDetail_entity_1.DepositJobDetail, {
|
|
166
|
+
jobSno: data.jobSno,
|
|
167
|
+
partnerSno: data.partnerSno,
|
|
168
|
+
memberNo: data.memberNo,
|
|
169
|
+
memberId: data.memberId,
|
|
170
|
+
memberName: data.memberName,
|
|
171
|
+
depositAmount: data.amount,
|
|
172
|
+
reason: data.reason,
|
|
173
|
+
expiredDt: data.expiredDt || null,
|
|
174
|
+
});
|
|
175
|
+
const [lastLedger] = await manager.query(`SELECT balanceAfter FROM gs_depositManager
|
|
176
|
+
WHERE partnerSno = ? AND memberNo = ?
|
|
177
|
+
ORDER BY sno DESC LIMIT 1 FOR UPDATE`, [data.partnerSno, data.memberNo]);
|
|
178
|
+
const prevBalance = Number((_a = lastLedger === null || lastLedger === void 0 ? void 0 : lastLedger.balanceAfter) !== null && _a !== void 0 ? _a : 0);
|
|
179
|
+
await manager.save(depositManager_entity_1.DepositManager, {
|
|
180
|
+
partnerSno: data.partnerSno,
|
|
181
|
+
memberNo: data.memberNo,
|
|
182
|
+
memberId: data.memberId,
|
|
183
|
+
memberName: data.memberName,
|
|
184
|
+
depositType: enum_1.DEPOSIT_TYPE.ISSUE,
|
|
185
|
+
source: enum_1.DEPOSIT_SOURCE.ADMIN,
|
|
186
|
+
managerSno: data.managerSno,
|
|
187
|
+
refType: enum_1.DEPOSIT_REF_TYPE.JOB_DETAIL,
|
|
188
|
+
refSno: jobDetail.sno,
|
|
189
|
+
depositAmount: data.amount,
|
|
190
|
+
balanceAfter: prevBalance + data.amount,
|
|
191
|
+
reason: data.reason,
|
|
192
|
+
});
|
|
193
|
+
return { success: true, jobDetailSno: jobDetail.sno };
|
|
126
194
|
}
|
|
127
195
|
async issueByExcel(file, managerSno, partnerSno, jobName) {
|
|
128
196
|
const workbook = XLSX.read(file.buffer, { type: 'buffer' });
|
|
@@ -133,146 +201,221 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
133
201
|
}
|
|
134
202
|
const entityManager = await this.getEntityManager();
|
|
135
203
|
const job = await entityManager.save(depositJob_entity_1.DepositJob, {
|
|
136
|
-
managerSno
|
|
204
|
+
managerSno,
|
|
137
205
|
jobType: enum_1.DEPOSIT_JOB_TYPE.EXCEL,
|
|
138
|
-
jobName
|
|
139
|
-
totalCount: rows.length,
|
|
206
|
+
jobName,
|
|
207
|
+
totalCount: rows.length - 1,
|
|
140
208
|
successCount: 0,
|
|
141
209
|
failCount: 0,
|
|
142
210
|
status: enum_1.DEPOSIT_STATUS.PROCESSING,
|
|
143
211
|
});
|
|
212
|
+
this.processExcelJobAsync(rows, job.sno, managerSno, partnerSno).catch((err) => this.logger.error(`Excel job ${job.sno} failed unexpectedly: ${err.message}`, err.stack));
|
|
213
|
+
return {
|
|
214
|
+
jobSno: job.sno,
|
|
215
|
+
totalCount: rows.length - 1,
|
|
216
|
+
status: enum_1.DEPOSIT_STATUS.PROCESSING,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async processExcelJobAsync(rows, jobSno, managerSno, partnerSno) {
|
|
220
|
+
const entityManager = await this.getEntityManager();
|
|
221
|
+
const partner = await this.partnerService.findOne(partnerSno);
|
|
144
222
|
let successCount = 0;
|
|
145
223
|
let failCount = 0;
|
|
146
|
-
const results = [];
|
|
147
|
-
const partner = await this.partnerService.findOne(partnerSno);
|
|
148
224
|
for (const [index, row] of rows.entries()) {
|
|
225
|
+
if (index === 0)
|
|
226
|
+
continue;
|
|
227
|
+
const memberNo = row.memberNo ? Number(row.memberNo) : 0;
|
|
228
|
+
const amount = Number(row.amount || 0);
|
|
229
|
+
const member = await (0, gsUtil_1.shopByServerApi)('/profile', enum_1.HTTP_METHOD.GET, partner.systemKey, partner.longLivedToken, { memberNo: memberNo });
|
|
230
|
+
if (!member) {
|
|
231
|
+
throw new Error('존재하지 않는 회원');
|
|
232
|
+
}
|
|
233
|
+
const memberId = member.memberId;
|
|
234
|
+
const memberName = member.memberName;
|
|
149
235
|
try {
|
|
150
|
-
if (index === 0) {
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
236
|
await this.issueSingleRow({
|
|
154
237
|
row,
|
|
155
|
-
jobSno
|
|
238
|
+
jobSno,
|
|
156
239
|
managerSno,
|
|
157
240
|
partnerSno,
|
|
158
241
|
systemKey: partner.systemKey,
|
|
159
242
|
longLivedToken: partner.longLivedToken,
|
|
160
243
|
});
|
|
161
244
|
successCount++;
|
|
162
|
-
|
|
245
|
+
await entityManager.save(depositJobResult_entity_1.DepositJobResult, {
|
|
246
|
+
jobSno,
|
|
247
|
+
partnerSno,
|
|
248
|
+
memberNo,
|
|
249
|
+
memberId,
|
|
250
|
+
memberName,
|
|
251
|
+
depositAmount: amount,
|
|
252
|
+
status: enum_1.DEPOSIT_RESULT.SUCCESS,
|
|
253
|
+
reason: row.reason || '',
|
|
254
|
+
});
|
|
163
255
|
}
|
|
164
256
|
catch (e) {
|
|
165
257
|
failCount++;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
258
|
+
await entityManager.save(depositJobResult_entity_1.DepositJobResult, {
|
|
259
|
+
jobSno,
|
|
260
|
+
partnerSno,
|
|
261
|
+
memberNo,
|
|
262
|
+
memberId,
|
|
263
|
+
memberName,
|
|
264
|
+
depositAmount: amount,
|
|
265
|
+
status: enum_1.DEPOSIT_RESULT.FAIL,
|
|
266
|
+
reason: e.message || '알 수 없는 오류',
|
|
170
267
|
});
|
|
171
268
|
}
|
|
172
269
|
}
|
|
173
|
-
await entityManager.update(depositJob_entity_1.DepositJob,
|
|
270
|
+
await entityManager.update(depositJob_entity_1.DepositJob, jobSno, {
|
|
174
271
|
successCount,
|
|
175
272
|
failCount,
|
|
176
273
|
status: enum_1.DEPOSIT_STATUS.DONE,
|
|
177
274
|
});
|
|
275
|
+
}
|
|
276
|
+
async getJobList(dto) {
|
|
277
|
+
const entityManager = await this.getEntityManager();
|
|
278
|
+
const qb = entityManager.createQueryBuilder(depositJob_entity_1.DepositJob, 'j');
|
|
279
|
+
qb.andWhere('j.managerSno IN (SELECT sno FROM gs_manager WHERE partnerSno = :partnerSno)', {
|
|
280
|
+
partnerSno: dto.partnerSno,
|
|
281
|
+
});
|
|
282
|
+
if (dto.jobName) {
|
|
283
|
+
qb.andWhere('j.jobName LIKE :jobName', {
|
|
284
|
+
jobName: `%${dto.jobName}%`,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
if (dto.jobType) {
|
|
288
|
+
qb.andWhere('j.jobType = :jobType', { jobType: dto.jobType });
|
|
289
|
+
}
|
|
290
|
+
if (dto.status) {
|
|
291
|
+
qb.andWhere('j.status = :status', { status: dto.status });
|
|
292
|
+
}
|
|
293
|
+
qb.select(['j.*']);
|
|
294
|
+
qb.orderBy('j.regDt', 'DESC');
|
|
295
|
+
qb.offset(dto.skip);
|
|
296
|
+
qb.limit(dto.take);
|
|
297
|
+
const list = await qb.getRawMany();
|
|
298
|
+
const count = await qb.getCount();
|
|
299
|
+
return (0, gsUtil_1.paginateResponse)([list, count], dto.page, dto.take);
|
|
300
|
+
}
|
|
301
|
+
async getJobResultList(jobSno, dto) {
|
|
302
|
+
const entityManager = await this.getEntityManager();
|
|
303
|
+
const qb = entityManager.createQueryBuilder(depositJobResult_entity_1.DepositJobResult, 'jr');
|
|
304
|
+
qb.andWhere('jr.jobSno = :jobSno', { jobSno });
|
|
305
|
+
qb.select(['jr.*']);
|
|
306
|
+
qb.orderBy('jr.sno', 'ASC');
|
|
307
|
+
qb.offset(dto.skip);
|
|
308
|
+
qb.limit(dto.take);
|
|
309
|
+
const list = await qb.getRawMany();
|
|
310
|
+
const count = await qb.getCount();
|
|
311
|
+
return (0, gsUtil_1.paginateResponse)([list, count], dto.page, dto.take);
|
|
312
|
+
}
|
|
313
|
+
async getJobStatus(jobSno, partnerSno) {
|
|
314
|
+
const entityManager = await this.getEntityManager();
|
|
315
|
+
const job = await entityManager.findOne(depositJob_entity_1.DepositJob, {
|
|
316
|
+
where: { sno: jobSno },
|
|
317
|
+
});
|
|
318
|
+
if (!job) {
|
|
319
|
+
throw new common_1.HttpException('작업을 찾을 수 없습니다.', common_1.HttpStatus.NOT_FOUND);
|
|
320
|
+
}
|
|
178
321
|
return {
|
|
179
322
|
jobSno: job.sno,
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
323
|
+
jobName: job.jobName,
|
|
324
|
+
totalCount: job.totalCount,
|
|
325
|
+
successCount: job.successCount,
|
|
326
|
+
failCount: job.failCount,
|
|
327
|
+
status: job.status,
|
|
184
328
|
};
|
|
185
329
|
}
|
|
330
|
+
async getJobResultExcel(jobSno, partnerSno) {
|
|
331
|
+
const entityManager = await this.getEntityManager();
|
|
332
|
+
const job = await entityManager.findOne(depositJob_entity_1.DepositJob, {
|
|
333
|
+
where: { sno: jobSno },
|
|
334
|
+
});
|
|
335
|
+
if (!job) {
|
|
336
|
+
throw new common_1.HttpException('작업을 찾을 수 없습니다.', common_1.HttpStatus.NOT_FOUND);
|
|
337
|
+
}
|
|
338
|
+
if (job.status !== enum_1.DEPOSIT_STATUS.DONE) {
|
|
339
|
+
throw new common_1.HttpException('작업이 아직 진행 중입니다.', common_1.HttpStatus.BAD_REQUEST);
|
|
340
|
+
}
|
|
341
|
+
const results = await entityManager.find(depositJobResult_entity_1.DepositJobResult, {
|
|
342
|
+
where: { jobSno },
|
|
343
|
+
order: { sno: 'ASC' },
|
|
344
|
+
});
|
|
345
|
+
const excelData = results.map((r) => ({
|
|
346
|
+
회원번호: r.memberNo,
|
|
347
|
+
회원아이디: r.memberId || '',
|
|
348
|
+
회원명: r.memberName || '',
|
|
349
|
+
금액: r.depositAmount,
|
|
350
|
+
처리결과: r.status === enum_1.DEPOSIT_RESULT.SUCCESS ? '성공' : '실패',
|
|
351
|
+
실패사유: r.status === enum_1.DEPOSIT_RESULT.FAIL ? r.reason : '',
|
|
352
|
+
}));
|
|
353
|
+
const workbook = XLSX.utils.book_new();
|
|
354
|
+
const sheet = XLSX.utils.json_to_sheet(excelData);
|
|
355
|
+
XLSX.utils.book_append_sheet(workbook, sheet, '결과');
|
|
356
|
+
return Buffer.from(XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }));
|
|
357
|
+
}
|
|
186
358
|
async issueSingleRow({ row, jobSno, managerSno, partnerSno, systemKey, longLivedToken, }) {
|
|
187
359
|
const { memberNo, amount, reason, expiredDt } = row;
|
|
188
360
|
const targetAmount = Number(amount);
|
|
361
|
+
const formattedExpiredDt = this.parseExcelDate(expiredDt);
|
|
189
362
|
if (!memberNo || isNaN(targetAmount) || targetAmount === 0 || !reason) {
|
|
190
363
|
throw new Error('필수 컬럼 누락 또는 금액 오류');
|
|
191
364
|
}
|
|
192
|
-
const member = await (0, gsUtil_1.shopByServerApi)('/profile', enum_1.HTTP_METHOD.GET, systemKey, longLivedToken, { memberNo
|
|
365
|
+
const member = await (0, gsUtil_1.shopByServerApi)('/profile', enum_1.HTTP_METHOD.GET, systemKey, longLivedToken, { memberNo });
|
|
193
366
|
if (!member)
|
|
194
367
|
throw new Error('존재하지 않는 회원');
|
|
195
368
|
const entityManager = await this.getEntityManager();
|
|
196
369
|
return await entityManager.transaction(async (manager) => {
|
|
197
|
-
var _a;
|
|
198
|
-
const [lastLedger] = await manager.query(`SELECT balanceAfter FROM gs_depositManager WHERE partnerSno = ? AND memberNo = ? ORDER BY sno DESC LIMIT 1 FOR UPDATE`, [partnerSno, memberNo]);
|
|
199
|
-
const prevBalance = Number((_a = lastLedger === null || lastLedger === void 0 ? void 0 : lastLedger.balanceAfter) !== null && _a !== void 0 ? _a : 0);
|
|
200
370
|
if (targetAmount > 0) {
|
|
201
|
-
|
|
202
|
-
jobSno,
|
|
371
|
+
return await this.processIssue(manager, {
|
|
203
372
|
partnerSno,
|
|
204
|
-
memberNo,
|
|
205
|
-
member
|
|
206
|
-
member
|
|
207
|
-
targetAmount,
|
|
373
|
+
memberNo: Number(memberNo),
|
|
374
|
+
memberId: member.memberId,
|
|
375
|
+
memberName: member.memberName,
|
|
376
|
+
amount: targetAmount,
|
|
208
377
|
reason,
|
|
209
|
-
expiredDt !== null && expiredDt !== void 0 ? expiredDt : null,
|
|
210
|
-
]);
|
|
211
|
-
await manager.query(`INSERT INTO gs_depositManager (partnerSno, memberNo, memberId, memberName, depositType, source, managerSno, refType, refSno, depositAmount, balanceAfter, reason)
|
|
212
|
-
VALUES (?, ?, ?, ?, 'ISSUE', 'ADMIN', ?, 'JOB_DETAIL', ?, ?, ?, ?)`, [
|
|
213
|
-
partnerSno,
|
|
214
|
-
memberNo,
|
|
215
|
-
member['memberId'],
|
|
216
|
-
member['memberName'],
|
|
217
378
|
managerSno,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
reason,
|
|
222
|
-
]);
|
|
379
|
+
jobSno,
|
|
380
|
+
expiredDt: formattedExpiredDt ? formattedExpiredDt : null,
|
|
381
|
+
});
|
|
223
382
|
}
|
|
224
383
|
else {
|
|
225
|
-
|
|
226
|
-
if (prevBalance < deductAmount)
|
|
227
|
-
throw new Error(`잔액 부족 (가용: ${prevBalance})`);
|
|
228
|
-
const deductOrder = await manager.save(depositOrder_entity_1.DepositOrder, {
|
|
384
|
+
return await this.executeFifoDeduct(manager, {
|
|
229
385
|
partnerSno,
|
|
230
|
-
memberNo,
|
|
231
|
-
memberId: member
|
|
232
|
-
memberName: member
|
|
386
|
+
memberNo: Number(memberNo),
|
|
387
|
+
memberId: member.memberId,
|
|
388
|
+
memberName: member.memberName,
|
|
389
|
+
amount: Math.abs(targetAmount),
|
|
390
|
+
reason,
|
|
391
|
+
managerSno,
|
|
233
392
|
orderNo: `DEDUCT_EXCEL_${Date.now()}`,
|
|
234
|
-
usedAmount: deductAmount,
|
|
235
|
-
orderStatus: 'DONE',
|
|
236
393
|
});
|
|
237
|
-
const availableIssues = await manager.query(`SELECT jd.sno AS issueDetailSno, jd.depositAmount, jd.expiredDt, (jd.depositAmount - IFNULL(usedSub.netUsed, 0)) AS remainAmount
|
|
238
|
-
FROM gs_depositJobDetail jd
|
|
239
|
-
LEFT JOIN (SELECT issueDetailSno, SUM(usedDepositAmount - refundAmount) AS netUsed FROM gs_depositOrderDetail GROUP BY issueDetailSno) usedSub ON jd.sno = usedSub.issueDetailSno
|
|
240
|
-
WHERE jd.partnerSno = ? AND jd.memberNo = ? AND jd.expireFl = 'N' AND (jd.expiredDt IS NULL OR jd.expiredDt >= NOW())
|
|
241
|
-
HAVING remainAmount > 0 ORDER BY CASE WHEN jd.expiredDt IS NULL THEN 1 ELSE 0 END ASC, jd.expiredDt ASC, jd.sno ASC FOR UPDATE`, [partnerSno, memberNo]);
|
|
242
|
-
let remainToDeduct = deductAmount;
|
|
243
|
-
for (const issue of availableIssues) {
|
|
244
|
-
if (remainToDeduct <= 0)
|
|
245
|
-
break;
|
|
246
|
-
const take = Math.min(Number(issue.remainAmount), remainToDeduct);
|
|
247
|
-
await manager.save(depositOrderDetail_entity_1.DepositOrderDetail, {
|
|
248
|
-
orderSno: deductOrder.sno,
|
|
249
|
-
partnerSno,
|
|
250
|
-
memberNo,
|
|
251
|
-
orderProductOptionNo: 'ADMIN_DEDUCT',
|
|
252
|
-
mallProductNo: 'ADMIN_DEDUCT',
|
|
253
|
-
orderProductName: `관리자 차감: ${reason}`,
|
|
254
|
-
usedDepositAmount: take,
|
|
255
|
-
issueDetailSno: issue.issueDetailSno,
|
|
256
|
-
expireDt: issue.expiredDt,
|
|
257
|
-
refundYn: enum_1.YN.N,
|
|
258
|
-
});
|
|
259
|
-
remainToDeduct -= take;
|
|
260
|
-
}
|
|
261
|
-
await manager.query(`INSERT INTO gs_depositManager (partnerSno, memberNo, memberId, memberName, depositType, source, managerSno, refType, refSno, depositAmount, balanceAfter, reason)
|
|
262
|
-
VALUES (?, ?, ?, ?, 'CANCEL', 'ADMIN', ?, 'ORDER', ?, ?, ?, ?)`, [
|
|
263
|
-
partnerSno,
|
|
264
|
-
memberNo,
|
|
265
|
-
member['memberId'],
|
|
266
|
-
member['memberName'],
|
|
267
|
-
managerSno,
|
|
268
|
-
deductOrder.sno,
|
|
269
|
-
-deductAmount,
|
|
270
|
-
prevBalance - deductAmount,
|
|
271
|
-
reason,
|
|
272
|
-
]);
|
|
273
394
|
}
|
|
274
395
|
});
|
|
275
396
|
}
|
|
397
|
+
parseExcelDate(dateStr) {
|
|
398
|
+
if (!dateStr)
|
|
399
|
+
return null;
|
|
400
|
+
if (dateStr instanceof Date) {
|
|
401
|
+
dateStr.setHours(23, 59, 59, 0);
|
|
402
|
+
return dateStr;
|
|
403
|
+
}
|
|
404
|
+
if (typeof dateStr === 'number') {
|
|
405
|
+
const date = new Date((dateStr - 25569) * 86400 * 1000);
|
|
406
|
+
date.setHours(23, 59, 59, 0);
|
|
407
|
+
return isNaN(date.getTime()) ? null : date;
|
|
408
|
+
}
|
|
409
|
+
if (typeof dateStr === 'string') {
|
|
410
|
+
const normalized = dateStr.replace(/\./g, '-');
|
|
411
|
+
const date = new Date(normalized);
|
|
412
|
+
if (!isNaN(date.getTime())) {
|
|
413
|
+
date.setHours(23, 59, 59, 0);
|
|
414
|
+
return date;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
276
419
|
async getDepositManagerList(dto) {
|
|
277
420
|
const entityManager = await this.getEntityManager();
|
|
278
421
|
const qb = entityManager.createQueryBuilder(depositManager_entity_1.DepositManager, 'dm');
|
|
@@ -419,7 +562,9 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
419
562
|
async getCouponManagerList(dto) {
|
|
420
563
|
const entityManager = await this.getEntityManager();
|
|
421
564
|
const qb = entityManager.createQueryBuilder(depositCouponManager_entity_1.DepositCouponManager, 'dcm');
|
|
422
|
-
qb.andWhere('dcm.partnerSno = :partnerSno', {
|
|
565
|
+
qb.andWhere('dcm.partnerSno = :partnerSno', {
|
|
566
|
+
partnerSno: dto.partnerSno,
|
|
567
|
+
});
|
|
423
568
|
if (dto.title) {
|
|
424
569
|
qb.andWhere('dcm.title LIKE :title', {
|
|
425
570
|
title: `%${dto.title}%`,
|
|
@@ -447,14 +592,18 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
447
592
|
async getCouponCodeList(dto) {
|
|
448
593
|
const entityManager = await this.getEntityManager();
|
|
449
594
|
const qb = entityManager.createQueryBuilder(depositCoupon_entity_1.DepositCoupon, 'dc');
|
|
450
|
-
qb.andWhere('dc.partnerSno = :partnerSno', {
|
|
595
|
+
qb.andWhere('dc.partnerSno = :partnerSno', {
|
|
596
|
+
partnerSno: dto.partnerSno,
|
|
597
|
+
});
|
|
451
598
|
if (dto.couponCode) {
|
|
452
599
|
qb.andWhere('dc.couponCode LIKE :couponCode', {
|
|
453
600
|
couponCode: `%${dto.couponCode}%`,
|
|
454
601
|
});
|
|
455
602
|
}
|
|
456
603
|
if (dto.policySno) {
|
|
457
|
-
qb.andWhere('dc.policySno = :policySno', {
|
|
604
|
+
qb.andWhere('dc.policySno = :policySno', {
|
|
605
|
+
policySno: dto.policySno,
|
|
606
|
+
});
|
|
458
607
|
}
|
|
459
608
|
qb.select(['dc.*']);
|
|
460
609
|
qb.orderBy('dc.sno', 'DESC');
|
|
@@ -480,7 +629,9 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
480
629
|
.addSelect('SUM(od.usedDepositAmount)', 'usedAmount')
|
|
481
630
|
.from(depositOrderDetail_entity_1.DepositOrderDetail, 'od')
|
|
482
631
|
.groupBy('od.issueDetailSno'), 'od', 'jd.sno = od.issueDetailSno')
|
|
483
|
-
.where('jd.partnerSno = :partnerSno', {
|
|
632
|
+
.where('jd.partnerSno = :partnerSno', {
|
|
633
|
+
partnerSno: dto.partnerSno,
|
|
634
|
+
})
|
|
484
635
|
.andWhere('jd.expireFl = :expireFl', { expireFl: 'n' })
|
|
485
636
|
.andWhere('(jd.expiredDt IS NULL OR jd.expiredDt >= NOW())')
|
|
486
637
|
.groupBy('jd.memberNo')
|
|
@@ -491,7 +642,9 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
491
642
|
const countQb = entityManager
|
|
492
643
|
.createQueryBuilder(depositJobDetail_entity_1.DepositJobDetail, 'jd')
|
|
493
644
|
.select('COUNT(DISTINCT jd.memberNo)', 'cnt')
|
|
494
|
-
.where('jd.partnerSno = :partnerSno', {
|
|
645
|
+
.where('jd.partnerSno = :partnerSno', {
|
|
646
|
+
partnerSno: dto.partnerSno,
|
|
647
|
+
})
|
|
495
648
|
.andWhere('jd.expireFl = :expireFl', { expireFl: 'n' })
|
|
496
649
|
.andWhere('(jd.expiredDt IS NULL OR jd.expiredDt >= NOW())');
|
|
497
650
|
const count = await countQb.getRawOne();
|
|
@@ -652,11 +805,17 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
652
805
|
const entityManager = await this.getEntityManager();
|
|
653
806
|
const partner = await this.partnerService.findOne(dto.partnerSno);
|
|
654
807
|
if (!partner) {
|
|
655
|
-
return {
|
|
808
|
+
return {
|
|
809
|
+
rescode: '0002',
|
|
810
|
+
message: '유효하지 않은 파트너 정보입니다.',
|
|
811
|
+
};
|
|
656
812
|
}
|
|
657
813
|
const user = await (0, gsUtil_1.shopByServerApi)('/profile', enum_1.HTTP_METHOD.GET, partner.systemKey, partner.longLivedToken, { memberNo: Number(dto.memberMappingKey) });
|
|
658
814
|
if (!user) {
|
|
659
|
-
return {
|
|
815
|
+
return {
|
|
816
|
+
rescode: '0001',
|
|
817
|
+
message: '사용자 정보를 찾을 수 없습니다.',
|
|
818
|
+
};
|
|
660
819
|
}
|
|
661
820
|
const targetMemberNo = Number(user.memberNo);
|
|
662
821
|
const targetPartnerSno = partner.sno;
|
|
@@ -770,7 +929,8 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
770
929
|
? remainToDeductMaster
|
|
771
930
|
: Math.round(depositOrder.usedAmount * ratio);
|
|
772
931
|
remainToDeductMaster -= amountToAllocate;
|
|
773
|
-
while (amountToAllocate > 0 &&
|
|
932
|
+
while (amountToAllocate > 0 &&
|
|
933
|
+
issueIndex < availableIssues.length) {
|
|
774
934
|
const currentIssue = availableIssues[issueIndex];
|
|
775
935
|
const canTake = Math.min(Number(currentIssue.remainAmount), amountToAllocate);
|
|
776
936
|
if (canTake > 0) {
|
|
@@ -854,8 +1014,10 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
854
1014
|
});
|
|
855
1015
|
if (!usedDetails.length)
|
|
856
1016
|
continue;
|
|
857
|
-
const totalRemainQty = Number(usedDetails[0].orderCnt) -
|
|
858
|
-
|
|
1017
|
+
const totalRemainQty = Number(usedDetails[0].orderCnt) -
|
|
1018
|
+
Number(usedDetails[0].refundCnt);
|
|
1019
|
+
const totalRemainAmt = usedDetails.reduce((sum, d) => sum +
|
|
1020
|
+
(Number(d.usedDepositAmount) - Number(d.refundAmount)), 0);
|
|
859
1021
|
if (totalRemainAmt <= 0 || totalRemainQty <= 0)
|
|
860
1022
|
continue;
|
|
861
1023
|
const isFullCancel = Number(item.orderCnt) === Number(item.originalOrderCnt);
|
|
@@ -870,7 +1032,8 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
870
1032
|
for (const detail of usedDetails) {
|
|
871
1033
|
if (remainRefundAmt <= 0)
|
|
872
1034
|
break;
|
|
873
|
-
const recordRemainAmt = Number(detail.usedDepositAmount) -
|
|
1035
|
+
const recordRemainAmt = Number(detail.usedDepositAmount) -
|
|
1036
|
+
Number(detail.refundAmount);
|
|
874
1037
|
if (recordRemainAmt <= 0)
|
|
875
1038
|
continue;
|
|
876
1039
|
const takeAmt = Math.min(recordRemainAmt, remainRefundAmt);
|
|
@@ -914,7 +1077,7 @@ let DepositService = class DepositService extends default_service_1.DefaultServi
|
|
|
914
1077
|
}
|
|
915
1078
|
};
|
|
916
1079
|
exports.DepositService = DepositService;
|
|
917
|
-
exports.DepositService = DepositService = __decorate([
|
|
1080
|
+
exports.DepositService = DepositService = DepositService_1 = __decorate([
|
|
918
1081
|
(0, common_1.Injectable)(),
|
|
919
1082
|
__metadata("design:paramtypes", [partner_service_1.PartnerService,
|
|
920
1083
|
core_1.ModuleRef])
|