generated from container/tmpl
			
				 12 changed files with 408 additions and 0 deletions
			
			
		| @ -0,0 +1,26 @@ | |||||
|  | { | ||||
|  |     // Use IntelliSense to learn about possible attributes. | ||||
|  |     // Hover to view descriptions of existing attributes. | ||||
|  |     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|  |     "version": "0.2.0", | ||||
|  |     "configurations": [ | ||||
|  |         { | ||||
|  |             "type": "node", | ||||
|  |             "request": "launch", | ||||
|  |             "name": "Launch Program", | ||||
|  |             "skipFiles": [ | ||||
|  |                 "<node_internals>/**" | ||||
|  |             ], | ||||
|  |             "program": "${workspaceFolder}\\index.js", | ||||
|  |             "args": [ | ||||
|  |                 "-g postgres://FashionAdmin:123456@10.8.30.36:5432/pepca", | ||||
|  |                 "--clickhouseUrl http://10.8.30.71", | ||||
|  |                 "--clickhousePort 30123", | ||||
|  |                 "--clickhouseDbPepcaM pg_pepca_m" | ||||
|  |             ], | ||||
|  |             "env": { | ||||
|  |                 "NODE_ENV": "development" | ||||
|  |             } | ||||
|  |         } | ||||
|  |     ] | ||||
|  | } | ||||
| @ -0,0 +1,7 @@ | |||||
|  | FROM repository.anxinyun.cn/base-images/nodejs12:20.10.12.2  | ||||
|  | 
 | ||||
|  | COPY . /var/app | ||||
|  | 
 | ||||
|  | WORKDIR /var/app | ||||
|  | 
 | ||||
|  | ENTRYPOINT [ "node", "index.js" ] | ||||
| @ -0,0 +1,91 @@ | |||||
|  | const args = require('args'); | ||||
|  | const path = require('path'); | ||||
|  | const moment = require('moment'); | ||||
|  | 
 | ||||
|  | const dev = process.env.NODE_ENV == 'development'; | ||||
|  | 
 | ||||
|  | // 启动参数 
 | ||||
|  | args.option(['g', 'postgresUrl'], 'PostgreSQL服务器URL'); | ||||
|  | 
 | ||||
|  | // clickHouse
 | ||||
|  | args.option('clickhouseUrl', 'ClickHouse URL'); | ||||
|  | args.option('clickhousePort', 'ClickHouse Port'); | ||||
|  | args.option('clickhouseDbPepcaM', 'ClickHouse pepca 数据库名称'); | ||||
|  | 
 | ||||
|  | const flags = args.parse(process.argv); | ||||
|  | 
 | ||||
|  | const POSTGRES_URL = process.env.POSTGRES_URL || flags.postgresUrl; | ||||
|  | const CLICKHOUST_URL = process.env.CLICKHOUST_URL || flags.clickhouseUrl; | ||||
|  | const CLICKHOUST_PORT = process.env.CLICKHOUST_PORT || flags.clickhousePort; | ||||
|  | const CLICKHOUST_USERNAME = process.env.CLICKHOUST_USERNAME || flags.clickhouseUsername; | ||||
|  | const CLICKHOUST_PASSWORD = process.env.CLICKHOUST_PASSWORD || flags.clickhousePassword; | ||||
|  | const CLICKHOUST_DB_PEP_CA_M = process.env.CLICKHOUST_DB_PEP_CA_M || flags.clickhouseDbPepcaM; | ||||
|  | 
 | ||||
|  | if (!POSTGRES_URL || !CLICKHOUST_URL || !CLICKHOUST_PORT || !CLICKHOUST_DB_PEP_CA_M) { | ||||
|  |     console.log('缺少启动参数,异常退出'); | ||||
|  |     args.showHelp(); | ||||
|  |     process.exit(1); | ||||
|  | } | ||||
|  | 
 | ||||
