rates-service 0.0.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/README.md +1 -0
- package/lib/index.js +268 -0
- package/package.json +27 -0
- package/src/index.ts +297 -0
- package/tsconfig.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# rates
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (_) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
function buildMap(attrs) {
|
|
40
|
+
var mp = {};
|
|
41
|
+
var ks = Object.keys(attrs);
|
|
42
|
+
for (var _i = 0, ks_1 = ks; _i < ks_1.length; _i++) {
|
|
43
|
+
var k = ks_1[_i];
|
|
44
|
+
var attr = attrs[k];
|
|
45
|
+
attr.name = k;
|
|
46
|
+
var field = attr.column ? attr.column : k;
|
|
47
|
+
var s = field.toLowerCase();
|
|
48
|
+
if (s !== k) {
|
|
49
|
+
mp[s] = k;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return mp;
|
|
53
|
+
}
|
|
54
|
+
exports.buildMap = buildMap;
|
|
55
|
+
var SqlRateRepository = (function () {
|
|
56
|
+
function SqlRateRepository(db, table, attributes, max, infoTable, buildToInsert, buildToUpdate, generateId, rateIdField, rateField, count, score, authorCol, id, idField, idCol, rateCol) {
|
|
57
|
+
this.db = db;
|
|
58
|
+
this.table = table;
|
|
59
|
+
this.attributes = attributes;
|
|
60
|
+
this.max = max;
|
|
61
|
+
this.infoTable = infoTable;
|
|
62
|
+
this.buildToInsert = buildToInsert;
|
|
63
|
+
this.buildToUpdate = buildToUpdate;
|
|
64
|
+
this.generateId = generateId;
|
|
65
|
+
this.rateIdField = rateIdField;
|
|
66
|
+
this.map = buildMap(attributes);
|
|
67
|
+
this.id = (id && id.length > 0 ? id : 'id');
|
|
68
|
+
this.rate = (rateCol && rateCol.length > 0 ? rateCol : 'rate');
|
|
69
|
+
this.count = (count && count.length > 0 ? count : 'count');
|
|
70
|
+
this.score = (score && score.length > 0 ? score : 'score');
|
|
71
|
+
this.idField = (idField && idField.length > 0 ? idField : 'id');
|
|
72
|
+
this.rateField = (rateField && rateField.length > 0 ? rateField : 'rate');
|
|
73
|
+
this.authorCol = (authorCol && authorCol.length > 0 ? authorCol : 'author');
|
|
74
|
+
if (idCol && idCol.length > 0) {
|
|
75
|
+
this.idCol = idCol;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
var c = attributes[this.idField];
|
|
79
|
+
if (c) {
|
|
80
|
+
this.idCol = (c.column && c.column.length > 0 ? c.column : this.idField);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.idCol = this.idField;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (rateCol && rateCol.length > 0) {
|
|
87
|
+
this.rate = rateCol;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
var c = attributes[this.rateField];
|
|
91
|
+
if (c) {
|
|
92
|
+
this.rate = (c.column && c.column.length > 0 ? c.column : this.rateField);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
this.rate = this.rateField;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
this.load = this.load.bind(this);
|
|
99
|
+
this.create = this.create.bind(this);
|
|
100
|
+
this.update = this.update.bind(this);
|
|
101
|
+
this.insertInfo = this.insertInfo.bind(this);
|
|
102
|
+
this.updateNewInfo = this.updateNewInfo.bind(this);
|
|
103
|
+
this.updateOldInfo = this.updateOldInfo.bind(this);
|
|
104
|
+
}
|
|
105
|
+
SqlRateRepository.prototype.load = function (id, author, tx) {
|
|
106
|
+
var db = tx ? tx : this.db;
|
|
107
|
+
return db.query("select * from " + this.table + " where " + this.idCol + " = " + this.db.param(1) + " and " + this.authorCol + " = " + this.db.param(2), [id, author], this.map).then(function (rates) {
|
|
108
|
+
return rates && rates.length > 0 ? rates[0] : null;
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
SqlRateRepository.prototype.create = function (rate, newInfo, tx) {
|
|
112
|
+
rate[this.rateIdField] = this.generateId();
|
|
113
|
+
var stmt = this.buildToInsert(rate, this.table, this.attributes, this.db.param);
|
|
114
|
+
if (stmt.query) {
|
|
115
|
+
var obj = rate;
|
|
116
|
+
var rateNum = obj[this.rateField];
|
|
117
|
+
var id = obj[this.idField];
|
|
118
|
+
var db = tx ? tx : this.db;
|
|
119
|
+
if (newInfo) {
|
|
120
|
+
var query = this.insertInfo(rateNum);
|
|
121
|
+
var s2 = { query: query, params: [id] };
|
|
122
|
+
return db.executeBatch([s2, stmt], true);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
var query = this.updateNewInfo(rateNum);
|
|
126
|
+
var s2 = { query: query, params: [id] };
|
|
127
|
+
return db.executeBatch([s2, stmt], true);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
return Promise.resolve(-1);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
SqlRateRepository.prototype.insertInfo = function (r) {
|
|
135
|
+
var rateCols = [];
|
|
136
|
+
var ps = [];
|
|
137
|
+
for (var i = 1; i <= this.max; i++) {
|
|
138
|
+
rateCols.push("" + this.rate + i);
|
|
139
|
+
if (i === r) {
|
|
140
|
+
ps.push('' + 1);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
ps.push('0');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
var query = "\n insert into " + this.infoTable + " (" + this.id + ", " + this.rate + ", " + this.count + ", " + this.score + ", " + rateCols.join(',') + ")\n values (" + this.db.param(1) + ", " + r + ", 1, " + r + ", " + ps.join(',') + ")";
|
|
147
|
+
return query;
|
|
148
|
+
};
|
|
149
|
+
SqlRateRepository.prototype.update = function (rate, oldRate, tx) {
|
|
150
|
+
var stmt = this.buildToUpdate(rate, this.table, this.attributes, this.db.param);
|
|
151
|
+
if (stmt.query) {
|
|
152
|
+
var obj = rate;
|
|
153
|
+
var rateNum = obj[this.rateField];
|
|
154
|
+
var id = obj[this.idField];
|
|
155
|
+
var query = this.updateOldInfo(rateNum, oldRate);
|
|
156
|
+
var s2 = { query: query, params: [id] };
|
|
157
|
+
var db = tx ? tx : this.db;
|
|
158
|
+
return db.executeBatch([s2, stmt], true);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
return Promise.resolve(-1);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
SqlRateRepository.prototype.updateNewInfo = function (r) {
|
|
165
|
+
var query = "\n update " + this.infoTable + " set " + this.rate + " = (" + this.score + " + " + r + ")/(" + this.count + " + 1), " + this.count + " = " + this.count + " + 1, " + this.score + " = " + this.score + " + " + r + ", " + this.rate + r + " = " + this.rate + r + " + 1\n where " + this.id + " = " + this.db.param(1);
|
|
166
|
+
return query;
|
|
167
|
+
};
|
|
168
|
+
SqlRateRepository.prototype.updateOldInfo = function (newRate, oldRate) {
|
|
169
|
+
if (newRate === oldRate) {
|
|
170
|
+
return '';
|
|
171
|
+
}
|
|
172
|
+
var delta = newRate - oldRate;
|
|
173
|
+
var query = "\n update " + this.infoTable + " set " + this.rate + " = (" + this.score + " + (" + delta + "))/" + this.count + ", " + this.score + " = " + this.score + " + (" + delta + "), " + this.rate + newRate + " = " + this.rate + newRate + " + 1, " + this.rate + oldRate + " = " + this.rate + oldRate + " - 1\n where " + this.id + " = " + this.db.param(1);
|
|
174
|
+
return query;
|
|
175
|
+
};
|
|
176
|
+
return SqlRateRepository;
|
|
177
|
+
}());
|
|
178
|
+
exports.SqlRateRepository = SqlRateRepository;
|
|
179
|
+
var Rater = (function () {
|
|
180
|
+
function Rater(db, rateRepository, rateSummaryRepository, usefulRepository) {
|
|
181
|
+
this.db = db;
|
|
182
|
+
this.rateRepository = rateRepository;
|
|
183
|
+
this.rateSummaryRepository = rateSummaryRepository;
|
|
184
|
+
this.usefulRepository = usefulRepository;
|
|
185
|
+
this.getRate = this.getRate.bind(this);
|
|
186
|
+
this.setUseful = this.setUseful.bind(this);
|
|
187
|
+
this.removeUseful = this.removeUseful.bind(this);
|
|
188
|
+
this.rate = this.rate.bind(this);
|
|
189
|
+
}
|
|
190
|
+
Rater.prototype.getRate = function (id, author) {
|
|
191
|
+
return this.rateRepository.load(id, author);
|
|
192
|
+
};
|
|
193
|
+
Rater.prototype.setUseful = function (rateId, userId) {
|
|
194
|
+
return this.usefulRepository.setUseful(rateId, userId);
|
|
195
|
+
};
|
|
196
|
+
Rater.prototype.removeUseful = function (rateId, userId) {
|
|
197
|
+
return this.usefulRepository.removeUseful(rateId, userId);
|
|
198
|
+
};
|
|
199
|
+
Rater.prototype.rate = function (rateReq) {
|
|
200
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
201
|
+
var rate, tx, summary, res, exist, res, history, histories, oldRate, count, err_1;
|
|
202
|
+
return __generator(this, function (_a) {
|
|
203
|
+
switch (_a.label) {
|
|
204
|
+
case 0:
|
|
205
|
+
rate = { id: rateReq.id, author: rateReq.author, rate: rateReq.rate, time: new Date(), review: rateReq.review };
|
|
206
|
+
return [4, this.db.beginTransaction()];
|
|
207
|
+
case 1:
|
|
208
|
+
tx = _a.sent();
|
|
209
|
+
_a.label = 2;
|
|
210
|
+
case 2:
|
|
211
|
+
_a.trys.push([2, 13, , 15]);
|
|
212
|
+
return [4, this.rateSummaryRepository.exist(rateReq.id, tx)];
|
|
213
|
+
case 3:
|
|
214
|
+
summary = _a.sent();
|
|
215
|
+
if (!!summary) return [3, 6];
|
|
216
|
+
return [4, this.rateRepository.create(rate, true, tx)];
|
|
217
|
+
case 4:
|
|
218
|
+
res = _a.sent();
|
|
219
|
+
return [4, tx.commit()];
|
|
220
|
+
case 5:
|
|
221
|
+
_a.sent();
|
|
222
|
+
return [2, res];
|
|
223
|
+
case 6: return [4, this.rateRepository.load(rateReq.id, rateReq.author, tx)];
|
|
224
|
+
case 7:
|
|
225
|
+
exist = _a.sent();
|
|
226
|
+
if (!!exist) return [3, 10];
|
|
227
|
+
return [4, this.rateRepository.create(rate, false, tx)];
|
|
228
|
+
case 8:
|
|
229
|
+
res = _a.sent();
|
|
230
|
+
return [4, tx.commit()];
|
|
231
|
+
case 9:
|
|
232
|
+
_a.sent();
|
|
233
|
+
return [2, res];
|
|
234
|
+
case 10:
|
|
235
|
+
history = { review: exist.review, rate: exist.rate, time: exist.time };
|
|
236
|
+
if (exist.histories && exist.histories.length > 0) {
|
|
237
|
+
histories = exist.histories;
|
|
238
|
+
histories.push(history);
|
|
239
|
+
exist.histories = histories;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
exist.histories = [history];
|
|
243
|
+
}
|
|
244
|
+
oldRate = exist.rate;
|
|
245
|
+
exist.rate = rateReq.rate;
|
|
246
|
+
exist.review = rateReq.review;
|
|
247
|
+
exist.time = new Date();
|
|
248
|
+
return [4, this.rateRepository.update(exist, oldRate, tx)];
|
|
249
|
+
case 11:
|
|
250
|
+
count = _a.sent();
|
|
251
|
+
return [4, tx.commit()];
|
|
252
|
+
case 12:
|
|
253
|
+
_a.sent();
|
|
254
|
+
return [2, count];
|
|
255
|
+
case 13:
|
|
256
|
+
err_1 = _a.sent();
|
|
257
|
+
return [4, tx.rollback()];
|
|
258
|
+
case 14:
|
|
259
|
+
_a.sent();
|
|
260
|
+
throw err_1;
|
|
261
|
+
case 15: return [2];
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
return Rater;
|
|
267
|
+
}());
|
|
268
|
+
exports.Rater = Rater;
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rates-service",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "rate",
|
|
5
|
+
"main": "./lib/index.js",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build:lib": "tsc",
|
|
9
|
+
"clean:lib": "rimraf lib"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"tslint": "5.10.0",
|
|
13
|
+
"typescript": "^3.3.3333"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"registry": "https://registry.npmjs.org/"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git@github.com/core-ts/rates"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"rates",
|
|
24
|
+
"rates service",
|
|
25
|
+
"rates-core"
|
|
26
|
+
]
|
|
27
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
export type DataType =
|
|
2
|
+
| "ObjectId"
|
|
3
|
+
| "date"
|
|
4
|
+
| "datetime"
|
|
5
|
+
| "time"
|
|
6
|
+
| "boolean"
|
|
7
|
+
| "number"
|
|
8
|
+
| "integer"
|
|
9
|
+
| "string"
|
|
10
|
+
| "text"
|
|
11
|
+
| "object"
|
|
12
|
+
| "array"
|
|
13
|
+
| "binary"
|
|
14
|
+
| "primitives"
|
|
15
|
+
| "booleans"
|
|
16
|
+
| "numbers"
|
|
17
|
+
| "integers"
|
|
18
|
+
| "strings"
|
|
19
|
+
| "dates"
|
|
20
|
+
| "datetimes"
|
|
21
|
+
| "times"
|
|
22
|
+
export type Operator = "=" | "like" | "!=" | "<>" | ">" | ">=" | "<" | "<="
|
|
23
|
+
|
|
24
|
+
export interface Attribute {
|
|
25
|
+
name?: string
|
|
26
|
+
column?: string
|
|
27
|
+
type?: DataType
|
|
28
|
+
default?: string | number | Date | boolean
|
|
29
|
+
key?: boolean
|
|
30
|
+
q?: boolean
|
|
31
|
+
noinsert?: boolean
|
|
32
|
+
noupdate?: boolean
|
|
33
|
+
nopatch?: boolean
|
|
34
|
+
version?: boolean
|
|
35
|
+
ignored?: boolean
|
|
36
|
+
true?: string | number
|
|
37
|
+
false?: string | number
|
|
38
|
+
createdAt?: boolean
|
|
39
|
+
updatedAt?: boolean
|
|
40
|
+
}
|
|
41
|
+
export interface Attributes {
|
|
42
|
+
[key: string]: Attribute
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface Executor {
|
|
46
|
+
driver: string
|
|
47
|
+
param(i: number): string
|
|
48
|
+
execute(sql: string, args?: any[], ctx?: any): Promise<number>
|
|
49
|
+
executeBatch(statements: Statement[], firstSuccess?: boolean, ctx?: any): Promise<number>
|
|
50
|
+
query<T>(sql: string, args?: any[], m?: StringMap, bools?: Attribute[], ctx?: any): Promise<T[]>
|
|
51
|
+
}
|
|
52
|
+
export interface Transaction extends Executor {
|
|
53
|
+
commit(): Promise<void>
|
|
54
|
+
rollback(): Promise<void>
|
|
55
|
+
}
|
|
56
|
+
export interface DB extends Executor {
|
|
57
|
+
beginTransaction(): Promise<Transaction>
|
|
58
|
+
}
|
|
59
|
+
export interface StringMap {
|
|
60
|
+
[key: string]: string
|
|
61
|
+
}
|
|
62
|
+
export interface Statement {
|
|
63
|
+
query: string
|
|
64
|
+
params?: any[]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function buildMap(attrs: Attributes): StringMap {
|
|
68
|
+
const mp: StringMap = {}
|
|
69
|
+
const ks = Object.keys(attrs)
|
|
70
|
+
for (const k of ks) {
|
|
71
|
+
const attr = attrs[k]
|
|
72
|
+
attr.name = k
|
|
73
|
+
const field = attr.column ? attr.column : k
|
|
74
|
+
const s = field.toLowerCase()
|
|
75
|
+
if (s !== k) {
|
|
76
|
+
mp[s] = k
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return mp
|
|
80
|
+
}
|
|
81
|
+
export class SqlRateRepository<R> {
|
|
82
|
+
constructor(protected db: DB, protected table: string, protected attributes: Attributes, protected max: number, protected infoTable: string,
|
|
83
|
+
protected buildToInsert: (obj: R, table: string, attrs: Attributes, buildParam: (i: number) => string) => Statement,
|
|
84
|
+
protected buildToUpdate: (obj: R, table: string, attrs: Attributes, buildParam: (i: number) => string) => Statement,
|
|
85
|
+
protected generateId: () => string, protected rateIdField: string, rateField?: string, count?: string, score?: string, authorCol?: string, id?: string, idField?: string, idCol?: string, rateCol?: string) {
|
|
86
|
+
this.map = buildMap(attributes);
|
|
87
|
+
this.id = (id && id.length > 0 ? id : 'id');
|
|
88
|
+
this.rate = (rateCol && rateCol.length > 0 ? rateCol : 'rate');
|
|
89
|
+
this.count = (count && count.length > 0 ? count : 'count');
|
|
90
|
+
this.score = (score && score.length > 0 ? score : 'score');
|
|
91
|
+
this.idField = (idField && idField.length > 0 ? idField : 'id');
|
|
92
|
+
this.rateField = (rateField && rateField.length > 0 ? rateField : 'rate');
|
|
93
|
+
this.authorCol = (authorCol && authorCol.length > 0 ? authorCol : 'author');
|
|
94
|
+
if (idCol && idCol.length > 0) {
|
|
95
|
+
this.idCol = idCol;
|
|
96
|
+
} else {
|
|
97
|
+
const c = attributes[this.idField];
|
|
98
|
+
if (c) {
|
|
99
|
+
this.idCol = (c.column && c.column.length > 0 ? c.column : this.idField);
|
|
100
|
+
} else {
|
|
101
|
+
this.idCol = this.idField;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (rateCol && rateCol.length > 0) {
|
|
105
|
+
this.rate = rateCol;
|
|
106
|
+
} else {
|
|
107
|
+
const c = attributes[this.rateField];
|
|
108
|
+
if (c) {
|
|
109
|
+
this.rate = (c.column && c.column.length > 0 ? c.column : this.rateField);
|
|
110
|
+
} else {
|
|
111
|
+
this.rate = this.rateField;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
this.load = this.load.bind(this);
|
|
115
|
+
this.create = this.create.bind(this);
|
|
116
|
+
this.update = this.update.bind(this);
|
|
117
|
+
this.insertInfo = this.insertInfo.bind(this);
|
|
118
|
+
this.updateNewInfo = this.updateNewInfo.bind(this);
|
|
119
|
+
this.updateOldInfo = this.updateOldInfo.bind(this);
|
|
120
|
+
}
|
|
121
|
+
map?: StringMap;
|
|
122
|
+
count: string;
|
|
123
|
+
score: string;
|
|
124
|
+
id: string;
|
|
125
|
+
rate: string;
|
|
126
|
+
idField: string;
|
|
127
|
+
rateField: string;
|
|
128
|
+
idCol: string;
|
|
129
|
+
authorCol: string;
|
|
130
|
+
load(id: string, author: string, tx?: Transaction): Promise<R | null> {
|
|
131
|
+
const db = tx ? tx : this.db
|
|
132
|
+
return db.query<R>(`select * from ${this.table} where ${this.idCol} = ${this.db.param(1)} and ${this.authorCol} = ${this.db.param(2)}`, [id, author], this.map).then(rates => {
|
|
133
|
+
return rates && rates.length > 0 ? rates[0] : null;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
create(rate: R, newInfo?: boolean, tx?: Transaction): Promise<number> {
|
|
137
|
+
(rate as any)[this.rateIdField] = this.generateId()
|
|
138
|
+
const stmt = this.buildToInsert(rate, this.table, this.attributes, this.db.param);
|
|
139
|
+
if (stmt.query) {
|
|
140
|
+
const obj: any = rate;
|
|
141
|
+
const rateNum: number = obj[this.rateField];
|
|
142
|
+
const id: string = obj[this.idField];
|
|
143
|
+
const db = tx ? tx : this.db
|
|
144
|
+
if (newInfo) {
|
|
145
|
+
const query = this.insertInfo(rateNum);
|
|
146
|
+
const s2: Statement = { query, params: [id] };
|
|
147
|
+
return db.executeBatch([s2, stmt], true);
|
|
148
|
+
} else {
|
|
149
|
+
const query = this.updateNewInfo(rateNum);
|
|
150
|
+
const s2: Statement = { query, params: [id] };
|
|
151
|
+
return db.executeBatch([s2, stmt], true);
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
return Promise.resolve(-1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
protected insertInfo(r: number): string {
|
|
158
|
+
const rateCols: string[] = [];
|
|
159
|
+
const ps: string[] = [];
|
|
160
|
+
for (let i = 1; i <= this.max; i++) {
|
|
161
|
+
rateCols.push(`${this.rate}${i}`);
|
|
162
|
+
if (i === r) {
|
|
163
|
+
ps.push('' + 1);
|
|
164
|
+
} else {
|
|
165
|
+
ps.push('0');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const query = `
|
|
169
|
+
insert into ${this.infoTable} (${this.id}, ${this.rate}, ${this.count}, ${this.score}, ${rateCols.join(',')})
|
|
170
|
+
values (${this.db.param(1)}, ${r}, 1, ${r}, ${ps.join(',')})`;
|
|
171
|
+
return query;
|
|
172
|
+
}
|
|
173
|
+
update(rate: R, oldRate: number, tx?: Transaction): Promise<number> {
|
|
174
|
+
const stmt = this.buildToUpdate(rate, this.table, this.attributes, this.db.param);
|
|
175
|
+
if (stmt.query) {
|
|
176
|
+
const obj: any = rate;
|
|
177
|
+
const rateNum: number = obj[this.rateField];
|
|
178
|
+
const id: string = obj[this.idField];
|
|
179
|
+
const query = this.updateOldInfo(rateNum, oldRate);
|
|
180
|
+
const s2: Statement = { query, params: [id] };
|
|
181
|
+
const db = tx ? tx : this.db
|
|
182
|
+
return db.executeBatch([s2, stmt], true);
|
|
183
|
+
} else {
|
|
184
|
+
return Promise.resolve(-1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
protected updateNewInfo(r: number): string {
|
|
188
|
+
const query = `
|
|
189
|
+
update ${this.infoTable} set ${this.rate} = (${this.score} + ${r})/(${this.count} + 1), ${this.count} = ${this.count} + 1, ${this.score} = ${this.score} + ${r}, ${this.rate}${r} = ${this.rate}${r} + 1
|
|
190
|
+
where ${this.id} = ${this.db.param(1)}`;
|
|
191
|
+
return query;
|
|
192
|
+
}
|
|
193
|
+
protected updateOldInfo(newRate: number, oldRate: number): string {
|
|
194
|
+
if (newRate === oldRate) {
|
|
195
|
+
return '';
|
|
196
|
+
}
|
|
197
|
+
const delta = newRate - oldRate;
|
|
198
|
+
const query = `
|
|
199
|
+
update ${this.infoTable} set ${this.rate} = (${this.score} + (${delta}))/${this.count}, ${this.score} = ${this.score} + (${delta}), ${this.rate}${newRate} = ${this.rate}${newRate} + 1, ${this.rate}${oldRate} = ${this.rate}${oldRate} - 1
|
|
200
|
+
where ${this.id} = ${this.db.param(1)}`;
|
|
201
|
+
return query;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface SubmittedRate {
|
|
206
|
+
id: string;
|
|
207
|
+
author: string;
|
|
208
|
+
rate: number;
|
|
209
|
+
review: string;
|
|
210
|
+
}
|
|
211
|
+
export interface Rate {
|
|
212
|
+
id: string;
|
|
213
|
+
author: string;
|
|
214
|
+
rate: number;
|
|
215
|
+
time: Date;
|
|
216
|
+
review: string;
|
|
217
|
+
histories?: History[];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface History {
|
|
221
|
+
rate: number;
|
|
222
|
+
time: Date;
|
|
223
|
+
review: string;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export interface RateRepository {
|
|
227
|
+
create(rate: Rate, newInfo?: boolean, tx?: Transaction): Promise<number>
|
|
228
|
+
update(rate: Rate, oldRate: number, tx?: Transaction): Promise<number>
|
|
229
|
+
load(id: string, author: string, tx?: Transaction): Promise<Rate | null>
|
|
230
|
+
}
|
|
231
|
+
export interface RateSummaryRepository {
|
|
232
|
+
exist(id: string, tx?: Transaction): Promise<boolean>
|
|
233
|
+
}
|
|
234
|
+
export interface RateService {
|
|
235
|
+
getRate(id: string, author: string): Promise<Rate | null>
|
|
236
|
+
setUseful(rateId: string, userId: string): Promise<number>
|
|
237
|
+
removeUseful(rateId: string, userId: string): Promise<number>
|
|
238
|
+
rate(rateReq: SubmittedRate): Promise<number>
|
|
239
|
+
}
|
|
240
|
+
export interface UsefulRepository {
|
|
241
|
+
setUseful(rateId: string, userId: string): Promise<number>
|
|
242
|
+
removeUseful(rateId: string, userId: string): Promise<number>
|
|
243
|
+
}
|
|
244
|
+
// tslint:disable-next-line:max-classes-per-file
|
|
245
|
+
export class Rater implements RateService {
|
|
246
|
+
constructor(protected db: DB, protected rateRepository: RateRepository, protected rateSummaryRepository: RateSummaryRepository, protected usefulRepository: UsefulRepository) {
|
|
247
|
+
this.getRate = this.getRate.bind(this)
|
|
248
|
+
this.setUseful = this.setUseful.bind(this)
|
|
249
|
+
this.removeUseful = this.removeUseful.bind(this)
|
|
250
|
+
this.rate = this.rate.bind(this)
|
|
251
|
+
}
|
|
252
|
+
getRate(id: string, author: string): Promise<Rate | null> {
|
|
253
|
+
return this.rateRepository.load(id, author)
|
|
254
|
+
}
|
|
255
|
+
setUseful(rateId: string, userId: string): Promise<number> {
|
|
256
|
+
return this.usefulRepository.setUseful(rateId, userId)
|
|
257
|
+
}
|
|
258
|
+
removeUseful(rateId: string, userId: string): Promise<number> {
|
|
259
|
+
return this.usefulRepository.removeUseful(rateId, userId)
|
|
260
|
+
}
|
|
261
|
+
async rate(rateReq: SubmittedRate): Promise<number> {
|
|
262
|
+
const rate: Rate = {id: rateReq.id, author: rateReq.author, rate: rateReq.rate, time: new Date(), review: rateReq.review}
|
|
263
|
+
const tx = await this.db.beginTransaction()
|
|
264
|
+
try {
|
|
265
|
+
const summary = await this.rateSummaryRepository.exist(rateReq.id, tx)
|
|
266
|
+
if (!summary) {
|
|
267
|
+
const res = await this.rateRepository.create(rate, true, tx)
|
|
268
|
+
await tx.commit()
|
|
269
|
+
return res;
|
|
270
|
+
}
|
|
271
|
+
const exist = await this.rateRepository.load(rateReq.id, rateReq.author, tx)
|
|
272
|
+
if (!exist) {
|
|
273
|
+
const res = await this.rateRepository.create(rate, false, tx)
|
|
274
|
+
await tx.commit()
|
|
275
|
+
return res;
|
|
276
|
+
}
|
|
277
|
+
const history: History = { review: exist.review, rate: exist.rate, time: exist.time }
|
|
278
|
+
if (exist.histories && exist.histories.length > 0) {
|
|
279
|
+
const histories = exist.histories;
|
|
280
|
+
histories.push(history);
|
|
281
|
+
exist.histories = histories;
|
|
282
|
+
} else {
|
|
283
|
+
exist.histories = [history];
|
|
284
|
+
}
|
|
285
|
+
const oldRate = exist.rate
|
|
286
|
+
exist.rate = rateReq.rate
|
|
287
|
+
exist.review = rateReq.review
|
|
288
|
+
exist.time = new Date()
|
|
289
|
+
const count = await this.rateRepository.update(exist, oldRate, tx)
|
|
290
|
+
await tx.commit()
|
|
291
|
+
return count
|
|
292
|
+
} catch (err) {
|
|
293
|
+
await tx.rollback()
|
|
294
|
+
throw err
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compileOnSave": true,
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "es5",
|
|
5
|
+
"baseUrl": "./src",
|
|
6
|
+
"outDir": "./lib",
|
|
7
|
+
"module": "commonjs",
|
|
8
|
+
"moduleResolution": "node",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"pretty": true,
|
|
11
|
+
"sourceMap": false,
|
|
12
|
+
"declaration": false,
|
|
13
|
+
"experimentalDecorators": false,
|
|
14
|
+
"removeComments": true,
|
|
15
|
+
"lib": [
|
|
16
|
+
"es6"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"include": [
|
|
20
|
+
"src/**/*.ts"
|
|
21
|
+
],
|
|
22
|
+
"exclude": [
|
|
23
|
+
"node_modules"
|
|
24
|
+
]
|
|
25
|
+
}
|