@chevre/domain 23.2.0-alpha.41 → 23.2.0-alpha.43
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/example/src/chevre/orderNumber/decryptOrderNumber.ts +14 -0
- package/example/src/chevre/orderNumber/testRandomness.ts +48 -0
- package/example/src/chevre/reIndex.ts +1 -1
- package/example/src/chevre/transactionNumber/publishByTimestamp.ts +23 -0
- package/example/src/chevre/transactionNumber/testRandomness.ts +42 -0
- package/lib/chevre/factory/transactionNumber.d.ts +19 -0
- package/lib/chevre/factory/transactionNumber.js +54 -0
- package/lib/chevre/repo/confirmationNumber.js +1 -1
- package/lib/chevre/repo/mongoose/schemas/offerCatalog.js +2 -8
- package/lib/chevre/repo/mongoose/schemas/offerCatalogItem.js +28 -2
- package/lib/chevre/repo/mongoose/schemas/setting.d.ts +22 -0
- package/lib/chevre/repo/mongoose/schemas/setting.js +2 -0
- package/lib/chevre/repo/orderNumber.d.ts +3 -0
- package/lib/chevre/repo/orderNumber.js +76 -17
- package/lib/chevre/repo/serviceOutputIdentifier.js +1 -1
- package/lib/chevre/repo/transactionNumber.d.ts +5 -0
- package/lib/chevre/repo/transactionNumber.js +71 -24
- package/package.json +3 -2
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// tslint:disable:no-console
|
|
2
|
+
// tslint:disable-next-line:no-require-imports no-var-requires
|
|
3
|
+
const fpe = require('node-fpe-v1');
|
|
4
|
+
|
|
5
|
+
// const orderNumber = 'CIN1-3796505-5174753';
|
|
6
|
+
|
|
7
|
+
const timestamp = '1769837978000';
|
|
8
|
+
const cipher = fpe({ secret: '3:v2-xxxxxx' });
|
|
9
|
+
// .encrypt(orderNumber);
|
|
10
|
+
|
|
11
|
+
const orderNumber = cipher.encrypt(`${timestamp}1`);
|
|
12
|
+
console.log(orderNumber);
|
|
13
|
+
console.log(cipher.decrypt(orderNumber));
|
|
14
|
+
// '1234567'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// tslint:disable:object-literal-key-quotes no-console
|
|
2
|
+
import * as mongoose from 'mongoose';
|
|
3
|
+
|
|
4
|
+
import { chevre } from '../../../../lib/index';
|
|
5
|
+
|
|
6
|
+
async function testGeneration() {
|
|
7
|
+
await mongoose.connect(<string>process.env.MONGOLAB_URI, { autoIndex: false });
|
|
8
|
+
|
|
9
|
+
const orderNumberRepo = await chevre.repository.OrderNumber.createInstance({
|
|
10
|
+
connection: mongoose.connection
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const ts = new Date(); // 同一ミリ秒固定
|
|
14
|
+
const results: Record<string, string>[] = [];
|
|
15
|
+
|
|
16
|
+
console.log(`--- 同一ミリ秒(${ts})内での連続生成テスト ---`);
|
|
17
|
+
|
|
18
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
19
|
+
for (let i = 0; i < 100; i += 1) {
|
|
20
|
+
// 同一ミリ秒内でシーケンス(00-99)だけが変わる状況を再現
|
|
21
|
+
const orderNumber = await orderNumberRepo.publishByTimestamp({
|
|
22
|
+
project: { alternateName: 'CIN' },
|
|
23
|
+
orderDate: ts
|
|
24
|
+
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// 復号して元のデータに戻るかチェック
|
|
28
|
+
const decoded = await orderNumberRepo.decrypt(orderNumber);
|
|
29
|
+
const isSuccess = decoded === `${ts.valueOf()}${(i + 1).toString()
|
|
30
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
31
|
+
.padStart(2, '0')}`;
|
|
32
|
+
|
|
33
|
+
results.push({
|
|
34
|
+
timestamp: ts.valueOf()
|
|
35
|
+
.toString(),
|
|
36
|
+
'シーケンス': (i + 1).toString(),
|
|
37
|
+
'生成された番号 (17桁)': orderNumber,
|
|
38
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
39
|
+
'ソルト(2文字目)': orderNumber[2],
|
|
40
|
+
'復号結果': decoded,
|
|
41
|
+
'復号確認': isSuccess ? '✅OK' : '❌NG'
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.table(results);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
testGeneration();
|
|
@@ -11,7 +11,7 @@ mongoose.Model.on('index', (...args) => {
|
|
|
11
11
|
async function main() {
|
|
12
12
|
await mongoose.connect(<string>process.env.MONGOLAB_URI, { autoIndex: false });
|
|
13
13
|
|
|
14
|
-
await chevre.repository.
|
|
14
|
+
await chevre.repository.OfferCatalogItem.createInstance(mongoose.connection);
|
|
15
15
|
console.log('success!');
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// tslint:disable:no-console
|
|
2
|
+
import * as mongoose from 'mongoose';
|
|
3
|
+
|
|
4
|
+
import { chevre } from '../../../../lib/index';
|
|
5
|
+
|
|
6
|
+
// tslint:disable-next-line:max-func-body-length
|
|
7
|
+
async function main() {
|
|
8
|
+
await mongoose.connect(<string>process.env.MONGOLAB_URI, { autoIndex: false });
|
|
9
|
+
|
|
10
|
+
const transactionNumberRepo = await chevre.repository.TransactionNumber.createInstance({
|
|
11
|
+
connection: mongoose.connection
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const result = await transactionNumberRepo.publishByTimestamp({ startDate: new Date() });
|
|
15
|
+
console.log(result);
|
|
16
|
+
console.log('length:', result.transactionNumber.length);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
main()
|
|
20
|
+
.then(() => {
|
|
21
|
+
console.log('success!');
|
|
22
|
+
})
|
|
23
|
+
.catch(console.error);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// tslint:disable:object-literal-key-quotes no-console
|
|
2
|
+
import * as mongoose from 'mongoose';
|
|
3
|
+
|
|
4
|
+
import { chevre } from '../../../../lib/index';
|
|
5
|
+
|
|
6
|
+
async function testGeneration() {
|
|
7
|
+
await mongoose.connect(<string>process.env.MONGOLAB_URI, { autoIndex: false });
|
|
8
|
+
|
|
9
|
+
const transactionNumberRepo = await chevre.repository.TransactionNumber.createInstance({
|
|
10
|
+
connection: mongoose.connection
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const ts = new Date(); // 同一ミリ秒固定
|
|
14
|
+
const results: Record<string, string>[] = [];
|
|
15
|
+
|
|
16
|
+
console.log(`--- 同一ミリ秒(${ts})内での連続生成テスト ---`);
|
|
17
|
+
|
|
18
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
19
|
+
for (let i = 0; i < 100; i += 1) {
|
|
20
|
+
// 同一ミリ秒内でシーケンス(00-99)だけが変わる状況を再現
|
|
21
|
+
const { transactionNumber, timestamp, incrReply } = await transactionNumberRepo.publishByTimestamp({ startDate: ts });
|
|
22
|
+
|
|
23
|
+
// 復号して元のデータに戻るかチェック
|
|
24
|
+
const decoded = await transactionNumberRepo.decrypt(transactionNumber);
|
|
25
|
+
const isSuccess = decoded === `${timestamp}${incrReply.toString()
|
|
26
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
27
|
+
.padStart(2, '0')}`;
|
|
28
|
+
|
|
29
|
+
results.push({
|
|
30
|
+
timestamp: timestamp,
|
|
31
|
+
'シーケンス': incrReply.toString(),
|
|
32
|
+
'生成された番号 (17桁)': transactionNumber,
|
|
33
|
+
'ソルト(2文字目)': transactionNumber[1],
|
|
34
|
+
'復号結果': decoded,
|
|
35
|
+
'復号確認': isSuccess ? '✅OK' : '❌NG'
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.table(results);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
testGeneration();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 取引番号ファクトリー
|
|
3
|
+
*/
|
|
4
|
+
export declare class TransactionNumberFactory {
|
|
5
|
+
private readonly fpeSecret;
|
|
6
|
+
private readonly version;
|
|
7
|
+
constructor(params: {
|
|
8
|
+
fpeSecret: string;
|
|
9
|
+
version: string;
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* 生成: SEQが100を超えると自動で18桁に拡張
|
|
13
|
+
*/
|
|
14
|
+
generate(timestamp: string, seq: number): string;
|
|
15
|
+
/**
|
|
16
|
+
* 復号: 17桁でも18桁でも、2文字目以降をまるごとデコード
|
|
17
|
+
*/
|
|
18
|
+
decrypt(transactionNumber: string): string;
|
|
19
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransactionNumberFactory = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const fpe2Module = require("node-fpe-v2");
|
|
6
|
+
const factory = require("../factory");
|
|
7
|
+
// 型定義が新しいtypescriptに適合しないので、強制的に型変更
|
|
8
|
+
const fpe2 = fpe2Module;
|
|
9
|
+
const TRANSACTION_NUMBER_MIN_LENGTH = 17;
|
|
10
|
+
const SEQUENCE_MAX_LENGTH = 2;
|
|
11
|
+
/**
|
|
12
|
+
* 取引番号ファクトリー
|
|
13
|
+
*/
|
|
14
|
+
class TransactionNumberFactory {
|
|
15
|
+
constructor(params) {
|
|
16
|
+
const { fpeSecret, version } = params;
|
|
17
|
+
this.fpeSecret = fpeSecret;
|
|
18
|
+
this.version = version;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 生成: SEQが100を超えると自動で18桁に拡張
|
|
22
|
+
*/
|
|
23
|
+
generate(timestamp, seq) {
|
|
24
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
25
|
+
const saltDigit = (0, crypto_1.randomInt)(0, 10);
|
|
26
|
+
const saltStr = saltDigit.toString();
|
|
27
|
+
const cipher = fpe2({ secret: `${this.fpeSecret}${saltStr}` });
|
|
28
|
+
// incrReplyが 0〜99 なら "00"〜"99" になり(15桁)
|
|
29
|
+
// incrReplyが 100〜999 なら "100"〜"999" になる(16桁)
|
|
30
|
+
const zeroPaddingSequence = seq.toString()
|
|
31
|
+
.padStart(SEQUENCE_MAX_LENGTH, '0');
|
|
32
|
+
const rawBody = `${timestamp}${zeroPaddingSequence}`;
|
|
33
|
+
const encryptedBody = cipher.encrypt(rawBody);
|
|
34
|
+
// ソルトを「あえて」2文字目に含める
|
|
35
|
+
const transactionNumber = `${this.version}${saltStr}${encryptedBody}`;
|
|
36
|
+
if (transactionNumber.length < TRANSACTION_NUMBER_MIN_LENGTH) {
|
|
37
|
+
throw new factory.errors.Internal(`transactionNumber length should be >= ${TRANSACTION_NUMBER_MIN_LENGTH}`);
|
|
38
|
+
}
|
|
39
|
+
return transactionNumber;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 復号: 17桁でも18桁でも、2文字目以降をまるごとデコード
|
|
43
|
+
*/
|
|
44
|
+
decrypt(transactionNumber) {
|
|
45
|
+
const saltStr = transactionNumber[1]; // 2文字目からソルト(鍵のヒント)を回収
|
|
46
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
47
|
+
const encryptedBody = transactionNumber.slice(2);
|
|
48
|
+
const cipher = fpe2({
|
|
49
|
+
secret: `${this.fpeSecret}${saltStr}`
|
|
50
|
+
});
|
|
51
|
+
return cipher.decrypt(encryptedBody);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.TransactionNumberFactory = TransactionNumberFactory;
|
|
@@ -14,7 +14,7 @@ const cdigit = require("cdigit");
|
|
|
14
14
|
const moment = require("moment-timezone");
|
|
15
15
|
// import { RedisClientType } from 'redis';
|
|
16
16
|
// tslint:disable-next-line:no-require-imports no-var-requires
|
|
17
|
-
const fpe = require('node-fpe');
|
|
17
|
+
const fpe = require('node-fpe-v1');
|
|
18
18
|
// import { createSchema as createSettingSchema, modelName as settingModelName } from './mongoose/schemas/setting';
|
|
19
19
|
const transactionNumber_1 = require("./mongoose/schemas/transactionNumber");
|
|
20
20
|
const transactionNumberCounter_1 = require("./transactionNumberCounter");
|
|
@@ -8,7 +8,7 @@ const settings_1 = require("../../../settings");
|
|
|
8
8
|
const modelName = 'OfferCatalog';
|
|
9
9
|
exports.modelName = modelName;
|
|
10
10
|
const schemaDefinition = {
|
|
11
|
-
project: mongoose_1.SchemaTypes.Mixed,
|
|
11
|
+
project: { type: mongoose_1.SchemaTypes.Mixed, required: true },
|
|
12
12
|
_id: String,
|
|
13
13
|
identifier: {
|
|
14
14
|
type: String,
|
|
@@ -22,7 +22,7 @@ const schemaDefinition = {
|
|
|
22
22
|
required: true
|
|
23
23
|
},
|
|
24
24
|
itemListElement: [mongoose_1.SchemaTypes.Mixed],
|
|
25
|
-
itemOffered: mongoose_1.SchemaTypes.Mixed,
|
|
25
|
+
itemOffered: { type: mongoose_1.SchemaTypes.Mixed, required: true },
|
|
26
26
|
additionalProperty: [mongoose_1.SchemaTypes.Mixed],
|
|
27
27
|
dateSynced: Date // 追加(2023-09-13~)
|
|
28
28
|
// createdAt: SchemaTypes.Mixed,
|
|
@@ -60,12 +60,6 @@ const indexes = [
|
|
|
60
60
|
name: 'searchByIdentifier'
|
|
61
61
|
}
|
|
62
62
|
],
|
|
63
|
-
[
|
|
64
|
-
{ 'project.id': 1, identifier: 1 },
|
|
65
|
-
{
|
|
66
|
-
name: 'searchByProjectId-v20220721'
|
|
67
|
-
}
|
|
68
|
-
],
|
|
69
63
|
[
|
|
70
64
|
{ 'itemListElement.typeOf': 1, identifier: 1 },
|
|
71
65
|
{
|
|
@@ -8,7 +8,7 @@ const settings_1 = require("../../../settings");
|
|
|
8
8
|
const modelName = 'OfferCatalogItem';
|
|
9
9
|
exports.modelName = modelName;
|
|
10
10
|
const schemaDefinition = {
|
|
11
|
-
project: mongoose_1.SchemaTypes.Mixed,
|
|
11
|
+
project: { type: mongoose_1.SchemaTypes.Mixed, required: true },
|
|
12
12
|
identifier: {
|
|
13
13
|
type: String,
|
|
14
14
|
required: true
|
|
@@ -21,7 +21,7 @@ const schemaDefinition = {
|
|
|
21
21
|
required: true
|
|
22
22
|
},
|
|
23
23
|
itemListElement: [mongoose_1.SchemaTypes.Mixed],
|
|
24
|
-
itemOffered: mongoose_1.SchemaTypes.Mixed,
|
|
24
|
+
itemOffered: { type: mongoose_1.SchemaTypes.Mixed, required: true },
|
|
25
25
|
additionalProperty: [mongoose_1.SchemaTypes.Mixed],
|
|
26
26
|
dateSynced: Date,
|
|
27
27
|
relatedOffer: mongoose_1.SchemaTypes.Mixed
|
|
@@ -61,6 +61,32 @@ const indexes = [
|
|
|
61
61
|
unique: true,
|
|
62
62
|
name: 'uniqueIdentifier'
|
|
63
63
|
}
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
{ identifier: 1 },
|
|
67
|
+
{ name: 'identifier' }
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
{ 'itemOffered.typeOf': 1, identifier: 1 },
|
|
71
|
+
{ name: 'itemOfferedTypeOf' }
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
{ 'itemListElement.typeOf': 1, identifier: 1 },
|
|
75
|
+
{
|
|
76
|
+
name: 'itemListElementTypeOf',
|
|
77
|
+
partialFilterExpression: {
|
|
78
|
+
'itemListElement.typeOf': { $exists: true }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
{ 'itemListElement.id': 1, identifier: 1 },
|
|
84
|
+
{
|
|
85
|
+
name: 'itemListElementId',
|
|
86
|
+
partialFilterExpression: {
|
|
87
|
+
'itemListElement.id': { $exists: true }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
64
90
|
]
|
|
65
91
|
];
|
|
66
92
|
exports.indexes = indexes;
|
|
@@ -110,6 +110,26 @@ export interface IJWTSetting {
|
|
|
110
110
|
*/
|
|
111
111
|
algorithm: Algorithm;
|
|
112
112
|
}
|
|
113
|
+
export interface IOrderNumberSetting {
|
|
114
|
+
/**
|
|
115
|
+
* fpe暗号鍵
|
|
116
|
+
*/
|
|
117
|
+
fpeSecret: string;
|
|
118
|
+
/**
|
|
119
|
+
* "1"
|
|
120
|
+
*/
|
|
121
|
+
version: string;
|
|
122
|
+
}
|
|
123
|
+
export interface ITransactionNumberSetting {
|
|
124
|
+
/**
|
|
125
|
+
* fpe暗号鍵
|
|
126
|
+
*/
|
|
127
|
+
fpeSecret: string;
|
|
128
|
+
/**
|
|
129
|
+
* "1"
|
|
130
|
+
*/
|
|
131
|
+
version: string;
|
|
132
|
+
}
|
|
113
133
|
export interface ISetting {
|
|
114
134
|
defaultSenderEmail?: string;
|
|
115
135
|
jwt?: IJWTSetting;
|
|
@@ -121,12 +141,14 @@ export interface ISetting {
|
|
|
121
141
|
informResource?: factory.project.IInformParams[];
|
|
122
142
|
};
|
|
123
143
|
onTransactionStatusChanged?: IOnTransactionStatusChanged;
|
|
144
|
+
orderNumber?: IOrderNumberSetting;
|
|
124
145
|
project: {
|
|
125
146
|
id: string;
|
|
126
147
|
typeOf: factory.organizationType.Project;
|
|
127
148
|
};
|
|
128
149
|
quota?: IQuotaSettings;
|
|
129
150
|
storage?: IStorageSettings;
|
|
151
|
+
transactionNumber?: ITransactionNumberSetting;
|
|
130
152
|
/**
|
|
131
153
|
* 通知設定
|
|
132
154
|
*/
|
|
@@ -17,8 +17,10 @@ const schemaDefinition = {
|
|
|
17
17
|
onTaskStatusChanged: mongoose_1.SchemaTypes.Mixed,
|
|
18
18
|
onResourceUpdated: mongoose_1.SchemaTypes.Mixed,
|
|
19
19
|
onTransactionStatusChanged: mongoose_1.SchemaTypes.Mixed,
|
|
20
|
+
orderNumber: mongoose_1.SchemaTypes.Mixed,
|
|
20
21
|
quota: mongoose_1.SchemaTypes.Mixed,
|
|
21
22
|
storage: mongoose_1.SchemaTypes.Mixed,
|
|
23
|
+
transactionNumber: mongoose_1.SchemaTypes.Mixed,
|
|
22
24
|
triggerWebhook: mongoose_1.SchemaTypes.Mixed,
|
|
23
25
|
useInformResourceTypes: [String],
|
|
24
26
|
userPoolIdOld: String,
|
|
@@ -4,6 +4,7 @@ import type { Connection } from 'mongoose';
|
|
|
4
4
|
*/
|
|
5
5
|
export declare class OrderNumberRepo {
|
|
6
6
|
private readonly counterRepo;
|
|
7
|
+
private readonly settingModel;
|
|
7
8
|
constructor(params: {
|
|
8
9
|
connection: Connection;
|
|
9
10
|
});
|
|
@@ -19,4 +20,6 @@ export declare class OrderNumberRepo {
|
|
|
19
20
|
*/
|
|
20
21
|
orderDate: Date;
|
|
21
22
|
}): Promise<string>;
|
|
23
|
+
decrypt(id: string): Promise<string>;
|
|
24
|
+
private findSetting;
|
|
22
25
|
}
|
|
@@ -14,9 +14,12 @@ const cdigit = require("cdigit");
|
|
|
14
14
|
const moment = require("moment-timezone");
|
|
15
15
|
// import { RedisClientType } from 'redis';
|
|
16
16
|
// tslint:disable-next-line:no-require-imports no-var-requires
|
|
17
|
-
const fpe = require('node-fpe');
|
|
17
|
+
const fpe = require('node-fpe-v1');
|
|
18
18
|
// import { createSchema as createSettingSchema, modelName as settingModelName } from './mongoose/schemas/setting';
|
|
19
|
-
const
|
|
19
|
+
const factory = require("../factory");
|
|
20
|
+
const transactionNumber_1 = require("../factory/transactionNumber");
|
|
21
|
+
const setting_1 = require("./mongoose/schemas/setting");
|
|
22
|
+
const transactionNumber_2 = require("./mongoose/schemas/transactionNumber");
|
|
20
23
|
const transactionNumberCounter_1 = require("./transactionNumberCounter");
|
|
21
24
|
const ORDER_NUMBER_SEPARATOR = '-';
|
|
22
25
|
/**
|
|
@@ -27,6 +30,7 @@ class OrderNumberRepo {
|
|
|
27
30
|
// const { connection } = params;
|
|
28
31
|
// this.settingModel = connection.model(settingModelName, createSettingSchema());
|
|
29
32
|
this.counterRepo = new transactionNumberCounter_1.TransactionNumberCounterRepo(params);
|
|
33
|
+
this.settingModel = params.connection.model(setting_1.modelName, (0, setting_1.createSchema)());
|
|
30
34
|
}
|
|
31
35
|
// private static createKey(params: {
|
|
32
36
|
// orderDate: Date;
|
|
@@ -45,6 +49,7 @@ class OrderNumberRepo {
|
|
|
45
49
|
*/
|
|
46
50
|
publishByTimestamp(params) {
|
|
47
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
const { fpeSecret, version } = yield this.findSetting();
|
|
48
53
|
const timestamp = moment(params.orderDate)
|
|
49
54
|
.valueOf()
|
|
50
55
|
.toString();
|
|
@@ -59,25 +64,79 @@ class OrderNumberRepo {
|
|
|
59
64
|
.toDate();
|
|
60
65
|
incrReply = yield this.counterRepo.incrementByMongo({
|
|
61
66
|
identifier: dataFeedIdentifier,
|
|
62
|
-
includedInDataCatalog: { identifier:
|
|
67
|
+
includedInDataCatalog: { identifier: transactionNumber_2.DataCatalogIdentifier.orderNumber },
|
|
63
68
|
expires: dataFeedExpires
|
|
64
69
|
});
|
|
65
|
-
let orderNumber
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
let orderNumber;
|
|
71
|
+
if (typeof fpeSecret === 'string' && typeof version === 'string') {
|
|
72
|
+
const transactionFactory = new transactionNumber_1.TransactionNumberFactory({ fpeSecret, version });
|
|
73
|
+
orderNumber = transactionFactory.generate(timestamp, incrReply);
|
|
74
|
+
orderNumber = `${projectPrefix.at(0)}${orderNumber}`;
|
|
75
|
+
orderNumber = `${[
|
|
76
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
77
|
+
orderNumber.slice(0, 4),
|
|
78
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
79
|
+
orderNumber.slice(4, 11),
|
|
80
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
81
|
+
orderNumber.slice(11)
|
|
82
|
+
].join(ORDER_NUMBER_SEPARATOR)}`;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
orderNumber = `${timestamp}${incrReply}`;
|
|
86
|
+
// checkdigit
|
|
87
|
+
const cd = cdigit.luhn.compute(orderNumber);
|
|
88
|
+
orderNumber = fpe({ password: cd })
|
|
89
|
+
.encrypt(orderNumber);
|
|
90
|
+
orderNumber = `${projectPrefix}${cd}${orderNumber}`;
|
|
91
|
+
orderNumber = `${[
|
|
92
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
93
|
+
orderNumber.slice(0, 4),
|
|
94
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
95
|
+
orderNumber.slice(4, 11),
|
|
96
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
97
|
+
orderNumber.slice(11)
|
|
98
|
+
].join(ORDER_NUMBER_SEPARATOR)}`;
|
|
99
|
+
}
|
|
79
100
|
return orderNumber;
|
|
80
101
|
});
|
|
81
102
|
}
|
|
103
|
+
decrypt(id) {
|
|
104
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
105
|
+
const { fpeSecret, version } = yield this.findSetting();
|
|
106
|
+
if (typeof fpeSecret === 'string' && typeof version === 'string') {
|
|
107
|
+
const transactionFactory = new transactionNumber_1.TransactionNumberFactory({ fpeSecret, version });
|
|
108
|
+
const originalOrderNumber = id.replace(/-/g, '')
|
|
109
|
+
// tslint:disable-next-line:no-magic-numbers
|
|
110
|
+
.slice(1);
|
|
111
|
+
return transactionFactory.decrypt(originalOrderNumber);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
throw new factory.errors.NotImplemented('no version not implemented');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
findSetting() {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
const filterQuery = { 'project.id': { $eq: '*' } };
|
|
121
|
+
const projection = {
|
|
122
|
+
_id: 0,
|
|
123
|
+
orderNumber: 1
|
|
124
|
+
};
|
|
125
|
+
const setting = yield this.settingModel.findOne(filterQuery, projection)
|
|
126
|
+
.lean()
|
|
127
|
+
.exec();
|
|
128
|
+
if (setting === null || setting.orderNumber === undefined) {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
const { fpeSecret, version } = setting.orderNumber;
|
|
132
|
+
if (typeof fpeSecret !== 'string' || fpeSecret === '') {
|
|
133
|
+
throw new factory.errors.NotFound('setting.orderNumber.secret');
|
|
134
|
+
}
|
|
135
|
+
if (typeof version !== 'string' || version === '') {
|
|
136
|
+
throw new factory.errors.NotFound('setting.orderNumber.version');
|
|
137
|
+
}
|
|
138
|
+
return { fpeSecret, version };
|
|
139
|
+
});
|
|
140
|
+
}
|
|
82
141
|
}
|
|
83
142
|
exports.OrderNumberRepo = OrderNumberRepo;
|
|
@@ -14,7 +14,7 @@ const cdigit = require("cdigit");
|
|
|
14
14
|
const moment = require("moment-timezone");
|
|
15
15
|
// import { RedisClientType } from 'redis';
|
|
16
16
|
// tslint:disable-next-line:no-require-imports no-var-requires
|
|
17
|
-
const fpe = require('node-fpe');
|
|
17
|
+
const fpe = require('node-fpe-v1');
|
|
18
18
|
// import { createSchema as createSettingSchema, modelName as settingModelName } from './mongoose/schemas/setting';
|
|
19
19
|
const transactionNumber_1 = require("./mongoose/schemas/transactionNumber");
|
|
20
20
|
const transactionNumberCounter_1 = require("./transactionNumberCounter");
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { Connection } from 'mongoose';
|
|
2
2
|
interface IPublishResult {
|
|
3
3
|
transactionNumber: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
incrReply: number;
|
|
4
6
|
}
|
|
5
7
|
/**
|
|
6
8
|
* 取引番号リポジトリ
|
|
7
9
|
*/
|
|
8
10
|
export declare class TransactionNumberRepo {
|
|
9
11
|
private readonly counterRepo;
|
|
12
|
+
private readonly settingModel;
|
|
10
13
|
constructor(params: {
|
|
11
14
|
connection: Connection;
|
|
12
15
|
});
|
|
@@ -16,5 +19,7 @@ export declare class TransactionNumberRepo {
|
|
|
16
19
|
publishByTimestamp(params: {
|
|
17
20
|
startDate: Date;
|
|
18
21
|
}): Promise<IPublishResult>;
|
|
22
|
+
decrypt(id: string): Promise<string>;
|
|
23
|
+
private findSetting;
|
|
19
24
|
}
|
|
20
25
|
export {};
|
|
@@ -12,36 +12,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.TransactionNumberRepo = void 0;
|
|
13
13
|
const cdigit = require("cdigit");
|
|
14
14
|
const moment = require("moment-timezone");
|
|
15
|
-
// import { RedisClientType } from 'redis';
|
|
16
15
|
// tslint:disable-next-line:no-require-imports no-var-requires
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const transactionNumber_1 = require("
|
|
16
|
+
const fpe1 = require('node-fpe-v1');
|
|
17
|
+
const factory = require("../factory");
|
|
18
|
+
const transactionNumber_1 = require("../factory/transactionNumber");
|
|
19
|
+
const setting_1 = require("./mongoose/schemas/setting");
|
|
20
|
+
const transactionNumber_2 = require("./mongoose/schemas/transactionNumber");
|
|
20
21
|
const transactionNumberCounter_1 = require("./transactionNumberCounter");
|
|
21
22
|
/**
|
|
22
23
|
* 取引番号リポジトリ
|
|
23
24
|
*/
|
|
24
25
|
class TransactionNumberRepo {
|
|
25
26
|
constructor(params) {
|
|
26
|
-
// const { connection } = params;
|
|
27
|
-
// this.settingModel = connection.model(settingModelName, createSettingSchema());
|
|
28
27
|
this.counterRepo = new transactionNumberCounter_1.TransactionNumberCounterRepo(params);
|
|
28
|
+
this.settingModel = params.connection.model(setting_1.modelName, (0, setting_1.createSchema)());
|
|
29
29
|
}
|
|
30
|
-
// private static createKey(params: {
|
|
31
|
-
// startDate: Date;
|
|
32
|
-
// timestamp: string;
|
|
33
|
-
// }): string {
|
|
34
|
-
// return util.format(
|
|
35
|
-
// '%s:%s',
|
|
36
|
-
// TransactionNumberRepo.REDIS_KEY_PREFIX,
|
|
37
|
-
// params.timestamp
|
|
38
|
-
// );
|
|
39
|
-
// }
|
|
40
30
|
/**
|
|
41
31
|
* タイムスタンプから発行する
|
|
42
32
|
*/
|
|
43
33
|
publishByTimestamp(params) {
|
|
44
34
|
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
const { fpeSecret, version } = yield this.findSetting();
|
|
45
36
|
const timestamp = moment(params.startDate)
|
|
46
37
|
.valueOf()
|
|
47
38
|
.toString();
|
|
@@ -54,16 +45,72 @@ class TransactionNumberRepo {
|
|
|
54
45
|
.toDate();
|
|
55
46
|
incrReply = yield this.counterRepo.incrementByMongo({
|
|
56
47
|
identifier: dataFeedIdentifier,
|
|
57
|
-
includedInDataCatalog: { identifier:
|
|
48
|
+
includedInDataCatalog: { identifier: transactionNumber_2.DataCatalogIdentifier.transactionNumber },
|
|
58
49
|
expires: dataFeedExpires
|
|
59
50
|
});
|
|
60
|
-
let transactionNumber
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
.
|
|
65
|
-
|
|
66
|
-
|
|
51
|
+
let transactionNumber;
|
|
52
|
+
// let secret: string | undefined;
|
|
53
|
+
if (typeof fpeSecret === 'string' && typeof version === 'string') {
|
|
54
|
+
const transactionFactory = new transactionNumber_1.TransactionNumberFactory({ fpeSecret, version });
|
|
55
|
+
transactionNumber = transactionFactory.generate(timestamp, incrReply);
|
|
56
|
+
// // tslint:disable-next-line:no-magic-numbers
|
|
57
|
+
// const saltDigit = randomInt(0, 10);
|
|
58
|
+
// const saltStr = saltDigit.toString();
|
|
59
|
+
// secret = `${fpeSecret}${saltStr}`;
|
|
60
|
+
// const cipher = fpe2({ secret });
|
|
61
|
+
// // incrReplyが 0〜99 なら "00"〜"99" になり(15桁)
|
|
62
|
+
// // incrReplyが 100〜999 なら "100"〜"999" になる(16桁)
|
|
63
|
+
// const rawBody = `${timestamp}${incrReply.toString()
|
|
64
|
+
// // tslint:disable-next-line:no-magic-numbers
|
|
65
|
+
// .padStart(2, '0')}`;
|
|
66
|
+
// const encryptedBody = cipher.encrypt(rawBody);
|
|
67
|
+
// // ソルトを「あえて」2文字目に含める
|
|
68
|
+
// transactionNumber = `${version}${saltStr}${encryptedBody}`;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
transactionNumber = `${timestamp}${incrReply}`;
|
|
72
|
+
// checkdigit
|
|
73
|
+
const cd = cdigit.luhn.compute(transactionNumber);
|
|
74
|
+
transactionNumber = fpe1({ password: cd })
|
|
75
|
+
.encrypt(transactionNumber);
|
|
76
|
+
transactionNumber = `${cd}${transactionNumber}`;
|
|
77
|
+
}
|
|
78
|
+
return { transactionNumber, timestamp, incrReply };
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
decrypt(id) {
|
|
82
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
+
const { fpeSecret, version } = yield this.findSetting();
|
|
84
|
+
if (typeof fpeSecret === 'string' && typeof version === 'string') {
|
|
85
|
+
const transactionFactory = new transactionNumber_1.TransactionNumberFactory({ fpeSecret, version });
|
|
86
|
+
return transactionFactory.decrypt(id);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
throw new factory.errors.NotImplemented('no version not implemented');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
findSetting() {
|
|
94
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
const filterQuery = { 'project.id': { $eq: '*' } };
|
|
96
|
+
const projection = {
|
|
97
|
+
_id: 0,
|
|
98
|
+
transactionNumber: 1
|
|
99
|
+
};
|
|
100
|
+
const setting = yield this.settingModel.findOne(filterQuery, projection)
|
|
101
|
+
.lean()
|
|
102
|
+
.exec();
|
|
103
|
+
if (setting === null || setting.transactionNumber === undefined) {
|
|
104
|
+
return {};
|
|
105
|
+
}
|
|
106
|
+
const { fpeSecret, version } = setting.transactionNumber;
|
|
107
|
+
if (typeof fpeSecret !== 'string' || fpeSecret === '') {
|
|
108
|
+
throw new factory.errors.NotFound('setting.transactionNumber.secret');
|
|
109
|
+
}
|
|
110
|
+
if (typeof version !== 'string' || version === '') {
|
|
111
|
+
throw new factory.errors.NotFound('setting.transactionNumber.version');
|
|
112
|
+
}
|
|
113
|
+
return { fpeSecret, version };
|
|
67
114
|
});
|
|
68
115
|
}
|
|
69
116
|
}
|
package/package.json
CHANGED
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"lodash.difference": "^4.5.0",
|
|
26
26
|
"moment": "^2.29.1",
|
|
27
27
|
"moment-timezone": "^0.5.33",
|
|
28
|
-
"node-fpe": "1.0.0",
|
|
28
|
+
"node-fpe-v1": "npm:node-fpe@1.0.0",
|
|
29
|
+
"node-fpe-v2": "npm:node-fpe@2.0.4",
|
|
29
30
|
"pug": "^2.0.4",
|
|
30
31
|
"uniqid": "5.4.0",
|
|
31
32
|
"uuid": "^3.4.0"
|
|
@@ -116,5 +117,5 @@
|
|
|
116
117
|
"postversion": "git push origin --tags",
|
|
117
118
|
"prepublishOnly": "npm run clean && npm run build && npm test && npm run doc"
|
|
118
119
|
},
|
|
119
|
-
"version": "23.2.0-alpha.
|
|
120
|
+
"version": "23.2.0-alpha.43"
|
|
120
121
|
}
|