|  | const product = { | ||||
|  |     postgres: { | ||||
|  |         url: POSTGRES_URL, | ||||
|  |         opts: { | ||||
|  |             pool: { | ||||
|  |                 max: 20, | ||||
|  |                 min: 10, | ||||
|  |                 idle: 10000 | ||||
|  |             }, | ||||
|  |             define: { | ||||
|  |                 freezeTableName: true, // 固定表名
 | ||||
|  |                 timestamps: false // 不含列 "createAt"/"updateAt"/"DeleteAt"
 | ||||
|  |             }, | ||||
|  |             timezone: '+08:00', | ||||
|  |             logging: false | ||||
|  |         }, | ||||
|  |         models: [ | ||||
|  |             require('./lib/models') | ||||
|  |         ] | ||||
|  |     }, | ||||
|  |     clickhouse: { | ||||
|  |         url: CLICKHOUST_URL, | ||||
|  |         port: CLICKHOUST_PORT, | ||||
|  |         username: CLICKHOUST_USERNAME, | ||||
|  |         password: CLICKHOUST_PASSWORD, | ||||
|  |         databases: [{ | ||||
|  |             key: 'pg_pepca_m', | ||||
|  |             name: CLICKHOUST_DB_PEP_CA_M | ||||
|  |         }] | ||||
|  |     }, | ||||
|  |     logger: { | ||||
|  |         level: 'info', | ||||
|  |         json: false, | ||||
|  |         filename: path.join(__dirname, 'log', 'runtime.txt'), | ||||
|  |         colorize: false, | ||||
|  |         maxsize: 1024 * 1024 * 5, | ||||
|  |         zippedArchive: true, | ||||
|  |         maxFiles: 10, | ||||
|  |         prettyPrint: true, | ||||
|  |         label: '', | ||||
|  |         timestamp: () => moment().format('YYYY-MM-DD HH:mm:ss.SSS'), | ||||
|  |         tailable: true, | ||||
|  |         depth: null, | ||||
|  |         showLevel: true, | ||||
|  |         maxRetries: 1 | ||||
|  |     } | ||||
|  | }; | ||||
|  | 
 | ||||
|  | const development = { | ||||
|  |     postgres: product.postgres, | ||||
|  |     clickhouse: product.clickhouse, | ||||
|  |     logger: product.logger | ||||
|  | }; | ||||
|  | 
 | ||||
|  | if (dev) { | ||||
|  |     development.logger.filename = path.join(__dirname, 'log', 'development.log'); | ||||
|  |     development.logger.level = 'debug'; | ||||
|  | 
 | ||||
|  |     development.postgres.opts.logging = console.log; | ||||
|  | } | ||||
|  | 
 | ||||
|  | module.exports = dev ? development : product; | ||||
| @ -0,0 +1,27 @@ | |||||
|  | /** | ||||
|  |  * Created by Julin on 2022/11/07. | ||||
|  |  */ | ||||
|  | 'use strict'; | ||||
|  | 
 | ||||
|  | const schedule = require('node-schedule'); | ||||
|  | const config = require('./config'); | ||||
|  | const logger = require('./lib/logger'); | ||||
|  | const clickhouse = require('./lib/dc/clickhouse'); | ||||
|  | const postgres = require('./lib/dc/postgres'); | ||||
|  | const statProcessNodes = require('./lib/statProcessNodes'); | ||||
|  | 
 | ||||
