rate-core 0.0.5 → 0.5.1
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/lib/index.js +260 -65
- package/lib/rate.js +20 -27
- package/package.json +1 -1
- package/src/index.ts +209 -60
- package/src/rate.ts +71 -43
package/lib/index.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
extendStatics(d, b);
|
|
11
|
+
function __() { this.constructor = d; }
|
|
12
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
2
15
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
16
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
17
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -40,64 +53,26 @@ function __export(m) {
|
|
|
40
53
|
}
|
|
41
54
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
55
|
__export(require("./rate"));
|
|
43
|
-
var
|
|
44
|
-
function
|
|
56
|
+
var BaseRateService = (function () {
|
|
57
|
+
function BaseRateService(find, repository, infoRepository, commentRepository, reactionRepository, queryURL) {
|
|
45
58
|
this.find = find;
|
|
46
59
|
this.repository = repository;
|
|
47
60
|
this.infoRepository = infoRepository;
|
|
48
61
|
this.commentRepository = commentRepository;
|
|
49
|
-
this.
|
|
62
|
+
this.reactionRepository = reactionRepository;
|
|
50
63
|
this.queryURL = queryURL;
|
|
51
|
-
this.
|
|
64
|
+
this.search = this.search.bind(this);
|
|
65
|
+
this.load = this.load.bind(this);
|
|
66
|
+
this.getRate = this.getRate.bind(this);
|
|
67
|
+
this.setUseful = this.setUseful.bind(this);
|
|
68
|
+
this.removeUseful = this.removeUseful.bind(this);
|
|
52
69
|
this.comment = this.comment.bind(this);
|
|
53
70
|
this.removeComment = this.removeComment.bind(this);
|
|
54
71
|
this.updateComment = this.updateComment.bind(this);
|
|
55
|
-
this.search = this.search.bind(this);
|
|
56
|
-
this.getComment = this.getComment.bind(this);
|
|
57
72
|
this.getComments = this.getComments.bind(this);
|
|
73
|
+
this.getComment = this.getComment.bind(this);
|
|
58
74
|
}
|
|
59
|
-
|
|
60
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
61
|
-
var info, r0, exist, r1, sr, history, res;
|
|
62
|
-
return __generator(this, function (_a) {
|
|
63
|
-
switch (_a.label) {
|
|
64
|
-
case 0:
|
|
65
|
-
rate.time = new Date();
|
|
66
|
-
return [4, this.infoRepository.load(rate.id)];
|
|
67
|
-
case 1:
|
|
68
|
-
info = _a.sent();
|
|
69
|
-
if (!!info) return [3, 3];
|
|
70
|
-
return [4, this.repository.insert(rate, true)];
|
|
71
|
-
case 2:
|
|
72
|
-
r0 = _a.sent();
|
|
73
|
-
return [2, r0];
|
|
74
|
-
case 3: return [4, this.repository.getRate(rate.id, rate.author)];
|
|
75
|
-
case 4:
|
|
76
|
-
exist = _a.sent();
|
|
77
|
-
if (!!exist) return [3, 6];
|
|
78
|
-
return [4, this.repository.insert(rate)];
|
|
79
|
-
case 5:
|
|
80
|
-
r1 = _a.sent();
|
|
81
|
-
return [2, r1];
|
|
82
|
-
case 6:
|
|
83
|
-
sr = { review: exist.review, rate: exist.rate, time: exist.time };
|
|
84
|
-
if (exist.histories && exist.histories.length > 0) {
|
|
85
|
-
history = exist.histories;
|
|
86
|
-
history.push(sr);
|
|
87
|
-
rate.histories = history;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
rate.histories = [sr];
|
|
91
|
-
}
|
|
92
|
-
return [4, this.repository.update(rate, exist.rate)];
|
|
93
|
-
case 7:
|
|
94
|
-
res = _a.sent();
|
|
95
|
-
return [2, res];
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
RateService.prototype.search = function (s, limit, offset, fields) {
|
|
75
|
+
BaseRateService.prototype.search = function (s, limit, offset, fields) {
|
|
101
76
|
var _this = this;
|
|
102
77
|
return this.find(s, limit, offset, fields).then(function (res) {
|
|
103
78
|
if (!_this.queryURL) {
|
|
@@ -127,18 +102,21 @@ var RateService = (function () {
|
|
|
127
102
|
}
|
|
128
103
|
});
|
|
129
104
|
};
|
|
130
|
-
|
|
131
|
-
return this.repository.
|
|
105
|
+
BaseRateService.prototype.load = function (id, author) {
|
|
106
|
+
return this.repository.load(id, author);
|
|
132
107
|
};
|
|
133
|
-
|
|
134
|
-
return this.
|
|
108
|
+
BaseRateService.prototype.getRate = function (id, author) {
|
|
109
|
+
return this.repository.load(id, author);
|
|
135
110
|
};
|
|
136
|
-
|
|
137
|
-
return this.
|
|
111
|
+
BaseRateService.prototype.setUseful = function (id, author, userId) {
|
|
112
|
+
return this.reactionRepository.save(id, author, userId, 1);
|
|
138
113
|
};
|
|
139
|
-
|
|
114
|
+
BaseRateService.prototype.removeUseful = function (id, author, userId) {
|
|
115
|
+
return this.reactionRepository.remove(id, author, userId);
|
|
116
|
+
};
|
|
117
|
+
BaseRateService.prototype.comment = function (comment) {
|
|
140
118
|
var _this = this;
|
|
141
|
-
return this.repository.
|
|
119
|
+
return this.repository.load(comment.id, comment.author).then(function (checkRate) {
|
|
142
120
|
if (!checkRate) {
|
|
143
121
|
return -1;
|
|
144
122
|
}
|
|
@@ -148,7 +126,7 @@ var RateService = (function () {
|
|
|
148
126
|
}
|
|
149
127
|
});
|
|
150
128
|
};
|
|
151
|
-
|
|
129
|
+
BaseRateService.prototype.removeComment = function (commentId, userId) {
|
|
152
130
|
var _this = this;
|
|
153
131
|
return this.commentRepository.load(commentId).then(function (comment) {
|
|
154
132
|
if (comment) {
|
|
@@ -164,7 +142,7 @@ var RateService = (function () {
|
|
|
164
142
|
}
|
|
165
143
|
});
|
|
166
144
|
};
|
|
167
|
-
|
|
145
|
+
BaseRateService.prototype.updateComment = function (comment) {
|
|
168
146
|
var _this = this;
|
|
169
147
|
return this.commentRepository.load(comment.commentId).then(function (exist) {
|
|
170
148
|
if (!exist) {
|
|
@@ -188,15 +166,164 @@ var RateService = (function () {
|
|
|
188
166
|
}
|
|
189
167
|
});
|
|
190
168
|
};
|
|
191
|
-
|
|
192
|
-
|
|
169
|
+
BaseRateService.prototype.getComments = function (id, author, limit) {
|
|
170
|
+
var _this = this;
|
|
171
|
+
return this.commentRepository.getComments(id, author, limit).then(function (comments) {
|
|
172
|
+
if (_this.queryURL) {
|
|
173
|
+
var ids = [];
|
|
174
|
+
for (var _i = 0, comments_1 = comments; _i < comments_1.length; _i++) {
|
|
175
|
+
var comment = comments_1[_i];
|
|
176
|
+
ids.push(comment.userId);
|
|
177
|
+
}
|
|
178
|
+
return _this.queryURL(ids).then(function (urls) {
|
|
179
|
+
for (var _i = 0, comments_2 = comments; _i < comments_2.length; _i++) {
|
|
180
|
+
var comment = comments_2[_i];
|
|
181
|
+
var i = binarySearch(urls, comment.userId);
|
|
182
|
+
if (i >= 0) {
|
|
183
|
+
comment.userURL = urls[i].url;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return comments;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
return comments;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
193
|
};
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
BaseRateService.prototype.getComment = function (id) {
|
|
195
|
+
var _this = this;
|
|
196
|
+
return this.commentRepository.load(id).then(function (comment) {
|
|
197
|
+
if (comment && _this.queryURL) {
|
|
198
|
+
return _this.queryURL([id]).then(function (urls) {
|
|
199
|
+
var i = binarySearch(urls, comment.userId);
|
|
200
|
+
if (i >= 0) {
|
|
201
|
+
comment.userURL = urls[i].url;
|
|
202
|
+
}
|
|
203
|
+
return comment;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
return comment;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
196
210
|
};
|
|
197
|
-
return
|
|
211
|
+
return BaseRateService;
|
|
198
212
|
}());
|
|
213
|
+
exports.BaseRateService = BaseRateService;
|
|
214
|
+
var RateService = (function (_super) {
|
|
215
|
+
__extends(RateService, _super);
|
|
216
|
+
function RateService(find, repository, infoRepository, commentRepository, reactionRepository, queryURL) {
|
|
217
|
+
var _this = _super.call(this, find, repository, infoRepository, commentRepository, reactionRepository, queryURL) || this;
|
|
218
|
+
_this.rate = _this.rate.bind(_this);
|
|
219
|
+
return _this;
|
|
220
|
+
}
|
|
221
|
+
RateService.prototype.rate = function (rate) {
|
|
222
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
223
|
+
var info, r0, exist, r1, sr, history, res;
|
|
224
|
+
return __generator(this, function (_a) {
|
|
225
|
+
switch (_a.label) {
|
|
226
|
+
case 0:
|
|
227
|
+
rate.time = new Date();
|
|
228
|
+
return [4, this.infoRepository.load(rate.id)];
|
|
229
|
+
case 1:
|
|
230
|
+
info = _a.sent();
|
|
231
|
+
if (!!info) return [3, 3];
|
|
232
|
+
return [4, this.repository.insert(rate, true)];
|
|
233
|
+
case 2:
|
|
234
|
+
r0 = _a.sent();
|
|
235
|
+
return [2, r0];
|
|
236
|
+
case 3: return [4, this.repository.load(rate.id, rate.author)];
|
|
237
|
+
case 4:
|
|
238
|
+
exist = _a.sent();
|
|
239
|
+
if (!!exist) return [3, 6];
|
|
240
|
+
return [4, this.repository.insert(rate)];
|
|
241
|
+
case 5:
|
|
242
|
+
r1 = _a.sent();
|
|
243
|
+
return [2, r1];
|
|
244
|
+
case 6:
|
|
245
|
+
sr = { review: exist.review, rate: exist.rate, time: exist.time };
|
|
246
|
+
if (exist.histories && exist.histories.length > 0) {
|
|
247
|
+
history = exist.histories;
|
|
248
|
+
history.push(sr);
|
|
249
|
+
rate.histories = history;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
rate.histories = [sr];
|
|
253
|
+
}
|
|
254
|
+
return [4, this.repository.update(rate, exist.rate)];
|
|
255
|
+
case 7:
|
|
256
|
+
res = _a.sent();
|
|
257
|
+
return [2, res];
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
return RateService;
|
|
263
|
+
}(BaseRateService));
|
|
199
264
|
exports.RateService = RateService;
|
|
265
|
+
function avg(n) {
|
|
266
|
+
var sum = 0;
|
|
267
|
+
for (var _i = 0, n_1 = n; _i < n_1.length; _i++) {
|
|
268
|
+
var s = n_1[_i];
|
|
269
|
+
sum = sum + s;
|
|
270
|
+
}
|
|
271
|
+
return sum / n.length;
|
|
272
|
+
}
|
|
273
|
+
exports.avg = avg;
|
|
274
|
+
var RatesService = (function (_super) {
|
|
275
|
+
__extends(RatesService, _super);
|
|
276
|
+
function RatesService(find, repository, infoRepository, commentRepository, reactionRepository, queryURL) {
|
|
277
|
+
var _this = _super.call(this, find, repository, infoRepository, commentRepository, reactionRepository, queryURL) || this;
|
|
278
|
+
_this.rate = _this.rate.bind(_this);
|
|
279
|
+
return _this;
|
|
280
|
+
}
|
|
281
|
+
RatesService.prototype.rate = function (rate) {
|
|
282
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
283
|
+
var info, r0, exist, r1, sr, history, res;
|
|
284
|
+
return __generator(this, function (_a) {
|
|
285
|
+
switch (_a.label) {
|
|
286
|
+
case 0: return [4, this.infoRepository.load(rate.id)];
|
|
287
|
+
case 1:
|
|
288
|
+
info = _a.sent();
|
|
289
|
+
if (rate.rates && rate.rates.length > 0) {
|
|
290
|
+
rate.rate = avg(rate.rates);
|
|
291
|
+
}
|
|
292
|
+
rate.time = new Date();
|
|
293
|
+
if (!!info) return [3, 3];
|
|
294
|
+
return [4, this.repository.insert(rate, true)];
|
|
295
|
+
case 2:
|
|
296
|
+
r0 = _a.sent();
|
|
297
|
+
return [2, r0];
|
|
298
|
+
case 3: return [4, this.repository.load(rate.id, rate.author)];
|
|
299
|
+
case 4:
|
|
300
|
+
exist = _a.sent();
|
|
301
|
+
if (!!exist) return [3, 6];
|
|
302
|
+
return [4, this.repository.insert(rate)];
|
|
303
|
+
case 5:
|
|
304
|
+
r1 = _a.sent();
|
|
305
|
+
return [2, r1];
|
|
306
|
+
case 6:
|
|
307
|
+
sr = { review: exist.review, rates: exist.rates, time: exist.time };
|
|
308
|
+
if (exist.histories && exist.histories.length > 0) {
|
|
309
|
+
history = exist.histories;
|
|
310
|
+
history.push(sr);
|
|
311
|
+
rate.histories = history;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
rate.histories = [sr];
|
|
315
|
+
}
|
|
316
|
+
return [4, this.repository.update(rate, exist.rate)];
|
|
317
|
+
case 7:
|
|
318
|
+
res = _a.sent();
|
|
319
|
+
return [2, res];
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
return RatesService;
|
|
325
|
+
}(BaseRateService));
|
|
326
|
+
exports.RatesService = RatesService;
|
|
200
327
|
var CommentQuery = (function () {
|
|
201
328
|
function CommentQuery(find, repository, queryURL) {
|
|
202
329
|
this.find = find;
|
|
@@ -207,10 +334,46 @@ var CommentQuery = (function () {
|
|
|
207
334
|
this.getComments = this.getComments.bind(this);
|
|
208
335
|
}
|
|
209
336
|
CommentQuery.prototype.load = function (id, ctx) {
|
|
210
|
-
|
|
337
|
+
var _this = this;
|
|
338
|
+
return this.repository.load(id, ctx).then(function (comment) {
|
|
339
|
+
if (comment && _this.queryURL) {
|
|
340
|
+
return _this.queryURL([id]).then(function (urls) {
|
|
341
|
+
var i = binarySearch(urls, comment.userId);
|
|
342
|
+
if (i >= 0) {
|
|
343
|
+
comment.userURL = urls[i].url;
|
|
344
|
+
}
|
|
345
|
+
return comment;
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
return comment;
|
|
350
|
+
}
|
|
351
|
+
});
|
|
211
352
|
};
|
|
212
353
|
CommentQuery.prototype.getComments = function (id, author, limit) {
|
|
213
|
-
|
|
354
|
+
var _this = this;
|
|
355
|
+
return this.repository.getComments(id, author, limit).then(function (comments) {
|
|
356
|
+
if (_this.queryURL) {
|
|
357
|
+
var ids = [];
|
|
358
|
+
for (var _i = 0, comments_3 = comments; _i < comments_3.length; _i++) {
|
|
359
|
+
var comment = comments_3[_i];
|
|
360
|
+
ids.push(comment.userId);
|
|
361
|
+
}
|
|
362
|
+
return _this.queryURL(ids).then(function (urls) {
|
|
363
|
+
for (var _i = 0, comments_4 = comments; _i < comments_4.length; _i++) {
|
|
364
|
+
var comment = comments_4[_i];
|
|
365
|
+
var i = binarySearch(urls, comment.userId);
|
|
366
|
+
if (i >= 0) {
|
|
367
|
+
comment.userURL = urls[i].url;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return comments;
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
return comments;
|
|
375
|
+
}
|
|
376
|
+
});
|
|
214
377
|
};
|
|
215
378
|
CommentQuery.prototype.search = function (s, limit, offset, fields) {
|
|
216
379
|
var _this = this;
|
|
@@ -305,6 +468,38 @@ var RateValidator = (function () {
|
|
|
305
468
|
return RateValidator;
|
|
306
469
|
}());
|
|
307
470
|
exports.RateValidator = RateValidator;
|
|
471
|
+
var RatesValidator = (function () {
|
|
472
|
+
function RatesValidator(attributes, check, max, length) {
|
|
473
|
+
this.attributes = attributes;
|
|
474
|
+
this.check = check;
|
|
475
|
+
this.max = max;
|
|
476
|
+
this.length = length;
|
|
477
|
+
this.validate = this.validate.bind(this);
|
|
478
|
+
}
|
|
479
|
+
RatesValidator.prototype.validate = function (rate) {
|
|
480
|
+
var errs = this.check(rate, this.attributes);
|
|
481
|
+
if (!rate.rates || rate.rates.length === 0) {
|
|
482
|
+
var err = createError('rates', 'required');
|
|
483
|
+
errs.push(err);
|
|
484
|
+
return Promise.resolve(errs);
|
|
485
|
+
}
|
|
486
|
+
if (rate.rates.length !== this.length) {
|
|
487
|
+
var err = createError('rates', 'length', this.length);
|
|
488
|
+
errs.push(err);
|
|
489
|
+
return Promise.resolve(errs);
|
|
490
|
+
}
|
|
491
|
+
for (var _i = 0, _a = rate.rates; _i < _a.length; _i++) {
|
|
492
|
+
var r = _a[_i];
|
|
493
|
+
if (r > this.max) {
|
|
494
|
+
var err = createError('rates', 'max', this.max);
|
|
495
|
+
errs.push(err);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return Promise.resolve(errs);
|
|
499
|
+
};
|
|
500
|
+
return RatesValidator;
|
|
501
|
+
}());
|
|
502
|
+
exports.RatesValidator = RatesValidator;
|
|
308
503
|
function createError(field, code, param) {
|
|
309
504
|
if (!code) {
|
|
310
505
|
code = 'string';
|
package/lib/rate.js
CHANGED
|
@@ -116,45 +116,38 @@ exports.info10Model = {
|
|
|
116
116
|
type: 'number',
|
|
117
117
|
}
|
|
118
118
|
};
|
|
119
|
-
exports.
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
exports.rateInfoModel = {
|
|
120
|
+
id: {
|
|
121
|
+
key: true
|
|
122
122
|
},
|
|
123
|
-
|
|
124
|
-
type: '
|
|
123
|
+
rate: {
|
|
124
|
+
type: 'number',
|
|
125
|
+
},
|
|
126
|
+
count: {
|
|
127
|
+
type: 'integer',
|
|
128
|
+
},
|
|
129
|
+
score: {
|
|
130
|
+
type: 'number',
|
|
125
131
|
}
|
|
126
132
|
};
|
|
127
|
-
exports.
|
|
128
|
-
commentId: {
|
|
129
|
-
key: true
|
|
130
|
-
},
|
|
133
|
+
exports.ratesModel = {
|
|
131
134
|
id: {
|
|
132
|
-
|
|
133
|
-
noupdate: true,
|
|
135
|
+
key: true,
|
|
134
136
|
match: 'equal'
|
|
135
137
|
},
|
|
136
138
|
author: {
|
|
137
|
-
|
|
138
|
-
noupdate: true,
|
|
139
|
+
key: true,
|
|
139
140
|
match: 'equal'
|
|
140
141
|
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
noupdate: true,
|
|
144
|
-
match: 'equal'
|
|
142
|
+
rate: {
|
|
143
|
+
type: 'number'
|
|
145
144
|
},
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
rates: {
|
|
146
|
+
required: true,
|
|
147
|
+
type: 'integers'
|
|
148
148
|
},
|
|
149
149
|
time: {
|
|
150
|
-
type: 'datetime',
|
|
151
|
-
noupdate: true,
|
|
152
|
-
},
|
|
153
|
-
updatedAt: {
|
|
154
150
|
type: 'datetime'
|
|
155
151
|
},
|
|
156
|
-
|
|
157
|
-
type: 'array',
|
|
158
|
-
typeof: exports.commentModel
|
|
159
|
-
}
|
|
152
|
+
review: {},
|
|
160
153
|
};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Attributes, Search, SearchResult } from './core';
|
|
2
2
|
import {
|
|
3
|
-
Comment, CommentFilter, InfoRepository, Rate, RateCommentQuery, RateCommentRepository, RateFilter, Rater,
|
|
4
|
-
|
|
3
|
+
BaseRate, Comment, CommentFilter, InfoRepository, Rate, RateCommentQuery, RateCommentRepository, RateFilter, Rater, RateReactionRepository, RateRepository,
|
|
4
|
+
Rates, RatesFilter, ShortComment, ShortRate, ShortRates
|
|
5
5
|
} from './rate';
|
|
6
6
|
|
|
7
7
|
export * from './rate';
|
|
@@ -10,45 +10,25 @@ export interface URL {
|
|
|
10
10
|
id: string;
|
|
11
11
|
url: string;
|
|
12
12
|
}
|
|
13
|
-
export class
|
|
14
|
-
constructor(protected find: Search<
|
|
15
|
-
public repository: RateRepository
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
this.
|
|
13
|
+
export class BaseRateService<R extends BaseRate, F, O> {
|
|
14
|
+
constructor(protected find: Search<R, F>,
|
|
15
|
+
public repository: RateRepository<R>,
|
|
16
|
+
protected infoRepository: InfoRepository<O>,
|
|
17
|
+
protected commentRepository: RateCommentRepository,
|
|
18
|
+
protected reactionRepository: RateReactionRepository,
|
|
19
|
+
protected queryURL?: (ids: string[]) => Promise<URL[]>) {
|
|
20
|
+
this.search = this.search.bind(this);
|
|
21
|
+
this.load = this.load.bind(this);
|
|
22
|
+
this.getRate = this.getRate.bind(this);
|
|
23
|
+
this.setUseful = this.setUseful.bind(this);
|
|
24
|
+
this.removeUseful = this.removeUseful.bind(this);
|
|
21
25
|
this.comment = this.comment.bind(this);
|
|
22
26
|
this.removeComment = this.removeComment.bind(this);
|
|
23
27
|
this.updateComment = this.updateComment.bind(this);
|
|
24
|
-
this.search = this.search.bind(this);
|
|
25
|
-
this.getComment = this.getComment.bind(this);
|
|
26
28
|
this.getComments = this.getComments.bind(this);
|
|
29
|
+
this.getComment = this.getComment.bind(this);
|
|
27
30
|
}
|
|
28
|
-
|
|
29
|
-
rate.time = new Date();
|
|
30
|
-
const info = await this.infoRepository.load(rate.id);
|
|
31
|
-
if (!info) {
|
|
32
|
-
const r0 = await this.repository.insert(rate, true);
|
|
33
|
-
return r0;
|
|
34
|
-
}
|
|
35
|
-
const exist = await this.repository.getRate(rate.id, rate.author);
|
|
36
|
-
if (!exist) {
|
|
37
|
-
const r1 = await this.repository.insert(rate);
|
|
38
|
-
return r1;
|
|
39
|
-
}
|
|
40
|
-
const sr: ShortRate = { review: exist.review, rate: exist.rate, time: exist.time };
|
|
41
|
-
if (exist.histories && exist.histories.length > 0) {
|
|
42
|
-
const history = exist.histories;
|
|
43
|
-
history.push(sr);
|
|
44
|
-
rate.histories = history;
|
|
45
|
-
} else {
|
|
46
|
-
rate.histories = [sr];
|
|
47
|
-
}
|
|
48
|
-
const res = await this.repository.update(rate, exist.rate);
|
|
49
|
-
return res;
|
|
50
|
-
}
|
|
51
|
-
search(s: RateFilter, limit?: number, offset?: number | string, fields?: string[]): Promise<SearchResult<Rate>> {
|
|
31
|
+
search(s: F, limit?: number, offset?: number | string, fields?: string[]): Promise<SearchResult<R>> {
|
|
52
32
|
return this.find(s, limit, offset, fields).then(res => {
|
|
53
33
|
if (!this.queryURL) {
|
|
54
34
|
return res;
|
|
@@ -73,17 +53,20 @@ export class RateService<O> implements Rater {
|
|
|
73
53
|
}
|
|
74
54
|
});
|
|
75
55
|
}
|
|
76
|
-
|
|
77
|
-
return this.repository.
|
|
56
|
+
load(id: string, author: string): Promise<R | null> {
|
|
57
|
+
return this.repository.load(id, author);
|
|
58
|
+
}
|
|
59
|
+
getRate(id: string, author: string): Promise<R | null> {
|
|
60
|
+
return this.repository.load(id, author);
|
|
78
61
|
}
|
|
79
62
|
setUseful(id: string, author: string, userId: string): Promise<number> {
|
|
80
|
-
return this.
|
|
63
|
+
return this.reactionRepository.save(id, author, userId, 1);
|
|
81
64
|
}
|
|
82
65
|
removeUseful(id: string, author: string, userId: string): Promise<number> {
|
|
83
|
-
return this.
|
|
66
|
+
return this.reactionRepository.remove(id, author, userId);
|
|
84
67
|
}
|
|
85
68
|
comment(comment: Comment): Promise<number> {
|
|
86
|
-
return this.repository.
|
|
69
|
+
return this.repository.load(comment.id, comment.author).then(checkRate => {
|
|
87
70
|
if (!checkRate) {
|
|
88
71
|
return -1;
|
|
89
72
|
} else {
|
|
@@ -121,34 +104,174 @@ export class RateService<O> implements Rater {
|
|
|
121
104
|
exist.histories = [c];
|
|
122
105
|
}
|
|
123
106
|
exist.comment = comment.comment;
|
|
124
|
-
const res =
|
|
107
|
+
const res = this.commentRepository.update(exist);
|
|
125
108
|
return res;
|
|
126
109
|
}
|
|
127
110
|
});
|
|
128
111
|
}
|
|
129
112
|
getComments(id: string, author: string, limit?: number): Promise<Comment[]> {
|
|
130
|
-
return this.commentRepository.getComments(id, author, limit)
|
|
113
|
+
return this.commentRepository.getComments(id, author, limit).then(comments => {
|
|
114
|
+
if (this.queryURL) {
|
|
115
|
+
const ids: string[] = [];
|
|
116
|
+
for (const comment of comments) {
|
|
117
|
+
ids.push(comment.userId);
|
|
118
|
+
}
|
|
119
|
+
return this.queryURL(ids).then(urls => {
|
|
120
|
+
for (const comment of comments) {
|
|
121
|
+
const i = binarySearch(urls, comment.userId);
|
|
122
|
+
if (i >= 0) {
|
|
123
|
+
comment.userURL = urls[i].url;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return comments;
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
return comments;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
131
132
|
}
|
|
132
|
-
getComment(id: string): Promise<Comment|null> {
|
|
133
|
-
return this.commentRepository.load(id)
|
|
133
|
+
getComment(id: string): Promise<Comment | null> {
|
|
134
|
+
return this.commentRepository.load(id).then(comment => {
|
|
135
|
+
if (comment && this.queryURL) {
|
|
136
|
+
return this.queryURL([id]).then(urls => {
|
|
137
|
+
const i = binarySearch(urls, comment.userId);
|
|
138
|
+
if (i >= 0) {
|
|
139
|
+
comment.userURL = urls[i].url;
|
|
140
|
+
}
|
|
141
|
+
return comment;
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
return comment;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
134
147
|
}
|
|
135
148
|
}
|
|
136
149
|
export interface CommentRepository {
|
|
137
|
-
load(commentId: string, ctx?: any): Promise<Comment|null>;
|
|
150
|
+
load(commentId: string, ctx?: any): Promise<Comment | null>;
|
|
138
151
|
getComments(id: string, author: string, limit?: number): Promise<Comment[]>;
|
|
139
152
|
}
|
|
140
153
|
// tslint:disable-next-line:max-classes-per-file
|
|
154
|
+
export class RateService<O> extends BaseRateService<Rate, RateFilter, O> implements Rater<Rate, RateFilter> {
|
|
155
|
+
constructor(find: Search<Rate, RateFilter>,
|
|
156
|
+
repository: RateRepository<Rate>,
|
|
157
|
+
infoRepository: InfoRepository<O>,
|
|
158
|
+
commentRepository: RateCommentRepository,
|
|
159
|
+
reactionRepository: RateReactionRepository,
|
|
160
|
+
queryURL?: (ids: string[]) => Promise<URL[]>) {
|
|
161
|
+
super(find, repository, infoRepository, commentRepository, reactionRepository, queryURL);
|
|
162
|
+
this.rate = this.rate.bind(this);
|
|
163
|
+
}
|
|
164
|
+
async rate(rate: Rate): Promise<number> {
|
|
165
|
+
rate.time = new Date();
|
|
166
|
+
const info = await this.infoRepository.load(rate.id);
|
|
167
|
+
if (!info) {
|
|
168
|
+
const r0 = await this.repository.insert(rate, true);
|
|
169
|
+
return r0;
|
|
170
|
+
}
|
|
171
|
+
const exist = await this.repository.load(rate.id, rate.author);
|
|
172
|
+
if (!exist) {
|
|
173
|
+
const r1 = await this.repository.insert(rate);
|
|
174
|
+
return r1;
|
|
175
|
+
}
|
|
176
|
+
const sr: ShortRate = { review: exist.review, rate: exist.rate, time: exist.time };
|
|
177
|
+
if (exist.histories && exist.histories.length > 0) {
|
|
178
|
+
const history = exist.histories;
|
|
179
|
+
history.push(sr);
|
|
180
|
+
rate.histories = history;
|
|
181
|
+
} else {
|
|
182
|
+
rate.histories = [sr];
|
|
183
|
+
}
|
|
184
|
+
const res = await this.repository.update(rate, exist.rate);
|
|
185
|
+
return res;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export function avg(n: number[]): number {
|
|
189
|
+
let sum = 0;
|
|
190
|
+
for (const s of n) {
|
|
191
|
+
sum = sum + s;
|
|
192
|
+
}
|
|
193
|
+
return sum / n.length;
|
|
194
|
+
}
|
|
195
|
+
// tslint:disable-next-line:max-classes-per-file
|
|
196
|
+
export class RatesService<O> extends BaseRateService<Rates, RatesFilter, O> implements Rater<Rates, RatesFilter> {
|
|
197
|
+
constructor(find: Search<Rates, RatesFilter>,
|
|
198
|
+
repository: RateRepository<Rates>,
|
|
199
|
+
infoRepository: InfoRepository<O>,
|
|
200
|
+
commentRepository: RateCommentRepository,
|
|
201
|
+
reactionRepository: RateReactionRepository,
|
|
202
|
+
queryURL?: (ids: string[]) => Promise<URL[]>) {
|
|
203
|
+
super(find, repository, infoRepository, commentRepository, reactionRepository, queryURL);
|
|
204
|
+
this.rate = this.rate.bind(this);
|
|
205
|
+
}
|
|
206
|
+
async rate(rate: Rates): Promise<number> {
|
|
207
|
+
const info = await this.infoRepository.load(rate.id);
|
|
208
|
+
if (rate.rates && rate.rates.length > 0) {
|
|
209
|
+
rate.rate = avg(rate.rates);
|
|
210
|
+
}
|
|
211
|
+
rate.time = new Date();
|
|
212
|
+
if (!info) {
|
|
213
|
+
const r0 = await this.repository.insert(rate, true);
|
|
214
|
+
return r0;
|
|
215
|
+
}
|
|
216
|
+
const exist = await this.repository.load(rate.id, rate.author);
|
|
217
|
+
if (!exist) {
|
|
218
|
+
const r1 = await this.repository.insert(rate);
|
|
219
|
+
return r1;
|
|
220
|
+
}
|
|
221
|
+
const sr: ShortRates = { review: exist.review, rates: exist.rates, time: exist.time };
|
|
222
|
+
if (exist.histories && exist.histories.length > 0) {
|
|
223
|
+
const history = exist.histories;
|
|
224
|
+
history.push(sr);
|
|
225
|
+
rate.histories = history;
|
|
226
|
+
} else {
|
|
227
|
+
rate.histories = [sr];
|
|
228
|
+
}
|
|
229
|
+
const res = await this.repository.update(rate, exist.rate);
|
|
230
|
+
return res;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// tslint:disable-next-line:max-classes-per-file
|
|
141
234
|
export class CommentQuery implements RateCommentQuery {
|
|
142
235
|
constructor(protected find: Search<Comment, CommentFilter>, protected repository: CommentRepository, private queryURL?: (ids: string[]) => Promise<URL[]>) {
|
|
143
236
|
this.load = this.load.bind(this);
|
|
144
237
|
this.search = this.search.bind(this);
|
|
145
238
|
this.getComments = this.getComments.bind(this);
|
|
146
239
|
}
|
|
147
|
-
load(id: string, ctx?: any): Promise<Comment|null> {
|
|
148
|
-
return this.repository.load(id, ctx)
|
|
240
|
+
load(id: string, ctx?: any): Promise<Comment | null> {
|
|
241
|
+
return this.repository.load(id, ctx).then(comment => {
|
|
242
|
+
if (comment && this.queryURL) {
|
|
243
|
+
return this.queryURL([id]).then(urls => {
|
|
244
|
+
const i = binarySearch(urls, comment.userId);
|
|
245
|
+
if (i >= 0) {
|
|
246
|
+
comment.userURL = urls[i].url;
|
|
247
|
+
}
|
|
248
|
+
return comment;
|
|
249
|
+
});
|
|
250
|
+
} else {
|
|
251
|
+
return comment;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
149
254
|
}
|
|
150
255
|
getComments(id: string, author: string, limit?: number): Promise<Comment[]> {
|
|
151
|
-
return this.repository.getComments(id, author, limit)
|
|
256
|
+
return this.repository.getComments(id, author, limit).then(comments => {
|
|
257
|
+
if (this.queryURL) {
|
|
258
|
+
const ids: string[] = [];
|
|
259
|
+
for (const comment of comments) {
|
|
260
|
+
ids.push(comment.userId);
|
|
261
|
+
}
|
|
262
|
+
return this.queryURL(ids).then(urls => {
|
|
263
|
+
for (const comment of comments) {
|
|
264
|
+
const i = binarySearch(urls, comment.userId);
|
|
265
|
+
if (i >= 0) {
|
|
266
|
+
comment.userURL = urls[i].url;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return comments;
|
|
270
|
+
});
|
|
271
|
+
} else {
|
|
272
|
+
return comments;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
152
275
|
}
|
|
153
276
|
search(s: CommentFilter, limit?: number, offset?: number | string, fields?: string[]): Promise<SearchResult<Comment>> {
|
|
154
277
|
return this.find(s, limit, offset, fields).then(res => {
|
|
@@ -180,16 +303,16 @@ function binarySearch(ar: URL[], el: string): number {
|
|
|
180
303
|
let m = 0;
|
|
181
304
|
let n = ar.length - 1;
|
|
182
305
|
while (m <= n) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
306
|
+
// tslint:disable-next-line:no-bitwise
|
|
307
|
+
const k = (n + m) >> 1;
|
|
308
|
+
const cmp = compare(el, ar[k].id);
|
|
309
|
+
if (cmp > 0) {
|
|
310
|
+
m = k + 1;
|
|
311
|
+
} else if (cmp < 0) {
|
|
312
|
+
n = k - 1;
|
|
313
|
+
} else {
|
|
314
|
+
return k;
|
|
315
|
+
}
|
|
193
316
|
}
|
|
194
317
|
return -m - 1;
|
|
195
318
|
}
|
|
@@ -199,7 +322,7 @@ function compare(s1: string, s2: string): number {
|
|
|
199
322
|
interface ErrorMessage {
|
|
200
323
|
field: string;
|
|
201
324
|
code: string;
|
|
202
|
-
param?: string|number|Date;
|
|
325
|
+
param?: string | number | Date;
|
|
203
326
|
message?: string;
|
|
204
327
|
}
|
|
205
328
|
// tslint:disable-next-line:max-classes-per-file
|
|
@@ -232,6 +355,32 @@ export class RateValidator {
|
|
|
232
355
|
}
|
|
233
356
|
}
|
|
234
357
|
}
|
|
358
|
+
// tslint:disable-next-line:max-classes-per-file
|
|
359
|
+
export class RatesValidator {
|
|
360
|
+
constructor(protected attributes: Attributes, protected check: (obj: any, attributes: Attributes) => ErrorMessage[], protected max: number, protected length: number) {
|
|
361
|
+
this.validate = this.validate.bind(this);
|
|
362
|
+
}
|
|
363
|
+
validate(rate: Rates): Promise<ErrorMessage[]> {
|
|
364
|
+
const errs = this.check(rate, this.attributes);
|
|
365
|
+
if (!rate.rates || rate.rates.length === 0) {
|
|
366
|
+
const err = createError('rates', 'required');
|
|
367
|
+
errs.push(err);
|
|
368
|
+
return Promise.resolve(errs);
|
|
369
|
+
}
|
|
370
|
+
if (rate.rates.length !== this.length) {
|
|
371
|
+
const err = createError('rates', 'length', this.length);
|
|
372
|
+
errs.push(err);
|
|
373
|
+
return Promise.resolve(errs);
|
|
374
|
+
}
|
|
375
|
+
for (const r of rate.rates) {
|
|
376
|
+
if (r > this.max) {
|
|
377
|
+
const err = createError('rates', 'max', this.max);
|
|
378
|
+
errs.push(err);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return Promise.resolve(errs);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
235
384
|
function createError(field: string, code?: string, param?: string | number | Date): ErrorMessage {
|
|
236
385
|
if (!code) {
|
|
237
386
|
code = 'string';
|
package/src/rate.ts
CHANGED
|
@@ -4,12 +4,13 @@ export interface RateId {
|
|
|
4
4
|
id: string;
|
|
5
5
|
author: string;
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
export interface Rate {
|
|
9
|
-
id: string;
|
|
7
|
+
export interface BaseRate {
|
|
10
8
|
author: string;
|
|
11
9
|
authorURL?: string;
|
|
12
10
|
rate: number;
|
|
11
|
+
}
|
|
12
|
+
export interface Rate extends BaseRate {
|
|
13
|
+
id: string;
|
|
13
14
|
time: Date;
|
|
14
15
|
review: string;
|
|
15
16
|
usefulCount: number;
|
|
@@ -30,24 +31,56 @@ export interface RateFilter extends Filter {
|
|
|
30
31
|
usefulCount?: number;
|
|
31
32
|
replyCount?: number;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
export interface RateInfo {
|
|
35
|
+
id: string;
|
|
36
|
+
rate: number;
|
|
37
|
+
count: number;
|
|
38
|
+
score: number;
|
|
39
|
+
}
|
|
40
|
+
export interface ShortRates {
|
|
41
|
+
rates: number[];
|
|
42
|
+
time: Date;
|
|
43
|
+
review: string;
|
|
44
|
+
}
|
|
45
|
+
export interface Rates extends BaseRate {
|
|
46
|
+
id: string;
|
|
47
|
+
rates: number[];
|
|
48
|
+
time: Date;
|
|
49
|
+
review: string;
|
|
50
|
+
usefulCount: number;
|
|
51
|
+
replyCount: number;
|
|
52
|
+
histories?: ShortRates[];
|
|
53
|
+
}
|
|
54
|
+
export interface RatesFilter extends RateFilter {
|
|
55
|
+
rates?: number[];
|
|
56
|
+
rate1: number;
|
|
57
|
+
rate2: number;
|
|
58
|
+
rate3: number;
|
|
59
|
+
rate4: number;
|
|
60
|
+
rate5: number;
|
|
61
|
+
rate6: number;
|
|
62
|
+
rate7: number;
|
|
63
|
+
rate8: number;
|
|
64
|
+
rate9: number;
|
|
65
|
+
rate10: number;
|
|
66
|
+
}
|
|
67
|
+
export interface RateRepository<R> {
|
|
35
68
|
// save(obj: Rate, info?: T, ctx?: any): Promise<number>;
|
|
36
|
-
insert(rate:
|
|
37
|
-
update(rate:
|
|
38
|
-
|
|
69
|
+
insert(rate: R, newInfo?: boolean): Promise<number>;
|
|
70
|
+
update(rate: R, oldRate: number): Promise<number>;
|
|
71
|
+
load(id: string, author: string): Promise<R | null>;
|
|
39
72
|
}
|
|
40
|
-
export interface Rater {
|
|
41
|
-
search(s:
|
|
42
|
-
|
|
43
|
-
rate(rate:
|
|
73
|
+
export interface Rater<R, F extends Filter> {
|
|
74
|
+
search(s: F, limit?: number, offset?: number | string, fields?: string[], ctx?: any): Promise<SearchResult<R>>;
|
|
75
|
+
load(id: string, author: string): Promise<R | null>;
|
|
76
|
+
rate(rate: R): Promise<number>;
|
|
44
77
|
setUseful(id: string, author: string, userId: string, ctx?: any): Promise<number>;
|
|
45
78
|
removeUseful(id: string, author: string, userId: string, ctx?: any): Promise<number>;
|
|
46
79
|
comment(comment: Comment): Promise<number>;
|
|
47
80
|
removeComment(id: string, author: string, ctx?: any): Promise<number>;
|
|
48
81
|
updateComment(comment: Comment): Promise<number>;
|
|
49
82
|
getComments(id: string, author: string, limit?: number): Promise<Comment[]>;
|
|
50
|
-
getComment(id: string): Promise<Comment|null>;
|
|
83
|
+
getComment(id: string): Promise<Comment | null>;
|
|
51
84
|
}
|
|
52
85
|
export interface RateReactionRepository {
|
|
53
86
|
remove(id: string, author: string, userId: string, ctx?: any): Promise<number>;
|
|
@@ -60,9 +93,9 @@ export interface RateCommentRepository extends Repository<Comment, string> {
|
|
|
60
93
|
}
|
|
61
94
|
|
|
62
95
|
export interface Query<T, ID, S> {
|
|
63
|
-
search: (s: S, limit?: number, skip?: number|string, fields?: string[]) => Promise<SearchResult<T>>;
|
|
64
|
-
metadata?(): Attributes|undefined;
|
|
65
|
-
load(id: ID, ctx?: any): Promise<T|null>;
|
|
96
|
+
search: (s: S, limit?: number, skip?: number | string, fields?: string[]) => Promise<SearchResult<T>>;
|
|
97
|
+
metadata?(): Attributes | undefined;
|
|
98
|
+
load(id: ID, ctx?: any): Promise<T | null>;
|
|
66
99
|
}
|
|
67
100
|
export interface RateCommentQuery extends Query<Comment, string, CommentFilter> {
|
|
68
101
|
getComments(id: string, author: string, limit?: number): Promise<Comment[]>;
|
|
@@ -247,45 +280,40 @@ export interface CommentFilter extends Filter {
|
|
|
247
280
|
time?: Date;
|
|
248
281
|
updatedAt?: Date;
|
|
249
282
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
283
|
+
|
|
284
|
+
export const rateInfoModel: Attributes = {
|
|
285
|
+
id: {
|
|
286
|
+
key: true
|
|
253
287
|
},
|
|
254
|
-
|
|
255
|
-
type: '
|
|
288
|
+
rate: {
|
|
289
|
+
type: 'number',
|
|
290
|
+
},
|
|
291
|
+
count: {
|
|
292
|
+
type: 'integer',
|
|
293
|
+
},
|
|
294
|
+
score: {
|
|
295
|
+
type: 'number',
|
|
256
296
|
}
|
|
257
297
|
};
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
key: true
|
|
261
|
-
},
|
|
298
|
+
|
|
299
|
+
export const ratesModel: Attributes = {
|
|
262
300
|
id: {
|
|
263
|
-
|
|
264
|
-
noupdate: true,
|
|
301
|
+
key: true,
|
|
265
302
|
match: 'equal'
|
|
266
303
|
},
|
|
267
304
|
author: {
|
|
268
|
-
|
|
269
|
-
noupdate: true,
|
|
305
|
+
key: true,
|
|
270
306
|
match: 'equal'
|
|
271
307
|
},
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
noupdate: true,
|
|
275
|
-
match: 'equal'
|
|
308
|
+
rate: {
|
|
309
|
+
type: 'number'
|
|
276
310
|
},
|
|
277
|
-
|
|
278
|
-
|
|
311
|
+
rates: {
|
|
312
|
+
required: true,
|
|
313
|
+
type: 'integers'
|
|
279
314
|
},
|
|
280
315
|
time: {
|
|
281
|
-
type: 'datetime',
|
|
282
|
-
noupdate: true,
|
|
283
|
-
},
|
|
284
|
-
updatedAt: {
|
|
285
316
|
type: 'datetime'
|
|
286
317
|
},
|
|
287
|
-
|
|
288
|
-
type: 'array',
|
|
289
|
-
typeof: commentModel
|
|
290
|
-
}
|
|
318
|
+
review: {},
|
|
291
319
|
};
|