flexbiz-server 12.5.6 → 12.5.8
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/package.json +1 -1
- package/server/controllers/controllerRPT.js +352 -25
- package/server/controllers/createHandler.js +839 -34
- package/server/modules/lists/ls-checkin.js +177 -179
package/package.json
CHANGED
|
@@ -1,30 +1,357 @@
|
|
|
1
|
-
const _=require("lodash")
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const _ = require("lodash");
|
|
2
|
+
const crypto = require("crypto");
|
|
3
|
+
const numeral = require('numeral');
|
|
4
|
+
const Moment = require("moment-timezone");
|
|
5
|
+
const moment = (time)=>{
|
|
6
|
+
return Moment.tz(time,configs.timezone|| 'Asia/Ho_Chi_Minh');
|
|
7
|
+
}
|
|
8
|
+
const reportinfo = global.getModel('reportinfo');
|
|
9
|
+
const {evalute,getParameterData} = require("../libs/utils");
|
|
10
|
+
const utils = require("../libs/utils");
|
|
11
|
+
const handlers = require("./handlers")
|
|
12
|
+
const {getCurrentSession,getCurrentStore} = require("../libs/sessionContext")
|
|
13
|
+
|
|
14
|
+
class controllerRPT {
|
|
15
|
+
constructor(router, rptId, fecthData, options = {}) {
|
|
16
|
+
this.name = rptId;
|
|
17
|
+
this.module = rptId;
|
|
18
|
+
this.router = router;
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.fecthDataFunc = fecthData;
|
|
21
|
+
global.report_controllers[rptId.toUpperCase()] = this;
|
|
22
|
+
//tạo base route
|
|
23
|
+
if (options.require_id_app === false) {
|
|
24
|
+
this.base_path = '/';
|
|
25
|
+
} else {
|
|
26
|
+
this.base_path = '/:id_app/';
|
|
27
|
+
}
|
|
28
|
+
//binding function
|
|
29
|
+
this.getData = this.getData.bind(this);
|
|
30
|
+
this.createRoute = this.createRoute.bind(this);
|
|
31
|
+
//Báo cáo có cần kiểm tra quyền truy cập hay không
|
|
32
|
+
this.notNeedRight = async (user,_options={})=>{
|
|
33
|
+
let nnr = options.notNeedRight;
|
|
34
|
+
if(_.isFunction(nnr)){
|
|
35
|
+
nnr = nnr(user,_options)
|
|
36
|
+
}
|
|
37
|
+
return nnr;
|
|
38
|
+
}
|
|
39
|
+
//xử lý kết quả báo cáo trước khi trả về cho user
|
|
40
|
+
this.handleResult = async (req,response,callback)=>{
|
|
41
|
+
if (response.error) {
|
|
42
|
+
return callback(response.error);
|
|
43
|
+
}else{
|
|
44
|
+
let data = response.result;
|
|
45
|
+
let rptInfo = await reportinfo.findOne({code:(req.query.report_info_code || rptId).toLowerCase()}).lean();
|
|
46
|
+
//console.log("dynamic hanlde data for",req.query.report_info_code || rptId,(rptInfo||{}).handle_data_expression_server);
|
|
47
|
+
if(rptInfo && rptInfo.handle_data_expression_server){
|
|
48
|
+
if(rptInfo.handle_data_expression_server.indexOf("async ")>=0) return callback("async function is not allow");
|
|
49
|
+
let func_string = `return (async ()=>{
|
|
5
50
|
try{
|
|
6
|
-
${
|
|
51
|
+
${rptInfo.handle_data_expression_server}
|
|
7
52
|
}catch(e){
|
|
8
53
|
return {error:e}
|
|
9
54
|
}
|
|
10
55
|
|
|
11
|
-
})`;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.
|
|
56
|
+
})`;
|
|
57
|
+
try{
|
|
58
|
+
let rs = await evalute(func_string,{data,moment,numeral,condition:req.query,user:req.user,_,utils,getData:getParameterData})();
|
|
59
|
+
if(rs){
|
|
60
|
+
if(rs.error){
|
|
61
|
+
console.error("handle_data_expression_server",rs.error,func_string,rptId);
|
|
62
|
+
return callback(rs.error.message || rs.error);
|
|
63
|
+
}
|
|
64
|
+
data = rs;
|
|
65
|
+
}
|
|
66
|
+
}catch(e){
|
|
67
|
+
console.error("handle_data_expression_server",e,func_string,rptId);
|
|
68
|
+
return callback(e.message || e);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
callback(null,data)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//Hàm tạo function chạy báo cáo
|
|
75
|
+
this.getDataFunc = ()=>{
|
|
76
|
+
const currentStore = getCurrentStore(); // lấy context tại thời điểm tạo hàm
|
|
77
|
+
console.log(`✅ [rptHanlder] [getDataFunc], storeId=${currentStore?.storeId || "none"},sessionID=${getCurrentSession()?._debugId}`);
|
|
78
|
+
if(options.stream ) return fecthData;
|
|
79
|
+
return async (req,callback)=>{
|
|
80
|
+
//chạy báo cáo
|
|
81
|
+
console.log(`✅ [rptHanlder] Chạy báo cáo ${rptId}, sessionID=${getCurrentSession()?._debugId}`);
|
|
82
|
+
try{
|
|
83
|
+
await fecthData(req,(e,data)=>{
|
|
84
|
+
const response={
|
|
85
|
+
error:e,
|
|
86
|
+
result:data
|
|
87
|
+
}
|
|
88
|
+
this.handleResult(req,response,(e,rs)=>{
|
|
89
|
+
callback(e,rs)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
}catch(e){
|
|
93
|
+
callback(e)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//route chạy báo cáo trả về dự liệu là json
|
|
98
|
+
const mainRoute =async (req,res)=>{
|
|
99
|
+
if(options.stream){
|
|
100
|
+
handlers.rptHandler(this,req,(e,rs)=>{
|
|
101
|
+
//có function xử lý kế quả
|
|
102
|
+
if(options.resHandler){
|
|
103
|
+
return options.resHandler(req,{error:e,data:rs},res);
|
|
104
|
+
}
|
|
105
|
+
//xử lý kết quả mặc định
|
|
106
|
+
if(e){
|
|
107
|
+
if(e.error){
|
|
108
|
+
return res.status(500).send(e);
|
|
109
|
+
}
|
|
110
|
+
return res.status(500).send({error:e.message || e});
|
|
111
|
+
}
|
|
112
|
+
if(rs=="__end_stream__"){
|
|
113
|
+
console.log("[controllerRPT] end stream...",rptId)
|
|
114
|
+
}else{
|
|
115
|
+
res.send(rs);
|
|
116
|
+
}
|
|
117
|
+
},res)
|
|
118
|
+
}else{
|
|
119
|
+
const {query,body,user,params,files} = req;
|
|
120
|
+
const user_agent = req.header('user-agent');
|
|
121
|
+
const ip = req.ip || req.headers['x-forwarded-for']||req.connection.remoteAddress;
|
|
122
|
+
try{
|
|
123
|
+
const _req = {handler:"rptHandler",query,body,user,params,user_agent,ip,files}
|
|
124
|
+
if(global.reportMainPool && !global.reportMainPool.fullQueue() && options.queue!=false ){
|
|
125
|
+
const id_task = `report-${crypto.randomBytes(20).toString('hex')}`;
|
|
126
|
+
//Nếu có thông báo phải chờ, thì gửi thông báo cho client
|
|
127
|
+
if(options.waiting_message){
|
|
128
|
+
const status_task = `${configs.api_url || configs.domain}/task-status/${id_task}`;
|
|
129
|
+
res.status(202).send([{message:options.waiting_message,status_task}])
|
|
130
|
+
}
|
|
131
|
+
global.reportMainPool.exec({
|
|
132
|
+
id_task,
|
|
133
|
+
req:_req,
|
|
134
|
+
module:rptId.toUpperCase(),
|
|
135
|
+
configs: JSON.stringify(configs)
|
|
136
|
+
},(response)=>{
|
|
137
|
+
setImmediate(()=>{
|
|
138
|
+
if(!options.waiting_message){
|
|
139
|
+
//có function xử lý kế quả
|
|
140
|
+
if(options.resHandler){
|
|
141
|
+
return options.resHandler(req,{error:response.error,data:response.result},res);
|
|
142
|
+
}
|
|
143
|
+
//xử lý kết quả mặc định
|
|
144
|
+
if(response.error) return res.status(400).send(response.error);
|
|
145
|
+
res.send(response.result);
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
}else{
|
|
150
|
+
if(options.waiting_message){
|
|
151
|
+
res.status(202).send([{message:options.waiting_message}])
|
|
152
|
+
}
|
|
153
|
+
handlers.rptHandler(this,_req,(e,returnvalue)=>{
|
|
154
|
+
if(!options.waiting_message){
|
|
155
|
+
//có function xử lý kế quả
|
|
156
|
+
if(options.resHandler){
|
|
157
|
+
return options.resHandler(req,{error:e,data:returnvalue},res);
|
|
158
|
+
}
|
|
159
|
+
//xử lý kết quả mặc định
|
|
160
|
+
if(e) return res.status(400).send(e);
|
|
161
|
+
res.send(returnvalue);
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
}catch(e){
|
|
167
|
+
console.error("Lỗi tạo job cho báo cáo",rptId,e);
|
|
168
|
+
res.status(400).send(e);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//Route chạy báo cáo trả về dữ liệu theo template (html,excel, docx hay json)
|
|
173
|
+
const excelRoute = async(req, res) => {
|
|
174
|
+
const {query,body,user,params,files} = req;
|
|
175
|
+
const user_agent = req.header('user-agent');
|
|
176
|
+
const ip = req.ip || req.headers['x-forwarded-for']||req.connection.remoteAddress;
|
|
177
|
+
try{
|
|
178
|
+
const _req = {handler:"rptExcelHandler",query,body,user,params,user_agent,ip,files}
|
|
179
|
+
if(global.reportMainPool && !global.reportMainPool.fullQueue() && options.queue!=false ){
|
|
180
|
+
const id_task = `report-export-${crypto.randomBytes(20).toString('hex')}`;
|
|
181
|
+
global.reportMainPool.exec({
|
|
182
|
+
id_task,
|
|
183
|
+
req:_req,
|
|
184
|
+
module:rptId.toUpperCase(),
|
|
185
|
+
configs: JSON.stringify(configs)
|
|
186
|
+
},(response)=>{
|
|
187
|
+
if(response.error) return res.status(400).send(response.error);
|
|
188
|
+
let returnvalue = response.result;
|
|
189
|
+
let ext = returnvalue.type;
|
|
190
|
+
const result = returnvalue.data;
|
|
191
|
+
switch(ext){
|
|
192
|
+
case "xlsx":{
|
|
193
|
+
console.log("[controllerRPT] send xls file from worker...");
|
|
194
|
+
res.setHeader('Content-Type', 'application/vnd.openxmlformats');
|
|
195
|
+
res.setHeader('Content-Disposition', 'attachment; filename=' + rptId + '.xlsx');
|
|
196
|
+
//convert sang buffer nếu data là một object có type ==Buffer
|
|
197
|
+
let data = result;
|
|
198
|
+
if(data?.type == "Buffer" && data?.data){
|
|
199
|
+
data = Buffer.from(data.data);
|
|
200
|
+
}
|
|
201
|
+
res.send(data);
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case "docx":{
|
|
205
|
+
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
|
|
206
|
+
res.setHeader('Content-Disposition', 'attachment; filename=' + rptId + '.docx');
|
|
207
|
+
//convert sang buffer nếu data là một object có type ==Buffer
|
|
208
|
+
let data = result;
|
|
209
|
+
if(data?.type == "Buffer" && data?.data){
|
|
210
|
+
data = Buffer.from(data.data);
|
|
211
|
+
}
|
|
212
|
+
res.send(data);
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
default:
|
|
216
|
+
res.send(result);
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
}else{
|
|
220
|
+
handlers.rptExcelHandler(this,_req,(e,returnvalue)=>{
|
|
221
|
+
if(e) return res.status(400).send(e);
|
|
222
|
+
let ext = returnvalue.type;
|
|
223
|
+
const result = returnvalue.data;
|
|
224
|
+
switch(ext){
|
|
225
|
+
case "xlsx":{
|
|
226
|
+
console.log("[controllerRPT] send xls file...");
|
|
227
|
+
res.setHeader('Content-Type', 'application/vnd.openxmlformats');
|
|
228
|
+
res.setHeader('Content-Disposition', 'attachment; filename=' + rptId + '.xlsx');
|
|
229
|
+
//convert sang buffer nếu data là một object có type ==Buffer
|
|
230
|
+
let data = result;
|
|
231
|
+
if(data?.type == "Buffer" && data?.data){
|
|
232
|
+
data = Buffer.from(data.data);
|
|
233
|
+
}
|
|
234
|
+
res.send(data);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
case "docx":{
|
|
238
|
+
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
|
|
239
|
+
res.setHeader('Content-Disposition', 'attachment; filename=' + rptId + '.docx');
|
|
240
|
+
//convert sang buffer nếu data là một object có type ==Buffer
|
|
241
|
+
let data = result;
|
|
242
|
+
if(data?.type == "Buffer" && data?.data){
|
|
243
|
+
data = Buffer.from(data.data);
|
|
244
|
+
}
|
|
245
|
+
res.send(data);
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
default:
|
|
249
|
+
res.send(result);
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
}catch(e){
|
|
254
|
+
res.status(400).send(e);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
router.route(`${this.base_path + this.module}`).get(mainRoute);
|
|
259
|
+
router.route(`${this.base_path + this.module}`).post(mainRoute);
|
|
260
|
+
|
|
261
|
+
router.route(`${this.base_path + this.module}/excel`).get(excelRoute);
|
|
262
|
+
router.route(`${this.base_path + this.module}/excel`).post(excelRoute);
|
|
263
|
+
}
|
|
264
|
+
//Hàm này sẽ chạy báo cáo trả về dữ liệu là json. Nếu báo cáo là dạng stream thì chỉ lấy kết quả cuối cùng
|
|
265
|
+
getData(req,callback){
|
|
266
|
+
const ctrl = this;
|
|
267
|
+
let callback_run = false;
|
|
268
|
+
setImmediate(()=>{
|
|
269
|
+
try{
|
|
270
|
+
ctrl.fecthDataFunc(req,(err,data,event)=>{
|
|
271
|
+
if(ctrl.options?.stream){
|
|
272
|
+
if(err) return callback(err);
|
|
273
|
+
//Chỉ gọi callback một lần khi event là data
|
|
274
|
+
if(event=="data" && !callback_run){
|
|
275
|
+
callback_run = true;
|
|
276
|
+
//Xử lý kết quả báo cáo
|
|
277
|
+
const response={
|
|
278
|
+
error:err,
|
|
279
|
+
result:data
|
|
280
|
+
}
|
|
281
|
+
this.handleResult(req,response,(e,rs)=>{
|
|
282
|
+
callback(e,rs)
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
}else{
|
|
286
|
+
callback_run = true;
|
|
287
|
+
//Xử lý kết quả báo cáo
|
|
288
|
+
const response={
|
|
289
|
+
error:err,
|
|
290
|
+
result:data
|
|
291
|
+
}
|
|
292
|
+
this.handleResult(req,response,(e,rs)=>{
|
|
293
|
+
callback(e,rs)
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}catch(e){
|
|
298
|
+
console.error("[report controller] [getData]",e);
|
|
299
|
+
callback(e.message);
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
//tạo custom route
|
|
304
|
+
createRoute(routeName, callbackRoute, _options = {method: 'GET',not_use_worker:false}) {
|
|
305
|
+
const self = this;
|
|
306
|
+
if (!_options.method) {
|
|
307
|
+
_options.method = 'GET';
|
|
308
|
+
} else {
|
|
309
|
+
_options.method = _options.method.toUpperCase();
|
|
310
|
+
}
|
|
311
|
+
const route_action_key = `rpt_route_action_${self.module}_${routeName}_${_options.method}`
|
|
312
|
+
self[route_action_key] = callbackRoute;
|
|
313
|
+
const handler = async function(req, res) {
|
|
314
|
+
const {query,body,user,params,files} = req;
|
|
315
|
+
const user_agent = req.header('user-agent');
|
|
316
|
+
const ip = req.ip || req.headers['x-forwarded-for']||req.connection.remoteAddress;
|
|
317
|
+
try{
|
|
318
|
+
const _req = {handler:"rptCreateRouteHandler",routeName,_options,route_action_key,query,body,user,params,user_agent,ip,files}
|
|
319
|
+
if(global.reportMainPool && !global.reportMainPool.fullQueue() && !_options.not_use_worker ){
|
|
320
|
+
const id_task = `report-create-route-${crypto.randomBytes(20).toString('hex')}`;
|
|
321
|
+
global.reportMainPool.exec({
|
|
322
|
+
id_task,
|
|
323
|
+
req:_req,
|
|
324
|
+
module:self.module.toUpperCase(),
|
|
325
|
+
configs: JSON.stringify(configs)
|
|
326
|
+
},(response)=>{
|
|
327
|
+
if(response.error) return res.status(400).send(response.error);
|
|
328
|
+
let {result} = response;
|
|
329
|
+
res.send(result);
|
|
330
|
+
})
|
|
331
|
+
}else{
|
|
332
|
+
handlers.rptCreateRouteHandler(this,_req,(e,returnvalue)=>{
|
|
333
|
+
if(e) return res.status(400).send(e);
|
|
334
|
+
res.send(returnvalue);
|
|
335
|
+
})
|
|
336
|
+
}
|
|
337
|
+
}catch(e){
|
|
338
|
+
res.status(400).send(e);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
if (_options.method == 'DELETE') {
|
|
342
|
+
this.router.route(`${this.base_path + this.module}/${routeName}`).delete(handler);
|
|
343
|
+
} else {
|
|
344
|
+
if (_options.method == 'POST') {
|
|
345
|
+
this.router.route(`${this.base_path + this.module}/${routeName}`).post(handler);
|
|
346
|
+
} else {
|
|
347
|
+
if (_options.method == 'PUT') {
|
|
348
|
+
this.router.route(`${this.base_path + this.module}/${routeName}`).put(handler);
|
|
349
|
+
} else {
|
|
350
|
+
this.router.route(`${this.base_path + this.module}/${routeName}`).get(handler);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
module.exports = controllerRPT;
|