|  | (function () { | ||||
|  |     logger(config.logger); | ||||
|  | 
 | ||||
|  |     clickhouse(config.clickhouse); | ||||
|  |     postgres(config.postgres); | ||||
|  | 
 | ||||
|  |     statProcessNodes(); | ||||
|  | 
 | ||||
|  |     process.logger.info('[FS-STATS]', 'started.'); | ||||
|  | 
 | ||||
|  |     // 每天 23:00 执行 
 | ||||
|  |     const job = schedule.scheduleJob('0 0 23 * * ?', function () { | ||||
|  |         statProcessNodes(); | ||||
|  |     }); | ||||
|  | })(); | ||||
| @ -0,0 +1,19 @@ | |||||
|  | pipeline { | ||||
|  |     agent { | ||||
|  |         node { | ||||
|  |             label 'jnlp-slave' | ||||
|  |         } | ||||
|  |     } | ||||
|  |      | ||||
|  |     stages { | ||||
|  |         stage('Building pep-stats ......') { | ||||
|  |             steps { | ||||
|  |                 sh 'switch-auth.sh anxinyun' | ||||
|  |                 buildName '#${BUILD_NUMBER} ~/fs-cloud/${JOB_NAME}:${IMAGE_VERSION}' | ||||
|  | 				buildDescription 'registry.cn-hangzhou.aliyuncs.com/${CLOUD}/${JOB_NAME}:${IMAGE_VERSION}' | ||||
|  |                 sh 'docker build -t registry.cn-hangzhou.aliyuncs.com/${CLOUD}/${JOB_NAME}:${IMAGE_VERSION} .' | ||||
|  | 				sh 'docker push registry.cn-hangzhou.aliyuncs.com/${CLOUD}/${JOB_NAME}:${IMAGE_VERSION}' | ||||
|  |             } | ||||
|  |         } | ||||
|  |     } | ||||
|  | } | ||||
| @ -0,0 +1,36 @@ | |||||
|  | /** | ||||
|  |  * Created by Julin on 2022/11/07. | ||||
|  |  */ | ||||
|  | 'use strict'; | ||||
|  | 
 | ||||
|  | const { ClickHouse } = require('clickhouse'); | ||||
|  | 
 | ||||
|  | module.exports = function (config) { | ||||
|  |     const defaultConfig = require('../../config').clickhouse; | ||||
|  |     config = config || defaultConfig; | ||||
|  |     if (config) { | ||||
|  |         try { | ||||
|  |             const { url, port, username, password, databases = [] } = config; | ||||
|  |             let clickhouse = {}; | ||||
|  |             for (let db of databases) { | ||||
|  |                 clickhouse[db.key] = new ClickHouse({ | ||||
|  |                     url, | ||||
|  |                     port, | ||||
|  |                     format: 'json', | ||||
|  |                     basicAuth: username && password ? { | ||||
|  |                         username, | ||||
|  |                         password, | ||||
|  |                     } : null, | ||||
|  |                     config: { | ||||
|  |                         database: db.name | ||||
|  |                     } | ||||
|  |                 }); | ||||
|  |             } | ||||
|  |             process.clickhouse = clickhouse; | ||||
|  |             return clickhouse; | ||||
|  |         } catch (err) { | ||||
|  |             process.logger.error('ClickHouse init error:', err); | ||||
|  |             process.exit(1); | ||||
|  |         } | ||||
|  |     } | ||||
|  | }; | ||||
| @ -0,0 +1,24 @@ | |||||
|  | 'use strict'; | ||||
|  | 
 | ||||
|  | const Sequelize = require('sequelize'); | ||||
|  | 
 | ||||
|  | module.exports = function (config) { | ||||
|  |     const defaultConfig = require('../../config').postgres; | ||||
|  |     config = config || defaultConfig; | ||||
|  | 
 | ||||
|  |     if (config) { | ||||
|  |         let orm = new Sequelize(config.url, config.opts); | ||||
|  |         let dc = { | ||||
|  |             orm, | ||||
|  |             ORM: Sequelize, | ||||
|  |             models: {} | ||||
|  |         }; | ||||
|  |         if (Array.isArray(config.models)) { | ||||
|  |             config.models.forEach(fn => { | ||||
|  |                 fn(dc); | ||||
|  |             }); | ||||
|  |         } | ||||
|  |         process.postgres = dc; | ||||
|  |         return dc; | ||||
|  |     } | ||||
|  | }; | ||||
| @ -0,0 +1,46 @@ | |||||
|  | 'use strict'; | ||||
|  | 
 | ||||
|  | const winston = require('winston'); | ||||
|  | const fs = require('fs'); | ||||
|  | const path = require('path'); | ||||
|  | const moment = require('moment'); | ||||
|  | 
 | ||||
