@chevre/domain 21.2.0-alpha.119 → 21.2.0-alpha.120
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.
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Connection } from 'mongoose';
|
|
2
2
|
import { RedisClientType } from 'redis';
|
|
3
|
+
import * as factory from '../factory';
|
|
3
4
|
export interface IOffer {
|
|
4
5
|
itemOffered?: {
|
|
5
6
|
serviceOutput?: {
|
|
@@ -25,6 +26,31 @@ export interface IUnlockKey {
|
|
|
25
26
|
offer: IOffer;
|
|
26
27
|
}
|
|
27
28
|
export type IGetHolderResult = string | null;
|
|
29
|
+
export interface ISubReservation {
|
|
30
|
+
id?: string;
|
|
31
|
+
identifier: string;
|
|
32
|
+
reservedTicket?: {
|
|
33
|
+
ticketedSeat?: Pick<factory.reservation.ISeat<factory.reservationType>, 'seatNumber' | 'seatSection'>;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface IReservationPackage {
|
|
37
|
+
typeOf: factory.reservationType.ReservationPackage;
|
|
38
|
+
reservationNumber: string;
|
|
39
|
+
subReservation: ISubReservation[];
|
|
40
|
+
}
|
|
41
|
+
export interface IAggregateReservation {
|
|
42
|
+
typeOf: 'AggregateReservation';
|
|
43
|
+
reservationCount: number;
|
|
44
|
+
reservationFor: {
|
|
45
|
+
id: string;
|
|
46
|
+
startDate: Date;
|
|
47
|
+
};
|
|
48
|
+
reservations: IReservationPackage[];
|
|
49
|
+
}
|
|
50
|
+
export interface ISearchSubReservationResult {
|
|
51
|
+
reservationNumber: string;
|
|
52
|
+
identifier: string;
|
|
53
|
+
}
|
|
28
54
|
/**
|
|
29
55
|
* イベントストックホルダーリポジトリ
|
|
30
56
|
*/
|
|
@@ -35,6 +61,7 @@ export declare class StockHolderRepository {
|
|
|
35
61
|
private readonly holdReservationModel;
|
|
36
62
|
constructor(redisClient: RedisClientType, connection: Connection);
|
|
37
63
|
private static offer2field;
|
|
64
|
+
private static offer2subReservation;
|
|
38
65
|
private static createKey;
|
|
39
66
|
/**
|
|
40
67
|
* 新リポジトリを使用するかどうか
|
|
@@ -78,4 +105,5 @@ export declare class StockHolderRepository {
|
|
|
78
105
|
offers: IOffer[];
|
|
79
106
|
}): Promise<IGetHolderResult[]>;
|
|
80
107
|
private checkIfConflicted;
|
|
108
|
+
private initializeHoldReservation;
|
|
81
109
|
}
|
|
@@ -39,6 +39,25 @@ class StockHolderRepository {
|
|
|
39
39
|
}
|
|
40
40
|
return `${params.seatSection}:${params.seatNumber}`;
|
|
41
41
|
}
|
|
42
|
+
static offer2subReservation(params) {
|
|
43
|
+
var _a, _b;
|
|
44
|
+
const serviceOutputId = (_b = (_a = params.itemOffered) === null || _a === void 0 ? void 0 : _a.serviceOutput) === null || _b === void 0 ? void 0 : _b.id;
|
|
45
|
+
if (typeof serviceOutputId === 'string') {
|
|
46
|
+
return {
|
|
47
|
+
id: serviceOutputId,
|
|
48
|
+
identifier: serviceOutputId
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
identifier: `${params.seatSection}:${params.seatNumber}`,
|
|
53
|
+
reservedTicket: {
|
|
54
|
+
ticketedSeat: {
|
|
55
|
+
seatSection: params.seatSection,
|
|
56
|
+
seatNumber: params.seatNumber
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
42
61
|
static createKey(params) {
|
|
43
62
|
if (!(params.startDate instanceof Date)) {
|
|
44
63
|
throw new factory.errors.Argument('startDate', 'must be Date');
|
|
@@ -67,7 +86,34 @@ class StockHolderRepository {
|
|
|
67
86
|
lockIfNotLimitExceeded(lockKey, maximum) {
|
|
68
87
|
return __awaiter(this, void 0, void 0, function* () {
|
|
69
88
|
if (StockHolderRepository.useMongoose({ eventId: lockKey.eventId, startDate: lockKey.startDate })) {
|
|
70
|
-
|
|
89
|
+
// reservationCount<=nであれば$push+$incする
|
|
90
|
+
yield this.initializeHoldReservation({ eventId: lockKey.eventId, startDate: lockKey.startDate });
|
|
91
|
+
const addedReservationCount = lockKey.offers.length;
|
|
92
|
+
const reservationCountLte = maximum - addedReservationCount;
|
|
93
|
+
const subReservations = lockKey.offers.map((offer) => StockHolderRepository.offer2subReservation(offer));
|
|
94
|
+
const reservationPackage = {
|
|
95
|
+
typeOf: factory.reservationType.ReservationPackage,
|
|
96
|
+
reservationNumber: lockKey.holder,
|
|
97
|
+
subReservation: subReservations
|
|
98
|
+
};
|
|
99
|
+
yield this.holdReservationModel.findOneAndUpdate({
|
|
100
|
+
reservationCount: { $lte: reservationCountLte },
|
|
101
|
+
'reservationFor.id': { $eq: lockKey.eventId },
|
|
102
|
+
// 座席有無に関わらずsubReservation.identifierはuniqueであるはず
|
|
103
|
+
'reservations.subReservation.identifier': {
|
|
104
|
+
$nin: subReservations.map((r) => r.identifier)
|
|
105
|
+
}
|
|
106
|
+
}, {
|
|
107
|
+
$inc: { reservationCount: addedReservationCount },
|
|
108
|
+
$push: { reservations: reservationPackage }
|
|
109
|
+
}, { new: true })
|
|
110
|
+
.select({ _id: 1 })
|
|
111
|
+
.exec()
|
|
112
|
+
.then((doc) => {
|
|
113
|
+
if (doc === null) {
|
|
114
|
+
throw new factory.errors.Argument('Event', 'maximumAttendeeCapacity exceeded');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
71
117
|
}
|
|
72
118
|
else {
|
|
73
119
|
const key = StockHolderRepository.createKey({ eventId: lockKey.eventId, startDate: lockKey.startDate });
|
|
@@ -89,7 +135,31 @@ class StockHolderRepository {
|
|
|
89
135
|
lock(lockKey) {
|
|
90
136
|
return __awaiter(this, void 0, void 0, function* () {
|
|
91
137
|
if (StockHolderRepository.useMongoose({ eventId: lockKey.eventId, startDate: lockKey.startDate })) {
|
|
92
|
-
|
|
138
|
+
yield this.initializeHoldReservation({ eventId: lockKey.eventId, startDate: lockKey.startDate });
|
|
139
|
+
const addedReservationCount = lockKey.offers.length;
|
|
140
|
+
const subReservations = lockKey.offers.map((offer) => StockHolderRepository.offer2subReservation(offer));
|
|
141
|
+
const reservationPackage = {
|
|
142
|
+
typeOf: factory.reservationType.ReservationPackage,
|
|
143
|
+
reservationNumber: lockKey.holder,
|
|
144
|
+
subReservation: subReservations
|
|
145
|
+
};
|
|
146
|
+
yield this.holdReservationModel.findOneAndUpdate({
|
|
147
|
+
'reservationFor.id': { $eq: lockKey.eventId },
|
|
148
|
+
// 座席有無に関わらずsubReservation.identifierはuniqueであるはず
|
|
149
|
+
'reservations.subReservation.identifier': {
|
|
150
|
+
$nin: subReservations.map((r) => r.identifier)
|
|
151
|
+
}
|
|
152
|
+
}, {
|
|
153
|
+
$inc: { reservationCount: addedReservationCount },
|
|
154
|
+
$push: { reservations: reservationPackage }
|
|
155
|
+
}, { new: true })
|
|
156
|
+
.select({ _id: 1 })
|
|
157
|
+
.exec()
|
|
158
|
+
.then((doc) => {
|
|
159
|
+
if (doc === null) {
|
|
160
|
+
throw new factory.errors.AlreadyInUse(factory.reservationType.EventReservation, ['ticketedSeat'], 'Already hold');
|
|
161
|
+
}
|
|
162
|
+
});
|
|
93
163
|
}
|
|
94
164
|
else {
|
|
95
165
|
if (!(lockKey.expires instanceof Date)) {
|
|
@@ -144,7 +214,21 @@ class StockHolderRepository {
|
|
|
144
214
|
unlock(params) {
|
|
145
215
|
return __awaiter(this, void 0, void 0, function* () {
|
|
146
216
|
if (StockHolderRepository.useMongoose({ eventId: params.eventId, startDate: params.startDate })) {
|
|
147
|
-
|
|
217
|
+
// [id]あるいは[seatNumber+seatSection]でreservations.subReservationsから$pull+$incする
|
|
218
|
+
yield this.initializeHoldReservation({ eventId: params.eventId, startDate: params.startDate });
|
|
219
|
+
const subReservation = StockHolderRepository.offer2subReservation(params.offer);
|
|
220
|
+
yield this.holdReservationModel.findOneAndUpdate({
|
|
221
|
+
'reservationFor.id': { $eq: params.eventId },
|
|
222
|
+
'reservations.subReservation.identifier': {
|
|
223
|
+
$eq: subReservation.identifier
|
|
224
|
+
}
|
|
225
|
+
}, {
|
|
226
|
+
$inc: { reservationCount: -1 },
|
|
227
|
+
$pull: { 'reservations.subReservation': { identifier: { $eq: subReservation.identifier } } }
|
|
228
|
+
}, { new: true })
|
|
229
|
+
.select({ _id: 1 })
|
|
230
|
+
.exec();
|
|
231
|
+
// docが存在しなくてもよい
|
|
148
232
|
}
|
|
149
233
|
else {
|
|
150
234
|
const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
|
|
@@ -179,6 +263,7 @@ class StockHolderRepository {
|
|
|
179
263
|
countUnavailableOffers(params) {
|
|
180
264
|
return __awaiter(this, void 0, void 0, function* () {
|
|
181
265
|
if (StockHolderRepository.useMongoose({ eventId: params.event.id, startDate: params.event.startDate })) {
|
|
266
|
+
// reservationCountを返す
|
|
182
267
|
return this.holdReservationModel.findOne({
|
|
183
268
|
'reservationFor.id': { $eq: params.event.id }
|
|
184
269
|
})
|
|
@@ -208,9 +293,40 @@ class StockHolderRepository {
|
|
|
208
293
|
* 保持者を取得する
|
|
209
294
|
*/
|
|
210
295
|
getHolder(params) {
|
|
296
|
+
var _a;
|
|
211
297
|
return __awaiter(this, void 0, void 0, function* () {
|
|
212
298
|
if (StockHolderRepository.useMongoose({ eventId: params.eventId, startDate: params.startDate })) {
|
|
213
|
-
|
|
299
|
+
// [id]あるいは[seatNumber+seatSection]でreservationNumberを返す
|
|
300
|
+
yield this.initializeHoldReservation({ eventId: params.eventId, startDate: params.startDate });
|
|
301
|
+
const subReservation = StockHolderRepository.offer2subReservation(params.offer);
|
|
302
|
+
const matchStages = [
|
|
303
|
+
{
|
|
304
|
+
$match: { 'reservationFor.id': { $eq: params.eventId } }
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
$match: { 'reservations.subReservation.identifier': { $eq: subReservation.identifier } }
|
|
308
|
+
}
|
|
309
|
+
];
|
|
310
|
+
const aggregate = this.holdReservationModel.aggregate([
|
|
311
|
+
{ $unwind: '$reservations' },
|
|
312
|
+
{ $unwind: '$reservations.subReservation' },
|
|
313
|
+
...matchStages,
|
|
314
|
+
{
|
|
315
|
+
$project: {
|
|
316
|
+
_id: 0,
|
|
317
|
+
reservationNumber: '$reservations.reservationNumber',
|
|
318
|
+
identifier: '$reservations.subReservation.identifier'
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
]);
|
|
322
|
+
const subReservations = yield aggregate.exec();
|
|
323
|
+
debug('getHolder found subReservations.', subReservation.identifier, subReservations);
|
|
324
|
+
if (subReservations.length > 0) {
|
|
325
|
+
return String((_a = subReservations.shift()) === null || _a === void 0 ? void 0 : _a.reservationNumber);
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
214
330
|
}
|
|
215
331
|
else {
|
|
216
332
|
const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
|
|
@@ -254,7 +370,37 @@ class StockHolderRepository {
|
|
|
254
370
|
searchHolders(params) {
|
|
255
371
|
return __awaiter(this, void 0, void 0, function* () {
|
|
256
372
|
if (StockHolderRepository.useMongoose({ eventId: params.eventId, startDate: params.startDate })) {
|
|
257
|
-
|
|
373
|
+
// [id]あるいは[seatNumber+seatSection]のリストでreservationNumberのリストを返す
|
|
374
|
+
yield this.initializeHoldReservation({ eventId: params.eventId, startDate: params.startDate });
|
|
375
|
+
const subReservations = params.offers.map((offer) => StockHolderRepository.offer2subReservation(offer));
|
|
376
|
+
const matchStages = [
|
|
377
|
+
{
|
|
378
|
+
$match: { 'reservationFor.id': { $eq: params.eventId } }
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
$match: { 'reservations.subReservation.identifier': { $in: subReservations.map((r) => r.identifier) } }
|
|
382
|
+
}
|
|
383
|
+
];
|
|
384
|
+
const aggregate = this.holdReservationModel.aggregate([
|
|
385
|
+
{ $unwind: '$reservations' },
|
|
386
|
+
{ $unwind: '$reservations.subReservation' },
|
|
387
|
+
...matchStages,
|
|
388
|
+
{
|
|
389
|
+
$project: {
|
|
390
|
+
_id: 0,
|
|
391
|
+
reservationNumber: '$reservations.reservationNumber',
|
|
392
|
+
identifier: '$reservations.subReservation.identifier'
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
]);
|
|
396
|
+
const subReservationsByDoc = yield aggregate.exec();
|
|
397
|
+
return subReservations.map((subReservation) => {
|
|
398
|
+
const subReservationByDoc = subReservationsByDoc.find((r) => r.identifier === subReservation.identifier);
|
|
399
|
+
return (subReservationByDoc !== undefined)
|
|
400
|
+
? String(subReservationByDoc.reservationNumber)
|
|
401
|
+
// tslint:disable-next-line:no-null-keyword
|
|
402
|
+
: null;
|
|
403
|
+
});
|
|
258
404
|
}
|
|
259
405
|
else {
|
|
260
406
|
const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
|
|
@@ -318,6 +464,28 @@ class StockHolderRepository {
|
|
|
318
464
|
}
|
|
319
465
|
});
|
|
320
466
|
}
|
|
467
|
+
initializeHoldReservation(params) {
|
|
468
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
469
|
+
if (!(params.startDate instanceof Date)) {
|
|
470
|
+
throw new factory.errors.Argument('startDate', 'must be Date');
|
|
471
|
+
}
|
|
472
|
+
const aggregateReservation = {
|
|
473
|
+
typeOf: 'AggregateReservation',
|
|
474
|
+
reservationCount: 0,
|
|
475
|
+
reservationFor: {
|
|
476
|
+
id: params.eventId,
|
|
477
|
+
startDate: params.startDate
|
|
478
|
+
},
|
|
479
|
+
reservations: []
|
|
480
|
+
};
|
|
481
|
+
const initializedHoldReservation = yield this.holdReservationModel.findOneAndUpdate({ 'reservationFor.id': { $eq: params.eventId } }, {
|
|
482
|
+
$setOnInsert: aggregateReservation
|
|
483
|
+
}, { new: true, upsert: true })
|
|
484
|
+
.select({ _id: 1 })
|
|
485
|
+
.exec();
|
|
486
|
+
debug('holdReservation initialized', initializedHoldReservation, params.eventId);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
321
489
|
}
|
|
322
490
|
StockHolderRepository.KEY_PREFIX_NEW = 'stockHolder';
|
|
323
491
|
StockHolderRepository.KEY_PREFIX = 'chevre:itemAvailability:screeningEvent';
|
package/package.json
CHANGED