mysqldb.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /**
  2. * @Author HonorLee (dev@honorlee.me)
  3. * @Version 1.0 (2018-05-04)
  4. * @License MIT
  5. */
  6. import Mysql = require('mysql');
  7. interface DBClient{
  8. getConnection:(callback:(err:Error,connection:any)=>void|null)=>void;
  9. }
  10. //数据对象
  11. export class MysqlDB{
  12. private _pool:DBClient;
  13. constructor(option:DBOption){
  14. this._pool = new MysqlPool().create(option);
  15. }
  16. public query(queryStr:string,callback:(err:Error,ret:any,fields:any)=>void){
  17. this._pool.getConnection((err,con)=>{
  18. if(err) return callback(err,null,null);
  19. con.query(queryStr,(err:Error, ret:any, fields:any)=>{
  20. if(err){
  21. LOGGER.error(`MysqlDB: "${queryStr}" error`);
  22. if(err.stack) LOGGER.error(err.stack);
  23. }
  24. con.release();
  25. if(callback) callback(err,ret,fields);
  26. });
  27. });
  28. }
  29. public querySync(queryStr:string){
  30. return new Promise((resolve)=>{
  31. this._pool.getConnection((err,con)=>{
  32. if(err){
  33. if(err.stack) LOGGER.error(err.stack);
  34. return resolve({err:err,data:[]});
  35. }
  36. con.query(queryStr,(err:Error, ret:any, fields:any)=>{
  37. con.release();
  38. if(err){
  39. LOGGER.error(`MysqlDB: "${queryStr}" error`);
  40. if(err.stack) LOGGER.error(err.stack);
  41. return resolve({err:err,data:[]});
  42. }
  43. resolve({err:null,data:ret});
  44. });
  45. });
  46. })
  47. }
  48. async count(tablename:string,whereObj?:any){
  49. let whereRule="";
  50. if(whereObj){
  51. whereRule = ` where ${MysqlUtil.objToKVString(whereObj,' and ')}`;
  52. }
  53. const sqlStr = `select count(1) as total from ${tablename} ${whereRule}`;
  54. const ret = await this.querySync(sqlStr) as MysqlResult;
  55. if(ret.err || ret.data.length==0) return 0;
  56. return ret.data[0].total;
  57. }
  58. async update(tablename:string,updateObj:any,whereObj:any){
  59. if(!tablename || !updateObj) return;
  60. let whereRule="";
  61. const updateRule = MysqlUtil.objToKVString(updateObj);
  62. if(whereObj){
  63. whereRule = ` where ${MysqlUtil.objToKVString(whereObj,' and ')}`;
  64. }
  65. const sqlStr = `update ${tablename} set ${updateRule} ${whereRule}`;
  66. return await this.querySync(sqlStr);
  67. }
  68. async select(tablename:string,queryField?:Array<string>|null,whereObj?:any){
  69. if(!tablename){
  70. const err = new Error("Missing table name")
  71. if(err.stack) LOGGER.error(err.stack);
  72. return {err:err,data:null};
  73. }
  74. let queryStr = "*",whereStr = "";
  75. if(queryField){
  76. queryField = queryField.map(v=>`\`${v}\``);
  77. queryStr = queryField.join(",");
  78. }
  79. if(whereObj) whereStr = ` where ${MysqlUtil.objToKVString(whereObj,' and ')}`;
  80. const sql = `select ${queryStr} from ${tablename} ${whereStr}`;
  81. return await this.querySync(sql);
  82. }
  83. async insert(tablename:string,insertData:any){
  84. if(!tablename){
  85. const err = new Error("Missing table name")
  86. if(err.stack) LOGGER.error(err.stack);
  87. return {err:err,data:null};
  88. }
  89. if(!insertData){
  90. const err = new Error("Missing insertData")
  91. if(err.stack) LOGGER.error(err.stack);
  92. return {err:err,data:null};
  93. }
  94. let sql = `insert into ${tablename} `;
  95. if(insertData.constructor == Object){
  96. sql += `set ${MysqlUtil.objToKVString(insertData)}`;
  97. }else if(insertData.constructor == Array && insertData.length>0 && insertData[0].constructor == Object){
  98. const fields = Object.keys(insertData[0]);
  99. const values = [];
  100. for(const data of insertData){
  101. const rawData = JSON.parse(JSON.stringify(data));
  102. const valueArr = [];
  103. for(const key of fields){
  104. if(rawData[key] != null || rawData[key] != undefined){
  105. if(rawData[key].constructor == String){
  106. rawData[key] = `"${rawData[key]}"`;
  107. }else if(rawData[key].constructor == Number){
  108. rawData[key] = Number(rawData[key]);
  109. }
  110. }else{
  111. rawData[key] = null;
  112. }
  113. valueArr.push(rawData[key]);
  114. }
  115. values.push(`(${valueArr.join(',')})`);
  116. }
  117. for(const i in fields){
  118. fields[i] = `\`${fields[i]}\``;
  119. }
  120. sql += `(${fields.join(',')}) values ${values.join(',')}`;
  121. }else{
  122. const err = new Error("InsertData type not support")
  123. if(err.stack) LOGGER.error(err.stack);
  124. return {err:err,data:null};
  125. }
  126. return await this.querySync(sql);
  127. }
  128. }
  129. export class MysqlUtil{
  130. /**
  131. * K-V数据转为Sql语句结构
  132. **/
  133. public static objToKVString(obj:any,joinStr?:string,keyPrefix?:string){
  134. const strArr:Array<string> = [];
  135. const dupObj = JSON.parse(JSON.stringify(obj));
  136. const keys = Object.keys(dupObj);
  137. let _extraSql = '',_extraLink = '';
  138. keyPrefix = keyPrefix?`${keyPrefix}.`:'';
  139. if(keys.length>0){
  140. keys.forEach(key =>{
  141. let v = dupObj[key];
  142. if(key=='_extra'){
  143. if(Array.isArray(v)){
  144. _extraSql = v.join(joinStr);
  145. }else if(v.constructor == Object){
  146. if(v.sql){
  147. _extraSql = v.sql;
  148. if(v.link!=null){
  149. _extraLink = v.link||'and';
  150. }
  151. }
  152. }else{
  153. _extraSql = v;
  154. }
  155. }else{
  156. if(typeof(v)=='string'){
  157. if(v[0]=='='){
  158. v = v.substring(1,-1);
  159. }else{
  160. if(!(v.substr(0,1)=="'" && v.substr(-1,1)=="'") && !(v.substr(0,1)=='"' && v.substr(-1,1)=='"')){
  161. v = `"${v}"`;
  162. }else{
  163. v = v;
  164. }
  165. }
  166. }
  167. keyPrefix = keyPrefix?`${keyPrefix}`:'';
  168. const sqlkey = `${keyPrefix}\`${key}\``;
  169. strArr.push(`${sqlkey}=${v}`);
  170. }
  171. })
  172. const sql = `${strArr.join(joinStr||',')} ${_extraLink} ${_extraSql}`;
  173. return sql;
  174. }else{
  175. return '';
  176. }
  177. }
  178. /**
  179. * 自动处理数据模型与数据,返回处理后符合模型的数据对象,包含数据安全性处理
  180. * @param module 数据模型
  181. * @param data 待处理数据
  182. * @param filter 自动过滤非模型字段数据(可选,默认不过滤)
  183. * @prarm reverse 反向处理,由写入处理变为取出处理,并自动根据模型字段类型进行可视化处理(可选,默认写入状态)
  184. **/
  185. public static analyzeFields(module:any,data:any,filter?:boolean,reverse?:boolean){
  186. const outData:AnyKeyString = {};
  187. Object.keys(data).forEach(key=>{
  188. if(module[key]!=undefined && data[key]!=null){
  189. let value = data[key];
  190. switch(module[key].type){
  191. case String:
  192. value = String(value);
  193. if(!reverse) value = Mysql.escape(value)
  194. break;
  195. case Number:
  196. value = Number(value)||0;
  197. break;
  198. case 'Date_Timestamp':
  199. case 'DateTime_Timestamp':
  200. case 'Time_Timestamp':
  201. value = Number(value)||0;
  202. if(value>0 && reverse){
  203. if(module[key].type=='Date_Timestamp') value = Moment.unix(value).format('YYYY-MM-DD');
  204. if(module[key].type=='DateTime_Timestamp') value = Moment.unix(value).format('YYYY-MM-DD HH:mm:ss');
  205. if(module[key].type=='Time_Timestamp') value = Moment.unix(value).format('HH:mm:ss');
  206. }
  207. break;
  208. }
  209. if(module[key].reverseKey && reverse) key = module[key].reverseKey;
  210. outData[key] = value;
  211. }else{
  212. if(!filter){
  213. if(data[key]==null){
  214. outData[key] = String(data[key]);
  215. }else{
  216. outData[key] = data[key];
  217. }
  218. }
  219. }
  220. })
  221. return outData;
  222. }
  223. public static escape(value:any){
  224. return Mysql.escape(value);
  225. }
  226. public static createPageLimit(page?:number,page_size?:number,orderField?:string){
  227. page = page||0;
  228. page_size = page_size||10;
  229. let sql = ``;
  230. if(orderField) sql += `ORDER BY \`${orderField}\` `;
  231. sql += `LIMIT ${page*page_size},${page_size}`;
  232. return sql;
  233. }
  234. public static createOrderBy(order_filed?:string,order_desc?:boolean){
  235. let orderStr = '';
  236. if(order_filed){
  237. orderStr = `order by \`${order_filed}\``;
  238. if(order_desc) orderStr += ' desc';
  239. }
  240. return orderStr;
  241. }
  242. }
  243. //连接池
  244. class MysqlPool{
  245. private _option:DBOption|null = null;
  246. private _pool:any;
  247. public Client:DBClient|null = null;
  248. create(option:DBOption):DBClient{
  249. this._option = option;
  250. this._pool = Mysql.createPool(option);
  251. this.Client = {
  252. getConnection:(callback)=>{
  253. if(!this._pool) throw new Error("Mysql pool not created!");
  254. if(!callback || typeof(callback)!='function') throw new Error("Mysql pool get connection lost callback!");
  255. this._pool.getConnection((err:any,connection:any)=>{
  256. callback(err,connection);
  257. });
  258. }
  259. }
  260. return this.Client;
  261. }
  262. }