|  | module.exports = function (config) { | ||||
|  |     const defaultConfig = require('../../config').logger; | ||||
|  |     config = config || defaultConfig; | ||||
|  |     config.level = config.level || 'error'; | ||||
|  |     config.filename = config.filename || path.join(process.cwd(), "log", "runtime.log"); | ||||
|  |     let dir = path.dirname(config.filename); | ||||
|  |     let logger = {}; | ||||
|  |     try { | ||||
|  |         if (!fs.existsSync(dir)) { | ||||
|  |             fs.mkdirSync(dir); | ||||
|  |         } | ||||
|  |         logger = new (winston.Logger)({ | ||||
|  |             level: config.level, | ||||
|  |             transports: [ | ||||
|  |                 new (winston.transports.Console)({ | ||||
|  |                     colorize: 'all', | ||||
|  |                     timestamp: () => moment().format('YYYY-MM-DD HH:mm:ss.SSS') | ||||
|  |                 }), | ||||
|  |                 new (winston.transports.File)(config) | ||||
|  |             ], | ||||
|  |             exitOnError: false | ||||
|  |         }); | ||||
|  |     } catch (err) { | ||||
|  |         console.log(err); | ||||
|  |         logger = new (winston.Logger)({ | ||||
|  |             level: config.level, | ||||
|  |             transports: [ | ||||
|  |                 new (winston.transports.Console)({ | ||||
|  |                     colorize: 'all', | ||||
|  |                     timestamp: () => moment().format('YYYY-MM-DD HH:mm:ss.SSS') | ||||
|  |                 }) | ||||
|  |             ], | ||||
|  |             exitOnError: false | ||||
|  |         }); | ||||
|  |     } | ||||
|  |     logger.log('debug', '[LOGGER]', 'Init.'); | ||||
|  |     process.logger = logger; | ||||
|  |     return logger; | ||||
|  | }; | ||||
| @ -0,0 +1,8 @@ | |||||
|  | /** | ||||
|  |  * Created by Julin on 2022/11/07. | ||||
|  |  */ | ||||
|  | 'use strict'; | ||||
|  | 
 | ||||
|  | module.exports = function (dc) { | ||||
|  |     require('./stats_process_nodes')(dc); | ||||
|  | }; | ||||
| @ -0,0 +1,32 @@ | |||||
|  | module.exports = function (dc) { | ||||
|  |     const StatsProcessNodes = dc.orm.define('statsProcessNodes', { | ||||
|  |         processVersionId: { | ||||
|  |             field: 'process_version_id', | ||||
|  |             type: dc.ORM.INTEGER, | ||||
|  |             primaryKey: true, | ||||
|  |             unique: true, | ||||
|  |             allowNull: false | ||||
|  |         }, | ||||
|  |         processId: { | ||||
|  |             field: 'process_id', | ||||
|  |             type: dc.ORM.INTEGER, | ||||
|  |             allowNull: false | ||||
|  |         }, | ||||
|  |         processName: { | ||||
|  |             field: 'process_name', | ||||
|  |             type: dc.ORM.STRING, | ||||
|  |             allowNull: false | ||||
|  |         }, | ||||
|  |         processNodesTotal: { | ||||
|  |             field: 'process_nodes_total', | ||||
|  |             type: dc.ORM.INTEGER, | ||||
|  |             allowNull: false | ||||
|  |         } | ||||
|  |     }, { | ||||
|  |         tableName: 'stats_process_nodes' | ||||
|  |     }); | ||||
|  | 
 | ||||
|  |     dc.models.StatsProcessNodes = StatsProcessNodes; | ||||
|  | 
 | ||||
|  |     return StatsProcessNodes; | ||||
|  | }; | ||||
| @ -0,0 +1,68 @@ | |||||
|  | /** | ||||
|  |  * Created by Julin on 2022/11/07. | ||||
|  |  */ | ||||
|  | 'use strict'; | ||||
|  | 
 | ||||
