rate-core 0.0.6 → 0.5.2

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 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,15 +53,14 @@ function __export(m) {
40
53
  }
41
54
  Object.defineProperty(exports, "__esModule", { value: true });
42
55
  __export(require("./rate"));
43
- var RateService = (function () {
44
- function RateService(find, repository, infoRepository, commentRepository, rateReactionRepository, queryURL) {
56
+ var ReactionService = (function () {
57
+ function ReactionService(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.rateReactionRepository = rateReactionRepository;
62
+ this.reactionRepository = reactionRepository;
50
63
  this.queryURL = queryURL;
51
- this.rate = this.rate.bind(this);
52
64
  this.search = this.search.bind(this);
53
65
  this.load = this.load.bind(this);
54
66
  this.getRate = this.getRate.bind(this);
@@ -60,48 +72,7 @@ var RateService = (function () {
60
72
  this.getComments = this.getComments.bind(this);
61
73
  this.getComment = this.getComment.bind(this);
62
74
  }
63
- RateService.prototype.rate = function (rate) {
64
- return __awaiter(this, void 0, void 0, function () {
65
- var info, r0, exist, r1, sr, history, res;
66
- return __generator(this, function (_a) {
67
- switch (_a.label) {
68
- case 0:
69
- rate.time = new Date();
70
- return [4, this.infoRepository.load(rate.id)];
71
- case 1:
72
- info = _a.sent();
73
- if (!!info) return [3, 3];
74
- return [4, this.repository.insert(rate, true)];
75
- case 2:
76
- r0 = _a.sent();
77
- return [2, r0];
78
- case 3: return [4, this.repository.load(rate.id, rate.author)];
79
- case 4:
80
- exist = _a.sent();
81
- if (!!exist) return [3, 6];
82
- return [4, this.repository.insert(rate)];
83
- case 5:
84
- r1 = _a.sent();
85
- return [2, r1];
86
- case 6:
87
- sr = { review: exist.review, rate: exist.rate, time: exist.time };
88
- if (exist.histories && exist.histories.length > 0) {
89
- history = exist.histories;
90
- history.push(sr);
91
- rate.histories = history;
92
- }
93
- else {
94
- rate.histories = [sr];
95
- }
96
- return [4, this.repository.update(rate, exist.rate)];
97
- case 7:
98
- res = _a.sent();
99
- return [2, res];
100
- }
101
- });
102
- });
103
- };
104
- RateService.prototype.search = function (s, limit, offset, fields) {
75
+ ReactionService.prototype.search = function (s, limit, offset, fields) {
105
76
  var _this = this;
106
77
  return this.find(s, limit, offset, fields).then(function (res) {
107
78
  if (!_this.queryURL) {
@@ -131,19 +102,19 @@ var RateService = (function () {
131
102
  }
132
103
  });
133
104
  };
134
- RateService.prototype.load = function (id, author) {
105
+ ReactionService.prototype.load = function (id, author) {
135
106
  return this.repository.load(id, author);
136
107
  };
137
- RateService.prototype.getRate = function (id, author) {
108
+ ReactionService.prototype.getRate = function (id, author) {
138
109
  return this.repository.load(id, author);
139
110
  };
140
- RateService.prototype.setUseful = function (id, author, userId) {
141
- return this.rateReactionRepository.save(id, author, userId, 1);
111
+ ReactionService.prototype.setUseful = function (id, author, userId) {
112
+ return this.reactionRepository.save(id, author, userId, 1);
142
113
  };
143
- RateService.prototype.removeUseful = function (id, author, userId) {
144
- return this.rateReactionRepository.remove(id, author, userId);
114
+ ReactionService.prototype.removeUseful = function (id, author, userId) {
115
+ return this.reactionRepository.remove(id, author, userId);
145
116
  };
146
- RateService.prototype.comment = function (comment) {
117
+ ReactionService.prototype.comment = function (comment) {
147
118
  var _this = this;
148
119
  return this.repository.load(comment.id, comment.author).then(function (checkRate) {
149
120
  if (!checkRate) {
@@ -155,7 +126,7 @@ var RateService = (function () {
155
126
  }
156
127
  });
157
128
  };
158
- RateService.prototype.removeComment = function (commentId, userId) {
129
+ ReactionService.prototype.removeComment = function (commentId, userId) {
159
130
  var _this = this;
160
131
  return this.commentRepository.load(commentId).then(function (comment) {
161
132
  if (comment) {
@@ -171,7 +142,7 @@ var RateService = (function () {
171
142
  }
172
143
  });
173
144
  };
174
- RateService.prototype.updateComment = function (comment) {
145
+ ReactionService.prototype.updateComment = function (comment) {
175
146
  var _this = this;
176
147
  return this.commentRepository.load(comment.commentId).then(function (exist) {
177
148
  if (!exist) {
@@ -195,63 +166,164 @@ var RateService = (function () {
195
166
  }
196
167
  });
197
168
  };
198
- RateService.prototype.getComments = function (id, author, limit) {
199
- return this.commentRepository.getComments(id, author, limit);
200
- };
201
- RateService.prototype.getComment = function (id) {
202
- return this.commentRepository.load(id);
203
- };
204
- return RateService;
205
- }());
206
- exports.RateService = RateService;
207
- var CommentQuery = (function () {
208
- function CommentQuery(find, repository, queryURL) {
209
- this.find = find;
210
- this.repository = repository;
211
- this.queryURL = queryURL;
212
- this.load = this.load.bind(this);
213
- this.search = this.search.bind(this);
214
- this.getComments = this.getComments.bind(this);
215
- }
216
- CommentQuery.prototype.load = function (id, ctx) {
217
- return this.repository.load(id, ctx);
218
- };
219
- CommentQuery.prototype.getComments = function (id, author, limit) {
220
- return this.repository.getComments(id, author, limit);
221
- };
222
- CommentQuery.prototype.search = function (s, limit, offset, fields) {
169
+ ReactionService.prototype.getComments = function (id, author, limit) {
223
170
  var _this = this;
224
- return this.find(s, limit, offset, fields).then(function (res) {
225
- if (!_this.queryURL) {
226
- return res;
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
+ });
227
188
  }
228
189
  else {
229
- if (res.list && res.list.length > 0) {
230
- var ids = [];
231
- for (var _i = 0, _a = res.list; _i < _a.length; _i++) {
232
- var rate = _a[_i];
233
- ids.push(rate.userId);
190
+ return comments;
191
+ }
192
+ });
193
+ };
194
+ ReactionService.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;
234
202
  }
235
- return _this.queryURL(ids).then(function (urls) {
236
- for (var _i = 0, _a = res.list; _i < _a.length; _i++) {
237
- var rate = _a[_i];
238
- var i = binarySearch(urls, rate.userId);
239
- if (i >= 0) {
240
- rate.userURL = urls[i].url;
241
- }
203
+ return comment;
204
+ });
205
+ }
206
+ else {
207
+ return comment;
208
+ }
209
+ });
210
+ };
211
+ return ReactionService;
212
+ }());
213
+ exports.ReactionService = ReactionService;
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.exist(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;
242
250
  }
243
- return res;
244
- });
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];
245
258
  }
246
- else {
247
- return res;
259
+ });
260
+ });
261
+ };
262
+ return RateService;
263
+ }(ReactionService));
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.exist(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];
248
320
  }
249
- }
321
+ });
250
322
  });
251
323
  };
252
- return CommentQuery;
253
- }());
254
- exports.CommentQuery = CommentQuery;
324
+ return RatesService;
325
+ }(ReactionService));
326
+ exports.RatesService = RatesService;
255
327
  function binarySearch(ar, el) {
256
328
  var m = 0;
257
329
  var n = ar.length - 1;
@@ -312,6 +384,38 @@ var RateValidator = (function () {
312
384
  return RateValidator;
313
385
  }());
314
386
  exports.RateValidator = RateValidator;
387
+ var RatesValidator = (function () {
388
+ function RatesValidator(attributes, check, max, length) {
389
+ this.attributes = attributes;
390
+ this.check = check;
391
+ this.max = max;
392
+ this.length = length;
393
+ this.validate = this.validate.bind(this);
394
+ }
395
+ RatesValidator.prototype.validate = function (rate) {
396
+ var errs = this.check(rate, this.attributes);
397
+ if (!rate.rates || rate.rates.length === 0) {
398
+ var err = createError('rates', 'required');
399
+ errs.push(err);
400
+ return Promise.resolve(errs);
401
+ }
402
+ if (rate.rates.length !== this.length) {
403
+ var err = createError('rates', 'length', this.length);
404
+ errs.push(err);
405
+ return Promise.resolve(errs);
406
+ }
407
+ for (var _i = 0, _a = rate.rates; _i < _a.length; _i++) {
408
+ var r = _a[_i];
409
+ if (r > this.max) {
410
+ var err = createError('rates', 'max', this.max);
411
+ errs.push(err);
412
+ }
413
+ }
414
+ return Promise.resolve(errs);
415
+ };
416
+ return RatesValidator;
417
+ }());
418
+ exports.RatesValidator = RatesValidator;
315
419
  function createError(field, code, param) {
316
420
  if (!code) {
317
421
  code = 'string';
package/lib/rate.js CHANGED
@@ -116,3 +116,38 @@ exports.info10Model = {
116
116
  type: 'number',
117
117
  }
118
118
  };
119
+ exports.rateInfoModel = {
120
+ id: {
121
+ key: true
122
+ },
123
+ rate: {
124
+ type: 'number',
125
+ },
126
+ count: {
127
+ type: 'integer',
128
+ },
129
+ score: {
130
+ type: 'number',
131
+ }
132
+ };
133
+ exports.ratesModel = {
134
+ id: {
135
+ key: true,
136
+ match: 'equal'
137
+ },
138
+ author: {
139
+ key: true,
140
+ match: 'equal'
141
+ },
142
+ rate: {
143
+ type: 'number'
144
+ },
145
+ rates: {
146
+ required: true,
147
+ type: 'integers'
148
+ },
149
+ time: {
150
+ type: 'datetime'
151
+ },
152
+ review: {},
153
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rate-core",
3
- "version": "0.0.6",
3
+ "version": "0.5.2",
4
4
  "description": "rate",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./src/index.ts",
@@ -17,9 +17,11 @@
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git@github.com/core-ts/rate"
20
+ "url": "git@github.com/core-ts/reaction"
21
21
  },
22
22
  "keywords": [
23
+ "reaction",
24
+ "reaction core",
23
25
  "rate",
24
26
  "rate core"
25
27
  ]
package/src/core.ts CHANGED
@@ -78,7 +78,7 @@ export interface ViewRepository<T, ID> {
78
78
  keys?(): string[];
79
79
  all?(ctx?: any): Promise<T[]>;
80
80
  load(id: ID, ctx?: any): Promise<T|null>;
81
- exist?(id: ID, ctx?: any): Promise<boolean>;
81
+ exist(id: ID, ctx?: any): Promise<boolean>;
82
82
  }
83
83
  export interface Repository<T, ID> extends ViewRepository<T, ID> {
84
84
  insert(obj: T, ctx?: any): Promise<number>;
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
- RateReactionRepository, RateRepository, ShortComment, ShortRate
3
+ BaseRate, BaseRepository, Comment, InfoRepository, Rate, RateCommentRepository, RateFilter, Rater, RateReactionRepository,
4
+ Rates, RatesFilter, ShortComment, ShortRate, ShortRates
5
5
  } from './rate';
6
6
 
7
7
  export * from './rate';
@@ -10,14 +10,13 @@ export interface URL {
10
10
  id: string;
11
11
  url: string;
12
12
  }
13
- export class RateService<O> implements Rater {
14
- constructor(protected find: Search<Rate, RateFilter>,
15
- public repository: RateRepository,
16
- private infoRepository: InfoRepository<O>,
17
- private commentRepository: RateCommentRepository,
18
- private rateReactionRepository: RateReactionRepository,
19
- private queryURL?: (ids: string[]) => Promise<URL[]>) {
20
- this.rate = this.rate.bind(this);
13
+ export class ReactionService<R extends BaseRate, F, O> {
14
+ constructor(protected find: Search<R, F>,
15
+ public repository: BaseRepository<R>,
16
+ protected infoRepository: InfoRepository<O>,
17
+ protected commentRepository: RateCommentRepository,
18
+ protected reactionRepository: RateReactionRepository,
19
+ protected queryURL?: (ids: string[]) => Promise<URL[]>) {
21
20
  this.search = this.search.bind(this);
22
21
  this.load = this.load.bind(this);
23
22
  this.getRate = this.getRate.bind(this);
@@ -29,30 +28,7 @@ export class RateService<O> implements Rater {
29
28
  this.getComments = this.getComments.bind(this);
30
29
  this.getComment = this.getComment.bind(this);
31
30
  }
32
- async rate(rate: Rate): Promise<number> {
33
- rate.time = new Date();
34
- const info = await this.infoRepository.load(rate.id);
35
- if (!info) {
36
- const r0 = await this.repository.insert(rate, true);
37
- return r0;
38
- }
39
- const exist = await this.repository.load(rate.id, rate.author);
40
- if (!exist) {
41
- const r1 = await this.repository.insert(rate);
42
- return r1;
43
- }
44
- const sr: ShortRate = { review: exist.review, rate: exist.rate, time: exist.time };
45
- if (exist.histories && exist.histories.length > 0) {
46
- const history = exist.histories;
47
- history.push(sr);
48
- rate.histories = history;
49
- } else {
50
- rate.histories = [sr];
51
- }
52
- const res = await this.repository.update(rate, exist.rate);
53
- return res;
54
- }
55
- 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>> {
56
32
  return this.find(s, limit, offset, fields).then(res => {
57
33
  if (!this.queryURL) {
58
34
  return res;
@@ -77,17 +53,17 @@ export class RateService<O> implements Rater {
77
53
  }
78
54
  });
79
55
  }
80
- load(id: string, author: string): Promise<Rate | null> {
56
+ load(id: string, author: string): Promise<R | null> {
81
57
  return this.repository.load(id, author);
82
58
  }
83
- getRate(id: string, author: string): Promise<Rate | null> {
59
+ getRate(id: string, author: string): Promise<R | null> {
84
60
  return this.repository.load(id, author);
85
61
  }
86
62
  setUseful(id: string, author: string, userId: string): Promise<number> {
87
- return this.rateReactionRepository.save(id, author, userId, 1);
63
+ return this.reactionRepository.save(id, author, userId, 1);
88
64
  }
89
65
  removeUseful(id: string, author: string, userId: string): Promise<number> {
90
- return this.rateReactionRepository.remove(id, author, userId);
66
+ return this.reactionRepository.remove(id, author, userId);
91
67
  }
92
68
  comment(comment: Comment): Promise<number> {
93
69
  return this.repository.load(comment.id, comment.author).then(checkRate => {
@@ -128,75 +104,146 @@ export class RateService<O> implements Rater {
128
104
  exist.histories = [c];
129
105
  }
130
106
  exist.comment = comment.comment;
131
- const res = this.commentRepository.update(exist);
107
+ const res = this.commentRepository.update(exist);
132
108
  return res;
133
109
  }
134
110
  });
135
111
  }
136
112
  getComments(id: string, author: string, limit?: number): Promise<Comment[]> {
137
- 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
+ });
138
132
  }
139
- getComment(id: string): Promise<Comment|null> {
140
- 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
+ });
141
147
  }
142
148
  }
143
149
  export interface CommentRepository {
144
- load(commentId: string, ctx?: any): Promise<Comment|null>;
150
+ load(commentId: string, ctx?: any): Promise<Comment | null>;
145
151
  getComments(id: string, author: string, limit?: number): Promise<Comment[]>;
146
152
  }
147
153
  // tslint:disable-next-line:max-classes-per-file
148
- export class CommentQuery implements RateCommentQuery {
149
- constructor(protected find: Search<Comment, CommentFilter>, protected repository: CommentRepository, private queryURL?: (ids: string[]) => Promise<URL[]>) {
150
- this.load = this.load.bind(this);
151
- this.search = this.search.bind(this);
152
- this.getComments = this.getComments.bind(this);
154
+ export class RateService<O> extends ReactionService<Rate, RateFilter, O> implements Rater<Rate, RateFilter> {
155
+ constructor(find: Search<Rate, RateFilter>,
156
+ repository: BaseRepository<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);
153
163
  }
154
- load(id: string, ctx?: any): Promise<Comment|null> {
155
- return this.repository.load(id, ctx);
164
+ async rate(rate: Rate): Promise<number> {
165
+ rate.time = new Date();
166
+ const info = await this.infoRepository.exist(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;
156
186
  }
157
- getComments(id: string, author: string, limit?: number): Promise<Comment[]> {
158
- return this.repository.getComments(id, author, limit);
187
+ }
188
+ export function avg(n: number[]): number {
189
+ let sum = 0;
190
+ for (const s of n) {
191
+ sum = sum + s;
159
192
  }
160
- search(s: CommentFilter, limit?: number, offset?: number | string, fields?: string[]): Promise<SearchResult<Comment>> {
161
- return this.find(s, limit, offset, fields).then(res => {
162
- if (!this.queryURL) {
163
- return res;
164
- } else {
165
- if (res.list && res.list.length > 0) {
166
- const ids: string[] = [];
167
- for (const rate of res.list) {
168
- ids.push(rate.userId);
169
- }
170
- return this.queryURL(ids).then(urls => {
171
- for (const rate of res.list) {
172
- const i = binarySearch(urls, rate.userId);
173
- if (i >= 0) {
174
- rate.userURL = urls[i].url;
175
- }
176
- }
177
- return res;
178
- });
179
- } else {
180
- return res;
181
- }
182
- }
183
- });
193
+ return sum / n.length;
194
+ }
195
+ // tslint:disable-next-line:max-classes-per-file
196
+ export class RatesService<O> extends ReactionService<Rates, RatesFilter, O> implements Rater<Rates, RatesFilter> {
197
+ constructor(find: Search<Rates, RatesFilter>,
198
+ repository: BaseRepository<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.exist(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;
184
231
  }
185
232
  }
186
233
  function binarySearch(ar: URL[], el: string): number {
187
234
  let m = 0;
188
235
  let n = ar.length - 1;
189
236
  while (m <= n) {
190
- // tslint:disable-next-line:no-bitwise
191
- const k = (n + m) >> 1;
192
- const cmp = compare(el, ar[k].id);
193
- if (cmp > 0) {
194
- m = k + 1;
195
- } else if (cmp < 0) {
196
- n = k - 1;
197
- } else {
198
- return k;
199
- }
237
+ // tslint:disable-next-line:no-bitwise
238
+ const k = (n + m) >> 1;
239
+ const cmp = compare(el, ar[k].id);
240
+ if (cmp > 0) {
241
+ m = k + 1;
242
+ } else if (cmp < 0) {
243
+ n = k - 1;
244
+ } else {
245
+ return k;
246
+ }
200
247
  }
201
248
  return -m - 1;
202
249
  }
@@ -206,7 +253,7 @@ function compare(s1: string, s2: string): number {
206
253
  interface ErrorMessage {
207
254
  field: string;
208
255
  code: string;
209
- param?: string|number|Date;
256
+ param?: string | number | Date;
210
257
  message?: string;
211
258
  }
212
259
  // tslint:disable-next-line:max-classes-per-file
@@ -239,6 +286,32 @@ export class RateValidator {
239
286
  }
240
287
  }
241
288
  }
289
+ // tslint:disable-next-line:max-classes-per-file
290
+ export class RatesValidator {
291
+ constructor(protected attributes: Attributes, protected check: (obj: any, attributes: Attributes) => ErrorMessage[], protected max: number, protected length: number) {
292
+ this.validate = this.validate.bind(this);
293
+ }
294
+ validate(rate: Rates): Promise<ErrorMessage[]> {
295
+ const errs = this.check(rate, this.attributes);
296
+ if (!rate.rates || rate.rates.length === 0) {
297
+ const err = createError('rates', 'required');
298
+ errs.push(err);
299
+ return Promise.resolve(errs);
300
+ }
301
+ if (rate.rates.length !== this.length) {
302
+ const err = createError('rates', 'length', this.length);
303
+ errs.push(err);
304
+ return Promise.resolve(errs);
305
+ }
306
+ for (const r of rate.rates) {
307
+ if (r > this.max) {
308
+ const err = createError('rates', 'max', this.max);
309
+ errs.push(err);
310
+ }
311
+ }
312
+ return Promise.resolve(errs);
313
+ }
314
+ }
242
315
  function createError(field: string, code?: string, param?: string | number | Date): ErrorMessage {
243
316
  if (!code) {
244
317
  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
- export interface RateRepository {
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 BaseRepository<R> {
35
68
  // save(obj: Rate, info?: T, ctx?: any): Promise<number>;
36
- insert(rate: Rate, newInfo?: boolean): Promise<number>;
37
- update(rate: Rate, oldRate: number): Promise<number>;
38
- load(id: string, author: string): Promise<Rate | null>;
39
- }
40
- export interface Rater {
41
- search(s: RateFilter, limit?: number, offset?: number | string, fields?: string[], ctx?: any): Promise<SearchResult<Rate>>;
42
- load(id: string, author: string): Promise<Rate | null>;
43
- rate(rate: Rate): Promise<number>;
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>;
72
+ }
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,3 +280,40 @@ export interface CommentFilter extends Filter {
247
280
  time?: Date;
248
281
  updatedAt?: Date;
249
282
  }
283
+
284
+ export const rateInfoModel: Attributes = {
285
+ id: {
286
+ key: true
287
+ },
288
+ rate: {
289
+ type: 'number',
290
+ },
291
+ count: {
292
+ type: 'integer',
293
+ },
294
+ score: {
295
+ type: 'number',
296
+ }
297
+ };
298
+
299
+ export const ratesModel: Attributes = {
300
+ id: {
301
+ key: true,
302
+ match: 'equal'
303
+ },
304
+ author: {
305
+ key: true,
306
+ match: 'equal'
307
+ },
308
+ rate: {
309
+ type: 'number'
310
+ },
311
+ rates: {
312
+ required: true,
313
+ type: 'integers'
314
+ },
315
+ time: {
316
+ type: 'datetime'
317
+ },
318
+ review: {},
319
+ };