|  | module.exports = async function () { | ||||
|  |     try { | ||||
|  |         // 1. delete all rows in the table 
 | ||||
|  |         await clearStatsProcessNodes(); | ||||
|  |         // 2. insert data into the table 
 | ||||
|  |         let processes = await process.clickhouse['pg_pepca_m'].query(` | ||||
|  |             select v.id as version_id, | ||||
|  |                 v.process_id as process_id, | ||||
|  |                 p.name as process_name, | ||||
|  |                 v.bpmn_json | ||||
|  |             from pg_pepca_m.workflow_process_version as v | ||||
|  |             inner join pg_pepca_m.workflow_process as p on v.process_id=p.id | ||||
|  |             where v.current=true and p.is_enable=true and p.deleted=false | ||||
|  |             order by v.id desc | ||||
|  |         `).toPromise();
 | ||||
|  |         let dataToDB = processes.reduce((p, c) => { | ||||
|  |             let nodes = JSON.parse(c.bpmn_json); | ||||
|  |             let taskNodesCount = 0; | ||||
|  |             for (let key in nodes) { | ||||
|  |                 if (nodes[key].type == 'bpmn:UserTask') taskNodesCount++; | ||||
|  |             } | ||||
|  |             p.push({ | ||||
|  |                 processVersionId: c.version_id, | ||||
|  |                 processId: c.process_id, | ||||
|  |                 processName: c.process_name, | ||||
|  |                 processNodesTotal: taskNodesCount | ||||
|  |             }); | ||||
|  |             return p; | ||||
|  |         }, []); | ||||
|  |         await storageStatsProcessNodes(dataToDB); | ||||
|  |     } catch (err) { | ||||
|  |         process.logger.error('Something error in function [statProcessNodes]:', err); | ||||
|  |     } | ||||
|  | }; | ||||
|  | 
 | ||||
|  | async function clearStatsProcessNodes() { | ||||
|  |     const transaction = await process.postgres.orm.transaction(); | ||||
|  |     const models = process.postgres.models; | ||||
|  |     const { Op } = process.postgres.ORM; | ||||
|  |     try { | ||||
|  |         await models.StatsProcessNodes.destroy({ | ||||
|  |             where: { processVersionId: { [Op.gt]: 0 } }, | ||||
|  |             transaction | ||||
|  |         }); | ||||
|  |         await transaction.commit(); | ||||
|  |     } catch (err) { | ||||
|  |         await transaction.rollback(); | ||||
|  |         process.logger.error('Destroy data from Postgres DB [stats_process_nodes] error:', err); | ||||
|  |     } | ||||
|  | }; | ||||
|  | 
 | ||||
|  | async function storageStatsProcessNodes(data) { | ||||
|  |     const transaction = await process.postgres.orm.transaction(); | ||||
|  |     const models = process.postgres.models; | ||||
|  |     try { | ||||
|  |         await models.StatsProcessNodes.bulkCreate(data, { transaction }); | ||||
|  |         await transaction.commit(); | ||||
|  |         process.logger.info('Sync data to Postgres DB [stats_process_nodes]'); | ||||
|  |     } catch (err) { | ||||
|  |         await transaction.rollback(); | ||||
|  |         process.logger.error('Storage data to Postgres DB [stats_process_nodes] error:', err); | ||||
|  |     } | ||||
|  | }; | ||||
| @ -0,0 +1,24 @@ | |||||
|  | { | ||||
|  |   "name": "pep-stats", | ||||
|  |   "version": "1.0.0", | ||||
|  |   "description": "pep project statistics and analysis", | ||||
|  |   "main": "index.js", | ||||
|  |   "scripts": { | ||||
|  |     "test": "echo \"Error: no test specified\" && exit 1" | ||||
|  |   }, | ||||
|  |   "keywords": [ | ||||
|  |     "pep", | ||||
|  |     "stats" | ||||
|  |   ], | ||||
|  |   "author": "pengling", | ||||
|  |   "license": "ISC", | ||||
|  |   "dependencies": { | ||||
|  |     "args": "^3.0.7", | ||||
|  |     "clickhouse": "^2.6.0", | ||||
|  |     "moment": "^2.29.4", | ||||
|  |     "node-schedule": "^2.1.0", | ||||
|  |     "pg": "^8.8.0", | ||||
|  |     "sequelize": "^7.0.0-alpha2.2", | ||||
|  |     "winston": "^2.3.1" | ||||
|  |   } | ||||
|  | } | ||||
					Loading…
					
					
				
		Reference in new issue