| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | 1 | var path = require('path'); |
| 3 | 1 | var util = require('util'); |
| 4 | 1 | var crypto = require('crypto'); |
| 5 | 1 | var net = require('net'); |
| 6 | ||
| 7 | /** | |
| 8 | * 由于非常依赖promise,所以将promise设置为全局变量 | |
| 9 | * @type {[type]} | |
| 10 | */ | |
| 11 | 1 | global.Promise = require('es6-promise').Promise; |
| 12 | ||
| 13 | /** | |
| 14 | * 动态创建一个类 | |
| 15 | * 提供了继承、扩展、调用父级别方法等方法 | |
| 16 | * @return {[type]} [description] | |
| 17 | */ | |
| 18 | 1 | global.Class = function (prop, superCls) { |
| 19 | 36 | 'use strict'; |
| 20 | 36 | var cls = function () { |
| 21 | 93 | function T(args) { |
| 22 | 93 | for(var name in cls.__prop){ |
| 23 | 445 | var val = cls.__prop[name]; |
| 24 | 445 | this[name] = isObject(val) ? extend({}, val) : val; |
| 25 | } | |
| 26 | //自动执行init方法 | |
| 27 | 93 | if(isFunction(this.init)){ |
| 28 | //获取init返回值,如果返回一个promise,可以让后续执行在then之后 | |
| 29 | 93 | this.__initReturn = this.init.apply(this, args); |
| 30 | } | |
| 31 | 93 | return this; |
| 32 | } | |
| 33 | 93 | T.prototype = cls.prototype; |
| 34 | 93 | T.constructor = cls; |
| 35 | 93 | return new T(arguments); |
| 36 | }; | |
| 37 | //类的属性,不放在原型上,实例化的时候调用 | |
| 38 | 36 | cls.__prop = {}; |
| 39 | 36 | cls.extend = function(prop){ |
| 40 | 37 | if (isFunction(prop)) { |
| 41 | 32 | prop = prop(); |
| 42 | } | |
| 43 | 37 | if (isObject(prop)) { |
| 44 | 35 | for(var name in prop){ |
| 45 | 331 | var val = prop[name]; |
| 46 | 331 | if (isFunction(val)) { |
| 47 | 288 | this.prototype[name] = val; |
| 48 | }else{ | |
| 49 | 43 | cls.__prop[name] = isObject(val) ? extend({}, val) : val; |
| 50 | } | |
| 51 | } | |
| 52 | } | |
| 53 | 37 | return this; |
| 54 | }; | |
| 55 | 36 | cls.inherits = function(superCls){ |
| 56 | 25 | util.inherits(this, superCls); |
| 57 | //将父级的属性复制到当前类上 | |
| 58 | 25 | extend(cls.__prop, superCls.__prop); |
| 59 | 25 | return this; |
| 60 | }; | |
| 61 | 36 | if (superCls === true && isFunction(prop)) { |
| 62 | 1 | superCls = prop; |
| 63 | 1 | prop = undefined; |
| 64 | } | |
| 65 | 36 | if (isFunction(superCls)) { |
| 66 | 25 | cls.inherits(superCls); |
| 67 | } | |
| 68 | //调用父级方法 | |
| 69 | 36 | cls.prototype.super = cls.prototype.super_ = function(name, data){ |
| 70 | //如果当前类没有这个方法,则直接返回。 | |
| 71 | //用于在a方法调用父级的b方法 | |
| 72 | 23 | if (!this[name]) { |
| 73 | 1 | return; |
| 74 | } | |
| 75 | 22 | var super_ = this.constructor.super_; |
| 76 | //如果父级没有这个方法,那么直接返回 | |
| 77 | 22 | if (!isFunction(super_.prototype[name])) { |
| 78 | 1 | return; |
| 79 | } | |
| 80 | //如果参数不是数组,自动转为数组 | |
| 81 | 21 | if (!isArray(data)) { |
| 82 | 21 | data = [data]; |
| 83 | } | |
| 84 | 21 | while(1){ |
| 85 | 22 | if (this[name] === super_.prototype[name] && super_.super_) { |
| 86 | 1 | super_ = super_.super_; |
| 87 | }else{ | |
| 88 | 21 | break; |
| 89 | } | |
| 90 | } | |
| 91 | 21 | var method = super_.prototype[name]; |
| 92 | 21 | delete super_.prototype[name]; |
| 93 | 21 | var ret = method.apply(this, data); |
| 94 | 21 | super_.prototype[name] = method; |
| 95 | 21 | return ret; |
| 96 | }; | |
| 97 | 36 | if (prop) { |
| 98 | 35 | cls.extend(prop); |
| 99 | } | |
| 100 | 36 | return cls; |
| 101 | }; | |
| 102 | /** | |
| 103 | * extend, from jquery,具有深度复制功能 | |
| 104 | * @return {[type]} [description] | |
| 105 | */ | |
| 106 | 1 | global.extend = function(){ |
| 107 | 945 | 'use strict'; |
| 108 | 945 | var args = [].slice.call(arguments); |
| 109 | 945 | var deep = true; |
| 110 | 945 | var target = args.shift(); |
| 111 | 945 | if (isBoolean(target)) { |
| 112 | 440 | deep = target; |
| 113 | 440 | target = args.shift(); |
| 114 | } | |
| 115 | 945 | target = target || {}; |
| 116 | 945 | var length = args.length; |
| 117 | 945 | var options, name, src, copy, copyAsArray, clone; |
| 118 | 945 | for(var i = 0; i < length; i++){ |
| 119 | 1299 | options = args[i] || {}; |
| 120 | 1299 | if (isFunction(options)) { |
| 121 | 1 | options = options(); |
| 122 | } | |
| 123 | 1299 | for(name in options){ |
| 124 | 1647 | src = target[name]; |
| 125 | 1647 | copy = options[name]; |
| 126 | 1647 | if (src === copy) { |
| 127 | 257 | continue; |
| 128 | } | |
| 129 | 1390 | if (deep && copy && (isObject(copy) || (copyAsArray = isArray(copy) ))) { |
| 130 | 438 | if (copyAsArray) { |
| 131 | 237 | copyAsArray = false; |
| 132 | 237 | clone = src && isArray(src) ? src : []; |
| 133 | }else{ | |
| 134 | 201 | clone = src && isObject(src) ? src : {}; |
| 135 | } | |
| 136 | 438 | target[name] = extend(deep, clone, copy); |
| 137 | 952 | }else if (copy !== undefined) { |
| 138 | 952 | target[name] = copy; |
| 139 | } | |
| 140 | } | |
| 141 | } | |
| 142 | 945 | return target; |
| 143 | }; | |
| 144 | ||
| 145 | ||
| 146 | //Object上toString方法 | |
| 147 | 1 | var toString = Object.prototype.toString; |
| 148 | ||
| 149 | /** | |
| 150 | * 是否是boolean | |
| 151 | * @param {[type]} obj | |
| 152 | * @return {Boolean} | |
| 153 | */ | |
| 154 | 1 | global.isBoolean = function(obj){ |
| 155 | 1167 | 'use strict'; |
| 156 | 1167 | return toString.call(obj) === '[object Boolean]'; |
| 157 | }; | |
| 158 | /** | |
| 159 | * 是否是数字 | |
| 160 | * @param {[type]} obj [description] | |
| 161 | * @return {Boolean} [description] | |
| 162 | */ | |
| 163 | 1 | global.isNumber = function(obj){ |
| 164 | 541 | 'use strict'; |
| 165 | 541 | return toString.call(obj) === '[object Number]'; |
| 166 | }; | |
| 167 | /** | |
| 168 | * 是否是个对象 | |
| 169 | * @param {[type]} obj [description] | |
| 170 | * @return {Boolean} [description] | |
| 171 | */ | |
| 172 | 1 | global.isObject = function(obj){ |
| 173 | 3075 | 'use strict'; |
| 174 | 3075 | if (isBuffer(obj)) { |
| 175 | 1 | return false; |
| 176 | } | |
| 177 | 3074 | return toString.call(obj) === '[object Object]'; |
| 178 | }; | |
| 179 | /** | |
| 180 | * 是否是字符串 | |
| 181 | * @param {[type]} obj [description] | |
| 182 | * @return {Boolean} [description] | |
| 183 | */ | |
| 184 | 1 | global.isString = function(obj){ |
| 185 | 2163 | 'use strict'; |
| 186 | 2163 | return toString.call(obj) === '[object String]'; |
| 187 | }; | |
| 188 | /** | |
| 189 | * 是否是个function | |
| 190 | * @param {[type]} obj [description] | |
| 191 | * @return {Boolean} [description] | |
| 192 | */ | |
| 193 | 1 | global.isFunction = function(obj){ |
| 194 | 2167 | 'use strict'; |
| 195 | 2167 | return typeof obj === 'function'; |
| 196 | }; | |
| 197 | /** | |
| 198 | * 是否是日期 | |
| 199 | * @return {Boolean} [description] | |
| 200 | */ | |
| 201 | 1 | global.isDate = function(obj){ |
| 202 | 5 | 'use strict'; |
| 203 | 5 | return util.isDate(obj); |
| 204 | }; | |
| 205 | /** | |
| 206 | * 是否是正则 | |
| 207 | * @param {[type]} reg [description] | |
| 208 | * @return {Boolean} [description] | |
| 209 | */ | |
| 210 | 1 | global.isRegexp = function(obj){ |
| 211 | 7 | 'use strict'; |
| 212 | 7 | return util.isRegExp(obj); |
| 213 | }; | |
| 214 | /** | |
| 215 | * 是否是个错误 | |
| 216 | * @param {[type]} obj [description] | |
| 217 | * @return {Boolean} [description] | |
| 218 | */ | |
| 219 | 1 | global.isError = function(obj){ |
| 220 | 2 | 'use strict'; |
| 221 | 2 | return util.isError(obj); |
| 222 | }; | |
| 223 | /** | |
| 224 | * 判断对象是否为空 | |
| 225 | * @param {[type]} obj | |
| 226 | * @return {Boolean} | |
| 227 | */ | |
| 228 | 1 | global.isEmpty = function(obj){ |
| 229 | 526 | 'use strict'; |
| 230 | 526 | if (isObject(obj)) { |
| 231 | 262 | var key; |
| 232 | 262 | for(key in obj){ |
| 233 | 234 | return false; |
| 234 | } | |
| 235 | 28 | return true; |
| 236 | 264 | }else if (isArray(obj)) { |
| 237 | 91 | return obj.length === 0; |
| 238 | 173 | }else if (isString(obj)) { |
| 239 | 2 | return obj.length === 0; |
| 240 | 171 | }else if (isNumber(obj)) { |
| 241 | 4 | return obj === 0; |
| 242 | 167 | }else if (obj === null || obj === undefined) { |
| 243 | 164 | return true; |
| 244 | 3 | }else if (isBoolean(obj)) { |
| 245 | 2 | return !obj; |
| 246 | } | |
| 247 | 1 | return false; |
| 248 | }; | |
| 249 | /** | |
| 250 | * 是否是个标量 | |
| 251 | * @param {[type]} obj [description] | |
| 252 | * @return {Boolean} [description] | |
| 253 | */ | |
| 254 | 1 | global.isScalar = function(obj){ |
| 255 | 136 | 'use strict'; |
| 256 | 136 | return isBoolean(obj) || isNumber(obj) || isString(obj); |
| 257 | }; | |
| 258 | /** | |
| 259 | * 是否是个数组 | |
| 260 | * @type {Boolean} | |
| 261 | */ | |
| 262 | 1 | global.isArray = Array.isArray; |
| 263 | /** | |
| 264 | * 是否是IP | |
| 265 | * @type {Boolean} | |
| 266 | */ | |
| 267 | 1 | global.isIP = net.isIP; |
| 268 | 1 | global.isIP4 = net.isIP4; |
| 269 | 1 | global.isIP6 = net.isIP6; |
| 270 | /** | |
| 271 | * 是否是个文件 | |
| 272 | * @param {[type]} p [description] | |
| 273 | * @return {Boolean} [description] | |
| 274 | */ | |
| 275 | 1 | global.isFile = function(p){ |
| 276 | 112 | 'use strict'; |
| 277 | 112 | if (!fs.existsSync(p)) { |
| 278 | 73 | return false; |
| 279 | } | |
| 280 | 39 | var stats = fs.statSync(p); |
| 281 | 39 | return stats.isFile(); |
| 282 | }; | |
| 283 | /** | |
| 284 | * 是否是个目录 | |
| 285 | * @param {[type]} p [description] | |
| 286 | * @return {Boolean} [description] | |
| 287 | */ | |
| 288 | 1 | global.isDir = function(p){ |
| 289 | 3 | 'use strict'; |
| 290 | 3 | if (!fs.existsSync(p)) { |
| 291 | 1 | return false; |
| 292 | } | |
| 293 | 2 | var stats = fs.statSync(p); |
| 294 | 2 | return stats.isDirectory(); |
| 295 | }; | |
| 296 | /** | |
| 297 | * 是否是buffer | |
| 298 | * @type {Boolean} | |
| 299 | */ | |
| 300 | 1 | global.isBuffer = Buffer.isBuffer; |
| 301 | /** | |
| 302 | * 是否是个数字的字符串 | |
| 303 | * @param {[type]} obj [description] | |
| 304 | * @return {Boolean} [description] | |
| 305 | */ | |
| 306 | 1 | var numberReg = /^((\-?\d*\.?\d*(?:e[+-]?\d*(?:\d?\.?|\.?\d?)\d*)?)|(0[0-7]+)|(0x[0-9a-f]+))$/i; |
| 307 | 1 | global.isNumberString = function(obj){ |
| 308 | 14 | 'use strict'; |
| 309 | 14 | return numberReg.test(obj); |
| 310 | }; | |
| 311 | /** | |
| 312 | * 判断是否是个promise | |
| 313 | * @param {[type]} obj [description] | |
| 314 | * @return {Boolean} [description] | |
| 315 | */ | |
| 316 | 1 | global.isPromise = function(obj){ |
| 317 | 444 | 'use strict'; |
| 318 | 444 | return !!(obj && typeof obj.then === 'function'); |
| 319 | }; | |
| 320 | ||
| 321 | /** | |
| 322 | * 判断一个文件或者目录是否可写 | |
| 323 | * @param {[type]} p [description] | |
| 324 | * @return {Boolean} [description] | |
| 325 | */ | |
| 326 | 1 | global.isWritable = function(p){ |
| 327 | 3 | 'use strict'; |
| 328 | 3 | if (!fs.existsSync(p)) { |
| 329 | 1 | return false; |
| 330 | } | |
| 331 | 2 | var stats = fs.statSync(p); |
| 332 | 2 | var mode = stats.mode; |
| 333 | 2 | var uid = process.getuid ? process.getuid() : 0; |
| 334 | 2 | var gid = process.getgid ? process.getgid() : 0; |
| 335 | 2 | var owner = uid === stats.uid; |
| 336 | 2 | var group = gid === stats.gid; |
| 337 | 2 | return !!(owner && (mode & parseInt('00200', 8)) || |
| 338 | group && (mode & parseInt('00020', 8)) || | |
| 339 | (mode & parseInt('00002', 8))); | |
| 340 | }; | |
| 341 | ||
| 342 | /** | |
| 343 | * 递归创建目录,同步模式 | |
| 344 | * @param {[type]} p [description] | |
| 345 | * @param {[type]} mode [description] | |
| 346 | * @return {[type]} [description] | |
| 347 | */ | |
| 348 | 1 | global.mkdir = function(p, mode){ |
| 349 | 35 | 'use strict'; |
| 350 | 35 | mode = mode || '0777'; |
| 351 | 35 | if (fs.existsSync(p)) { |
| 352 | 34 | chmod(p, mode); |
| 353 | 34 | return true; |
| 354 | } | |
| 355 | 1 | var pp = path.dirname(p); |
| 356 | 1 | if (fs.existsSync(pp)) { |
| 357 | 1 | fs.mkdirSync(p, mode); |
| 358 | }else{ | |
| 359 | 0 | mkdir(pp, mode); |
| 360 | 0 | mkdir(p, mode); |
| 361 | } | |
| 362 | 1 | return true; |
| 363 | }; | |
| 364 | /** | |
| 365 | * 修改目录或者文件权限 | |
| 366 | * @param {[type]} p [description] | |
| 367 | * @param {[type]} mode [description] | |
| 368 | * @return {[type]} [description] | |
| 369 | */ | |
| 370 | 1 | global.chmod = function(p, mode){ |
| 371 | 44 | 'use strict'; |
| 372 | 44 | mode = mode || '0777'; |
| 373 | 44 | if (!fs.existsSync(p)) { |
| 374 | 0 | return true; |
| 375 | } | |
| 376 | 44 | return fs.chmodSync(p, mode); |
| 377 | }; | |
| 378 | /** | |
| 379 | * 获取文件内容 | |
| 380 | * @param {[type]} file [description] | |
| 381 | * @return {[type]} [description] | |
| 382 | */ | |
| 383 | 1 | global.getFileContent = function(file, encoding){ |
| 384 | 5 | 'use strict'; |
| 385 | 5 | if (!fs.existsSync(file)) { |
| 386 | 0 | return ''; |
| 387 | } | |
| 388 | 5 | return fs.readFileSync(file, { |
| 389 | encoding: encoding || 'utf8' | |
| 390 | }); | |
| 391 | }; | |
| 392 | /** | |
| 393 | * 设置文件内容 | |
| 394 | * @param {[type]} file [description] | |
| 395 | * @param {[type]} data [description] | |
| 396 | * @return {[type]} [description] | |
| 397 | */ | |
| 398 | 1 | global.setFileContent = function(file, data){ |
| 399 | 0 | 'use strict'; |
| 400 | 0 | return fs.writeFileSync(file, data); |
| 401 | }; | |
| 402 | /** | |
| 403 | * 大写首字符 | |
| 404 | * @param {[type]} name [description] | |
| 405 | * @return {[type]} [description] | |
| 406 | */ | |
| 407 | 1 | global.ucfirst = function(name){ |
| 408 | 1735 | 'use strict'; |
| 409 | 1735 | name = (name || '') + ''; |
| 410 | 1735 | return name.substr(0,1).toUpperCase() + name.substr(1).toLowerCase(); |
| 411 | }; | |
| 412 | /** | |
| 413 | * 获取字符串的md5 | |
| 414 | * @param {[type]} str [description] | |
| 415 | * @return {[type]} [description] | |
| 416 | */ | |
| 417 | 1 | global.md5 = function(str){ |
| 418 | 34 | 'use strict'; |
| 419 | 34 | var instance = crypto.createHash('md5'); |
| 420 | 34 | instance.update(str + ''); |
| 421 | 34 | return instance.digest('hex'); |
| 422 | }; | |
| 423 | /** | |
| 424 | * 生成一个promise,如果传入的参数是promise则直接返回 | |
| 425 | * @param {[type]} obj [description] | |
| 426 | * @return {[type]} [description] | |
| 427 | */ | |
| 428 | 1 | global.getPromise = function(obj, reject){ |
| 429 | 439 | 'use strict'; |
| 430 | 439 | if (isPromise(obj)) { |
| 431 | 1 | return obj; |
| 432 | } | |
| 433 | 438 | if (reject) { |
| 434 | 11 | return Promise.reject(obj); |
| 435 | } | |
| 436 | 427 | return Promise.resolve(obj); |
| 437 | }; | |
| 438 | /** | |
| 439 | * 生成一个defer对象 | |
| 440 | * @return {[type]} [description] | |
| 441 | */ | |
| 442 | 1 | global.getDefer = function(){ |
| 443 | 18 | 'use strict'; |
| 444 | 18 | var deferred = {}; |
| 445 | 18 | deferred.promise = new Promise(function(resolve, reject){ |
| 446 | 18 | deferred.resolve = resolve; |
| 447 | 18 | deferred.reject = reject; |
| 448 | }); | |
| 449 | 18 | return deferred; |
| 450 | }; | |
| 451 | /** | |
| 452 | * 快速生成一个object | |
| 453 | * @param {[type]} key [description] | |
| 454 | * @param {[type]} value [description] | |
| 455 | * @return {[type]} [description] | |
| 456 | */ | |
| 457 | 1 | global.getObject = function(key, value){ |
| 458 | 19 | 'use strict'; |
| 459 | 19 | var obj = {}; |
| 460 | 19 | if (!isArray(key)) { |
| 461 | 16 | obj[key] = value; |
| 462 | 16 | return obj; |
| 463 | } | |
| 464 | 3 | key.forEach(function(item, i){ |
| 465 | 5 | obj[item] = value[i]; |
| 466 | }); | |
| 467 | 3 | return obj; |
| 468 | }; | |
| 469 | /** | |
| 470 | * 将数组变成对象 | |
| 471 | * @param {[type]} arr [description] | |
| 472 | * @param {[type]} key [description] | |
| 473 | * @param {[type]} valueKeys [description] | |
| 474 | * @return {[type]} [description] | |
| 475 | */ | |
| 476 | 1 | global.arrToObj = function(arr, key, valueKey){ |
| 477 | 4 | 'use strict'; |
| 478 | 4 | var result = {}; |
| 479 | 4 | var arrResult = []; |
| 480 | 4 | arr.forEach(function(item){ |
| 481 | 8 | var keyValue = item[key]; |
| 482 | 8 | if (valueKey === null) { |
| 483 | 4 | arrResult.push(keyValue); |
| 484 | 4 | }else if (valueKey) { |
| 485 | 2 | result[keyValue] = item[valueKey]; |
| 486 | }else{ | |
| 487 | 2 | result[keyValue] = item; |
| 488 | } | |
| 489 | }) | |
| 490 | 4 | return valueKey === null ? arrResult : result; |
| 491 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | //该文件内容为原生对象的扩展 | |
| 2 | ||
| 3 | /** | |
| 4 | * 获取对象的值 | |
| 5 | * @param {[type]} obj [description] | |
| 6 | * @return {[type]} [description] | |
| 7 | */ | |
| 8 | 1 | Object.values = function(obj){ |
| 9 | 121 | 'use strict'; |
| 10 | 121 | var values = []; |
| 11 | 121 | for(var key in obj){ |
| 12 | 1522 | if (obj.hasOwnProperty(key)) { |
| 13 | 1522 | values.push(obj[key]) |
| 14 | } | |
| 15 | } | |
| 16 | 121 | return values; |
| 17 | }; | |
| 18 | /** | |
| 19 | * 数组求和 | |
| 20 | * @return {[type]} [description] | |
| 21 | */ | |
| 22 | 1 | Array.prototype.sum = function(){ |
| 23 | 7 | 'use strict'; |
| 24 | 7 | var count = 0; |
| 25 | 7 | this.forEach(function(item){ |
| 26 | 23 | count += parseFloat(item) || 0; |
| 27 | }); | |
| 28 | 7 | return count; |
| 29 | }; | |
| 30 |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | 1 | var path = require('path'); |
| 3 | ||
| 4 | ||
| 5 | 1 | var _alias = {}; |
| 6 | 1 | var _autoload_callbacks = []; |
| 7 | /** | |
| 8 | * thinkRequire获取到的路径 | |
| 9 | * @param {[type]} name [description] | |
| 10 | * @return {[type]} [description] | |
| 11 | */ | |
| 12 | 1 | global.getThinkRequirePath = function(name){ |
| 13 | 210 | 'use strict'; |
| 14 | 210 | if (_alias[name]) { |
| 15 | 164 | return _alias[name]; |
| 16 | } | |
| 17 | 46 | var result = ''; |
| 18 | 46 | _autoload_callbacks.some(function(callback){ |
| 19 | 46 | result = callback && callback(name); |
| 20 | 46 | if (result) { |
| 21 | 21 | return true; |
| 22 | } | |
| 23 | }); | |
| 24 | 46 | return result; |
| 25 | } | |
| 26 | /** | |
| 27 | * 自定义的require, 加入别名功能 | |
| 28 | * @type {[type]} | |
| 29 | */ | |
| 30 | 1 | global.thinkRequire = function(name){ |
| 31 | 176 | 'use strict'; |
| 32 | //如果不是字符串则直接返回 | |
| 33 | 176 | if (!isString(name)) { |
| 34 | 1 | return name; |
| 35 | } | |
| 36 | 175 | var path = name; |
| 37 | 175 | if (path[0] !== '/') { |
| 38 | 175 | path = getThinkRequirePath(name); |
| 39 | } | |
| 40 | 175 | if (path) { |
| 41 | 174 | var obj = require(path); |
| 42 | 174 | if (isFunction(obj)) { |
| 43 | //修正子类继承的方法获取到正确的文件名 | |
| 44 | 156 | obj.prototype.__filename = path; |
| 45 | } | |
| 46 | 174 | return obj; |
| 47 | } | |
| 48 | 1 | return require(name); |
| 49 | }; | |
| 50 | /** | |
| 51 | * 注册require | |
| 52 | * @param {Function} callback [description] | |
| 53 | * @return {[type]} [description] | |
| 54 | */ | |
| 55 | 1 | global.registerAutoload = function(callback){ |
| 56 | 1 | 'use strict'; |
| 57 | 1 | _autoload_callbacks.push(callback); |
| 58 | }; | |
| 59 | /** | |
| 60 | * 别名 | |
| 61 | * @return {[type]} [description] | |
| 62 | */ | |
| 63 | 1 | global.aliasImport = function(alias, classFile){ |
| 64 | 22 | 'use strict'; |
| 65 | 22 | if (isString(alias)) { |
| 66 | 21 | _alias[alias] = classFile; |
| 67 | }else{ | |
| 68 | 1 | _alias = extend(_alias, alias); |
| 69 | } | |
| 70 | }; | |
| 71 | ||
| 72 | //常用类的基类 | |
| 73 | 1 | ['Cache', 'Behavior', 'Controller', 'Session', 'Model', 'Db'].forEach(function(item){ |
| 74 | 6 | 'use strict'; |
| 75 | 6 | global[item] = function(super_, obj){ |
| 76 | 20 | if (isString(super_)) { |
| 77 | 1 | return Class(obj, thinkRequire(super_)); |
| 78 | } | |
| 79 | 19 | return Class(super_, thinkRequire(item)); |
| 80 | }; | |
| 81 | }); | |
| 82 | ||
| 83 | ||
| 84 | /** | |
| 85 | * 调用一个指定的行为 | |
| 86 | * @param {[type]} name [description] | |
| 87 | */ | |
| 88 | 1 | global.B = function(name, http, data){ |
| 89 | 12 | 'use strict'; |
| 90 | 12 | if (!name) { |
| 91 | 1 | return data; |
| 92 | } | |
| 93 | 11 | if (typeof name === 'function') { |
| 94 | 3 | return name(http, data); |
| 95 | } | |
| 96 | 8 | return thinkRequire(name + 'Behavior')(http).run(data); |
| 97 | }; | |
| 98 | ||
| 99 | /** | |
| 100 | * 处理标签扩展 | |
| 101 | * @return {[type]} [description] | |
| 102 | */ | |
| 103 | 1 | global.tag = function(name, http, data){ |
| 104 | 16 | 'use strict'; |
| 105 | 16 | var tags = (C('tag.' + name) || []).slice(); |
| 106 | //tag处理的数据 | |
| 107 | 16 | http.tag_data = data; |
| 108 | 16 | if (!tags.length) { |
| 109 | 8 | return getPromise(http.tag_data); |
| 110 | } | |
| 111 | 8 | var index = 0; |
| 112 | 8 | function runBehavior(){ |
| 113 | 16 | var behavior = tags[index++]; |
| 114 | 16 | if (!behavior) { |
| 115 | 8 | return getPromise(http.tag_data); |
| 116 | } | |
| 117 | 8 | var result = B(behavior, http, http.tag_data); |
| 118 | 8 | return getPromise(result).then(function(data){ |
| 119 | //如果返回值不是undefined,那么认为有返回值 | |
| 120 | 8 | if (data !== undefined) { |
| 121 | 6 | http.tag_data = data; |
| 122 | } | |
| 123 | 8 | return runBehavior(); |
| 124 | }) | |
| 125 | } | |
| 126 | 8 | return runBehavior(); |
| 127 | }; | |
| 128 | /** | |
| 129 | * 配置读取和写入 | |
| 130 | */ | |
| 131 | 1 | var _config = {}; |
| 132 | 1 | global.C = function(name, value){ |
| 133 | 259 | 'use strict'; |
| 134 | //获取所有的配置 | |
| 135 | 259 | if (arguments.length === 0) { |
| 136 | 10 | return _config; |
| 137 | } | |
| 138 | //清除所有的配置 | |
| 139 | 249 | if (name === null) { |
| 140 | 2 | _config = {}; |
| 141 | 2 | return; |
| 142 | } | |
| 143 | 247 | if (isString(name)) { |
| 144 | 243 | name = name.toLowerCase(); |
| 145 | //name里不含. 一级 | |
| 146 | 243 | if (name.indexOf('.') === -1) { |
| 147 | 224 | if (value === undefined) { |
| 148 | 221 | return _config[name]; |
| 149 | } | |
| 150 | 3 | _config[name] = value; |
| 151 | 3 | return; |
| 152 | } | |
| 153 | //name中含有. 二级 | |
| 154 | 19 | name = name.split('.'); |
| 155 | 19 | if (value === undefined) { |
| 156 | 18 | value = _config[name[0]] || {}; |
| 157 | 18 | return value[name[1]]; |
| 158 | } | |
| 159 | 1 | if (!_config[name[0]]) { |
| 160 | 1 | _config[name[0]] = {}; |
| 161 | } | |
| 162 | 1 | _config[name[0]][name[1]] = value; |
| 163 | }else{ | |
| 164 | 4 | _config = extend(_config, name); |
| 165 | } | |
| 166 | }; | |
| 167 | /** | |
| 168 | * 实例化Controller类,可以调用一个具体的Action | |
| 169 | * A('Home/Index'), A('Admin/Index/test') | |
| 170 | * @param {[type]} name [description] | |
| 171 | */ | |
| 172 | 1 | global.A = function(name, http, data){ |
| 173 | 8 | 'use strict'; |
| 174 | //将/转为:,兼容之前的方式 | |
| 175 | 8 | name = name.replace(/\//g, ':').split(':'); |
| 176 | 8 | http.group = name[0]; |
| 177 | 8 | http.controller = name[1]; |
| 178 | 8 | var App = thinkRequire('App'); |
| 179 | 8 | var instance = App.getBaseController(http); |
| 180 | 8 | if (!instance) { |
| 181 | 2 | return instance; |
| 182 | } | |
| 183 | 6 | var action = name[2]; |
| 184 | 6 | if (!action) { |
| 185 | 1 | return instance; |
| 186 | } | |
| 187 | 5 | http.action = action; |
| 188 | 5 | return getPromise(instance.__initReturn).then(function(){ |
| 189 | 5 | if (data && !isArray(data)) { |
| 190 | 1 | data = [data]; |
| 191 | } | |
| 192 | 5 | return App.execAction(instance, action, data); |
| 193 | }) | |
| 194 | }; | |
| 195 | ||
| 196 | /** | |
| 197 | * 快速文件读取和写入 | |
| 198 | * 默认写入到App/Runtime/Data目录下 | |
| 199 | */ | |
| 200 | 1 | global.F = function(name, value, rootPath){ |
| 201 | 9 | 'use strict'; |
| 202 | 9 | rootPath = rootPath || DATA_PATH; |
| 203 | 9 | var filePath = rootPath + '/' + name + '.json'; |
| 204 | 9 | if (value !== undefined) { |
| 205 | 4 | mkdir(path.dirname(filePath)); |
| 206 | 4 | fs.writeFileSync(filePath, JSON.stringify(value)); |
| 207 | 4 | chmod(filePath); |
| 208 | 4 | return; |
| 209 | } | |
| 210 | 5 | if (isFile(filePath)) { |
| 211 | 4 | var content = getFileContent(filePath); |
| 212 | 4 | if (content) { |
| 213 | 4 | return JSON.parse(content); |
| 214 | } | |
| 215 | } | |
| 216 | 1 | return false; |
| 217 | }; | |
| 218 | /** | |
| 219 | * 实例化模型 | |
| 220 | */ | |
| 221 | 1 | global.D = function(name, tablePrefix, config){ |
| 222 | 25 | 'use strict'; |
| 223 | 25 | if (name === undefined) { |
| 224 | 1 | return thinkRequire('Model')(name, tablePrefix, config); |
| 225 | } | |
| 226 | 24 | name = name.split(':'); |
| 227 | 24 | name[0] = name[0][0].toUpperCase() + name[0].slice(1); |
| 228 | 24 | var path = getThinkRequirePath(name[0] + 'Model'); |
| 229 | 24 | if (path) { |
| 230 | 3 | return thinkRequire(name[0] + 'Model')(name[1], tablePrefix, config); |
| 231 | }else{ | |
| 232 | 21 | return thinkRequire(name[1] === 'AdvModel' ? 'AdvModel' : 'Model')(name[0], tablePrefix, config); |
| 233 | } | |
| 234 | }; | |
| 235 | /** | |
| 236 | * 实例化模型基类 | |
| 237 | * @param {[type]} name [description] | |
| 238 | * @param {[type]} tablePrefix [description] | |
| 239 | * @param {[type]} config [description] | |
| 240 | */ | |
| 241 | 1 | global.M = function(name, tablePrefix, config){ |
| 242 | 5 | 'use strict'; |
| 243 | 5 | if (name === undefined) { |
| 244 | 1 | return thinkRequire('Model')(name, tablePrefix, config); |
| 245 | } | |
| 246 | 4 | name = name.split(':'); |
| 247 | 4 | var model = name[1] === 'AdvModel' ? 'AdvModel' : 'Model'; |
| 248 | 4 | return thinkRequire(model)(name[0], tablePrefix, config) |
| 249 | } | |
| 250 | /** | |
| 251 | * 缓存的设置和读取 | |
| 252 | * 获取返回的是一个promise | |
| 253 | */ | |
| 254 | 1 | global.S = function(name, value, options){ |
| 255 | 26 | 'use strict'; |
| 256 | 26 | if (isNumber(options)) { |
| 257 | 3 | options = {timeout: options}; |
| 258 | 23 | }else if (options === true) { |
| 259 | 9 | options = {type: true} |
| 260 | } | |
| 261 | 26 | options = options || {}; |
| 262 | 26 | var type = options.type === undefined ? C('cache_type') : options.type; |
| 263 | 26 | var cls = (type === true ? '' : ucfirst(type)) + 'Cache'; |
| 264 | 26 | var instance = thinkRequire(cls)(options); |
| 265 | //获取缓存 | |
| 266 | 26 | if (value === undefined) { |
| 267 | 12 | return instance.get(name); |
| 268 | } | |
| 269 | //移除缓存 | |
| 270 | 14 | if (value === null) { |
| 271 | 2 | return instance.rm(name); |
| 272 | } | |
| 273 | 12 | return instance.set(name, value, options.timeout); |
| 274 | }; | |
| 275 | /** | |
| 276 | * 语言 | |
| 277 | * @param {[type]} name [description] | |
| 278 | */ | |
| 279 | 1 | global.L = function(name){ |
| 280 | 4 | 'use strict'; |
| 281 | 4 | return name; |
| 282 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 模块别名,模块名到具体的路径,模块名不能有重复 | |
| 3 | * 使用thinkRequire加载模块时有效 | |
| 4 | * @type {Object} | |
| 5 | */ | |
| 6 | 1 | module.exports = { |
| 7 | Controller: THINK_LIB_PATH + '/Core/Controller.js', | |
| 8 | App: THINK_LIB_PATH + '/Core/App.js', | |
| 9 | Behavior: THINK_LIB_PATH + '/Util/Behavior.js', | |
| 10 | Cache: THINK_LIB_PATH + '/Util/Cache.js', | |
| 11 | Db: THINK_LIB_PATH + '/Core/Db.js', | |
| 12 | Dispatcher: THINK_LIB_PATH + '/Core/Dispatcher.js', | |
| 13 | Filter: THINK_LIB_PATH + '/Util/Filter.js', | |
| 14 | Http: THINK_LIB_PATH + '/Core/Http.js', | |
| 15 | //Log: THINK_LIB_PATH + '/Util/Log.js', | |
| 16 | Model: THINK_LIB_PATH + '/Core/Model.js', | |
| 17 | Session: THINK_LIB_PATH + '/Util/Session.js', | |
| 18 | Think: THINK_LIB_PATH + '/Core/Think.js', | |
| 19 | Valid: THINK_LIB_PATH + '/Util/Valid.js', | |
| 20 | View: THINK_LIB_PATH + '/Core/View.js', | |
| 21 | Cookie: THINK_LIB_PATH + '/Util/Cookie.js', | |
| 22 | WebSocket: THINK_LIB_PATH + '/Util/WebSocket.js' | |
| 23 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 框架默认配置 | |
| 3 | * 可以在App/Conf/config.js里修改下面的配置值 | |
| 4 | * @type {Object} | |
| 5 | */ | |
| 6 | 1 | module.exports = { |
| 7 | port: 8360, //监听端口 | |
| 8 | use_proxy: false, //是否使用代理访问,如:nginx。开启后不能通过ip+端口直接访问 | |
| 9 | encoding: 'utf8', //输出数据的编码 | |
| 10 | url_pathname_prefix: '', //不解析的pathname前缀 | |
| 11 | url_pathname_suffix: '.html', //不解析的pathname后缀,这样利于seo | |
| 12 | app_tag_on: true, //是否支持标签功能 | |
| 13 | url_resource_on: true, //是否监听静态资源类请求 | |
| 14 | url_resource_reg: /^(resource\/|static\/|favicon\.ico)/, //判断是否是静态资源的正则 | |
| 15 | url_route_on: true, //是否开启自定义路由功能 | |
| 16 | ||
| 17 | post_json_content_type: ['application/json'], //post数据为json时的content-type | |
| 18 | post_max_file_size: 1024 * 1024 * 1024, //上传文件大小限制,默认1G | |
| 19 | post_max_fields: 1000, //最大表单数 | |
| 20 | post_max_fields_size: 2 * 1024, //单个表单最大值 | |
| 21 | ||
| 22 | app_group_list: ['Home', 'Admin'], //分组列表 | |
| 23 | default_group: 'Home', //默认分组 | |
| 24 | default_controller: 'Index', //默认模块 | |
| 25 | default_action: 'index', //默认Action | |
| 26 | call_controller: 'Home:Index:_404', //controller不存在时执行方法,此配置表示调用Home分组下IndexController的_404Action方法 | |
| 27 | call_method: '__call', //当找不到方法时调用什么方法,这个方法存在时才有效 | |
| 28 | before_action: '__before', //调用一个action前调用的方法,会将action名传递进去 | |
| 29 | after_action: '__after', //调用一个action之后调用的方法,会将action名传递进去 | |
| 30 | url_params_bind: true, //方法参数绑定,将URL参数值绑定到action的参数上 | |
| 31 | action_suffix: 'Action', //action后缀 | |
| 32 | url_callback_name: 'callback', //jsonp格式的callback名字 | |
| 33 | json_content_type: 'application/json', //发送json时的content-type | |
| 34 | auto_send_content_type: true, //是否自动发送Content-Type,默认值为`tpl_content_type`配置值 | |
| 35 | log_process_pid: true, //记录进程的id,方便其他脚本处理。 | |
| 36 | use_cluster: false, //是否使用cluster,默认不使用,0:为cpu的数量,可以自定义值 | |
| 37 | autoload_path: {}, //autoload查找的path,用于thinkRequire加载自定义库的时候查找 | |
| 38 | create_server_fn: '', //自定义create server全局函数名,可以在Common/common.js里实现 | |
| 39 | ||
| 40 | load_ext_config: [], //加载额外的配置文件 CONF_PATH | |
| 41 | load_ext_file: [], //加载额外的文件 COMMON_PATH | |
| 42 | ||
| 43 | use_websocket: false, //是否使用websocket | |
| 44 | websocket_allow_origin: '', //允许从那里发送过来的websocket,可以是字符串、数组、回调函数,为空表示不检测 | |
| 45 | websocket_sub_protocal: '', //websocket子协议,可以是个字符串也可以是回调函数 | |
| 46 | websocket_message_handle: undefined, //websocket消息处理函数 | |
| 47 | ||
| 48 | error_tpl_path: THINK_PATH + '/View/error.html', //错误页模版 | |
| 49 | error_no_key: 'errno', //错误number的key | |
| 50 | error_no_default_value: 1000, //错误号默认值 | |
| 51 | error_msg_key: 'errmsg', //错误消息的key | |
| 52 | ||
| 53 | cookie_domain: '', //cookie有效域名 | |
| 54 | cookie_path: '/', //cookie路径 | |
| 55 | cookie_timeout: 0, //cookie失效时间,0为浏览器关闭,单位:秒 | |
| 56 | ||
| 57 | session_name: 'thinkjs', //session对应的cookie名称 | |
| 58 | session_type: 'File', //session存储类型, 空为内存,还可以为File | |
| 59 | session_path: '', //File类型下文件存储位置,默认为系统的tmp目录 | |
| 60 | session_options: {}, //session对应的cookie选项 | |
| 61 | session_sign: '', //session对应的cookie使用签名 | |
| 62 | session_timeout: 24 * 3600, //session失效时间,单位:秒 | |
| 63 | ||
| 64 | db_type: 'mysql', // 数据库类型 | |
| 65 | db_host: 'localhost', // 服务器地址 | |
| 66 | db_port: '', // 端口 | |
| 67 | db_name: '', // 数据库名 | |
| 68 | db_user: 'root', // 用户名 | |
| 69 | db_pwd: '', // 密码 | |
| 70 | db_prefix: 'think_', // 数据库表前缀 | |
| 71 | db_fieldtype_check: false, // 是否进行字段类型检查 | |
| 72 | db_fields_cache: true, // 启用字段缓存 | |
| 73 | db_charset: 'utf8', // 数据库编码默认采用utf8 | |
| 74 | db_nums_per_page: 20, //默认每页显示的条数 | |
| 75 | db_like_fields: [], //自动进行模糊查询,|连接,如: ['title', 'content'] | |
| 76 | db_cache_on: true, //是否启用查询缓存,如果关闭那么cache方法则无效 | |
| 77 | db_cache_type: '', //缓存类型,默认为内存缓存 | |
| 78 | db_cache_path: CACHE_PATH + '/db', //缓存路径,File类型下有效 | |
| 79 | db_cache_timeout: 3600, //缓存时间,默认为1个小时 | |
| 80 | ||
| 81 | tpl_content_type: 'text/html', //模版输出类型 | |
| 82 | tpl_file_suffix: '.html', //模版文件名后缀 | |
| 83 | tpl_file_depr: '_', //controller和action之间的分隔符 | |
| 84 | tpl_engine_type: 'ejs', //模版引擎名称 | |
| 85 | tpl_engine_config: {}, | |
| 86 | ||
| 87 | cache_type: 'File', //数据缓存类型 | |
| 88 | cache_timeout: 6 * 3600, //数据缓存有效期,单位: 秒 | |
| 89 | cache_path: CACHE_PATH, //缓存路径设置 (File缓存方式有效) | |
| 90 | cache_file_suffix: '.json', //File缓存方式下文件后缀名 | |
| 91 | cache_gc_hour: [4], //缓存清除的时间点,数据为小时 | |
| 92 | ||
| 93 | html_cache_on: false, //HTML静态缓存 | |
| 94 | html_cache_timeout: 3600, //缓存时间,单位为秒 | |
| 95 | html_cache_rules: {}, //缓存规则 | |
| 96 | html_cache_path: CACHE_PATH + '/html', | |
| 97 | html_cache_file_callback: undefined, //生成缓存文件的回调函数 | |
| 98 | html_cache_file_suffix: '.html', //缓存文件后缀名 | |
| 99 | ||
| 100 | memcache_host: '127.0.0.1', //memcache host | |
| 101 | memcache_port: 11211, //memecache端口 | |
| 102 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 不同模式下的配置文件 | |
| 3 | * 由于每个模式下的配置可能都比较少,所以放在一个文件里 | |
| 4 | * @type {Object} | |
| 5 | */ | |
| 6 | 1 | module.exports = { |
| 7 | cli: { | |
| 8 | use_cluster: false, | |
| 9 | html_cache_on: false, | |
| 10 | log_process_pid: false, | |
| 11 | clear_require_cache: false | |
| 12 | }, | |
| 13 | cli_debug: { | |
| 14 | clear_require_cache: false | |
| 15 | } | |
| 16 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 系统标签配置 | |
| 3 | * 可以在App/Conf/tag.js里进行修改 | |
| 4 | * @type {Object} | |
| 5 | */ | |
| 6 | ||
| 7 | /** | |
| 8 | * 命令行模式下执行后自动关闭数据库连接 | |
| 9 | * @return {[type]} [description] | |
| 10 | */ | |
| 11 | 1 | var closeDbConnect = function(){ |
| 12 | 1 | 'use strict'; |
| 13 | 1 | if(APP_MODE === 'cli'){ |
| 14 | 1 | thinkRequire('Model').close(); |
| 15 | } | |
| 16 | } | |
| 17 | /** | |
| 18 | * 解析提交的json数据 | |
| 19 | * @param {[type]} http [description] | |
| 20 | * @return {[type]} [description] | |
| 21 | */ | |
| 22 | 1 | var jsonParse = function(http){ |
| 23 | 1 | 'use strict'; |
| 24 | 1 | var jsonConentType = C('post_json_content_type'); |
| 25 | 1 | if (!isArray(jsonConentType)) { |
| 26 | 0 | jsonConentType = [jsonConentType]; |
| 27 | } | |
| 28 | 1 | if (jsonConentType.indexOf(http.contentType) > -1) { |
| 29 | 0 | http.post = JSON.parse(http.payload) || {}; |
| 30 | } | |
| 31 | } | |
| 32 | ||
| 33 | 1 | module.exports = { |
| 34 | //应用初始化 | |
| 35 | app_init: [], | |
| 36 | //表单数据解析 | |
| 37 | form_parse: [jsonParse], | |
| 38 | //pathinfo解析 | |
| 39 | path_info: [], | |
| 40 | //静态资源请求检测 | |
| 41 | resource_check: ['CheckResource'], | |
| 42 | //路由检测 | |
| 43 | route_check: ['CheckRoute'], | |
| 44 | //应用开始 | |
| 45 | app_begin: ['ReadHtmlCache'], | |
| 46 | //action执行初始化 | |
| 47 | action_init: [], | |
| 48 | //模版解析初始化 | |
| 49 | view_init: [], | |
| 50 | //定位模版文件 | |
| 51 | view_template: ['LocationTemplate'], | |
| 52 | //模版解析 | |
| 53 | view_parse: ['ParseTemplate'], | |
| 54 | //模版内容过滤 | |
| 55 | view_filter: [], | |
| 56 | //模版解析结束 | |
| 57 | view_end: ['WriteHtmlCache'], | |
| 58 | //action结束 | |
| 59 | action_end: [], | |
| 60 | //应用结束 | |
| 61 | app_end: [closeDbConnect] | |
| 62 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | 1 | var mime = require('mime'); |
| 3 | /** | |
| 4 | * 静态资源请求 | |
| 5 | * @return {[type]} [description] | |
| 6 | */ | |
| 7 | 1 | module.exports = Behavior(function(){ |
| 8 | 1 | 'use strict'; |
| 9 | 1 | return { |
| 10 | options: { | |
| 11 | 'url_resource_on': false | |
| 12 | }, | |
| 13 | run: function(){ | |
| 14 | 1 | if (!RESOURCE_PATH || !this.options.url_resource_on || !this.http.pathname) { |
| 15 | 0 | return false; |
| 16 | } | |
| 17 | 1 | var pathname = this.http.pathname; |
| 18 | 1 | if (pathname.indexOf('/') === 0) { |
| 19 | 1 | pathname = pathname.substr(1); |
| 20 | } | |
| 21 | 1 | var reg = C('url_resource_reg'); |
| 22 | //通过正则判断是否是静态资源请求 | |
| 23 | 1 | if (!reg.test(pathname)) { |
| 24 | 1 | return false; |
| 25 | } | |
| 26 | ||
| 27 | 0 | var file = RESOURCE_PATH + '/' + pathname; |
| 28 | 0 | var res = this.http.res; |
| 29 | 0 | if (fs.existsSync(file)) { |
| 30 | 0 | var contentType = mime.lookup(file); |
| 31 | 0 | var fileStream = fs.createReadStream(file); |
| 32 | 0 | res.setHeader('Content-Type', contentType + '; charset=' + C('encoding')); |
| 33 | 0 | fileStream.pipe(res); |
| 34 | 0 | fileStream.on('end', function(){ |
| 35 | 0 | res.end(); |
| 36 | }); | |
| 37 | }else{ | |
| 38 | 0 | res.statusCode = 404; |
| 39 | 0 | res.end(); |
| 40 | } | |
| 41 | //返回一个pendding promise, 不让后续执行 | |
| 42 | 0 | return getDefer().promise; |
| 43 | } | |
| 44 | }; | |
| 45 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 检测路由行为 | |
| 3 | * 通过自定义路由识别到对应的URL上 | |
| 4 | * @return {[type]} [description] | |
| 5 | */ | |
| 6 | 1 | var url = require('url'); |
| 7 | 1 | var Dispatcher = thinkRequire('Dispatcher'); |
| 8 | ||
| 9 | 1 | module.exports = Behavior(function(){ |
| 10 | 1 | 'use strict'; |
| 11 | 1 | return { |
| 12 | options: { | |
| 13 | 'url_route_on': false, //是否开启自定义URL路由 | |
| 14 | 'url_route_rules': [] //自定义URL路由规则 | |
| 15 | }, | |
| 16 | run: function(){ | |
| 17 | 1 | if (!this.options.url_route_on) { |
| 18 | 0 | return false; |
| 19 | } | |
| 20 | 1 | var routes = this.options.url_route_rules; |
| 21 | 1 | var length = routes.length; |
| 22 | 1 | if (length === 0) { |
| 23 | 1 | return false; |
| 24 | } | |
| 25 | 0 | var pathname = this.http.pathname; |
| 26 | 0 | var match; |
| 27 | 0 | for(var i = 0; i < length; i++){ |
| 28 | 0 | var route = routes[i]; |
| 29 | 0 | var rule = route[0]; |
| 30 | //正则路由 | |
| 31 | 0 | if (isRegexp(rule)) { |
| 32 | 0 | match = pathname.match(rule); |
| 33 | 0 | if (match) { |
| 34 | 0 | var result = this.parseRegExp(match, route[1], pathname); |
| 35 | 0 | if (result) { |
| 36 | 0 | return result; |
| 37 | } | |
| 38 | } | |
| 39 | }else{ | |
| 40 | //字符串路由 | |
| 41 | 0 | match = this.checkUrlMatch(pathname, rule); |
| 42 | 0 | if (match) { |
| 43 | 0 | return this.parseRule(rule, route[1], pathname); |
| 44 | } | |
| 45 | } | |
| 46 | } | |
| 47 | 0 | return false; |
| 48 | }, | |
| 49 | /** | |
| 50 | * 解析字符串路由 | |
| 51 | * @param {[type]} rule [description] | |
| 52 | * @param {[type]} route [description] | |
| 53 | * @param {[type]} pathname [description] | |
| 54 | * @return {[type]} [description] | |
| 55 | */ | |
| 56 | parseRule: function(rule, route, pathname){ | |
| 57 | 0 | route = this.getRoute(route); |
| 58 | 0 | if (!route) { |
| 59 | 0 | return false; |
| 60 | } | |
| 61 | 0 | pathname = pathname.split('/').filter(function(item){ |
| 62 | 0 | return item.trim(); |
| 63 | }); | |
| 64 | 0 | rule = rule.split('/').filter(function(item){ |
| 65 | 0 | return item.trim(); |
| 66 | }); | |
| 67 | 0 | var matches = {}; |
| 68 | 0 | rule.forEach(function(item){ |
| 69 | 0 | var pathitem = pathname.shift(); |
| 70 | 0 | if (item.indexOf(':') === 0) { |
| 71 | 0 | matches[item] = pathitem; |
| 72 | } | |
| 73 | }); | |
| 74 | //将剩余的pathname分割为querystring | |
| 75 | 0 | if (pathname.length) { |
| 76 | 0 | for(var i = 0,length = Math.ceil(pathname.length)/2; i < length; i++){ |
| 77 | 0 | this.http.get[pathname[i * 2]] = pathname[i * 2 + 1] || ''; |
| 78 | } | |
| 79 | } | |
| 80 | 0 | var values = Object.values(matches); |
| 81 | 0 | route = route.replace(/:(\d+)/g, function(a, b){ |
| 82 | 0 | return values[b - 1] || ''; |
| 83 | }); | |
| 84 | 0 | this.parseUrl(route); |
| 85 | 0 | return true; |
| 86 | }, | |
| 87 | /** | |
| 88 | * 检测URL是否匹配 | |
| 89 | * @param {[type]} pathname [description] | |
| 90 | * @param {[type]} rule [description] | |
| 91 | * @return {[type]} [description] | |
| 92 | */ | |
| 93 | checkUrlMatch: function(pathname, rule){ | |
| 94 | 0 | pathname = pathname.split('/').filter(function(item){ |
| 95 | 0 | return item.trim(); |
| 96 | }); | |
| 97 | 0 | rule = rule.split('/').filter(function(item){ |
| 98 | 0 | return item.trim(); |
| 99 | }); | |
| 100 | 0 | return rule.every(function(item, i){ |
| 101 | 0 | if (item.indexOf(':') === 0) { |
| 102 | 0 | if (item.indexOf('\\') > -1) { |
| 103 | 0 | var type = item.substr(-1); |
| 104 | 0 | if (type === 'd' && !isNumberString(pathname[i])) { |
| 105 | 0 | return false; |
| 106 | } | |
| 107 | } | |
| 108 | }else{ | |
| 109 | 0 | var pitem = pathname[i] || ''; |
| 110 | 0 | if (pitem.toLowerCase() !== item.toLowerCase()) { |
| 111 | 0 | return false; |
| 112 | } | |
| 113 | } | |
| 114 | 0 | return true; |
| 115 | }); | |
| 116 | }, | |
| 117 | /** | |
| 118 | * 解析转化后的url | |
| 119 | * @param {[type]} urlInfo [description] | |
| 120 | * @return {[type]} [description] | |
| 121 | */ | |
| 122 | parseUrl: function(urlInfo){ | |
| 123 | 0 | urlInfo = url.parse(urlInfo, true); |
| 124 | 0 | if (urlInfo.query) { |
| 125 | 0 | for(var key in urlInfo.query){ |
| 126 | 0 | if (urlInfo.query[key] || !(key in this.http.get)) { |
| 127 | 0 | this.http.get[key] = urlInfo.query[key]; |
| 128 | } | |
| 129 | } | |
| 130 | } | |
| 131 | 0 | var pathname = urlInfo.pathname || ''; |
| 132 | // 过滤调用pathname最后有/的情况 | |
| 133 | 0 | pathname = pathname.split('/').filter(function(item){ |
| 134 | 0 | return item.trim(); |
| 135 | }); | |
| 136 | 0 | this.http.action = Dispatcher.getAction(pathname.pop()); |
| 137 | 0 | this.http.controller = Dispatcher.getController(pathname.pop()); |
| 138 | 0 | this.http.group = Dispatcher.getGroup(pathname.pop()); |
| 139 | }, | |
| 140 | /** | |
| 141 | * 获取route | |
| 142 | * @param {[type]} route [description] | |
| 143 | * @return {[type]} [description] | |
| 144 | */ | |
| 145 | getRoute: function(route){ | |
| 146 | 0 | if (isObject(route)) { |
| 147 | //对应的请求类型 | |
| 148 | 0 | for(var method in route){ |
| 149 | //由于请求类型没有包含关系,这里可以直接用indexOf判断 | |
| 150 | 0 | if (method.toUpperCase().indexOf(this.http.method) > -1) { |
| 151 | 0 | return route[method]; |
| 152 | } | |
| 153 | } | |
| 154 | 0 | return; |
| 155 | } | |
| 156 | 0 | return route; |
| 157 | }, | |
| 158 | /** | |
| 159 | * 正则匹配路由 | |
| 160 | * @param {[type]} matches [description] | |
| 161 | * @param {[type]} route [description] | |
| 162 | * @param {[type]} pathname [description] | |
| 163 | * @return {[type]} [description] | |
| 164 | */ | |
| 165 | parseRegExp: function(matches, route, pathname){ | |
| 166 | 0 | route = this.getRoute(route); |
| 167 | 0 | if (!route) { |
| 168 | 0 | return false; |
| 169 | } | |
| 170 | 0 | route = route.replace(/:(\d+)/g, function(a, b){ |
| 171 | 0 | return matches[b] || ''; |
| 172 | }); | |
| 173 | 0 | pathname = pathname.replace(matches[0], ''); |
| 174 | 0 | pathname = pathname.split('/').filter(function(item){ |
| 175 | 0 | return item; |
| 176 | }); | |
| 177 | //将剩余的pathname分割为querystring | |
| 178 | 0 | if (pathname.length) { |
| 179 | 0 | for(var i = 0,length = Math.ceil(pathname.length)/2; i < length; i++){ |
| 180 | 0 | this.http.get[pathname[i * 2]] = pathname[i * 2 + 1] || ''; |
| 181 | } | |
| 182 | } | |
| 183 | 0 | this.parseUrl(route); |
| 184 | 0 | return true; |
| 185 | } | |
| 186 | }; | |
| 187 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 阻止ip来源访问 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Behavior(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | options: { | |
| 9 | deny_ip: [] //阻止的ip列表 | |
| 10 | }, | |
| 11 | run: function(){ | |
| 12 | 2 | if (this.options.deny_ip.length === 0) { |
| 13 | 1 | return true; |
| 14 | } | |
| 15 | 1 | var clientIps = this.http.ip().split('.'); |
| 16 | 1 | var flag = this.options.deny_ip.some(function(item){ |
| 17 | 1 | return item.split('.').every(function(num, i){ |
| 18 | 4 | if (num === '*' || num === clientIps[i]) { |
| 19 | 4 | return true; |
| 20 | } | |
| 21 | }); | |
| 22 | }); | |
| 23 | //如果在阻止的ip在列表里,则返回一个pendding promise,让后面的代码不执行 | |
| 24 | 1 | if (flag) { |
| 25 | 1 | this.http.res.statusCode = 403; |
| 26 | 1 | this.http.res.end(); |
| 27 | 1 | return getDefer().promise; |
| 28 | } | |
| 29 | 0 | return true; |
| 30 | } | |
| 31 | }; | |
| 32 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 定位模版的行为 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | ||
| 6 | 1 | module.exports = Behavior(function(){ |
| 7 | 1 | 'use strict'; |
| 8 | 1 | return { |
| 9 | run: function(templateFile){ | |
| 10 | 1 | if (!isFile(templateFile)) { |
| 11 | 1 | return this.parseTemplateFile(templateFile); |
| 12 | } | |
| 13 | }, | |
| 14 | /** | |
| 15 | * 解析模版文件 | |
| 16 | * @param {[type]} templateFile [description] | |
| 17 | * @return {[type]} [description] | |
| 18 | */ | |
| 19 | parseTemplateFile: function(templateFile){ | |
| 20 | 1 | templateFile = templateFile || ''; |
| 21 | 1 | if (!templateFile) { |
| 22 | 1 | templateFile = [ |
| 23 | VIEW_PATH, '/', this.http.group, '/', | |
| 24 | this.http.controller.toLowerCase(), | |
| 25 | C('tpl_file_depr'), | |
| 26 | this.http.action.toLowerCase(), | |
| 27 | C('tpl_file_suffix') | |
| 28 | ].join(''); | |
| 29 | 0 | }else if(templateFile.indexOf('/') > -1){ |
| 30 | //自动追加VIEW_PATH前缀 | |
| 31 | 0 | if (templateFile.indexOf('/') !== 0) { |
| 32 | 0 | templateFile = VIEW_PATH + '/' + templateFile; |
| 33 | } | |
| 34 | 0 | }else if(templateFile.indexOf(C('tpl_file_suffix')) === -1){ |
| 35 | 0 | var path = templateFile.split(':'); |
| 36 | 0 | var action = path.pop(); |
| 37 | 0 | var controller = path.pop() || this.http.controller.toLowerCase(); |
| 38 | 0 | var group = ucfirst(path.pop()) || this.http.group; |
| 39 | 0 | templateFile = [ |
| 40 | VIEW_PATH, '/', group, '/', | |
| 41 | controller, | |
| 42 | C('tpl_file_depr'), | |
| 43 | action, | |
| 44 | C('tpl_file_suffix') | |
| 45 | ].join(''); | |
| 46 | } | |
| 47 | 1 | if (!isFile(templateFile)) { |
| 48 | 0 | console.log(templateFile + ' is not exist', this.http); |
| 49 | 0 | return false; |
| 50 | } | |
| 51 | 1 | return templateFile; |
| 52 | } | |
| 53 | }; | |
| 54 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 调用对应的模版引擎解析模版 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Behavior(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | run: function(data){ | |
| 9 | 1 | var file = data.content || data.file; |
| 10 | //将模版文件路径写入到http对象上,供writehtmlcache里使用 | |
| 11 | 1 | this.http.tpl_file = file; |
| 12 | 1 | var engine = C('tpl_engine_type'); |
| 13 | //不使用模版引擎,直接返回文件内容 | |
| 14 | 1 | if (!engine) { |
| 15 | 0 | return getFileContent(file); |
| 16 | } | |
| 17 | 1 | var engineClass = ucfirst(engine) + 'Template'; |
| 18 | 1 | return thinkRequire(engineClass).fetch(file, data.var); |
| 19 | } | |
| 20 | }; | |
| 21 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | //获取模版文件 | |
| 2 | 1 | var getViewFile = thinkRequire('WriteHtmlCacheBehavior').getViewFile; |
| 3 | 1 | var fs = require('fs'); |
| 4 | /** | |
| 5 | * 读取HTML缓存 | |
| 6 | * @return {[type]} [description] | |
| 7 | */ | |
| 8 | 1 | module.exports = Behavior(function(){ |
| 9 | 1 | 'use strict'; |
| 10 | 1 | return { |
| 11 | options:{ | |
| 12 | 'html_cache_on': false, //是否开启缓存 | |
| 13 | 'html_cache_timeout': 3600, //缓存时间 | |
| 14 | 'html_cache_rules': {}, //缓存规则 | |
| 15 | 'html_cache_path': '', | |
| 16 | 'html_cache_file_callback': undefined, //生成缓存文件的回调函数 | |
| 17 | 'html_cache_file_suffix': '.html', //缓存文件扩展名 | |
| 18 | }, | |
| 19 | run: function(){ | |
| 20 | 1 | if (!this.options.html_cache_on || isEmpty(this.options.html_cache_rules)) { |
| 21 | 1 | return false; |
| 22 | } | |
| 23 | 0 | var cacheTime = this.getCacheTime(); |
| 24 | 0 | if (cacheTime === false) { |
| 25 | 0 | return false; |
| 26 | } | |
| 27 | 0 | if (this.checkCacheTime(cacheTime)) { |
| 28 | 0 | this.responseCacheContent(); |
| 29 | //return a pending promise | |
| 30 | 0 | return getDefer().promise; |
| 31 | } | |
| 32 | 0 | return false; |
| 33 | }, | |
| 34 | /** | |
| 35 | * 返回缓存内容 | |
| 36 | * @return {[type]} [description] | |
| 37 | */ | |
| 38 | responseCacheContent: function(){ | |
| 39 | 0 | var http = this.http; |
| 40 | 0 | var fileStream = fs.createReadStream(this.options.html_cache_path + '/' + http.html_filename); |
| 41 | 0 | http.setHeader('Content-Type', 'text/html'); |
| 42 | 0 | http.sendTime('Exec-Time'); |
| 43 | 0 | http.sendCookie(); |
| 44 | 0 | fileStream.pipe(http.res); |
| 45 | 0 | fileStream.on('end', function(){ |
| 46 | 0 | http.end(); |
| 47 | }); | |
| 48 | }, | |
| 49 | /** | |
| 50 | * 获取缓存时间 | |
| 51 | * @return {[type]} [description] | |
| 52 | */ | |
| 53 | getCacheTime: function(){ | |
| 54 | /** | |
| 55 | * rules数据格式 | |
| 56 | * { | |
| 57 | * 'index:index': ['index_home', 1800, html_cache_callback] | |
| 58 | * } | |
| 59 | * @type {[type]} | |
| 60 | */ | |
| 61 | 0 | var rules = this.options.html_cache_rules; |
| 62 | 0 | var group = this.http.group.toLowerCase(); |
| 63 | 0 | var controller = this.http.controller.toLowerCase(); |
| 64 | 0 | var action = this.http.action.toLowerCase(); |
| 65 | 0 | var list = [ |
| 66 | group + ':' + controller + ':' + action, | |
| 67 | controller + ':' + action, | |
| 68 | action, | |
| 69 | '*' | |
| 70 | ]; | |
| 71 | 0 | var html = []; |
| 72 | 0 | list.some(function(item){ |
| 73 | 0 | if (item in rules) { |
| 74 | 0 | html = rules[item]; |
| 75 | 0 | return true; |
| 76 | } | |
| 77 | }); | |
| 78 | 0 | if (isEmpty(html)) { |
| 79 | 0 | return false; |
| 80 | } | |
| 81 | 0 | if (!isArray(html)) { |
| 82 | 0 | html = [html]; |
| 83 | } | |
| 84 | 0 | var rule = html[0]; |
| 85 | //将cookie变量传递进去 | |
| 86 | 0 | var cookiePars = {}; |
| 87 | 0 | for(var name in this.http.cookie){ |
| 88 | 0 | cookiePars['cookie.' + name] = this.http.cookie[name]; |
| 89 | } | |
| 90 | 0 | var pars = extend({}, this.http.get, cookiePars, { |
| 91 | ':group': group, | |
| 92 | ':controller': controller, | |
| 93 | ':action': action | |
| 94 | }); | |
| 95 | 0 | rule = rule.replace(/\{([\w\:]+)\}/g, function(a, name){ |
| 96 | 0 | return pars[name] || ''; |
| 97 | }); | |
| 98 | 0 | var callback = html[2] || C('html_cache_file_callback') || this.getCacheFilename; |
| 99 | 0 | var filename = callback(rule, this.http) + this.options.html_cache_file_suffix; |
| 100 | //静态缓存文件名 | |
| 101 | 0 | this.http.html_filename = filename; |
| 102 | 0 | var cacheTime = html[1] || this.options.html_cache_timeout; |
| 103 | 0 | return cacheTime; |
| 104 | }, | |
| 105 | /** | |
| 106 | * | |
| 107 | * @param {[type]} key [description] | |
| 108 | * @return {[type]} [description] | |
| 109 | */ | |
| 110 | getCacheFilename: function(key){ | |
| 111 | 0 | var value = md5(key); |
| 112 | 0 | return value.substr(0, 1) + '/' + value; |
| 113 | }, | |
| 114 | /** | |
| 115 | * [checkCacheTime description] | |
| 116 | * @return {[type]} [description] | |
| 117 | */ | |
| 118 | checkCacheTime: function(cacheTime){ | |
| 119 | 0 | var cacheFile = this.options.html_cache_path + '/' + this.http.html_filename; |
| 120 | 0 | if (!isFile(cacheFile)) { |
| 121 | 0 | return false; |
| 122 | } | |
| 123 | 0 | var cacheFileMtime = fs.statSync(cacheFile).mtime.getTime(); |
| 124 | 0 | var tplFile = getViewFile(this.http); |
| 125 | 0 | if (tplFile) { |
| 126 | 0 | if (!isFile(tplFile)) { |
| 127 | 0 | return false; |
| 128 | } | |
| 129 | 0 | var tplFileMtime = fs.statSync(tplFile).mtime.getTime(); |
| 130 | //模版文件有更新 | |
| 131 | 0 | if (tplFileMtime > cacheFileMtime) { |
| 132 | 0 | return false; |
| 133 | } | |
| 134 | } | |
| 135 | 0 | if (Date.now() > (cacheFileMtime + cacheTime * 1000)) { |
| 136 | 0 | return false; |
| 137 | } | |
| 138 | 0 | return true; |
| 139 | } | |
| 140 | }; | |
| 141 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 模版文件列表 | |
| 3 | * @type {Object} | |
| 4 | */ | |
| 5 | 1 | var path = require('path'); |
| 6 | 1 | var fs = require('fs'); |
| 7 | ||
| 8 | 1 | var tplFiles = {}; |
| 9 | /** | |
| 10 | * 写入html缓存 | |
| 11 | * @return {[type]} [description] | |
| 12 | */ | |
| 13 | 1 | module.exports = Behavior(function(){ |
| 14 | 1 | 'use strict'; |
| 15 | 1 | return { |
| 16 | options: { | |
| 17 | 'html_cache_on': false, //是否开启缓存 | |
| 18 | 'html_cache_path': '' | |
| 19 | }, | |
| 20 | run: function(content){ | |
| 21 | 1 | if (!this.options.html_cache_on || !this.http.html_filename) { |
| 22 | 1 | return content; |
| 23 | } | |
| 24 | 0 | this.recordViewFile(); |
| 25 | 0 | var file = this.options.html_cache_path + '/' + this.http.html_filename; |
| 26 | 0 | mkdir(path.dirname(file)); |
| 27 | //异步方式写入缓存 | |
| 28 | 0 | fs.writeFile(file, content); |
| 29 | 0 | return content; |
| 30 | }, | |
| 31 | /** | |
| 32 | * 记录模版文件名 | |
| 33 | * @return {[type]} [description] | |
| 34 | */ | |
| 35 | recordViewFile: function(){ | |
| 36 | 0 | var tplFile = this.http.tpl_file; |
| 37 | 0 | var key = this.http.group + ':' + this.http.controller + ':' + this.http.action; |
| 38 | 0 | tplFiles[key] = tplFile; |
| 39 | } | |
| 40 | }; | |
| 41 | }); | |
| 42 | /** | |
| 43 | * 获取模版文件 | |
| 44 | * @param {[type]} http [description] | |
| 45 | * @return {[type]} [description] | |
| 46 | */ | |
| 47 | 1 | module.exports.getViewFile = function(http){ |
| 48 | 0 | 'use strict'; |
| 49 | 0 | var key = http.group + ':' + http.controller + ':' + http.action; |
| 50 | 0 | return tplFiles[key]; |
| 51 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var cluster = require('cluster'); |
| 2 | 1 | var fs = require('fs'); |
| 3 | 1 | var domain = require('domain'); |
| 4 | ||
| 5 | 1 | var thinkHttp = thinkRequire('Http'); |
| 6 | 1 | var Dispatcher = thinkRequire('Dispatcher'); |
| 7 | ||
| 8 | ||
| 9 | 1 | var App = module.exports = {}; |
| 10 | /** | |
| 11 | * 根据http里的group和controller获取对应的controller实例 | |
| 12 | * @param {[type]} http [description] | |
| 13 | * @return {[type]} [description] | |
| 14 | */ | |
| 15 | 1 | App.getBaseController = function(http){ |
| 16 | 9 | 'use strict'; |
| 17 | 9 | var gc = ucfirst(http.group) + '/' + ucfirst(http.controller) + 'Controller'; |
| 18 | 9 | var path = getThinkRequirePath(gc); |
| 19 | 9 | if (path) { |
| 20 | 7 | return require(path)(http); |
| 21 | } | |
| 22 | } | |
| 23 | /** | |
| 24 | * controller不存在时调用的默认controller | |
| 25 | * @return {[type]} [description] | |
| 26 | */ | |
| 27 | 1 | App.getCallController = function(http){ |
| 28 | 0 | 'use strict'; |
| 29 | 0 | var config = C('call_controller'); |
| 30 | 0 | if (!config) { |
| 31 | 0 | return; |
| 32 | } | |
| 33 | 0 | if (isString(config)) { |
| 34 | 0 | config = config.split(':'); |
| 35 | } | |
| 36 | 0 | var action = Dispatcher.getAction(config.pop()); |
| 37 | 0 | var controller = Dispatcher.getController(config.pop()); |
| 38 | 0 | var group = Dispatcher.getGroup(config.pop()); |
| 39 | 0 | var instance = this.getBaseController({ |
| 40 | group: group, | |
| 41 | controller: controller | |
| 42 | }) | |
| 43 | 0 | if (instance && isFunction(instance[action + C('action_suffix')])) { |
| 44 | 0 | http.group = group; |
| 45 | 0 | http.controller = controller; |
| 46 | 0 | http.action = action; |
| 47 | } | |
| 48 | 0 | return instance; |
| 49 | } | |
| 50 | ||
| 51 | /** | |
| 52 | * 执行具体的action,调用前置和后置操作 | |
| 53 | * @return {[type]} [description] | |
| 54 | */ | |
| 55 | 1 | App.execAction = function(controller, action, data, callMethod){ |
| 56 | 6 | 'use strict'; |
| 57 | //action操作 | |
| 58 | 6 | var act = action + C('action_suffix'); |
| 59 | 6 | var flag = false; |
| 60 | //action不存在时执行魔术方法 | |
| 61 | 6 | if (callMethod && !isFunction(controller[act])) { |
| 62 | 0 | var call = C('call_method'); |
| 63 | 0 | if (call && isFunction(controller[call])) { |
| 64 | 0 | flag = true; |
| 65 | 0 | act = call; |
| 66 | } | |
| 67 | } | |
| 68 | //action不存在 | |
| 69 | 6 | if (!isFunction(controller[act])) { |
| 70 | 0 | return getPromise('action `' + action + '` not found. ', true); |
| 71 | } | |
| 72 | 6 | var promise = getPromise(); |
| 73 | //action前置操作 | |
| 74 | 6 | var before = C('before_action'); |
| 75 | 6 | if (before && isFunction(controller[before])) { |
| 76 | 0 | promise = controller[before](action); |
| 77 | } | |
| 78 | 6 | promise = promise.then(function(){ |
| 79 | //action魔术方法只传递action参数 | |
| 80 | 6 | if (flag) { |
| 81 | 0 | return controller[act](action); |
| 82 | } | |
| 83 | 6 | if (data) { |
| 84 | 4 | return controller[act].apply(controller, data); |
| 85 | }else{ | |
| 86 | 2 | return controller[act](); |
| 87 | } | |
| 88 | }); | |
| 89 | //action后置操作 | |
| 90 | 6 | var after = C('after_action'); |
| 91 | 6 | if (after && isFunction(controller[after])) { |
| 92 | 0 | promise = promise.then(function(){ |
| 93 | 0 | return controller[after](action); |
| 94 | }) | |
| 95 | } | |
| 96 | 6 | return promise; |
| 97 | } | |
| 98 | ||
| 99 | /** | |
| 100 | * 获取action的形参 | |
| 101 | * @return {[type]} [description] | |
| 102 | */ | |
| 103 | 1 | App.getActionParams = function(fn, http){ |
| 104 | 1 | 'use strict'; |
| 105 | //注释的正则 | |
| 106 | 1 | var commentReg = /((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg; |
| 107 | //获取形参的正则 | |
| 108 | 1 | var parsReg = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; |
| 109 | 1 | var toString = fn.toString().replace(commentReg, ''); |
| 110 | 1 | var match = toString.match(parsReg)[1].split(/\s*,\s*/); |
| 111 | //匹配到形参 | |
| 112 | 1 | var params; |
| 113 | 1 | if (match && match.length) { |
| 114 | 1 | params = match.map(function(item){ |
| 115 | 1 | return http.post[item] || http.get[item] || ''; |
| 116 | }); | |
| 117 | } | |
| 118 | 1 | return params; |
| 119 | } | |
| 120 | /** | |
| 121 | * 执行 | |
| 122 | * @param {[type]} http [description] | |
| 123 | * @return {[type]} [description] | |
| 124 | */ | |
| 125 | 1 | App.exec = function(http){ |
| 126 | 1 | 'use strict'; |
| 127 | 1 | var controller = this.getBaseController(http) || this.getCallController(http); |
| 128 | //controller不存在 | |
| 129 | 1 | if (!controller) { |
| 130 | 0 | var err = new Error('Controller `' + http.controller + '` not found. ' + http.pathname); |
| 131 | 0 | return getPromise(err, true); |
| 132 | } | |
| 133 | 1 | var params; |
| 134 | 1 | var actionFn = controller[http.action + C('action_suffix')]; |
| 135 | //参数绑定 | |
| 136 | 1 | if (C('url_params_bind') && isFunction(actionFn)) { |
| 137 | 1 | params = this.getActionParams(actionFn, http); |
| 138 | } | |
| 139 | 1 | var promise = getPromise(controller.__initReturn); |
| 140 | 1 | var self = this; |
| 141 | 1 | return promise.then(function(){ |
| 142 | 1 | return self.execAction(controller, http.action, params, true); |
| 143 | }) | |
| 144 | } | |
| 145 | /** | |
| 146 | * 发送错误信息 | |
| 147 | * @param {[type]} error [description] | |
| 148 | * @return {[type]} [description] | |
| 149 | */ | |
| 150 | 1 | App.sendError = function(http, error){ |
| 151 | 0 | 'use strict'; |
| 152 | 0 | var message = isError(error) ? error.stack : error; |
| 153 | 0 | console.log(message); |
| 154 | 0 | if (!http.res) { |
| 155 | 0 | return; |
| 156 | } | |
| 157 | 0 | if (APP_DEBUG) { |
| 158 | 0 | http.res.statusCode = 500; |
| 159 | 0 | http.res.end(message); |
| 160 | }else{ | |
| 161 | 0 | http.res.statusCode = 500; |
| 162 | 0 | http.setHeader('Content-Type', 'text/html; charset=' + C('encoding')); |
| 163 | 0 | var readStream = fs.createReadStream(C('error_tpl_path')); |
| 164 | 0 | readStream.pipe(http.res); |
| 165 | 0 | readStream.on('end', function(){ |
| 166 | 0 | http.res.end(); |
| 167 | }); | |
| 168 | } | |
| 169 | } | |
| 170 | ||
| 171 | /** | |
| 172 | * run | |
| 173 | * @return {[type]} [description] | |
| 174 | */ | |
| 175 | 1 | App.run = function(){ |
| 176 | 1 | 'use strict'; |
| 177 | 1 | if (APP_MODE && App.mode[APP_MODE]) { |
| 178 | 1 | return App.mode[APP_MODE](); |
| 179 | } | |
| 180 | 0 | return App.mode.http(); |
| 181 | }; | |
| 182 | /** | |
| 183 | * 不同模式下的run | |
| 184 | * @type {Object} | |
| 185 | */ | |
| 186 | 1 | App.mode = { |
| 187 | //命令行模式 | |
| 188 | cli: function(){ | |
| 189 | 1 | 'use strict'; |
| 190 | 1 | var defaultHttp = thinkHttp.getDefaultHttp(process.argv[2]); |
| 191 | 1 | thinkHttp(defaultHttp.req, defaultHttp.res).run().then(App.listener); |
| 192 | }, | |
| 193 | //HTTP模式 | |
| 194 | http: function(){ | |
| 195 | 0 | 'use strict'; |
| 196 | 0 | var clusterNums = C('use_cluster'); |
| 197 | //不使用cluster | |
| 198 | 0 | if (!clusterNums) { |
| 199 | 0 | return App.createServer(); |
| 200 | } | |
| 201 | //使用cpu的个数 | |
| 202 | 0 | if (clusterNums === true) { |
| 203 | 0 | clusterNums = require('os').cpus().length; |
| 204 | } | |
| 205 | 0 | if (cluster.isMaster) { |
| 206 | 0 | for (var i = 0; i < clusterNums; i++) { |
| 207 | 0 | cluster.fork(); |
| 208 | } | |
| 209 | 0 | cluster.on('exit', function(worker) { |
| 210 | 0 | console.log('worker ' + worker.process.pid + ' died'); |
| 211 | 0 | process.nextTick(function(){ |
| 212 | 0 | cluster.fork(); |
| 213 | }); | |
| 214 | }); | |
| 215 | }else { | |
| 216 | 0 | App.createServer(); |
| 217 | } | |
| 218 | } | |
| 219 | }; | |
| 220 | /** | |
| 221 | * 创建服务 | |
| 222 | * @return {[type]} [description] | |
| 223 | */ | |
| 224 | 1 | App.createServer = function(){ |
| 225 | 0 | 'use strict'; |
| 226 | //自定义创建server | |
| 227 | 0 | var createServerFn = C('create_server_fn'); |
| 228 | 0 | if (createServerFn) { |
| 229 | 0 | if (isFunction(createServerFn)) { |
| 230 | 0 | return createServerFn(App); |
| 231 | 0 | }else if (isFunction(global[createServerFn])) { |
| 232 | 0 | return global[createServerFn](App); |
| 233 | } | |
| 234 | } | |
| 235 | 0 | var server = require('http').createServer(function (req, res) { |
| 236 | 0 | thinkHttp(req, res).run().then(App.listener); |
| 237 | }); | |
| 238 | 0 | thinkRequire('WebSocket')(server, App).run(); |
| 239 | 0 | server.listen(C('port')); |
| 240 | } | |
| 241 | /** | |
| 242 | * 监听回调函数 | |
| 243 | * @param {[type]} http [description] | |
| 244 | * @return {[type]} [description] | |
| 245 | */ | |
| 246 | 1 | App.listener = function(http){ |
| 247 | 1 | 'use strict'; |
| 248 | //自动发送thinkjs和版本的header | |
| 249 | 1 | http.setHeader('X-Powered-By', 'thinkjs-' + THINK_VERSION); |
| 250 | //禁止远程直接用带端口的访问,websocket下允许 | |
| 251 | 1 | if (C('use_proxy') && http.host !== http.hostname && !http.websocket) { |
| 252 | 0 | http.res.statusCode = 403; |
| 253 | 0 | http.res.end(); |
| 254 | 0 | return getDefer().promise; |
| 255 | } | |
| 256 | 1 | var domainInstance = domain.create(); |
| 257 | 1 | var deferred = getDefer(); |
| 258 | 1 | domainInstance.on('error', function(err){ |
| 259 | 0 | App.sendError(http, err); |
| 260 | 0 | deferred.reject(err); |
| 261 | }); | |
| 262 | 1 | domainInstance.run(function(){ |
| 263 | 1 | return tag('app_init', http).then(function(){ |
| 264 | 1 | return Dispatcher(http).run(); |
| 265 | }).then(function(){ | |
| 266 | 1 | return tag('app_begin', http); |
| 267 | }).then(function(){ | |
| 268 | 1 | return tag('action_init', http); |
| 269 | }).then(function(){ | |
| 270 | 1 | return App.exec(http); |
| 271 | }).then(function(){ | |
| 272 | 1 | return tag('app_end', http); |
| 273 | }).catch(function(err){ | |
| 274 | 0 | App.sendError(http, err); |
| 275 | }).then(function(){ | |
| 276 | 1 | deferred.resolve(); |
| 277 | }) | |
| 278 | }); | |
| 279 | 1 | return deferred.promise; |
| 280 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Controller 基类 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | var fs = require('fs'); |
| 6 | 1 | var path = require('path'); |
| 7 | 1 | var url = require('url'); |
| 8 | ||
| 9 | 1 | module.exports = Class(function() { |
| 10 | 1 | 'use strict'; |
| 11 | //callback正则 | |
| 12 | 1 | var callbackReg = /[^\w\.]/g; |
| 13 | ||
| 14 | 1 | return { |
| 15 | /** | |
| 16 | * 初始化执行方法 | |
| 17 | * @param {[type]} http [description] | |
| 18 | * @return {[type]} [description] | |
| 19 | */ | |
| 20 | init: function(http) { | |
| 21 | 8 | this.http = http; |
| 22 | 8 | this.view = null; |
| 23 | //将http数据打到模版里 | |
| 24 | 8 | this.assign('http', this.http); |
| 25 | //将配置信息打到模版里 | |
| 26 | 8 | this.assign('config', C()); |
| 27 | //设置变量别名 | |
| 28 | 8 | this.set = this.assign; |
| 29 | }, | |
| 30 | /** | |
| 31 | * 获取客户端的ip | |
| 32 | * @return {[type]} [description] | |
| 33 | */ | |
| 34 | ip: function() { | |
| 35 | 1 | return this.http.ip(); |
| 36 | }, | |
| 37 | /** | |
| 38 | * 实例化View类 | |
| 39 | * @return {[type]} [description] | |
| 40 | */ | |
| 41 | initView: function() { | |
| 42 | 17 | if (!this.view) { |
| 43 | 8 | this.view = thinkRequire('View')(this.http); |
| 44 | } | |
| 45 | 17 | return this.view; |
| 46 | }, | |
| 47 | /** | |
| 48 | * 是否是GET请求 | |
| 49 | * @return {Boolean} [description] | |
| 50 | */ | |
| 51 | isGet: function() { | |
| 52 | 1 | return this.http.method === 'GET'; |
| 53 | }, | |
| 54 | /** | |
| 55 | * 是否是POST请求 | |
| 56 | * @return {Boolean} [description] | |
| 57 | */ | |
| 58 | isPost: function() { | |
| 59 | 1 | return this.http.method === 'POST'; |
| 60 | }, | |
| 61 | /** | |
| 62 | * 是否是特定METHOD请求 | |
| 63 | * @param {[type]} method [description] | |
| 64 | * @return {Boolean} [description] | |
| 65 | */ | |
| 66 | isMethod: function(method) { | |
| 67 | 2 | return this.http.method === method.toUpperCase(); |
| 68 | }, | |
| 69 | /** | |
| 70 | * 是否是AJAX请求 | |
| 71 | * @return {Boolean} [description] | |
| 72 | */ | |
| 73 | isAjax: function(method) { | |
| 74 | //请求类型判断 | |
| 75 | 1 | if (method && this.http.method !== method) { |
| 76 | 0 | return false; |
| 77 | } | |
| 78 | 1 | return this.header('x-requested-with') === 'XMLHttpRequest'; |
| 79 | }, | |
| 80 | /** | |
| 81 | * 是否是websocket请求 | |
| 82 | * @return {Boolean} [description] | |
| 83 | */ | |
| 84 | isWebSocket: function(){ | |
| 85 | 1 | return !!this.http.websocket; |
| 86 | }, | |
| 87 | /** | |
| 88 | * 是否是命令行模式 | |
| 89 | * @return {Boolean} [description] | |
| 90 | */ | |
| 91 | isCli: function(){ | |
| 92 | 1 | return APP_MODE === 'cli'; |
| 93 | }, | |
| 94 | /** | |
| 95 | * 是否是jsonp接口 | |
| 96 | * @return {Boolean} [description] | |
| 97 | */ | |
| 98 | isJsonp: function(name){ | |
| 99 | 1 | name = name || C('url_callback_name'); |
| 100 | 1 | return !!this.get(name); |
| 101 | }, | |
| 102 | /** | |
| 103 | * 获取QUERY参数 | |
| 104 | * @param {[type]} name [description] | |
| 105 | * @return {[type]} [description] | |
| 106 | */ | |
| 107 | get: function(name) { | |
| 108 | 5 | if (name === undefined) { |
| 109 | 1 | return this.http.get; |
| 110 | } | |
| 111 | 4 | return this.http.get[name] || ''; |
| 112 | }, | |
| 113 | /** | |
| 114 | * 获取POST参数 | |
| 115 | * @param {[type]} name [description] | |
| 116 | * @return {[type]} [description] | |
| 117 | */ | |
| 118 | post: function(name) { | |
| 119 | 2 | var http = this.http; |
| 120 | 2 | return name ? (http.post[name] || '') : http.post; |
| 121 | }, | |
| 122 | /** | |
| 123 | * 获取参数 | |
| 124 | * @param {[type]} name [description] | |
| 125 | * @return {[type]} [description] | |
| 126 | */ | |
| 127 | param: function(name) { | |
| 128 | 0 | return this.post(name) || this.get(name); |
| 129 | }, | |
| 130 | /** | |
| 131 | * 获取上传的文件 | |
| 132 | * @param {[type]} name [description] | |
| 133 | * @return {[type]} [description] | |
| 134 | */ | |
| 135 | file: function(name) { | |
| 136 | 0 | var http = this.http; |
| 137 | 0 | return name ? (http.file[name] || '') : http.file; |
| 138 | }, | |
| 139 | /** | |
| 140 | * header操作 | |
| 141 | * @param {[type]} name [description] | |
| 142 | * @param {[type]} value [description] | |
| 143 | * @return {[type]} [description] | |
| 144 | */ | |
| 145 | header: function(name, value) { | |
| 146 | 1 | if (name === undefined) { |
| 147 | 0 | return this.http.headers; |
| 148 | 1 | }else if (isObject(name)) { |
| 149 | 0 | for (var key in name) { |
| 150 | 0 | this.header(key, name[key]); |
| 151 | } | |
| 152 | 0 | return this; |
| 153 | 1 | }else if (value !== undefined) { |
| 154 | 0 | this.http.setHeader(name, value); |
| 155 | 0 | return this; |
| 156 | }else{ | |
| 157 | 1 | return this.http.getHeader(name); |
| 158 | } | |
| 159 | }, | |
| 160 | /** | |
| 161 | * 获取userAgent | |
| 162 | * @return {[type]} [description] | |
| 163 | */ | |
| 164 | userAgent: function(){ | |
| 165 | 1 | return this.http.headers['user-agent'] || ''; |
| 166 | }, | |
| 167 | /** | |
| 168 | * 获取referrer | |
| 169 | * @return {[type]} [description] | |
| 170 | */ | |
| 171 | referer: function(host){ | |
| 172 | 1 | var referer = this.http.headers.referer || this.http.headers.referfer || ''; |
| 173 | 1 | if (!referer || !host) { |
| 174 | 1 | return referer; |
| 175 | } | |
| 176 | 0 | var info = url.parse(referer); |
| 177 | 0 | return info.hostname; |
| 178 | }, | |
| 179 | /** | |
| 180 | * cookie操作 | |
| 181 | * @param {[type]} name [description] | |
| 182 | * @param {[type]} value [description] | |
| 183 | * @param {[type]} options [description] | |
| 184 | * @return {[type]} [description] | |
| 185 | */ | |
| 186 | cookie: function(name, value, options) { | |
| 187 | 3 | if (value !== undefined) { |
| 188 | 0 | this.http.setCookie(name, value, options); |
| 189 | 0 | return this; |
| 190 | } | |
| 191 | 3 | return name === undefined ? this.http.cookie : (this.http.cookie[name] || ''); |
| 192 | }, | |
| 193 | /** | |
| 194 | * session | |
| 195 | * 如果是get操作,则返回一个promise | |
| 196 | * @param {[type]} name [description] | |
| 197 | * @param {[type]} value [description] | |
| 198 | * @return {[type]} [description] | |
| 199 | */ | |
| 200 | session: function(name, value) { | |
| 201 | 11 | thinkRequire('Session').start(this.http); |
| 202 | 11 | var instance = this.http.session; |
| 203 | 11 | if (name === undefined) { |
| 204 | 1 | return instance.rm(); |
| 205 | } | |
| 206 | 10 | if (value !== undefined) { |
| 207 | 5 | return instance.set(name, value); |
| 208 | } | |
| 209 | 5 | return instance.get(name); |
| 210 | }, | |
| 211 | /** | |
| 212 | * 跳转,返回一个pendding promise阻止后面继续执行 | |
| 213 | * @param {[type]} url [description] | |
| 214 | * @param {[type]} code [description] | |
| 215 | * @return {[type]} [description] | |
| 216 | */ | |
| 217 | redirect: function(url, code) { | |
| 218 | 0 | this.http.redirect(url, code); |
| 219 | 0 | return getDefer().promise; |
| 220 | }, | |
| 221 | /** | |
| 222 | * 赋值变量到模版 | |
| 223 | * @param {[type]} name [description] | |
| 224 | * @param {[type]} value [description] | |
| 225 | * @return {[type]} [description] | |
| 226 | */ | |
| 227 | assign: function(name, value) { | |
| 228 | 16 | if (arguments.length === 1) { |
| 229 | 0 | return this.initView().assign(name); |
| 230 | } | |
| 231 | 16 | return this.initView().assign(name, value); |
| 232 | }, | |
| 233 | /** | |
| 234 | * 获取解析后的模版内容 | |
| 235 | * @param {[type]} templateFile [description] | |
| 236 | * @param {[type]} content [description] | |
| 237 | * @return {[type]} [description] | |
| 238 | */ | |
| 239 | fetch: function(templateFile, content) { | |
| 240 | 0 | return this.initView().fetch(templateFile, content); |
| 241 | }, | |
| 242 | /** | |
| 243 | * 输出模版内容 | |
| 244 | * @param {[type]} templateFile [description] | |
| 245 | * @param {[type]} charset [description] | |
| 246 | * @param {[type]} contentType [description] | |
| 247 | * @param {[type]} content [description] | |
| 248 | * @return {[type]} [description] | |
| 249 | */ | |
| 250 | display: function(templateFile, charset, contentType, content) { | |
| 251 | 1 | return this.initView().display(templateFile, charset, contentType, content); |
| 252 | }, | |
| 253 | /** | |
| 254 | * 调用另一个controll里的aciton | |
| 255 | * 可以跨分组 | |
| 256 | * A('Admin/Test/index') | |
| 257 | * @param {[type]} action [description] | |
| 258 | * @return {[type]} [description] | |
| 259 | */ | |
| 260 | action: function(action, data) { | |
| 261 | //自动补group | |
| 262 | 0 | action = action.replace(/\//g, ':'); |
| 263 | 0 | if (action.split(':').length === 2) { |
| 264 | 0 | action = this.http.group + ':' + action; |
| 265 | } | |
| 266 | 0 | return A(action, this.http, data); |
| 267 | }, | |
| 268 | /** | |
| 269 | * jsonp格式输出 | |
| 270 | * @param {[type]} data [description] | |
| 271 | * @param {[type]} jsonp [description] | |
| 272 | * @return {[type]} [description] | |
| 273 | */ | |
| 274 | jsonp: function(data) { | |
| 275 | 0 | this.type(C('json_content_type')); |
| 276 | 0 | var callback = this.get(C('url_callback_name')); |
| 277 | //过滤callback值里的非法字符 | |
| 278 | 0 | callback = callback.replace(callbackReg, ''); |
| 279 | 0 | if (callback) { |
| 280 | 0 | this.echo(callback + '('); |
| 281 | 0 | this.echo(data); |
| 282 | 0 | this.end(')'); |
| 283 | } else { | |
| 284 | 0 | this.end(data); |
| 285 | } | |
| 286 | }, | |
| 287 | /** | |
| 288 | * json格式输出 | |
| 289 | * @param {[type]} data [description] | |
| 290 | * @return {[type]} [description] | |
| 291 | */ | |
| 292 | json: function(data){ | |
| 293 | 0 | this.type(C('json_content_type')); |
| 294 | 0 | return this.end(data); |
| 295 | }, | |
| 296 | /** | |
| 297 | * 设置http响应状态码 | |
| 298 | * @param {[type]} status [description] | |
| 299 | * @return {[type]} [description] | |
| 300 | */ | |
| 301 | status: function(status) { | |
| 302 | 0 | var res = this.http.res; |
| 303 | 0 | if (!res.headersSent) { |
| 304 | 0 | res.statusCode = status || 404; |
| 305 | } | |
| 306 | 0 | return this; |
| 307 | }, | |
| 308 | /** | |
| 309 | * 阻止访问 | |
| 310 | * @param {[type]} status [description] | |
| 311 | * @return {[type]} [description] | |
| 312 | */ | |
| 313 | deny: function(status){ | |
| 314 | 0 | var res = this.http.res; |
| 315 | 0 | if (!res.headersSent) { |
| 316 | 0 | res.statusCode = status || 403; |
| 317 | 0 | this.http.end(); |
| 318 | } | |
| 319 | 0 | return getDefer().promise; |
| 320 | }, | |
| 321 | /** | |
| 322 | * 输出内容 | |
| 323 | * 自动JSON.stringify | |
| 324 | * 自定将数字等转化为字符串 | |
| 325 | * @param {[type]} obj [description] | |
| 326 | * @return {[type]} [description] | |
| 327 | */ | |
| 328 | echo: function(obj, encoding) { | |
| 329 | //自动发送Content-Type的header | |
| 330 | 0 | if (C('auto_send_content_type')) { |
| 331 | 0 | this.type(C('tpl_content_type')); |
| 332 | } | |
| 333 | 0 | return this.http.echo(obj, encoding); |
| 334 | }, | |
| 335 | /** | |
| 336 | * 结束输出,输出完成时一定要调用这个方法 | |
| 337 | * @param {[type]} obj [description] | |
| 338 | * @return {[type]} [description] | |
| 339 | */ | |
| 340 | end: function(obj, encoding) { | |
| 341 | 0 | if (obj) { |
| 342 | 0 | this.echo(obj, encoding); |
| 343 | } | |
| 344 | 0 | this.http.end(); |
| 345 | }, | |
| 346 | /** | |
| 347 | * 发送Content-Type | |
| 348 | * @param {[type]} type [description] | |
| 349 | * @return {[type]} [description] | |
| 350 | */ | |
| 351 | type: function(ext){ | |
| 352 | 0 | if (ext.indexOf('/') === -1) { |
| 353 | 0 | ext = require('mime').lookup(ext); |
| 354 | } | |
| 355 | 0 | if (!this.http.cthIsSend) { |
| 356 | 0 | if (ext.toLowerCase().indexOf('charset=') === -1) { |
| 357 | 0 | ext += '; charset=' + C('encoding'); |
| 358 | } | |
| 359 | //Content-Type Header has been Send | |
| 360 | 0 | this.http.cthIsSend = true; |
| 361 | 0 | this.http.setHeader('Content-Type', ext); |
| 362 | }else{ | |
| 363 | 0 | console.log('Content-Type has been send'); |
| 364 | } | |
| 365 | }, | |
| 366 | /** | |
| 367 | * 下载文件 | |
| 368 | * @return {[type]} [description] | |
| 369 | */ | |
| 370 | download: function(file, contentType, callback) { | |
| 371 | 0 | if (isFunction(contentType)) { |
| 372 | 0 | callback = contentType; |
| 373 | 0 | contentType = ''; |
| 374 | } | |
| 375 | 0 | if (!contentType || contentType.indexOf('/') === -1) { |
| 376 | 0 | contentType = require('mime').lookup(contentType || file); |
| 377 | } | |
| 378 | 0 | var http = this.http; |
| 379 | 0 | var fileStream = fs.createReadStream(file); |
| 380 | 0 | this.type(contentType); |
| 381 | 0 | http.setHeader('Content-Disposition', 'attachment; filename="' + path.basename(file) + '"'); |
| 382 | 0 | fileStream.pipe(http.res); |
| 383 | 0 | fileStream.on('end', function() { |
| 384 | 0 | http.end(); |
| 385 | 0 | return callback && callback(); |
| 386 | }); | |
| 387 | }, | |
| 388 | /** | |
| 389 | * 正常json数据输出 | |
| 390 | * @param {[type]} data [description] | |
| 391 | * @return {[type]} [description] | |
| 392 | */ | |
| 393 | success: function(data){ | |
| 394 | 0 | var obj = getObject([C('error_no_key'), C('error_msg_key')], [0, '']); |
| 395 | 0 | if (data !== undefined) { |
| 396 | 0 | obj.data = data; |
| 397 | } | |
| 398 | 0 | this.type(C('json_content_type')); |
| 399 | 0 | this.end(obj); |
| 400 | 0 | return getDefer().promise; |
| 401 | }, | |
| 402 | /** | |
| 403 | * 异常json数据数据 | |
| 404 | * @param {[type]} errno [description] | |
| 405 | * @param {[type]} errmsg [description] | |
| 406 | * @param {[type]} extra [description] | |
| 407 | * @return {[type]} [description] | |
| 408 | */ | |
| 409 | error: function(errno, errmsg, data){ | |
| 410 | 0 | var obj; |
| 411 | 0 | if (isObject(errno)) { |
| 412 | 0 | data = errmsg; |
| 413 | 0 | obj = extend({}, errno); |
| 414 | }else{ | |
| 415 | 0 | if (!isNumber(errno)) { |
| 416 | 0 | data = errmsg; |
| 417 | 0 | errmsg = errno; |
| 418 | 0 | errno = C('error_no_default_value'); |
| 419 | } | |
| 420 | 0 | obj = getObject([C('error_no_key'), C('error_msg_key')], [errno, errmsg]); |
| 421 | } | |
| 422 | 0 | if (data !== undefined) { |
| 423 | 0 | obj.data = data; |
| 424 | } | |
| 425 | 0 | this.type(C('json_content_type')); |
| 426 | 0 | this.end(obj); |
| 427 | 0 | return getDefer().promise; |
| 428 | }, | |
| 429 | /** | |
| 430 | * 发送执行时间 | |
| 431 | * @param {[type]} name [description] | |
| 432 | * @return {[type]} [description] | |
| 433 | */ | |
| 434 | sendTime: function(name){ | |
| 435 | 0 | return this.http.sendTime(name); |
| 436 | }, | |
| 437 | /** | |
| 438 | * 对数据进行过滤 | |
| 439 | * @param {[type]} data [description] | |
| 440 | * @param {[type]} type [description] | |
| 441 | * @return {[type]} [description] | |
| 442 | */ | |
| 443 | filter: function() { | |
| 444 | 0 | var filter = thinkRequire('Filter').filter; |
| 445 | 0 | return filter.apply(null, arguments); |
| 446 | }, | |
| 447 | /** | |
| 448 | * 校验一个值是否合法 | |
| 449 | * @param {[type]} data [description] | |
| 450 | * @param {[type]} validType [description] | |
| 451 | * @return {[type]} [description] | |
| 452 | */ | |
| 453 | valid: function(data, validType) { | |
| 454 | //单个值检测,只返回是否正常 | |
| 455 | 0 | if (validType !== undefined) { |
| 456 | 0 | data = [{ |
| 457 | value: data, | |
| 458 | valid: validType | |
| 459 | }]; | |
| 460 | 0 | var result = thinkRequire('Valid')(data); |
| 461 | 0 | return isEmpty(result); |
| 462 | } | |
| 463 | 0 | return thinkRequire('Valid')(data); |
| 464 | } | |
| 465 | }; | |
| 466 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var url = require('url'); |
| 2 | 1 | var querystring = require('querystring'); |
| 3 | /** | |
| 4 | * 数据库基类 | |
| 5 | * @return {[type]} [description] | |
| 6 | */ | |
| 7 | 1 | var Db = module.exports = Class(function(){ |
| 8 | 1 | 'use strict'; |
| 9 | //用于查询的sql语句,所有select语句根据该语句解析 | |
| 10 | 1 | var selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%'; |
| 11 | //where条件里的表达式 | |
| 12 | 1 | var comparison = { |
| 13 | 'EQ': '=', | |
| 14 | 'NEQ': '!=', | |
| 15 | '<>': '!=', | |
| 16 | 'GT': '>', | |
| 17 | 'EGT': '>=', | |
| 18 | 'LT': '<', | |
| 19 | 'ELT': '<=', | |
| 20 | 'NOTLIKE': 'NOT LIKE', | |
| 21 | 'LIKE': 'LIKE', | |
| 22 | 'IN': 'IN', | |
| 23 | 'NOTIN': 'NOT IN' | |
| 24 | }; | |
| 25 | 1 | return { |
| 26 | // 数据库类型 | |
| 27 | dbType: null, | |
| 28 | // 当前操作所属的模型名 | |
| 29 | model: '_think_', | |
| 30 | // 当前SQL指令 | |
| 31 | queryStr: '', | |
| 32 | // 操作的sql列表 | |
| 33 | modelSql: [], | |
| 34 | // 当前连接ID | |
| 35 | linkId: null, | |
| 36 | // 数据库连接参数配置 | |
| 37 | config: '', | |
| 38 | /** | |
| 39 | * 初始化 | |
| 40 | * @return {[type]} [description] | |
| 41 | */ | |
| 42 | init: function(){ | |
| 43 | ||
| 44 | }, | |
| 45 | /** | |
| 46 | * 解析set集合 | |
| 47 | * @param {[type]} data [description] | |
| 48 | * @return {[type]} [description] | |
| 49 | */ | |
| 50 | parseSet: function(data){ | |
| 51 | 13 | data = data || {}; |
| 52 | 13 | var set = []; |
| 53 | 13 | for(var key in data){ |
| 54 | 13 | var value = this.parseValue(data[key]); |
| 55 | 13 | if (isScalar(value)) { |
| 56 | 13 | set.push(this.parseKey(key) + '=' + value); |
| 57 | } | |
| 58 | } | |
| 59 | 13 | return ' SET ' + set.join(','); |
| 60 | }, | |
| 61 | /** | |
| 62 | * 解析字段名,具体的数据库里实现 | |
| 63 | * @param {[type]} key [description] | |
| 64 | * @return {[type]} [description] | |
| 65 | */ | |
| 66 | parseKey: function(key){ | |
| 67 | 0 | return key; |
| 68 | }, | |
| 69 | /** | |
| 70 | * value分析 | |
| 71 | * @param {[type]} value [description] | |
| 72 | * @return {[type]} [description] | |
| 73 | */ | |
| 74 | parseValue: function(value){ | |
| 75 | 155 | if (isString(value)) { |
| 76 | 64 | value = '\'' + this.escapeString(value) + '\''; |
| 77 | 91 | }else if(isArray(value)){ |
| 78 | 17 | if ((value[0] + '').toLowerCase() === 'exp') { |
| 79 | 8 | value = this.escapeString(value[1]); |
| 80 | }else{ | |
| 81 | 9 | var self = this; |
| 82 | 9 | value = value.map(function(item){ |
| 83 | 17 | return self.parseValue(item); |
| 84 | }); | |
| 85 | } | |
| 86 | 74 | }else if(isBoolean(value)){ |
| 87 | 0 | value = value ? '1' : '0'; |
| 88 | 74 | }else if (value === null) { |
| 89 | 0 | value = 'null'; |
| 90 | } | |
| 91 | 155 | return value; |
| 92 | }, | |
| 93 | /** | |
| 94 | * field分析 | |
| 95 | * parseField('name'); | |
| 96 | * parseField('name,email'); | |
| 97 | * parseField({ | |
| 98 | * xx_name: 'name', | |
| 99 | * xx_email: 'email' | |
| 100 | * }) | |
| 101 | * @return {[type]} [description] | |
| 102 | */ | |
| 103 | parseField: function(fields){ | |
| 104 | 154 | if (isString(fields) && fields.indexOf(',') > -1) { |
| 105 | 4 | fields = fields.split(','); |
| 106 | } | |
| 107 | 154 | if (isArray(fields)) { |
| 108 | 4 | var self = this; |
| 109 | 4 | return fields.map(function(item){ |
| 110 | 17 | return self.parseKey(item); |
| 111 | }).join(','); | |
| 112 | 150 | }else if(isObject(fields)){ |
| 113 | 0 | var data = []; |
| 114 | 0 | for(var key in fields){ |
| 115 | 0 | data.push(this.parseKey(key) + ' AS ' + this.parseKey(fields[key])); |
| 116 | } | |
| 117 | 0 | return data.join(','); |
| 118 | 150 | }else if(isString(fields) && fields){ |
| 119 | 17 | return this.parseKey(fields); |
| 120 | } | |
| 121 | 133 | return '*'; |
| 122 | }, | |
| 123 | /** | |
| 124 | * table别名分析 | |
| 125 | * @param {[type]} tables [description] | |
| 126 | * @return {[type]} [description] | |
| 127 | */ | |
| 128 | parseTable: function(tables){ | |
| 129 | 176 | if (isString(tables)) { |
| 130 | 174 | tables = tables.split(','); |
| 131 | } | |
| 132 | 176 | if (isArray(tables)) { |
| 133 | 175 | var self = this; |
| 134 | 175 | return tables.map(function(item){ |
| 135 | 176 | return self.parseKey(item); |
| 136 | }).join(','); | |
| 137 | 1 | }else if (isObject(tables)) { |
| 138 | 1 | var data = []; |
| 139 | 1 | for(var key in tables){ |
| 140 | 1 | data.push(this.parseKey(key) + ' AS ' + this.parseKey(tables[key])); |
| 141 | } | |
| 142 | 1 | return data.join(','); |
| 143 | } | |
| 144 | 0 | return ''; |
| 145 | }, | |
| 146 | /** | |
| 147 | * where条件分析 | |
| 148 | * @param {[type]} where [description] | |
| 149 | * @return {[type]} [description] | |
| 150 | */ | |
| 151 | parseWhere: function(where){ | |
| 152 | 171 | var whereStr = ''; |
| 153 | 171 | var self = this; |
| 154 | 171 | where = where || {}; |
| 155 | 171 | if (isString(where)) { |
| 156 | 0 | whereStr = where; |
| 157 | }else{ | |
| 158 | // 定义逻辑运算规则 例如 OR XOR AND NOT | |
| 159 | 171 | var oList = ['AND', 'OR', 'XOR']; |
| 160 | 171 | var operate = (where._logic + '').toUpperCase(); |
| 161 | 171 | delete where._logic; |
| 162 | 171 | operate = oList.indexOf(operate) > -1 ? ' ' + operate + ' ' : ' AND '; |
| 163 | //key值的安全检测正则 | |
| 164 | 171 | var keySafeRegExp = /^[\w\|\&\-\.\(\)\,]+$/; |
| 165 | 171 | var multi = where._multi; |
| 166 | 171 | delete where._multi; |
| 167 | ||
| 168 | 171 | var val; |
| 169 | 171 | var fn = function(item, i){ |
| 170 | 8 | var v = multi ? val[i] : val; |
| 171 | 8 | return '(' + self.parseWhereItem(self.parseKey(item), v) + ')'; |
| 172 | }; | |
| 173 | 171 | for(var key in where){ |
| 174 | 100 | key = key.trim(); |
| 175 | 100 | val = where[key]; |
| 176 | 100 | whereStr += '( '; |
| 177 | 100 | if (key.indexOf('_') === 0) { |
| 178 | // 解析特殊条件表达式 | |
| 179 | 3 | whereStr += this.parseThinkWhere(key, val); |
| 180 | }else{ | |
| 181 | 97 | if (!keySafeRegExp.test(key)) { |
| 182 | 0 | console.log(key + ' is not safe'); |
| 183 | 0 | continue; |
| 184 | } | |
| 185 | 97 | var arr; |
| 186 | // 支持 name|title|nickname 方式定义查询字段 | |
| 187 | 97 | if (key.indexOf('|') > -1) { |
| 188 | 2 | arr = key.split('|'); |
| 189 | 2 | whereStr += arr.map(fn).join(' OR '); |
| 190 | 95 | }else if (key.indexOf('&') > -1) { |
| 191 | 2 | arr = key.split('&'); |
| 192 | 2 | whereStr += arr.map(fn).join(' AND '); |
| 193 | }else{ | |
| 194 | 93 | whereStr += this.parseWhereItem(this.parseKey(key), val); |
| 195 | } | |
| 196 | } | |
| 197 | 100 | whereStr += ' )' + operate; |
| 198 | } | |
| 199 | 171 | whereStr = whereStr.substr(0, whereStr.length - operate.length); |
| 200 | } | |
| 201 | ||
| 202 | 171 | return whereStr ? (' WHERE ' + whereStr) : ''; |
| 203 | }, | |
| 204 | /** | |
| 205 | * 解析单个查询条件 | |
| 206 | * @param {[type]} key [description] | |
| 207 | * @param {[type]} val [description] | |
| 208 | * @return {[type]} [description] | |
| 209 | */ | |
| 210 | parseWhereItem: function(key, val){ | |
| 211 | 101 | if (isObject(val)) { // {id: {'<': 10, '>': 1}} |
| 212 | 7 | var logic = (val._logic || 'AND').toUpperCase(); |
| 213 | 7 | delete val._logic; |
| 214 | 7 | var result = []; |
| 215 | 7 | for(var opr in val){ |
| 216 | 14 | var nop = opr.toUpperCase(); |
| 217 | 14 | nop = comparison[nop] || nop; |
| 218 | 14 | result.push(key + ' ' + nop + ' ' + this.parseValue(val[opr])); |
| 219 | } | |
| 220 | 7 | return result.join(' ' + logic + ' '); |
| 221 | 94 | }else if (!isArray(val)) { |
| 222 | //对字符串类型字段采用模糊匹配 | |
| 223 | 31 | if (C('db_like_fields').indexOf(key) > -1) { |
| 224 | 0 | return key + ' LIKE ' + this.parseValue('%' + val + '%'); |
| 225 | }else{ | |
| 226 | 31 | return key + ' = ' + this.parseValue(val); |
| 227 | } | |
| 228 | } | |
| 229 | 63 | var whereStr = ''; |
| 230 | 63 | var data; |
| 231 | 63 | if (isString(val[0])) { |
| 232 | 62 | var val0 = val[0].toUpperCase(); |
| 233 | 62 | val0 = comparison[val0] || val0; |
| 234 | 62 | if (/^(=|!=|>|>=|<|<=)$/.test(val0)) { // 比较运算 |
| 235 | 22 | whereStr += key + ' ' + val0 + ' ' + this.parseValue(val[1]); |
| 236 | 40 | }else if (/^(NOT\s+LIKE|LIKE)$/.test(val0)) { // 模糊查找 |
| 237 | 23 | if (isArray(val[1])) { //多个like |
| 238 | 7 | var likeLogic = (val[2] || 'OR').toUpperCase(); |
| 239 | 7 | var likesLogic = ['AND','OR','XOR']; |
| 240 | 7 | var self = this; |
| 241 | 7 | if (likesLogic.indexOf(likeLogic) > -1) { |
| 242 | 7 | var like = val[1].map(function(item){ |
| 243 | 13 | return key + ' ' + val0 + ' ' + self.parseValue(item); |
| 244 | }).join(' ' + likeLogic + ' '); | |
| 245 | 7 | whereStr += '(' + like + ')'; |
| 246 | } | |
| 247 | }else{ | |
| 248 | 16 | whereStr += key + ' ' + val0 + ' ' + this.parseValue(val[1]); |
| 249 | } | |
| 250 | 17 | }else if(val0 === 'EXP'){ // 使用表达式 |
| 251 | 2 | whereStr += '(' + key + ' ' + val[1] + ')'; |
| 252 | 15 | }else if(val0 === 'IN' || val0 === 'NOT IN'){ // IN 运算 |
| 253 | 12 | if (val[2] === 'exp') { |
| 254 | 3 | whereStr += key + ' ' + val0 + ' ' + val[1]; |
| 255 | }else{ | |
| 256 | 9 | if (isString(val[1])) { |
| 257 | 4 | val[1] = val[1].split(','); |
| 258 | } | |
| 259 | //如果不是数组,自动转为数组 | |
| 260 | 9 | if (!isArray(val[1])) { |
| 261 | 2 | val[1] = [val[1]]; |
| 262 | } | |
| 263 | 9 | val[1] = this.parseValue(val[1]); |
| 264 | //如果只有一个值,那么变成=或者!= | |
| 265 | 9 | if (val[1].length === 1) { |
| 266 | 2 | whereStr += key + (val0 === 'IN' ? ' = ' : ' != ') + val[1]; |
| 267 | }else{ | |
| 268 | 7 | whereStr += key + ' ' + val0 + ' (' + val[1].join(',') + ')'; |
| 269 | } | |
| 270 | } | |
| 271 | 3 | }else if(val0 === 'BETWEEN'){ // BETWEEN运算 |
| 272 | 3 | data = isString(val[1]) ? val[1].split(',') : val[1]; |
| 273 | 3 | if (!isArray(data)) { |
| 274 | 2 | data = [val[1], val[2]]; |
| 275 | } | |
| 276 | 3 | whereStr += ' (' + key + ' ' + val0 + ' ' + this.parseValue(data[0]); |
| 277 | 3 | whereStr += ' AND ' + this.parseValue(data[1]) + ')'; |
| 278 | }else{ | |
| 279 | 0 | console.log('_EXPRESS_ERROR_', key, val); |
| 280 | 0 | return ''; |
| 281 | } | |
| 282 | }else{ | |
| 283 | 1 | var length = val.length; |
| 284 | 1 | var rule = 'AND'; |
| 285 | 1 | if (isString(val[length - 1])) { |
| 286 | 0 | var last = val[length - 1].toUpperCase(); |
| 287 | 0 | if (last && ['AND', 'OR', 'XOR'].indexOf(last) > -1) { |
| 288 | 0 | rule = last; |
| 289 | 0 | length--; |
| 290 | } | |
| 291 | } | |
| 292 | 1 | for(var i = 0; i < length; i++){ |
| 293 | 3 | var isArr = isArray(val[i]); |
| 294 | 3 | data = isArr ? val[i][1] : val[i]; |
| 295 | 3 | var exp = ((isArr ? val[i][0] : '') + '').toUpperCase(); |
| 296 | 3 | if (exp === 'EXP') { |
| 297 | 1 | whereStr += '(' + key + ' ' + data + ') ' + rule + ' '; |
| 298 | }else{ | |
| 299 | 2 | var op = isArr ? (comparison[val[i][0].toUpperCase()] || val[i][0]) : '='; |
| 300 | 2 | whereStr += '(' + key + ' ' + op + ' ' + this.parseValue(data) + ') ' + rule + ' '; |
| 301 | } | |
| 302 | } | |
| 303 | 1 | whereStr = whereStr.substr(0, whereStr.length - 4); |
| 304 | } | |
| 305 | 63 | return whereStr; |
| 306 | }, | |
| 307 | /** | |
| 308 | * 解析一些特殊的where条件 | |
| 309 | * @param {[type]} key [description] | |
| 310 | * @param {[type]} val [description] | |
| 311 | * @return {[type]} [description] | |
| 312 | */ | |
| 313 | parseThinkWhere: function(key, val){ | |
| 314 | 3 | switch(key){ |
| 315 | // 字符串模式查询条件 | |
| 316 | case '_string': | |
| 317 | 2 | return val; |
| 318 | // 复合查询条件 | |
| 319 | case '_complex': | |
| 320 | 1 | return this.parseWhere(val).substr(6); |
| 321 | // 字符串模式查询条件 | |
| 322 | case '_query': | |
| 323 | 0 | var where = isString(val) ? querystring.parse(val) : val; |
| 324 | 0 | var op = ' AND '; |
| 325 | 0 | if ('_logic' in where) { |
| 326 | 0 | op = ' ' + where._logic.toUpperCase() + ' '; |
| 327 | 0 | delete where._logic; |
| 328 | } | |
| 329 | 0 | var arr = []; |
| 330 | 0 | for(var name in where){ |
| 331 | 0 | val = where[name]; |
| 332 | 0 | val = this.parseKey(name) + ' = ' + this.parseValue(val); |
| 333 | 0 | arr.push(val); |
| 334 | } | |
| 335 | 0 | return arr.join(op); |
| 336 | default: | |
| 337 | 0 | return ''; |
| 338 | } | |
| 339 | 0 | return ''; |
| 340 | }, | |
| 341 | /** | |
| 342 | * 解析limit,对非法的limit进行过滤 | |
| 343 | * @param {[type]} limit [description] | |
| 344 | * @return {[type]} [description] | |
| 345 | */ | |
| 346 | parseLimit: function(limit){ | |
| 347 | 170 | if (!limit) { |
| 348 | 147 | return ''; |
| 349 | } | |
| 350 | 23 | limit = (limit + '').split(','); |
| 351 | 23 | var data = []; |
| 352 | 23 | for(var i = 0; i < Math.min(2, limit.length); i++){ |
| 353 | 31 | data[i] = limit[i] | 0; |
| 354 | } | |
| 355 | 23 | return ' LIMIT ' + data.join(','); |
| 356 | }, | |
| 357 | /** | |
| 358 | * 解析join | |
| 359 | * @param {[type]} join [description] | |
| 360 | * @return {[type]} [description] | |
| 361 | */ | |
| 362 | parseJoin: function(join, options){ | |
| 363 | 154 | if (!join) { |
| 364 | 137 | return ''; |
| 365 | } | |
| 366 | 17 | var joinStr = ''; |
| 367 | 17 | var defaultJoin = ' LEFT JOIN '; |
| 368 | 17 | if (isArray(join)) { |
| 369 | 17 | var joins = { |
| 370 | 'left': ' LEFT JOIN ', | |
| 371 | 'right': ' RIGHT JOIN ', | |
| 372 | 'inner': ' INNER JOIN ' | |
| 373 | }; | |
| 374 | 17 | join.forEach(function(val){ |
| 375 | 19 | if (isString(val)) {//字符串,直接拼接 |
| 376 | 6 | var hasJoin = val.toLowerCase().indexOf(' join ') > -1; |
| 377 | 6 | joinStr += (hasJoin ? ' ' : defaultJoin) + val; |
| 378 | 13 | }else if (isObject(val)) { |
| 379 | 13 | var ret = []; |
| 380 | 13 | if (!('on' in val)) { |
| 381 | 7 | for(var key in val){ |
| 382 | 15 | var v = val[key]; |
| 383 | 15 | v.table = key; |
| 384 | 15 | ret.push(v); |
| 385 | } | |
| 386 | }else{ | |
| 387 | 6 | ret.push(val); |
| 388 | } | |
| 389 | 13 | ret.forEach(function(item){ |
| 390 | 21 | var joinType = joins[item.join] || item.join || defaultJoin; |
| 391 | 21 | var table = options.tablePrefix + item.table; |
| 392 | 21 | joinStr += joinType + '`' + table + '`'; |
| 393 | 21 | if (item.as) { |
| 394 | 12 | joinStr += ' AS ' + item.as; |
| 395 | } | |
| 396 | //ON条件 | |
| 397 | 21 | if (item.on) { |
| 398 | 21 | var mTable = options.alias || options.table; |
| 399 | 21 | var jTable = item.as || table; |
| 400 | //多个=条件 | |
| 401 | 21 | if (isObject(item.on)) { |
| 402 | 2 | var where = []; |
| 403 | 2 | for(var key in item.on){ |
| 404 | 4 | where.push(mTable + '.`' + key + '`' + '=' + jTable + '.`' + item.on[key] + '`'); |
| 405 | } | |
| 406 | 2 | joinStr += ' ON (' + where.join(' AND ') + ')'; |
| 407 | }else{ | |
| 408 | 19 | if (isString(item.on)) { |
| 409 | 3 | item.on = item.on.split(/\s*,\s*/); |
| 410 | } | |
| 411 | 19 | joinStr += ' ON ' + mTable + '.`' + item.on[0] + '`'; |
| 412 | 19 | joinStr += '=' + jTable + '.`' + item.on[1] + '`'; |
| 413 | } | |
| 414 | } | |
| 415 | }) | |
| 416 | } | |
| 417 | }); | |
| 418 | }else{ | |
| 419 | 0 | joinStr += defaultJoin + join; |
| 420 | } | |
| 421 | 17 | return joinStr; |
| 422 | }, | |
| 423 | /** | |
| 424 | * 解析order | |
| 425 | * @param {[type]} order [description] | |
| 426 | * @return {[type]} [description] | |
| 427 | */ | |
| 428 | parseOrder: function(order){ | |
| 429 | 170 | var self = this; |
| 430 | 170 | if (isArray(order)) { |
| 431 | 2 | order = order.map(function(item){ |
| 432 | 3 | return self.parseKey(item); |
| 433 | }).join(','); | |
| 434 | 168 | }else if (isObject(order)) { |
| 435 | 2 | var arr = []; |
| 436 | 2 | for(var key in order){ |
| 437 | 3 | var val = order[key]; |
| 438 | 3 | val = this.parseKey(key) + ' ' + val; |
| 439 | 3 | arr.push(val); |
| 440 | } | |
| 441 | 2 | order = arr.join(','); |
| 442 | } | |
| 443 | 170 | return order ? (' ORDER BY ' + order) : ''; |
| 444 | }, | |
| 445 | /** | |
| 446 | * 解析group | |
| 447 | * @param {[type]} group [description] | |
| 448 | * @return {[type]} [description] | |
| 449 | */ | |
| 450 | parseGroup: function(group){ | |
| 451 | 154 | return group ? (' GROUP BY `' + group + '`' ) : ''; |
| 452 | }, | |
| 453 | /** | |
| 454 | * 解析having | |
| 455 | * @param {[type]} having [description] | |
| 456 | * @return {[type]} [description] | |
| 457 | */ | |
| 458 | parseHaving: function(having){ | |
| 459 | 154 | return having ? (' HAVING ' + having) : ''; |
| 460 | }, | |
| 461 | /** | |
| 462 | * 解析注释,一般情况下用不到 | |
| 463 | * @param {[type]} comment [description] | |
| 464 | * @return {[type]} [description] | |
| 465 | */ | |
| 466 | parseComment: function(comment){ | |
| 467 | 172 | return comment ? (' /* ' + comment + '*/') : ''; |
| 468 | }, | |
| 469 | /** | |
| 470 | * 解析Distinct | |
| 471 | * @param {[type]} distinct [description] | |
| 472 | * @return {[type]} [description] | |
| 473 | */ | |
| 474 | parseDistinct: function(distinct){ | |
| 475 | 154 | return distinct ? ' Distinct ' : ''; |
| 476 | }, | |
| 477 | /** | |
| 478 | * 解析Union | |
| 479 | * @param {[type]} union [description] | |
| 480 | * @return {[type]} [description] | |
| 481 | */ | |
| 482 | parseUnion: function(union){ | |
| 483 | 154 | if (!union) { |
| 484 | 150 | return ''; |
| 485 | } | |
| 486 | 4 | if (isArray(union)) { |
| 487 | 4 | var self = this; |
| 488 | 4 | var sql = ''; |
| 489 | 4 | union.forEach(function(item){ |
| 490 | 5 | sql += item.all ? 'UNION ALL ' : 'UNION '; |
| 491 | 5 | sql += '(' + (isObject(item.union) ? self.buildSelectSql(item.union).trim() : item.union) + ') '; |
| 492 | }) | |
| 493 | 4 | return sql; |
| 494 | }else{ | |
| 495 | 0 | return 'UNION (' + (isObject(union) ? this.buildSelectSql(union).trim() : union) + ') '; |
| 496 | } | |
| 497 | }, | |
| 498 | /** | |
| 499 | * 解析Lock | |
| 500 | * @param {[type]} lock [description] | |
| 501 | * @return {[type]} [description] | |
| 502 | */ | |
| 503 | parseLock: function(lock){ | |
| 504 | 172 | if (!lock) { |
| 505 | 171 | return ''; |
| 506 | } | |
| 507 | 1 | return ' FOR UPDATE '; |
| 508 | }, | |
| 509 | /** | |
| 510 | * 将page转化为sql里的limit | |
| 511 | * @return {[type]} [description] | |
| 512 | */ | |
| 513 | pageToLimit: function(options){ | |
| 514 | 154 | options = options || {}; |
| 515 | //根据page生成limit | |
| 516 | 154 | if ('page' in options) { |
| 517 | 6 | var page = options.page + ''; |
| 518 | 6 | var listRows = 0; |
| 519 | 6 | if (page.indexOf(',') > -1) { |
| 520 | 3 | page = page.split(','); |
| 521 | 3 | listRows = page[1] | 0; |
| 522 | 3 | page = page[0]; |
| 523 | } | |
| 524 | 6 | page = parseInt(page, 10) || 1; |
| 525 | 6 | if (!listRows) { |
| 526 | 4 | listRows = isNumberString(options.limit) ? options.limit : C('db_nums_per_page'); |
| 527 | } | |
| 528 | 6 | var offset = listRows * (page - 1); |
| 529 | 6 | options.limit = offset + ',' + listRows; |
| 530 | } | |
| 531 | 154 | return options; |
| 532 | }, | |
| 533 | /** | |
| 534 | * 拼接select查询语句 | |
| 535 | * @param {[type]} options [description] | |
| 536 | * @return {[type]} [description] | |
| 537 | */ | |
| 538 | buildSelectSql: function(options){ | |
| 539 | 154 | options = this.pageToLimit(options); |
| 540 | 154 | var sql = this.parseSql(selectSql, options); |
| 541 | 154 | sql += this.parseLock(options.lock); |
| 542 | 154 | return sql; |
| 543 | }, | |
| 544 | /** | |
| 545 | * 解析sql语句 | |
| 546 | * @param {[type]} sql [description] | |
| 547 | * @param {[type]} options [description] | |
| 548 | * @return {[type]} [description] | |
| 549 | */ | |
| 550 | parseSql: function(sql, options){ | |
| 551 | 154 | options = options || {}; |
| 552 | 154 | var self = this; |
| 553 | 154 | return sql.replace(/\%([A-Z]+)\%/g, function(a, type){ |
| 554 | 1694 | type = type.toLowerCase(); |
| 555 | 1694 | return self['parse' + ucfirst(type)](options[type] || '', options); |
| 556 | }).replace(/__([A-Z_-]+)__/g, function(a, b){ | |
| 557 | 0 | return '`' + C('db_prefix') + b.toLowerCase() + '`'; |
| 558 | }); | |
| 559 | }, | |
| 560 | /** | |
| 561 | * 插入一条记录 | |
| 562 | * @param {[type]} data [description] | |
| 563 | * @param {[type]} options [description] | |
| 564 | * @param {[type]} replace [description] | |
| 565 | * @return {[type]} [description] | |
| 566 | */ | |
| 567 | insert: function(data, options, replace){ | |
| 568 | 2 | data = data || {}; |
| 569 | 2 | options = options || {}; |
| 570 | 2 | var values = []; |
| 571 | 2 | var fields = []; |
| 572 | 2 | this.model = options.model; |
| 573 | 2 | for(var key in data){ |
| 574 | 3 | var val = data[key]; |
| 575 | 3 | val = this.parseValue(val); |
| 576 | 3 | if (isScalar(val)) { |
| 577 | 3 | values.push(val); |
| 578 | 3 | fields.push(this.parseKey(key)); |
| 579 | } | |
| 580 | } | |
| 581 | 2 | var sql = (replace ? 'REPLACE' : 'INSERT') + ' INTO '; |
| 582 | 2 | sql += this.parseTable(options.table) + ' (' + fields.join(',') + ') '; |
| 583 | 2 | sql += 'VALUES(' + values.join(',') + ')'; |
| 584 | 2 | sql += this.parseLock(options.lock) + this.parseComment(options.comment); |
| 585 | 2 | return this.execute(sql); |
| 586 | }, | |
| 587 | /** | |
| 588 | * 插入多条记录 | |
| 589 | * @param {[type]} data [description] | |
| 590 | * @param {[type]} options [description] | |
| 591 | * @param {[type]} replace [description] | |
| 592 | * @return {[type]} [description] | |
| 593 | */ | |
| 594 | insertAll: function(data, options, replace){ | |
| 595 | 4 | var fields = Object.keys(data[0]); |
| 596 | 4 | var self = this; |
| 597 | 4 | fields = fields.map(function(item){ |
| 598 | 6 | return self.parseKey(item); |
| 599 | }).join(','); | |
| 600 | 4 | var values = data.map(function(item){ |
| 601 | 6 | var value = []; |
| 602 | 6 | for(var key in item){ |
| 603 | 9 | var val = item[key]; |
| 604 | 9 | val = self.parseValue(val); |
| 605 | 9 | if (isScalar(val)) { |
| 606 | 9 | value.push(val); |
| 607 | } | |
| 608 | } | |
| 609 | 6 | return '(' + value.join(',') + ')'; |
| 610 | }).join(','); | |
| 611 | 4 | var sql = replace ? 'REPLACE' : 'INSERT'; |
| 612 | 4 | sql += ' INTO ' + this.parseTable(options.table) + '(' + fields + ') VALUES ' + values; |
| 613 | 4 | return this.execute(sql); |
| 614 | }, | |
| 615 | /** | |
| 616 | * 从一个选择条件的结果插入记录 | |
| 617 | * @param {[type]} fields [description] | |
| 618 | * @param {[type]} table [description] | |
| 619 | * @param {[type]} options [description] | |
| 620 | * @return {[type]} [description] | |
| 621 | */ | |
| 622 | selectInsert: function(fields, table, options){ | |
| 623 | 0 | options = options || {}; |
| 624 | 0 | this.model = options.model; |
| 625 | 0 | if (isString(fields)) { |
| 626 | 0 | fields = fields.split(','); |
| 627 | } | |
| 628 | 0 | var self = this; |
| 629 | 0 | fields = fields.map(function(item){ |
| 630 | 0 | return self.parseKey(item); |
| 631 | }); | |
| 632 | 0 | var sql = 'INSERT INTO ' + this.parseTable(options.table) + ' (' + fields.join(',') + ')'; |
| 633 | 0 | sql += this.buildSelectSql(options); |
| 634 | 0 | return this.execute(sql); |
| 635 | }, | |
| 636 | /** | |
| 637 | * 删除记录 | |
| 638 | * @param {[type]} options [description] | |
| 639 | * @return {[type]} [description] | |
| 640 | */ | |
| 641 | delete: function(options){ | |
| 642 | 3 | options = options || {}; |
| 643 | 3 | this.model = options.model; |
| 644 | 3 | var sql = [ |
| 645 | 'DELETE FROM ', | |
| 646 | this.parseTable(options.table), | |
| 647 | this.parseWhere(options.where), | |
| 648 | this.parseOrder(options.order), | |
| 649 | this.parseLimit(options.limit), | |
| 650 | this.parseLock(options.lock), | |
| 651 | this.parseComment(options.comment) | |
| 652 | ].join(''); | |
| 653 | 3 | return this.execute(sql); |
| 654 | }, | |
| 655 | /** | |
| 656 | * 更新数据 | |
| 657 | * @param {[type]} data [description] | |
| 658 | * @param {[type]} options [description] | |
| 659 | * @return {[type]} [description] | |
| 660 | */ | |
| 661 | update: function(data, options){ | |
| 662 | 13 | options = options || {}; |
| 663 | 13 | this.model = options.model; |
| 664 | 13 | var sql = [ |
| 665 | 'UPDATE ', | |
| 666 | this.parseTable(options.table), | |
| 667 | this.parseSet(data), | |
| 668 | this.parseWhere(options.where), | |
| 669 | this.parseOrder(options.order), | |
| 670 | this.parseLimit(options.limit), | |
| 671 | this.parseLock(options.lock), | |
| 672 | this.parseComment(options.comment) | |
| 673 | ].join(''); | |
| 674 | 13 | return this.execute(sql); |
| 675 | }, | |
| 676 | /** | |
| 677 | * 数据查询 | |
| 678 | * @todo 返回是个promise,缓存调用需要修改 | |
| 679 | * @param {[type]} options [description] | |
| 680 | * @return {[type]} [description] | |
| 681 | */ | |
| 682 | select: function(options){ | |
| 683 | 151 | var sql, cache; |
| 684 | 151 | if (isString(options) && options.indexOf('SELECT') > -1) { |
| 685 | 0 | sql = options; |
| 686 | 0 | cache = arguments[1]; |
| 687 | }else{ | |
| 688 | 151 | options = options || {}; |
| 689 | 151 | this.model = options.model; |
| 690 | 151 | sql = this.buildSelectSql(options); |
| 691 | 151 | cache = options.cache; |
| 692 | } | |
| 693 | 151 | var self = this; |
| 694 | 151 | var cacheOn = !isEmpty(cache) && C('db_cache_on'); |
| 695 | //获取数据 | |
| 696 | 151 | function queryData(){ |
| 697 | 151 | return self.query(sql).then(function(data){ |
| 698 | 151 | if (cacheOn) { |
| 699 | 0 | S(key, data, cache); |
| 700 | } | |
| 701 | 151 | return data; |
| 702 | }); | |
| 703 | } | |
| 704 | 151 | if (cacheOn) { |
| 705 | 0 | var key = isString(cache.key) && cache.key ? cache.key : md5(sql); |
| 706 | 0 | return S(key, undefined, cache).then(function(value){ |
| 707 | 0 | return value || queryData(); |
| 708 | }); | |
| 709 | } | |
| 710 | 151 | return queryData(); |
| 711 | }, | |
| 712 | /** | |
| 713 | * 转义字符 | |
| 714 | * @param {[type]} str [description] | |
| 715 | * @return {[type]} [description] | |
| 716 | */ | |
| 717 | escapeString: function(str){ | |
| 718 | 72 | if (!str) { |
| 719 | 0 | return ''; |
| 720 | } | |
| 721 | 72 | return str.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) { |
| 722 | 6 | switch(s) { |
| 723 | case '\0': | |
| 724 | 0 | return '\\0'; |
| 725 | case '\n': | |
| 726 | 3 | return '\\n'; |
| 727 | case '\r': | |
| 728 | 0 | return '\\r'; |
| 729 | case '\b': | |
| 730 | 0 | return '\\b'; |
| 731 | case '\t': | |
| 732 | 0 | return '\\t'; |
| 733 | case '\x1a': | |
| 734 | 0 | return '\\Z'; |
| 735 | default: | |
| 736 | 3 | return '\\'+s; |
| 737 | } | |
| 738 | }); | |
| 739 | }, | |
| 740 | /** | |
| 741 | * 获取上次的sql语句 | |
| 742 | * @param {[type]} model [description] | |
| 743 | * @return {[type]} [description] | |
| 744 | */ | |
| 745 | getLastSql: function(model){ | |
| 746 | 171 | return model ? this.modelSql[model] : this.queryStr; |
| 747 | }, | |
| 748 | /** | |
| 749 | * 设置模型 | |
| 750 | * @param {[type]} model [description] | |
| 751 | */ | |
| 752 | setModel: function(model){ | |
| 753 | 0 | this.model = model; |
| 754 | 0 | return this; |
| 755 | } | |
| 756 | }; | |
| 757 | }); | |
| 758 | /** | |
| 759 | * 解析dsn | |
| 760 | * 格式: mysql://username:passwd@localhost:3306/DbName | |
| 761 | * @param string dsn [description] | |
| 762 | * @return {[type]} [description] | |
| 763 | */ | |
| 764 | 1 | Db.parseDSN = function(dsn){ |
| 765 | 0 | 'use strict'; |
| 766 | 0 | if (!dsn) { |
| 767 | 0 | return false; |
| 768 | } | |
| 769 | 0 | var info = url.parse(dsn); |
| 770 | 0 | var auth = (info.auth || '').split(':'); |
| 771 | 0 | return { |
| 772 | 'dbms': info.protocol, | |
| 773 | 'username': auth[0] || '', | |
| 774 | 'password': auth[1] || '', | |
| 775 | 'hostname': info.hostname || '', | |
| 776 | 'hostport': info.port || '', | |
| 777 | 'database': (info.pathname || '').substr(1), | |
| 778 | 'dsn': '' | |
| 779 | }; | |
| 780 | }; | |
| 781 | /** | |
| 782 | * 解析配置 | |
| 783 | * @param {[type]} config [description] | |
| 784 | * @return {[type]} [description] | |
| 785 | */ | |
| 786 | 1 | Db.parseConfig = function(config){ |
| 787 | 1 | 'use strict'; |
| 788 | 1 | if (config && isString(config)) { |
| 789 | 0 | return this.parseDSN(config); |
| 790 | 1 | }else if(isObject(config)){ |
| 791 | 0 | return { |
| 792 | 'dbms': config.db_type || 'mysql', | |
| 793 | 'username': config.db_user, | |
| 794 | 'password': config.db_pwd, | |
| 795 | 'hostname': config.db_host, | |
| 796 | 'hostport': config.db_port, | |
| 797 | 'database': config.db_name, | |
| 798 | 'dsn': config.db_dsn, | |
| 799 | 'params': config.db_params | |
| 800 | }; | |
| 801 | 1 | }else if(!config){ |
| 802 | 1 | if (C('db_dsn')) { |
| 803 | 0 | return this.parseDSN(C('db_dsn')); |
| 804 | } | |
| 805 | 1 | return { |
| 806 | 'dbms' : C('db_type'), | |
| 807 | 'username' : C('db_user'), | |
| 808 | 'password' : C('db_pwd'), | |
| 809 | 'hostname' : C('db_host'), | |
| 810 | 'hostport' : C('db_port'), | |
| 811 | 'database' : C('db_name'), | |
| 812 | 'dsn' : C('db_dsn'), | |
| 813 | 'params' : C('db_params'), | |
| 814 | }; | |
| 815 | } | |
| 816 | 0 | return config; |
| 817 | }; | |
| 818 | /** | |
| 819 | * 根据配置获取对应的数据库实例 | |
| 820 | * @param {[type]} config [description] | |
| 821 | * @return {[type]} [description] | |
| 822 | */ | |
| 823 | 1 | Db.getInstance = function(config){ |
| 824 | 1 | 'use strict'; |
| 825 | 1 | config = this.parseConfig(config); |
| 826 | 1 | if (!config.dbms) { |
| 827 | 0 | console.log('no dbms config'); |
| 828 | 0 | return false; |
| 829 | } | |
| 830 | //数据库类型 | |
| 831 | 1 | var dbType = config.dbms.toLowerCase(); |
| 832 | 1 | dbType = dbType.substr(0, 1).toUpperCase() + dbType.substr(1); |
| 833 | 1 | var instance = thinkRequire(dbType + 'Db')(config); |
| 834 | 1 | instance.dbType = dbType.toUpperCase(); |
| 835 | 1 | return instance; |
| 836 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 路由识别 | |
| 3 | * @type {Object} | |
| 4 | */ | |
| 5 | 1 | var Dispatcher = module.exports = Class(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | /** | |
| 9 | * 初始化 | |
| 10 | * @param {[type]} http [description] | |
| 11 | * @return {[type]} [description] | |
| 12 | */ | |
| 13 | init: function(http){ | |
| 14 | 1 | this.http = http; |
| 15 | }, | |
| 16 | /** | |
| 17 | * 准备pathanem | |
| 18 | * @return {[type]} [description] | |
| 19 | */ | |
| 20 | preparePathName: function(){ | |
| 21 | 1 | var pathname = this.http.pathname.split('/').filter(function(item){ |
| 22 | 2 | return item.trim(); |
| 23 | }).join('/'); | |
| 24 | //去除pathname前缀 | |
| 25 | 1 | var prefix = C('url_pathname_prefix'); |
| 26 | 1 | if (prefix && pathname.indexOf(prefix) === 0) { |
| 27 | 0 | pathname = pathname.substr(prefix.length); |
| 28 | } | |
| 29 | //判断URL后缀 | |
| 30 | 1 | var suffix = C('url_pathname_suffix'); |
| 31 | 1 | if (suffix && pathname.substr(0 - suffix.length) === suffix) { |
| 32 | 0 | pathname = pathname.substr(0, pathname.length - suffix.length); |
| 33 | } | |
| 34 | 1 | this.http.pathname = pathname; |
| 35 | }, | |
| 36 | /** | |
| 37 | * 解析pathname | |
| 38 | * @return {[type]} [description] | |
| 39 | */ | |
| 40 | parsePathName: function(){ | |
| 41 | 1 | if (this.http.group) { |
| 42 | 0 | return true; |
| 43 | } | |
| 44 | 1 | var paths = this.http.pathname.split('/'); |
| 45 | //将group list变为小写 | |
| 46 | 1 | var groupList = C('app_group_list').map(function(item){ |
| 47 | 2 | return item.toLowerCase(); |
| 48 | }); | |
| 49 | 1 | var group = ''; |
| 50 | 1 | if (groupList.length && paths[0] && groupList.indexOf(paths[0].toLowerCase()) > -1) { |
| 51 | 0 | group = paths.shift(); |
| 52 | } | |
| 53 | 1 | var controller = paths.shift(); |
| 54 | 1 | var action = paths.shift(); |
| 55 | //解析剩余path的参数 | |
| 56 | 1 | if (paths.length) { |
| 57 | 0 | for(var i = 0,length = Math.ceil(paths.length) / 2; i < length; i++){ |
| 58 | 0 | this.http.get[paths[i * 2]] = paths[i * 2 + 1] || ''; |
| 59 | } | |
| 60 | } | |
| 61 | 1 | this.http.group = Dispatcher.getGroup(group); |
| 62 | 1 | this.http.controller = Dispatcher.getController(controller); |
| 63 | 1 | this.http.action = Dispatcher.getAction(action); |
| 64 | }, | |
| 65 | /** | |
| 66 | * run | |
| 67 | * @return {[type]} [description] | |
| 68 | */ | |
| 69 | run: function(){ | |
| 70 | 1 | var self = this; |
| 71 | 1 | return tag('resource_check', this.http).then(function(){ |
| 72 | 1 | return self.preparePathName(); |
| 73 | }).then(function(){ | |
| 74 | 1 | return tag('path_info', self.http); |
| 75 | }).then(function(){ | |
| 76 | 1 | return tag('route_check', self.http); |
| 77 | }).then(function(){ | |
| 78 | 1 | return self.parsePathName(); |
| 79 | }); | |
| 80 | } | |
| 81 | }; | |
| 82 | }); | |
| 83 | /** | |
| 84 | * 获取group | |
| 85 | * @param {[type]} group [description] | |
| 86 | * @return {[type]} [description] | |
| 87 | */ | |
| 88 | 1 | Dispatcher.getGroup = function(group){ |
| 89 | 1 | 'use strict'; |
| 90 | 1 | group = group || C('default_group'); |
| 91 | 1 | return ucfirst(group); |
| 92 | }; | |
| 93 | ||
| 94 | /** | |
| 95 | * 检测Controller和Action是否合法的正则 | |
| 96 | * @type {RegExp} | |
| 97 | */ | |
| 98 | 1 | var nameReg = /^[A-Za-z\_](\w)*$/; |
| 99 | /** | |
| 100 | * 获取controller | |
| 101 | * @param {[type]} controller [description] | |
| 102 | * @return {[type]} [description] | |
| 103 | */ | |
| 104 | 1 | Dispatcher.getController = function(controller){ |
| 105 | 1 | 'use strict'; |
| 106 | 1 | if (!controller || !nameReg.test(controller)) { |
| 107 | 1 | return ucfirst(C('default_controller')); |
| 108 | } | |
| 109 | 0 | return ucfirst(controller); |
| 110 | }; | |
| 111 | /** | |
| 112 | * 获取action | |
| 113 | * @param {[type]} action [description] | |
| 114 | * @return {[type]} [description] | |
| 115 | */ | |
| 116 | 1 | Dispatcher.getAction = function(action){ |
| 117 | 1 | 'use strict'; |
| 118 | 1 | if (!action || !nameReg.test(action)) { |
| 119 | 1 | return C('default_action'); |
| 120 | } | |
| 121 | 0 | return action; |
| 122 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 对HttpRequest和HttpResponse 2个对象重新包装 | |
| 3 | * @type {Object} | |
| 4 | */ | |
| 5 | 1 | var querystring = require('querystring'); |
| 6 | 1 | var url = require('url'); |
| 7 | 1 | var cookie = thinkRequire('Cookie'); |
| 8 | 1 | var EventEmitter = require('events').EventEmitter; |
| 9 | 1 | var multiparty = require('multiparty'); |
| 10 | ||
| 11 | 1 | var localIp = '127.0.0.1'; |
| 12 | 1 | module.exports = Class(function(){ |
| 13 | 1 | 'use strict'; |
| 14 | 1 | return { |
| 15 | init: function(req, res){ | |
| 16 | 5 | this.req = req; |
| 17 | 5 | this.res = res; |
| 18 | //http对象为EventEmitter的实例 | |
| 19 | 5 | this.http = new EventEmitter(); |
| 20 | //记录当前请求的开始时间 | |
| 21 | 5 | this.http.startTime = Date.now(); |
| 22 | }, | |
| 23 | /** | |
| 24 | * 执行 | |
| 25 | * @param {Function} callback [description] | |
| 26 | * @return Promise [description] | |
| 27 | */ | |
| 28 | run: function(){ | |
| 29 | 5 | this._request(); |
| 30 | 5 | this._response(); |
| 31 | //数组的indexOf要比字符串的indexOf略快 | |
| 32 | 5 | var methods = ['POST', 'PUT', 'PATCH']; |
| 33 | 5 | if (methods.indexOf(this.req.method) > -1) { |
| 34 | 0 | return this.getPostData(); |
| 35 | } | |
| 36 | 5 | return getPromise(this.http); |
| 37 | }, | |
| 38 | /** | |
| 39 | * 检测是否含有post数据 | |
| 40 | * @return {Boolean} [description] | |
| 41 | */ | |
| 42 | hasPostData: function(){ | |
| 43 | 0 | if ('transfer-encoding' in this.req.headers) { |
| 44 | 0 | return true; |
| 45 | } | |
| 46 | 0 | var contentLength = this.req.headers['content-length'] | 0; |
| 47 | 0 | return contentLength > 0; |
| 48 | }, | |
| 49 | /** | |
| 50 | * 含有文件的表单上传 | |
| 51 | * @return {[type]} [description] | |
| 52 | */ | |
| 53 | _filePost: function(){ | |
| 54 | 0 | var deferred = getDefer(); |
| 55 | 0 | var self = this; |
| 56 | 0 | var form = new multiparty.Form({ |
| 57 | maxFieldsSize: C('post_max_fields_size'), | |
| 58 | maxFields: C('post_max_fields'), | |
| 59 | maxFilesSize: C('post_max_file_size') | |
| 60 | }); | |
| 61 | 0 | form.on('file', function(name, value){ |
| 62 | 0 | self.http.file[name] = value; |
| 63 | }); | |
| 64 | 0 | form.on('field', function(name, value){ |
| 65 | 0 | self.http.post[name] = value; |
| 66 | }); | |
| 67 | 0 | form.on('close', function(){ |
| 68 | 0 | deferred.resolve(self.http); |
| 69 | }); | |
| 70 | //有错误后直接拒绝当前请求 | |
| 71 | 0 | form.on('error', function(){ |
| 72 | 0 | self.res.statusCode = 413; |
| 73 | 0 | self.res.end(); |
| 74 | }); | |
| 75 | 0 | form.parse(this.req); |
| 76 | 0 | return deferred.promise; |
| 77 | }, | |
| 78 | /** | |
| 79 | * 普通的表单上传 | |
| 80 | * @return {[type]} [description] | |
| 81 | */ | |
| 82 | _commonPost: function(){ | |
| 83 | 0 | var buffers = []; |
| 84 | 0 | var length = 0; |
| 85 | 0 | var self = this; |
| 86 | 0 | var deferred = getDefer(); |
| 87 | 0 | this.req.on('data', function(chunk){ |
| 88 | 0 | buffers.push(chunk); |
| 89 | 0 | length += chunk.length; |
| 90 | }); | |
| 91 | 0 | this.req.on('end', function(){ |
| 92 | //如果长度超过限制,直接拒绝 | |
| 93 | 0 | if (length > C('post_max_fields_size')) { |
| 94 | 0 | self.res.statusCode = 413; |
| 95 | 0 | self.res.end(); |
| 96 | 0 | return; |
| 97 | } | |
| 98 | 0 | self.http.payload = Buffer.concat(buffers).toString(); |
| 99 | 0 | tag('form_parse', self.http).then(function(){ |
| 100 | //默认使用querystring.parse解析 | |
| 101 | 0 | if (isEmpty(self.http.post) && self.http.payload) { |
| 102 | 0 | self.http.post = querystring.parse(self.http.payload) || {} |
| 103 | } | |
| 104 | 0 | deferred.resolve(self.http); |
| 105 | }) | |
| 106 | }); | |
| 107 | 0 | return deferred.promise; |
| 108 | }, | |
| 109 | /** | |
| 110 | * 获取POST过来的数据,包含上传的文件 | |
| 111 | * 依赖multiparty库 | |
| 112 | * @return {[type]} [description] | |
| 113 | */ | |
| 114 | getPostData: function(){ | |
| 115 | //没有post数据,直接返回 | |
| 116 | 0 | if (!this.hasPostData()) { |
| 117 | 0 | return getPromise(this.http); |
| 118 | } | |
| 119 | //上传的数据中是否含有文件的检测正则 | |
| 120 | 0 | var multiReg = /^multipart\/(form-data|related);\s*boundary=(?:"([^"]+)"|([^;]+))$/i; |
| 121 | 0 | if (multiReg.test(this.http.contentType)) { |
| 122 | 0 | return this._filePost(); |
| 123 | }else{ | |
| 124 | 0 | return this._commonPost(); |
| 125 | } | |
| 126 | }, | |
| 127 | /** | |
| 128 | * HttpRequest增强 | |
| 129 | * @return {[type]} [description] | |
| 130 | */ | |
| 131 | _request: function(){ | |
| 132 | 5 | var req = { |
| 133 | //http版本号 | |
| 134 | version: this.req.httpVersion, | |
| 135 | //请求方法 | |
| 136 | method: this.req.method, | |
| 137 | //请求头 | |
| 138 | headers: this.req.headers, | |
| 139 | getHeader: function(name){ | |
| 140 | 4 | return this.headers[name] || ''; |
| 141 | }, | |
| 142 | //请求的Content-Type | |
| 143 | contentType: (this.req.headers['content-type'] || '').split(';')[0].trim(), | |
| 144 | //post信息 | |
| 145 | post: {}, | |
| 146 | //上传的文件信息 | |
| 147 | file: {}, | |
| 148 | //请求用户的ip | |
| 149 | ip: function(){ | |
| 150 | 2 | var connection = this.req.connection; |
| 151 | 2 | var socket = this.req.socket; |
| 152 | 2 | var ip = (connection && connection.remoteAddress) || (socket && socket.remoteAddress); |
| 153 | 2 | if (ip && ip !== localIp) { |
| 154 | 0 | return ip; |
| 155 | } | |
| 156 | 2 | return this.headers['x-forwarded-for'] || this.headers['x-real-ip'] || localIp; |
| 157 | }, | |
| 158 | //请求的cookie | |
| 159 | cookie: cookie.parse(this.req.headers.cookie || '') | |
| 160 | }; | |
| 161 | 5 | extend(this.http, req); |
| 162 | ||
| 163 | //解析url中的参数 | |
| 164 | 5 | var urlInfo = url.parse('//' + req.headers.host + this.req.url, true, true); |
| 165 | 5 | this.http.pathname = urlInfo.pathname; |
| 166 | //query只记录?后面的参数 | |
| 167 | 5 | this.http.query = urlInfo.query; |
| 168 | //get包含路由解析追加的参数 | |
| 169 | 5 | this.http.get = extend({}, urlInfo.query); |
| 170 | //主机名,带端口 | |
| 171 | 5 | this.http.host = urlInfo.host; |
| 172 | //主机名,不带端口 | |
| 173 | 5 | this.http.hostname = urlInfo.hostname; |
| 174 | //将原生的request对象放在http上,方便后续在controller等地方使用 | |
| 175 | 5 | this.http.req = this.req; |
| 176 | }, | |
| 177 | /** | |
| 178 | * HttpResponse增强 | |
| 179 | * @return {[type]} [description] | |
| 180 | */ | |
| 181 | _response: function(){ | |
| 182 | 5 | var res = { |
| 183 | /** | |
| 184 | * 一次请求下,可能会发送多个Cookie,所以这里不能立即发送 | |
| 185 | * 需要临时存起来,到输出内容前统一发送 | |
| 186 | * @type {Object} | |
| 187 | */ | |
| 188 | _cookie: {}, | |
| 189 | /** | |
| 190 | * 发送header | |
| 191 | * @param {[type]} name [description] | |
| 192 | * @param {[type]} value [description] | |
| 193 | */ | |
| 194 | setHeader: function(name, value){ | |
| 195 | 2 | if (this.res.headersSent) { |
| 196 | 0 | if (APP_DEBUG) { |
| 197 | 0 | console.log('headers has been sent.', name, value); |
| 198 | } | |
| 199 | 0 | return; |
| 200 | } | |
| 201 | 2 | this.res.setHeader(name, value); |
| 202 | }, | |
| 203 | /** | |
| 204 | * 设置cookie | |
| 205 | * @param {[type]} name [description] | |
| 206 | * @param {[type]} value [description] | |
| 207 | * @param {[type]} options [description] | |
| 208 | */ | |
| 209 | setCookie: function(name, value, options){ | |
| 210 | 0 | options = options || {}; |
| 211 | 0 | if (typeof options === 'number') { |
| 212 | 0 | options = {timeout: options}; |
| 213 | } | |
| 214 | 0 | var timeout = options.timeout; |
| 215 | 0 | if (timeout === undefined) { |
| 216 | 0 | timeout = C('cookie_timeout'); |
| 217 | } | |
| 218 | 0 | delete options.timeout; |
| 219 | //if value is null, remove cookie | |
| 220 | 0 | if (value === null) { |
| 221 | 0 | timeout = -1000; |
| 222 | } | |
| 223 | 0 | var defaultOptions = { |
| 224 | path: C('cookie_path'), | |
| 225 | domain: C('cookie_domain'), | |
| 226 | expires: new Date (Date.now() + timeout * 1000) | |
| 227 | }; | |
| 228 | 0 | if (timeout === 0) { |
| 229 | 0 | delete defaultOptions.expires; |
| 230 | } | |
| 231 | 0 | for(var key in options){ |
| 232 | 0 | defaultOptions[key.toLowerCase()] = options[key]; |
| 233 | } | |
| 234 | 0 | defaultOptions.name = name; |
| 235 | 0 | defaultOptions.value = encodeURIComponent(value + ''); |
| 236 | 0 | this._cookie[name] = defaultOptions; |
| 237 | }, | |
| 238 | /** | |
| 239 | * 将队列中的cookie发送出去 | |
| 240 | * @return {[type]} [description] | |
| 241 | */ | |
| 242 | sendCookie: function(){ | |
| 243 | 2 | var cookies = Object.values(this._cookie).map(function(item){ |
| 244 | 0 | return cookie.stringify(item.name, item.value, item); |
| 245 | }); | |
| 246 | 2 | if (cookies.length) { |
| 247 | 0 | this.setHeader('Set-Cookie', cookies); |
| 248 | //发送Cookie后不清除_cookie内容,websocket里需要读取 | |
| 249 | //this._cookie = {}; | |
| 250 | } | |
| 251 | }, | |
| 252 | /** | |
| 253 | * url跳转 | |
| 254 | * @param {[type]} url [description] | |
| 255 | * @param {[type]} code [description] | |
| 256 | * @return {[type]} [description] | |
| 257 | */ | |
| 258 | redirect: function(url, code){ | |
| 259 | 0 | this.res.statusCode = code || 302; |
| 260 | 0 | this.setHeader('Location', url || '/'); |
| 261 | 0 | this.end(); |
| 262 | }, | |
| 263 | /** | |
| 264 | * 发送执行时间 | |
| 265 | * @param {[type]} name [description] | |
| 266 | * @return {[type]} [description] | |
| 267 | */ | |
| 268 | sendTime: function(name){ | |
| 269 | 0 | var time = Date.now() - this.startTime; |
| 270 | 0 | this.setHeader('X-' + name, time + 'ms'); |
| 271 | }, | |
| 272 | /** | |
| 273 | * 输出内容 | |
| 274 | * @param {[type]} obj [description] | |
| 275 | * @param {[type]} encoding [description] | |
| 276 | * @return {[type]} [description] | |
| 277 | */ | |
| 278 | echo: function(obj, encoding){ | |
| 279 | 1 | this.sendCookie(); |
| 280 | 1 | if (isArray(obj) || isObject(obj)) { |
| 281 | 0 | obj = JSON.stringify(obj); |
| 282 | } | |
| 283 | 1 | if (!isString(obj) && !(obj instanceof Buffer)) { |
| 284 | 0 | obj += ''; |
| 285 | } | |
| 286 | 1 | this.res.write(obj, encoding || C('encoding')); |
| 287 | }, | |
| 288 | /** | |
| 289 | * 结束URL | |
| 290 | * @return {[type]} [description] | |
| 291 | */ | |
| 292 | end: function(){ | |
| 293 | 1 | this.emit('beforeEnd', this); |
| 294 | 1 | this.sendCookie(); |
| 295 | 1 | this.res.end(); |
| 296 | 1 | this.emit('afterEnd', this); |
| 297 | } | |
| 298 | }; | |
| 299 | 5 | extend(this.http, res); |
| 300 | //将原生的response对象放在http上,方便后续controller等地方使用 | |
| 301 | 5 | this.http.res = this.res; |
| 302 | } | |
| 303 | }; | |
| 304 | }); | |
| 305 | /** | |
| 306 | * 获取默认的http信息 | |
| 307 | * @param {[type]} data [description] | |
| 308 | * @return {[type]} [description] | |
| 309 | */ | |
| 310 | 1 | module.exports.getDefaultHttp = function(data){ |
| 311 | 4 | 'use strict'; |
| 312 | 4 | data = data || {}; |
| 313 | 4 | if (isString(data)) { |
| 314 | 4 | if (data[0] === '{') { |
| 315 | 0 | data = JSON.parse(data); |
| 316 | 4 | }else if (/^[\w]+\=/.test(data)) { |
| 317 | 0 | data = querystring.parse(data); |
| 318 | }else{ | |
| 319 | 4 | data = {url: data}; |
| 320 | } | |
| 321 | } | |
| 322 | 4 | var fn = function(){ |
| 323 | 4 | return ''; |
| 324 | }; | |
| 325 | 4 | var url = data.url || ''; |
| 326 | 4 | if (url.indexOf('/') !== 0) { |
| 327 | 0 | url = '/' + url; |
| 328 | } | |
| 329 | 4 | return { |
| 330 | req: { | |
| 331 | httpVersion: '1.1', | |
| 332 | method: data.method || 'GET', | |
| 333 | url: url, | |
| 334 | headers: extend({ | |
| 335 | host: data.host || localIp | |
| 336 | }, data.headers || {}), | |
| 337 | connection: { | |
| 338 | remoteAddress: data.ip || localIp | |
| 339 | } | |
| 340 | }, | |
| 341 | res: { | |
| 342 | end: data.end || data.close || fn, | |
| 343 | write: data.write || data.send || fn, | |
| 344 | setHeader: fn | |
| 345 | } | |
| 346 | }; | |
| 347 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var util = require('util'); |
| 2 | 1 | var querystring = require('querystring'); |
| 3 | ||
| 4 | //数据库实例化对象 | |
| 5 | 1 | var dbInstances = {}; |
| 6 | //数据表的字段信息 | |
| 7 | 1 | var tableFields = {}; |
| 8 | //db缓存数据 | |
| 9 | 1 | var dbCacheData = {}; |
| 10 | ||
| 11 | /** | |
| 12 | * Model类 | |
| 13 | * @type {[type]} | |
| 14 | */ | |
| 15 | 1 | var Model = module.exports = Class(function(){ |
| 16 | 1 | 'use strict'; |
| 17 | //解析page参数 | |
| 18 | 1 | var parsePage = function(options){ |
| 19 | 0 | if ('page' in options) { |
| 20 | 0 | var page = options.page + ''; |
| 21 | 0 | var num = 0; |
| 22 | 0 | if (page.indexOf(',') > -1) { |
| 23 | 0 | page = page.split(','); |
| 24 | 0 | num = parseInt(page[1], 10); |
| 25 | 0 | page = page[0]; |
| 26 | } | |
| 27 | 0 | num = num || C('db_nums_per_page'); |
| 28 | 0 | page = parseInt(page, 10) || 1; |
| 29 | 0 | return { |
| 30 | page: page, | |
| 31 | num: num | |
| 32 | }; | |
| 33 | } | |
| 34 | 0 | return { |
| 35 | page: 1, | |
| 36 | num: C('db_nums_per_page') | |
| 37 | }; | |
| 38 | }; | |
| 39 | /** | |
| 40 | * 字符串命名风格转换 | |
| 41 | * @param {[type]} name [description] | |
| 42 | * @param {[type]} type [description] | |
| 43 | * @return {[type]} [description] | |
| 44 | */ | |
| 45 | 1 | var parseName = function(name, type){ |
| 46 | 17 | name = (name + '').trim(); |
| 47 | 17 | if (type) { |
| 48 | 0 | name = name.replace(/_([a-zA-Z])/g, function(a, b){ |
| 49 | 0 | return b.toUpperCase(); |
| 50 | }); | |
| 51 | 0 | return name.substr(0, 1).toUpperCase() + name.substr(1); |
| 52 | } else { | |
| 53 | //首字母如果是大写,不转义为_x | |
| 54 | 17 | if (name.length >= 1) { |
| 55 | 17 | name = name.substr(0, 1).toLowerCase() + name.substr(1); |
| 56 | } | |
| 57 | 17 | return name.replace(/[A-Z]/g, function(a){ |
| 58 | 0 | return '_' + a; |
| 59 | }).toLowerCase(); | |
| 60 | } | |
| 61 | }; | |
| 62 | ||
| 63 | 1 | return { |
| 64 | // 当前数据库操作对象 | |
| 65 | db: null, | |
| 66 | // 主键名称 | |
| 67 | pk: 'id', | |
| 68 | // 数据表前缀 | |
| 69 | tablePrefix: '', | |
| 70 | // 数据库配置信息 | |
| 71 | config: null, | |
| 72 | // 配置信息key | |
| 73 | configKey: '', | |
| 74 | // 模型名称 | |
| 75 | name: '', | |
| 76 | // 数据库名称 | |
| 77 | dbName: '', | |
| 78 | // 数据表名(不包含表前缀) | |
| 79 | tableName: '', | |
| 80 | // 实际数据表名(包含表前缀) | |
| 81 | trueTableName: '', | |
| 82 | // 数据表子度信息 | |
| 83 | fields: {}, | |
| 84 | // 数据信息 | |
| 85 | _data: {}, | |
| 86 | // 参数 | |
| 87 | _options: {}, | |
| 88 | /** | |
| 89 | * 取得DB类的实例对象 字段检查 | |
| 90 | * @access public | |
| 91 | * @param string $name 模型名称 | |
| 92 | * @param string $tablePrefix 表前缀 | |
| 93 | * @param mixed config 数据库连接信息 | |
| 94 | */ | |
| 95 | init: function(name, tablePrefix, config){ | |
| 96 | // 获取模型名称 | |
| 97 | 30 | if (name) { |
| 98 | 26 | if (name.indexOf('.') > -1) { |
| 99 | 0 | name = name.split('.'); |
| 100 | 0 | this.dbName = name[0]; |
| 101 | 0 | this.name = name[1]; |
| 102 | }else{ | |
| 103 | 26 | this.name = name; |
| 104 | } | |
| 105 | 4 | }else if(!this.name){ |
| 106 | 4 | this.getModelName(); |
| 107 | } | |
| 108 | 30 | if (!isString(tablePrefix)) { |
| 109 | 30 | config = tablePrefix; |
| 110 | 30 | tablePrefix = undefined; |
| 111 | } | |
| 112 | 30 | this.config = config || ''; |
| 113 | //设置数据表前缀 | |
| 114 | 30 | if (tablePrefix || this.config.db_prefix) { |
| 115 | 0 | this.tablePrefix = tablePrefix || this.config.db_prefix; |
| 116 | }else{ | |
| 117 | 30 | if (!this.tablePrefix) { |
| 118 | 30 | this.tablePrefix = C('db_prefix'); |
| 119 | } | |
| 120 | } | |
| 121 | //子类的init方法 | |
| 122 | 30 | if (this._init) { |
| 123 | 0 | this._init(); |
| 124 | } | |
| 125 | }, | |
| 126 | /** | |
| 127 | * 初始化数据库连接 | |
| 128 | * @access public | |
| 129 | * @param integer $linkNum 连接序号 | |
| 130 | * @param mixed $config 数据库连接信息 | |
| 131 | * @param array $params 模型参数 | |
| 132 | * @return Model | |
| 133 | */ | |
| 134 | initDb: function(){ | |
| 135 | 365 | if (this.db) { |
| 136 | 348 | return this.db; |
| 137 | } | |
| 138 | 17 | var config = this.config; |
| 139 | 17 | var configKey = md5(JSON.stringify(config)); |
| 140 | 17 | if (!dbInstances[configKey]) { |
| 141 | 1 | if (config && isString(config) && config.indexOf('/') === -1) { |
| 142 | 0 | config = C(config); |
| 143 | } | |
| 144 | 1 | dbInstances[configKey] = thinkRequire('Db').getInstance(config); |
| 145 | } | |
| 146 | 17 | this.db = dbInstances[configKey]; |
| 147 | 17 | this.configKey = configKey; |
| 148 | 17 | return this.db; |
| 149 | }, | |
| 150 | /** | |
| 151 | * 获取模型名 | |
| 152 | * @access public | |
| 153 | * @return string | |
| 154 | */ | |
| 155 | getModelName: function(){ | |
| 156 | 22 | if (this.name) { |
| 157 | 18 | return this.name; |
| 158 | } | |
| 159 | 4 | var filename = this.__filename || __filename; |
| 160 | 4 | var name = filename.split('/').pop(); |
| 161 | 4 | this.name = name.substr(0, name.length - 8); |
| 162 | 4 | return this.name; |
| 163 | }, | |
| 164 | /** | |
| 165 | * 获取表名 | |
| 166 | * @return {[type]} [description] | |
| 167 | */ | |
| 168 | getTableName: function(){ | |
| 169 | 185 | var tableName; |
| 170 | 185 | if (!this.trueTableName) { |
| 171 | 17 | tableName = this.tablePrefix || ''; |
| 172 | 17 | tableName += this.tableName || parseName(this.getModelName()); |
| 173 | 17 | this.trueTableName = tableName.toLowerCase(); |
| 174 | } | |
| 175 | 185 | tableName = (this.dbName ? this.dbName + '.' : '') + this.trueTableName; |
| 176 | 185 | return tableName; |
| 177 | }, | |
| 178 | /** | |
| 179 | * 获取数据表信息 | |
| 180 | * @access protected | |
| 181 | * @return Promise | |
| 182 | */ | |
| 183 | getTableFields: function(table, all){ | |
| 184 | 189 | this.initDb(); |
| 185 | 189 | if (table === true) { |
| 186 | 0 | table = undefined; |
| 187 | 0 | all = true; |
| 188 | } | |
| 189 | 189 | if (!isEmpty(this.fields)) { |
| 190 | 172 | return getPromise(all ? this.fields : this.fields._field); |
| 191 | } | |
| 192 | 17 | var tableName = table || this.getTableName(); |
| 193 | 17 | var fields = tableFields[tableName]; |
| 194 | 17 | if (!isEmpty(fields)) { |
| 195 | 14 | this.fields = fields; |
| 196 | 14 | return getPromise(all ? fields : fields._field); |
| 197 | } | |
| 198 | 3 | var self = this; |
| 199 | //从数据表里查询字段信息 | |
| 200 | 3 | return this.flushFields().then(function(fields){ |
| 201 | 3 | self.fields = fields; |
| 202 | 3 | if (C('db_fields_cache')) { |
| 203 | 3 | tableFields[tableName] = fields; |
| 204 | } | |
| 205 | 3 | return getPromise(all ? fields : fields._field); |
| 206 | }); | |
| 207 | }, | |
| 208 | /** | |
| 209 | * 获取数据表信息 | |
| 210 | * @param {[type]} table [description] | |
| 211 | * @return Promise [description] | |
| 212 | */ | |
| 213 | flushFields: function(table){ | |
| 214 | 4 | table = table || this.getTableName(); |
| 215 | 4 | return this.initDb().getFields(table).then(function(data){ |
| 216 | 4 | var fields = { |
| 217 | '_field': Object.keys(data || {}), | |
| 218 | '_autoinc': false, | |
| 219 | '_unique': [] | |
| 220 | }; | |
| 221 | 4 | var types = {}; |
| 222 | 4 | for(var key in data){ |
| 223 | 52 | var val = data[key]; |
| 224 | 52 | types[key] = val.type; |
| 225 | 52 | if (val.primary) { |
| 226 | 4 | fields._pk = key; |
| 227 | 4 | if (val.autoinc) { |
| 228 | 4 | fields._autoinc = true; |
| 229 | } | |
| 230 | 48 | }else if (val.unique) { |
| 231 | 4 | fields._unique.push(key); |
| 232 | } | |
| 233 | } | |
| 234 | 4 | fields._type = types; |
| 235 | 4 | return fields; |
| 236 | }) | |
| 237 | }, | |
| 238 | /** | |
| 239 | * 获取类型为唯一的字段 | |
| 240 | * @return {[type]} [description] | |
| 241 | */ | |
| 242 | // getUniqueField: function(data){ | |
| 243 | // var unqiueFileds = this.fields._unique; | |
| 244 | // var unqiue = ''; | |
| 245 | // unqiueFileds.some(function(item){ | |
| 246 | // if (!data || data[item]) { | |
| 247 | // unqiue = item; | |
| 248 | // return unqiue; | |
| 249 | // } | |
| 250 | // }); | |
| 251 | // return unqiue; | |
| 252 | // }, | |
| 253 | /** | |
| 254 | * 获取上一次操作的sql | |
| 255 | * @return {[type]} [description] | |
| 256 | */ | |
| 257 | getLastSql: function(){ | |
| 258 | 171 | return this.initDb().getLastSql(); |
| 259 | }, | |
| 260 | /** | |
| 261 | * 获取主键名称,必须在getTableFields().then之后才能调用该方法 | |
| 262 | * @access public | |
| 263 | * @return string | |
| 264 | */ | |
| 265 | getPk: function(){ | |
| 266 | 26 | return this.fields._pk || this.pk; |
| 267 | }, | |
| 268 | /** | |
| 269 | * 缓存 | |
| 270 | * @param {[type]} key [description] | |
| 271 | * @param {[type]} expire [description] | |
| 272 | * @param {[type]} type [description] | |
| 273 | * @return {[type]} [description] | |
| 274 | */ | |
| 275 | cache: function(key, timeout){ | |
| 276 | 0 | if (key === undefined) { |
| 277 | 0 | return this; |
| 278 | } | |
| 279 | 0 | var options = this._getCacheOptions(key, timeout); |
| 280 | 0 | this._options.cache = options; |
| 281 | 0 | return this; |
| 282 | }, | |
| 283 | /** | |
| 284 | * 获取缓存的选项 | |
| 285 | * @param {[type]} key [description] | |
| 286 | * @param {[type]} timeout [description] | |
| 287 | * @return {[type]} [description] | |
| 288 | */ | |
| 289 | _getCacheOptions: function(key, timeout, type){ | |
| 290 | 0 | if (isObject(key)) { |
| 291 | 0 | return key; |
| 292 | } | |
| 293 | 0 | if (isNumber(key)) { |
| 294 | 0 | timeout = key; |
| 295 | 0 | key = ''; |
| 296 | } | |
| 297 | 0 | var cacheType = type === undefined ? C('db_cache_type') : type; |
| 298 | 0 | var options = { |
| 299 | key: key, | |
| 300 | timeout: timeout || C('db_cache_timeout'), | |
| 301 | type: cacheType, | |
| 302 | gcType: 'dbCache' | |
| 303 | } | |
| 304 | 0 | if (cacheType === 'File') { |
| 305 | 0 | options.cache_path = C('db_cache_path'); |
| 306 | }else{ | |
| 307 | 0 | options.cacheData = dbCacheData; |
| 308 | } | |
| 309 | 0 | return options; |
| 310 | }, | |
| 311 | /** | |
| 312 | * 指定查询数量 | |
| 313 | * @param {[type]} offset [description] | |
| 314 | * @param {[type]} length [description] | |
| 315 | * @return {[type]} [description] | |
| 316 | */ | |
| 317 | limit: function(offset, length){ | |
| 318 | 4 | this._options.limit = length === undefined ? offset : offset + ',' + length; |
| 319 | 4 | return this; |
| 320 | }, | |
| 321 | /** | |
| 322 | * 指定分页 | |
| 323 | * @return {[type]} [description] | |
| 324 | */ | |
| 325 | page: function(page, listRows){ | |
| 326 | 7 | this._options.page = listRows === undefined ? page : page + ',' + listRows; |
| 327 | 7 | return this; |
| 328 | }, | |
| 329 | /** | |
| 330 | * where条件 | |
| 331 | * @return {[type]} [description] | |
| 332 | */ | |
| 333 | where: function(where){ | |
| 334 | 91 | if (!where) { |
| 335 | 2 | return this; |
| 336 | } | |
| 337 | 89 | if (isString(where)) { |
| 338 | 3 | where = {_string: where}; |
| 339 | } | |
| 340 | 89 | this._options.where = extend(this._options.where || {}, where); |
| 341 | 89 | return this; |
| 342 | }, | |
| 343 | /** | |
| 344 | * 要查询的字段 | |
| 345 | * @param {[type]} field [description] | |
| 346 | * @param {[type]} reverse [description] | |
| 347 | * @return {[type]} [description] | |
| 348 | */ | |
| 349 | field: function(field, reverse){ | |
| 350 | 5 | if (isArray(field)) { |
| 351 | 2 | field = field.join(','); |
| 352 | 3 | }else if (!field) { |
| 353 | 1 | field = '*'; |
| 354 | } | |
| 355 | 5 | this._options.field = field; |
| 356 | 5 | this._options.fieldReverse = reverse; |
| 357 | 5 | return this; |
| 358 | }, | |
| 359 | /** | |
| 360 | * 联合查询 | |
| 361 | * @return {[type]} [description] | |
| 362 | */ | |
| 363 | union: function(union, all){ | |
| 364 | 5 | if (!this._options.union) { |
| 365 | 4 | this._options.union = []; |
| 366 | } | |
| 367 | 5 | this._options.union.push({ |
| 368 | union: union, | |
| 369 | all: all | |
| 370 | }); | |
| 371 | 5 | return this; |
| 372 | }, | |
| 373 | /** | |
| 374 | * .join({ | |
| 375 | * 'xxx': { | |
| 376 | * join: 'left', | |
| 377 | * as: 'c', | |
| 378 | * on: ['id', 'cid'] | |
| 379 | * } | |
| 380 | * }) | |
| 381 | * 联合查询 | |
| 382 | * @param {[type]} join [description] | |
| 383 | * @return {[type]} [description] | |
| 384 | */ | |
| 385 | join: function(join){ | |
| 386 | 18 | if (!this._options.join) { |
| 387 | 17 | this._options.join = []; |
| 388 | } | |
| 389 | 18 | if (isArray(join)) { |
| 390 | 1 | this._options.join = this._options.join.concat(join); |
| 391 | }else{ | |
| 392 | 17 | this._options.join.push(join); |
| 393 | } | |
| 394 | 18 | return this; |
| 395 | }, | |
| 396 | /** | |
| 397 | * 生成查询SQL 可用于子查询 | |
| 398 | * @param {[type]} options [description] | |
| 399 | * @return {[type]} [description] | |
| 400 | */ | |
| 401 | buildSql: function(options){ | |
| 402 | 1 | var self = this; |
| 403 | 1 | return this.parseOptions(options).then(function(options){ |
| 404 | 1 | return '( ' + self.db.buildSelectSql(options) + ' )'; |
| 405 | }); | |
| 406 | }, | |
| 407 | /** | |
| 408 | * 解析参数 | |
| 409 | * @param {[type]} options [description] | |
| 410 | * @return promise [description] | |
| 411 | */ | |
| 412 | parseOptions: function(options, extraOptions){ | |
| 413 | 177 | options = extend({}, this._options, this.parseWhereOptions(options), extraOptions); |
| 414 | // 查询过后清空sql表达式组装 避免影响下次查询 | |
| 415 | 177 | this._options = {}; |
| 416 | 177 | options.table = options.table || this.getTableName(); |
| 417 | //表前缀,Db里会使用 | |
| 418 | 177 | options.tablePrefix = this.tablePrefix; |
| 419 | 177 | options.model = this.name; |
| 420 | 177 | var promise = this.getTableFields(options.table); |
| 421 | //数据表别名 | |
| 422 | 177 | if (options.alias) { |
| 423 | 6 | options.table += ' AS ' + options.alias; |
| 424 | } | |
| 425 | 177 | var self = this; |
| 426 | 177 | var keyReg = /[\.\|\&]/; |
| 427 | 177 | return promise.then(function(fields){ |
| 428 | // 字段类型验证 | |
| 429 | 177 | if (isObject(options.where) && !isEmpty(fields)) { |
| 430 | // 对数组查询条件进行字段类型检查 | |
| 431 | 90 | for(var key in options.where){ |
| 432 | 103 | var val = options.where[key]; |
| 433 | 103 | key = key.trim(); |
| 434 | 103 | if (fields.indexOf(key) > -1) { |
| 435 | 89 | if (isScalar(val)) { |
| 436 | 24 | options.where[key] = self.parseType(options.where, key)[key]; |
| 437 | } | |
| 438 | 14 | }else if(key[0] !== '_' && !keyReg.test(key)){ |
| 439 | 1 | delete options.where[key]; |
| 440 | } | |
| 441 | } | |
| 442 | } | |
| 443 | //field反选 | |
| 444 | 177 | if (options.field && options.fieldReverse) { |
| 445 | 1 | var optionsField = options.field.split(','); |
| 446 | 1 | options.field = fields.filter(function(item){ |
| 447 | 13 | if (optionsField.indexOf(item) > -1) { |
| 448 | 2 | return; |
| 449 | } | |
| 450 | 11 | return item; |
| 451 | }).join(','); | |
| 452 | } | |
| 453 | 177 | return self._optionsFilter(options, fields); |
| 454 | }); | |
| 455 | }, | |
| 456 | /** | |
| 457 | * 选项过滤器 | |
| 458 | * 具体的Model类里进行实现 | |
| 459 | * @param {[type]} options [description] | |
| 460 | * @return {[type]} [description] | |
| 461 | */ | |
| 462 | _optionsFilter: function(options){ | |
| 463 | 177 | return options; |
| 464 | }, | |
| 465 | /** | |
| 466 | * 数据类型检测 | |
| 467 | * @param {[type]} data [description] | |
| 468 | * @param {[type]} key [description] | |
| 469 | * @return {[type]} [description] | |
| 470 | */ | |
| 471 | parseType: function(data, key){ | |
| 472 | 37 | var fieldType = this.fields._type[key] || ''; |
| 473 | 37 | if (fieldType.indexOf('bigint') === -1 && fieldType.indexOf('int') > -1) { |
| 474 | 19 | data[key] = parseInt(data[key], 10) || 0; |
| 475 | 18 | }else if(fieldType.indexOf('double') > -1 || fieldType.indexOf('float') > -1){ |
| 476 | 0 | data[key] = parseFloat(data[key]) || 0.0; |
| 477 | 18 | }else if(fieldType.indexOf('bool') > -1){ |
| 478 | 0 | data[key] = !! data[key]; |
| 479 | } | |
| 480 | 37 | return data; |
| 481 | }, | |
| 482 | /** | |
| 483 | * 对插入到数据库中的数据进行处理,要在parseOptions后执行 | |
| 484 | * @param {[type]} data [description] | |
| 485 | * @return {[type]} [description] | |
| 486 | */ | |
| 487 | parseData: function(data){ | |
| 488 | 18 | data = extend({}, data); |
| 489 | 18 | var key; |
| 490 | 18 | if (!isEmpty(this.fields)) { |
| 491 | 18 | for(key in data){ |
| 492 | 21 | var val = data[key]; |
| 493 | 21 | if (this.fields._field.indexOf(key) === -1) { |
| 494 | 0 | delete data[key]; |
| 495 | 21 | }else if(isScalar(val)){ |
| 496 | 13 | data = this.parseType(data, key); |
| 497 | } | |
| 498 | } | |
| 499 | } | |
| 500 | //安全过滤 | |
| 501 | 18 | if (typeof this._options.filter === 'function') { |
| 502 | 0 | for(key in data){ |
| 503 | 0 | data[key] = this._options.filter.call(this, key, data[key]); |
| 504 | } | |
| 505 | 0 | delete this._options.filter; |
| 506 | } | |
| 507 | 18 | data = this._dataFilter(data); |
| 508 | 18 | return data; |
| 509 | }, | |
| 510 | /** | |
| 511 | * 数据过滤器 | |
| 512 | * 具体的Model类里进行实现 | |
| 513 | * @param {[type]} data [description] | |
| 514 | * @return {[type]} [description] | |
| 515 | */ | |
| 516 | _dataFilter: function(data){ | |
| 517 | 18 | return data; |
| 518 | }, | |
| 519 | /** | |
| 520 | * 数据插入之前操作,可以返回一个promise | |
| 521 | * @param {[type]} data [description] | |
| 522 | * @param {[type]} options [description] | |
| 523 | * @return {[type]} [description] | |
| 524 | */ | |
| 525 | _beforeAdd: function(data){ | |
| 526 | 2 | return data; |
| 527 | }, | |
| 528 | /** | |
| 529 | * 数据插入之后操作,可以返回一个promise | |
| 530 | * @param {[type]} data [description] | |
| 531 | * @param {[type]} options [description] | |
| 532 | * @return {[type]} [description] | |
| 533 | */ | |
| 534 | _afterAdd: function(data){ | |
| 535 | 2 | return data; |
| 536 | }, | |
| 537 | /** | |
| 538 | * 添加一条数据 | |
| 539 | * @param {[type]} data [description] | |
| 540 | * @param {[type]} options [description] | |
| 541 | * @param int 返回插入的id | |
| 542 | */ | |
| 543 | add: function(data, options, replace){ | |
| 544 | //copy data | |
| 545 | 3 | data = extend({}, data); |
| 546 | 3 | if (isEmpty(data)) { |
| 547 | 1 | if (!isEmpty(this._data)) { |
| 548 | 0 | data = this._data; |
| 549 | 0 | this._data = {}; |
| 550 | }else{ | |
| 551 | 1 | return getPromise('_DATA_TYPE_INVALID_', true); |
| 552 | } | |
| 553 | } | |
| 554 | 2 | var self = this; |
| 555 | //解析后的选项 | |
| 556 | 2 | var parsedOptions = {}; |
| 557 | //解析后的数据 | |
| 558 | 2 | var parsedData = {}; |
| 559 | 2 | return this.parseOptions(options).then(function(options){ |
| 560 | 2 | parsedOptions = options; |
| 561 | 2 | return self._beforeAdd(data, parsedOptions); |
| 562 | }).then(function(data){ | |
| 563 | 2 | parsedData = data; |
| 564 | 2 | data = self.parseData(data); |
| 565 | 2 | return self.db.insert(data, parsedOptions, replace); |
| 566 | }).then(function(){ | |
| 567 | 2 | parsedData[self.getPk()] = self.db.getLastInsertId(); |
| 568 | 2 | return self._afterAdd(parsedData, parsedOptions); |
| 569 | }).then(function(){ | |
| 570 | 2 | return parsedData[self.getPk()]; |
| 571 | }); | |
| 572 | }, | |
| 573 | /** | |
| 574 | * 如果当前条件的数据不存在,才添加 | |
| 575 | * @param {[type]} data 要插入的数据 | |
| 576 | * @param {[type]} where where条件 | |
| 577 | * @param boolean returnType 返回值是否包含type | |
| 578 | * @return {[type]} promise | |
| 579 | */ | |
| 580 | thenAdd: function(data, where, returnType){ | |
| 581 | 0 | this.where(where); |
| 582 | 0 | var self = this; |
| 583 | 0 | return this.find().then(function(findData){ |
| 584 | 0 | if (!isEmpty(findData)) { |
| 585 | 0 | var idValue = findData[self.getPk()]; |
| 586 | 0 | return returnType ? {id: idValue, type: 'exist'} : idValue; |
| 587 | } | |
| 588 | 0 | return self.add(data).then(function(insertId){ |
| 589 | 0 | return returnType ? {id: insertId, type: 'add'} : insertId; |
| 590 | }); | |
| 591 | }); | |
| 592 | }, | |
| 593 | /** | |
| 594 | * 插入多条数据 | |
| 595 | * @param {[type]} data [description] | |
| 596 | * @param {[type]} options [description] | |
| 597 | * @param {[type]} replace [description] | |
| 598 | */ | |
| 599 | addAll: function(data, options, replace){ | |
| 600 | 6 | if (!isArray(data) || !isObject(data[0])) { |
| 601 | 2 | return getPromise(L('_DATA_TYPE_INVALID_'), true); |
| 602 | } | |
| 603 | 4 | var self = this; |
| 604 | 4 | return this.parseOptions(options).then(function(options){ |
| 605 | 4 | return self.db.insertAll(data, options, replace); |
| 606 | }).then(function(){ | |
| 607 | 4 | return self.db.getLastInsertId(); |
| 608 | }); | |
| 609 | }, | |
| 610 | /** | |
| 611 | * 删除后续操作 | |
| 612 | * @return {[type]} [description] | |
| 613 | */ | |
| 614 | _afterDelete: function(data){ | |
| 615 | 3 | return data; |
| 616 | }, | |
| 617 | /** | |
| 618 | * 删除数据 | |
| 619 | * @return {[type]} [description] | |
| 620 | */ | |
| 621 | delete: function(options){ | |
| 622 | 3 | var self = this; |
| 623 | 3 | var parsedOptions = {}; |
| 624 | 3 | var affectedRows = 0; |
| 625 | 3 | return this.parseOptions(options).then(function(options){ |
| 626 | 3 | parsedOptions = options; |
| 627 | 3 | return self.db.delete(options); |
| 628 | }).then(function(rows){ | |
| 629 | 3 | affectedRows = rows; |
| 630 | 3 | return self._afterDelete(parsedOptions.where || {}, parsedOptions); |
| 631 | }).then(function(){ | |
| 632 | 3 | return affectedRows; |
| 633 | }) | |
| 634 | }, | |
| 635 | /** | |
| 636 | * 更新前置操作 | |
| 637 | * @param {[type]} data [description] | |
| 638 | * @param {[type]} options [description] | |
| 639 | * @return {[type]} [description] | |
| 640 | */ | |
| 641 | _beforeUpdate: function(data){ | |
| 642 | 16 | return data; |
| 643 | }, | |
| 644 | /** | |
| 645 | * 更新后置操作 | |
| 646 | * @param {[type]} data [description] | |
| 647 | * @param {[type]} options [description] | |
| 648 | * @return {[type]} [description] | |
| 649 | */ | |
| 650 | _afterUpdate: function(data){ | |
| 651 | 13 | return data; |
| 652 | }, | |
| 653 | /** | |
| 654 | * 更新数据 | |
| 655 | * @return {[type]} [description] | |
| 656 | */ | |
| 657 | update: function(data, options){ | |
| 658 | 17 | data = extend({}, data); |
| 659 | 17 | if (isEmpty(data)) { |
| 660 | 1 | if (!isEmpty(this._data)) { |
| 661 | 0 | data = this._data; |
| 662 | 0 | this._data = {}; |
| 663 | }else{ | |
| 664 | 1 | return getPromise('_DATA_TYPE_INVALID_', true); |
| 665 | } | |
| 666 | } | |
| 667 | 16 | var self = this; |
| 668 | 16 | var pk = self.getPk(); |
| 669 | 16 | var parsedOptions = {}; |
| 670 | 16 | var parsedData = {}; |
| 671 | 16 | var affectedRows = 0; |
| 672 | 16 | return this.parseOptions(options).then(function(options){ |
| 673 | 16 | parsedOptions = options; |
| 674 | 16 | return self._beforeUpdate(data, options); |
| 675 | }).then(function(data){ | |
| 676 | 16 | parsedData = data; |
| 677 | 16 | data = self.parseData(data); |
| 678 | 16 | if (isEmpty(parsedOptions.where)) { |
| 679 | // 如果存在主键数据 则自动作为更新条件 | |
| 680 | 5 | if (!isEmpty(data[pk])) { |
| 681 | 2 | parsedOptions.where = getObject(pk, data[pk]); |
| 682 | 2 | delete data[pk]; |
| 683 | }else{ | |
| 684 | 3 | return getPromise('_OPERATION_WRONG_', true); |
| 685 | } | |
| 686 | }else{ | |
| 687 | 11 | parsedData[pk] = parsedOptions.where[pk]; |
| 688 | } | |
| 689 | 13 | return self.db.update(data, parsedOptions); |
| 690 | }).then(function(rows){ | |
| 691 | 13 | affectedRows = rows; |
| 692 | 13 | return self._afterUpdate(parsedData, parsedOptions); |
| 693 | }).then(function(){ | |
| 694 | 13 | return affectedRows; |
| 695 | }); | |
| 696 | }, | |
| 697 | /** | |
| 698 | * 更新多个数据,自动用主键作为查询条件 | |
| 699 | * @param {[type]} dataList [description] | |
| 700 | * @return {[type]} [description] | |
| 701 | */ | |
| 702 | updateAll: function(dataList){ | |
| 703 | 4 | if (!isArray(dataList) || !isObject(dataList[0])) { |
| 704 | 2 | return getPromise('_DATA_TYPE_INVALID_', true); |
| 705 | } | |
| 706 | 2 | var self = this; |
| 707 | 2 | var promises = dataList.map(function(data){ |
| 708 | 3 | return self.update(data); |
| 709 | }); | |
| 710 | 2 | return Promise.all(promises); |
| 711 | }, | |
| 712 | /** | |
| 713 | * 更新某个字段的值 | |
| 714 | * @param {[type]} field [description] | |
| 715 | * @param {[type]} value [description] | |
| 716 | * @return {[type]} [description] | |
| 717 | */ | |
| 718 | updateField: function(field, value){ | |
| 719 | 10 | var data = {}; |
| 720 | 10 | if (isObject(field)) { |
| 721 | 0 | data = field; |
| 722 | }else{ | |
| 723 | 10 | data[field] = value; |
| 724 | } | |
| 725 | 10 | return this.update(data); |
| 726 | }, | |
| 727 | /** | |
| 728 | * 字段值增长 | |
| 729 | * @return {[type]} [description] | |
| 730 | */ | |
| 731 | updateInc: function(field, step){ | |
| 732 | 4 | step = parseInt(step, 10) || 1; |
| 733 | 4 | return this.updateField(field, ['exp', field + '+' + step]); |
| 734 | }, | |
| 735 | /** | |
| 736 | * 字段值减少 | |
| 737 | * @return {[type]} [description] | |
| 738 | */ | |
| 739 | updateDec: function(field, step){ | |
| 740 | 4 | step = parseInt(step, 10) || 1; |
| 741 | 4 | return this.updateField(field, ['exp', field + '-' + step]); |
| 742 | }, | |
| 743 | /** | |
| 744 | * 解析options中简洁的where条件 | |
| 745 | * @return {[type]} [description] | |
| 746 | */ | |
| 747 | parseWhereOptions: function(options){ | |
| 748 | 177 | if (isNumber(options) || isString(options)) { |
| 749 | 0 | var pk = this.getPk(); |
| 750 | 0 | options += ''; |
| 751 | 0 | var where = {}; |
| 752 | 0 | if (options.indexOf(',') > -1) { |
| 753 | 0 | where[pk] = ['IN', options]; |
| 754 | }else{ | |
| 755 | 0 | where[pk] = options; |
| 756 | } | |
| 757 | 0 | options = { |
| 758 | where: where | |
| 759 | }; | |
| 760 | } | |
| 761 | 177 | return options || {}; |
| 762 | }, | |
| 763 | /** | |
| 764 | * find查询后置操作 | |
| 765 | * @return {[type]} [description] | |
| 766 | */ | |
| 767 | _afterFind: function(result){ | |
| 768 | 1 | return result; |
| 769 | }, | |
| 770 | /** | |
| 771 | * 查询一条数据 | |
| 772 | * @return 返回一个promise | |
| 773 | */ | |
| 774 | find: function(options){ | |
| 775 | 1 | var self = this; |
| 776 | 1 | var parsedOptions = {}; |
| 777 | 1 | return this.parseOptions(options, { |
| 778 | limit: 1 | |
| 779 | }).then(function(options){ | |
| 780 | 1 | parsedOptions = options; |
| 781 | 1 | return self.db.select(options); |
| 782 | }).then(function(data){ | |
| 783 | 1 | return self._afterFind(data[0] || {}, parsedOptions); |
| 784 | }); | |
| 785 | }, | |
| 786 | /** | |
| 787 | * 查询后置操作 | |
| 788 | * @param {[type]} result [description] | |
| 789 | * @param {[type]} options [description] | |
| 790 | * @return {[type]} [description] | |
| 791 | */ | |
| 792 | _afterSelect: function(result){ | |
| 793 | 136 | return result; |
| 794 | }, | |
| 795 | /** | |
| 796 | * 查询数据 | |
| 797 | * @return 返回一个promise | |
| 798 | */ | |
| 799 | select: function(options){ | |
| 800 | 136 | var self = this; |
| 801 | 136 | var parsedOptions = {}; |
| 802 | 136 | return this.parseOptions(options).then(function(options){ |
| 803 | 136 | parsedOptions = options; |
| 804 | 136 | return self.db.select(options); |
| 805 | }).then(function(result){ | |
| 806 | 136 | return self._afterSelect(result, parsedOptions); |
| 807 | }); | |
| 808 | }, | |
| 809 | selectAdd: function(fields, table, options){ | |
| 810 | 0 | var self = this; |
| 811 | 0 | return this.parseOptions(options).then(function(options){ |
| 812 | 0 | fields = fields || options.field; |
| 813 | 0 | table = table || self.getTableName(); |
| 814 | 0 | return self.db.selectInsert(fields, table, options); |
| 815 | }); | |
| 816 | }, | |
| 817 | /** | |
| 818 | * 返回数据里含有count信息的查询 | |
| 819 | * @param options 查询选项 | |
| 820 | * @param pageFlag 当页面不合法时的处理方式,true为获取第一页,false为获取最后一页,undefined获取为空 | |
| 821 | * @return promise | |
| 822 | */ | |
| 823 | countSelect: function(options, pageFlag){ | |
| 824 | 0 | if (isBoolean(options)) { |
| 825 | 0 | pageFlag = options; |
| 826 | 0 | options = {}; |
| 827 | } | |
| 828 | 0 | var self = this; |
| 829 | //解析后的options | |
| 830 | 0 | var parsedOptions = {}; |
| 831 | 0 | var result = {}; |
| 832 | 0 | return this.parseOptions(options).then(function(options){ |
| 833 | 0 | delete options.table; |
| 834 | 0 | parsedOptions = options; |
| 835 | 0 | return self.options({ |
| 836 | where: options.where, | |
| 837 | cache: options.cache, | |
| 838 | join: options.join, | |
| 839 | alias: options.alias | |
| 840 | }).count((options.alias || self.getTableName()) + '.' + self.getPk()); | |
| 841 | }).then(function(count){ | |
| 842 | 0 | var pageOptions = parsePage(parsedOptions); |
| 843 | 0 | var totalPage = Math.ceil(count / pageOptions.num); |
| 844 | 0 | if (isBoolean(pageFlag)) { |
| 845 | 0 | if (pageOptions.page > totalPage) { |
| 846 | 0 | pageOptions.page = pageFlag === true ? 1 : totalPage; |
| 847 | } | |
| 848 | 0 | parsedOptions.page = pageOptions.page + ',' + pageOptions.num; |
| 849 | } | |
| 850 | 0 | result = extend({count: count, total: totalPage}, pageOptions); |
| 851 | 0 | return self.select(parsedOptions); |
| 852 | }).then(function(data){ | |
| 853 | 0 | result.data = data; |
| 854 | 0 | return result; |
| 855 | }); | |
| 856 | }, | |
| 857 | /** | |
| 858 | * 获取某个字段下的记录 | |
| 859 | * @return {[type]} [description] | |
| 860 | */ | |
| 861 | getField: function(field, one){ | |
| 862 | 14 | var self = this; |
| 863 | 14 | return this.parseOptions({'field': field}).then(function(options){ |
| 864 | 14 | if (isNumber(one)) { |
| 865 | 1 | options.limit = one; |
| 866 | 13 | }else if (one === true) { |
| 867 | 11 | options.limit = 1; |
| 868 | } | |
| 869 | 14 | return self.db.select(options); |
| 870 | }).then(function(data){ | |
| 871 | 14 | var multi = field.indexOf(',') > -1; |
| 872 | 14 | if (multi) { |
| 873 | 1 | var fields = field.split(/\s*,\s*/); |
| 874 | 1 | var result = {}; |
| 875 | 1 | fields.forEach(function(item){ |
| 876 | 2 | result[item] = []; |
| 877 | }) | |
| 878 | 1 | data.every(function(item){ |
| 879 | 9 | fields.forEach(function(fItem){ |
| 880 | 18 | if (one === true) { |
| 881 | 0 | result[fItem] = item[fItem]; |
| 882 | }else{ | |
| 883 | 18 | result[fItem].push(item[fItem]); |
| 884 | } | |
| 885 | }) | |
| 886 | 9 | return one === true ? false : true; |
| 887 | }) | |
| 888 | 1 | return result; |
| 889 | }else{ | |
| 890 | 13 | data = data.map(function(item){ |
| 891 | 117 | return Object.values(item)[0]; |
| 892 | }) | |
| 893 | 13 | return one === true ? data[0] : data; |
| 894 | } | |
| 895 | }); | |
| 896 | }, | |
| 897 | /** | |
| 898 | * 根据某个字段值获取一条数据 | |
| 899 | * @param {[type]} name [description] | |
| 900 | * @param {[type]} value [description] | |
| 901 | * @return {[type]} [description] | |
| 902 | */ | |
| 903 | getBy: function(name, value){ | |
| 904 | 0 | var where = getObject(name, value); |
| 905 | 0 | return this.where(where).find(); |
| 906 | }, | |
| 907 | /** | |
| 908 | * SQL查询 | |
| 909 | * @return {[type]} [description] | |
| 910 | */ | |
| 911 | query: function(sql, parse){ | |
| 912 | 0 | if (parse !== undefined && !isBoolean(parse) && !isArray(parse)) { |
| 913 | 0 | parse = [].slice.call(arguments); |
| 914 | 0 | parse.shift(); |
| 915 | } | |
| 916 | 0 | var self = this; |
| 917 | 0 | return this.parseSql(sql, parse).then(function(sql){ |
| 918 | 0 | return self.db.query(sql); |
| 919 | }); | |
| 920 | }, | |
| 921 | /** | |
| 922 | * 执行SQL语法,非查询类的SQL语句,返回值为影响的行数 | |
| 923 | * @param {[type]} sql [description] | |
| 924 | * @param {[type]} parse [description] | |
| 925 | * @return {[type]} [description] | |
| 926 | */ | |
| 927 | execute: function(sql, parse){ | |
| 928 | 0 | if (parse !== undefined && !isBoolean(parse) && !isArray(parse)) { |
| 929 | 0 | parse = [].slice.call(arguments); |
| 930 | 0 | parse.shift(); |
| 931 | } | |
| 932 | 0 | var self = this; |
| 933 | 0 | return this.parseSql(sql, parse).then(function(sql){ |
| 934 | 0 | return self.db.execute(sql); |
| 935 | }); | |
| 936 | }, | |
| 937 | /** | |
| 938 | * 解析SQL语句 | |
| 939 | * @return promise [description] | |
| 940 | */ | |
| 941 | parseSql: function(sql, parse){ | |
| 942 | 0 | var promise = null; |
| 943 | 0 | var self = this; |
| 944 | 0 | if (parse === true) { |
| 945 | 0 | promise = this.parseOptions().then(function(options){ |
| 946 | 0 | return self.db.parseSql(options); |
| 947 | }); | |
| 948 | }else{ | |
| 949 | 0 | if (parse === undefined) { |
| 950 | 0 | parse = []; |
| 951 | }else{ | |
| 952 | 0 | parse = isArray(parse) ? parse : [parse]; |
| 953 | } | |
| 954 | 0 | parse.unshift(sql); |
| 955 | 0 | sql = util.format.apply(null, parse); |
| 956 | 0 | var map = { |
| 957 | '__TABLE__': '`' + this.getTableName() + '`' | |
| 958 | }; | |
| 959 | 0 | sql = sql.replace(/__([A-Z]+)__/g, function(a, b){ |
| 960 | 0 | return map[a] || ('`' + C('db_prefix') + b.toLowerCase() + '`'); |
| 961 | }); | |
| 962 | 0 | promise = getPromise(sql); |
| 963 | } | |
| 964 | 0 | this.initDb().setModel(self.name); |
| 965 | 0 | return promise; |
| 966 | }, | |
| 967 | /** | |
| 968 | * 设置数据对象值 | |
| 969 | * @return {[type]} [description] | |
| 970 | */ | |
| 971 | data: function(data){ | |
| 972 | 0 | if (data === true) { |
| 973 | 0 | return this._data; |
| 974 | } | |
| 975 | 0 | if (isString(data)) { |
| 976 | 0 | data = querystring.parse(data); |
| 977 | } | |
| 978 | 0 | this._data = data; |
| 979 | 0 | return this; |
| 980 | }, | |
| 981 | /** | |
| 982 | * 设置操作选项 | |
| 983 | * @param {[type]} options [description] | |
| 984 | * @return {[type]} [description] | |
| 985 | */ | |
| 986 | options: function(options){ | |
| 987 | 0 | if (options === true) { |
| 988 | 0 | return this._options; |
| 989 | } | |
| 990 | 0 | this._options = options; |
| 991 | 0 | return this; |
| 992 | }, | |
| 993 | /** | |
| 994 | * 关闭数据库连接 | |
| 995 | * @return {[type]} [description] | |
| 996 | */ | |
| 997 | close: function(){ | |
| 998 | 0 | delete dbInstances[this.configKey]; |
| 999 | 0 | if (this.db) { |
| 1000 | 0 | this.db.close(); |
| 1001 | 0 | this.db = null; |
| 1002 | } | |
| 1003 | } | |
| 1004 | }; | |
| 1005 | }).extend(function(){ | |
| 1006 | 1 | 'use strict'; |
| 1007 | //追加的方法 | |
| 1008 | 1 | var methods = {}; |
| 1009 | // 链操作方法列表 | |
| 1010 | 1 | var methodNameList = [ |
| 1011 | 'table','order','alias','having','group', | |
| 1012 | 'lock','auto','filter','validate' | |
| 1013 | ]; | |
| 1014 | 1 | methodNameList.forEach(function(item){ |
| 1015 | 9 | methods[item] = function(data){ |
| 1016 | 23 | this._options[item] = data; |
| 1017 | 23 | return this; |
| 1018 | }; | |
| 1019 | }); | |
| 1020 | 1 | methods.distinct = function(data){ |
| 1021 | 2 | this._options.distinct = data; |
| 1022 | //如果传过来一个字段,则映射到field上 | |
| 1023 | 2 | if (isString(data)) { |
| 1024 | 2 | this._options.field = data; |
| 1025 | } | |
| 1026 | 2 | return this; |
| 1027 | }; | |
| 1028 | 1 | ['count','sum','min','max','avg'].forEach(function(item){ |
| 1029 | 5 | methods[item] = function(field){ |
| 1030 | 10 | var self = this; |
| 1031 | 10 | return this.getTableFields().then(function(){ |
| 1032 | 10 | field = field || self.getPk(); |
| 1033 | 10 | return self.getField(item.toUpperCase() + '(' + field + ') AS thinkjs_' + item, true); |
| 1034 | }); | |
| 1035 | }; | |
| 1036 | }); | |
| 1037 | //方法别名 | |
| 1038 | 1 | var aliasMethodMap = { |
| 1039 | update: 'save', | |
| 1040 | updateField: 'setField', | |
| 1041 | updateInc: 'setInc', | |
| 1042 | updateDec: 'setDec' | |
| 1043 | }; | |
| 1044 | 1 | Object.keys(aliasMethodMap).forEach(function(key){ |
| 1045 | 4 | var value = aliasMethodMap[key]; |
| 1046 | 4 | methods[value] = function(){ |
| 1047 | 0 | return this[key].apply(this, arguments); |
| 1048 | }; | |
| 1049 | }); | |
| 1050 | 1 | return methods; |
| 1051 | }); | |
| 1052 | /** | |
| 1053 | * 关闭所有的数据库连接 | |
| 1054 | * @return {[type]} [description] | |
| 1055 | */ | |
| 1056 | 1 | Model.close = function(){ |
| 1057 | 1 | 'use strict'; |
| 1058 | 1 | for(var key in dbInstances) { |
| 1059 | 0 | dbInstances[key].close(); |
| 1060 | } | |
| 1061 | 1 | dbInstances = {}; |
| 1062 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | 1 | var cluster = require('cluster'); |
| 3 | ||
| 4 | //自动加载进行识别的路径 | |
| 5 | 1 | var autoloadPaths = {}; |
| 6 | /** | |
| 7 | * [exports description] | |
| 8 | * @type {Object} | |
| 9 | */ | |
| 10 | 1 | module.exports = { |
| 11 | /** | |
| 12 | * [start description] | |
| 13 | * @return {[type]} [description] | |
| 14 | */ | |
| 15 | start: function(){ | |
| 16 | 1 | 'use strict'; |
| 17 | 1 | this.init(); |
| 18 | 1 | this.processEvent(); |
| 19 | //加载文件 | |
| 20 | 1 | this.loadFiles(); |
| 21 | //合并自动加载的路径 | |
| 22 | 1 | this.mergeAutoloadPath(); |
| 23 | //thinkRequire的autoload | |
| 24 | 1 | registerAutoload(this.autoload); |
| 25 | //debug模式 | |
| 26 | 1 | if (APP_DEBUG) { |
| 27 | 0 | this.debug(); |
| 28 | } | |
| 29 | //记录进程的id | |
| 30 | 1 | this.logPid(); |
| 31 | ||
| 32 | 1 | thinkRequire('App').run(); |
| 33 | }, | |
| 34 | /** | |
| 35 | * 定义一些目录,加载框架的基础文件 | |
| 36 | * @return {[type]} [description] | |
| 37 | */ | |
| 38 | init: function(){ | |
| 39 | 1 | 'use strict'; |
| 40 | //系统路径设置 | |
| 41 | 1 | global.THINK_LIB_PATH = THINK_PATH + '/Lib'; |
| 42 | 1 | global.THINK_EXTEND_PATH = THINK_LIB_PATH + '/Extend'; |
| 43 | //应用路径设置 | |
| 44 | 1 | var config = { |
| 45 | COMMON_PATH: APP_PATH + '/Common', | |
| 46 | LIB_PATH: APP_PATH + '/Lib', | |
| 47 | CONF_PATH: APP_PATH + '/Conf', | |
| 48 | LANG_PATH: APP_PATH + '/Lang', | |
| 49 | VIEW_PATH: APP_PATH + '/View', | |
| 50 | //HTML_PATH: RUNTIME_PATH + '/Html', | |
| 51 | LOG_PATH: RUNTIME_PATH + '/Log', | |
| 52 | TEMP_PATH: RUNTIME_PATH + '/Temp', | |
| 53 | DATA_PATH: RUNTIME_PATH + '/Data', | |
| 54 | CACHE_PATH: RUNTIME_PATH + '/Cache' | |
| 55 | }; | |
| 56 | 1 | for (var name in config) { |
| 57 | 9 | if (global[name] === undefined) { |
| 58 | 9 | global[name] = config[name]; |
| 59 | } | |
| 60 | } | |
| 61 | 1 | require(THINK_PATH + '/Common/extend.js'); |
| 62 | 1 | require(THINK_PATH + '/Common/common.js'); |
| 63 | 1 | require(THINK_PATH + '/Common/function.js'); |
| 64 | //别名导入 | |
| 65 | 1 | aliasImport(require(THINK_PATH + '/Conf/alias.js')); |
| 66 | }, | |
| 67 | /** | |
| 68 | * 注册异常处理 | |
| 69 | * @return {[type]} [description] | |
| 70 | */ | |
| 71 | processEvent: function(){ | |
| 72 | 1 | 'use strict'; |
| 73 | 1 | process.on('uncaughtException', function(err) { |
| 74 | 0 | console.log(isError(err) ? err.stack : err); |
| 75 | }); | |
| 76 | }, | |
| 77 | /** | |
| 78 | * 加载项目下对应的文件 | |
| 79 | * @return {[type]} [description] | |
| 80 | */ | |
| 81 | loadFiles: function(){ | |
| 82 | 1 | 'use strict'; |
| 83 | 1 | C(null); //移除之前的所有配置 |
| 84 | //加载系统默认配置 | |
| 85 | 1 | C(require(THINK_PATH + '/Conf/config.js')); |
| 86 | //加载用户配置 | |
| 87 | 1 | var file = CONF_PATH + '/config.js'; |
| 88 | 1 | if (isFile(file)) { |
| 89 | 1 | C(require(file)); |
| 90 | } | |
| 91 | //加载模式的配置文件 | |
| 92 | 1 | if (APP_MODE) { |
| 93 | 1 | var modeFiles = [ |
| 94 | THINK_PATH + '/Conf/mode.js', | |
| 95 | CONF_PATH + '/mode.js' | |
| 96 | ]; | |
| 97 | 1 | modeFiles.forEach(function(file){ |
| 98 | 2 | if (isFile(file)) { |
| 99 | 1 | var conf = require(file); |
| 100 | 1 | if (conf[APP_MODE]) { |
| 101 | 1 | C(conf[APP_MODE]); |
| 102 | } | |
| 103 | } | |
| 104 | }); | |
| 105 | } | |
| 106 | //自定义路由 | |
| 107 | 1 | if (C('url_route_on') && isFile(CONF_PATH + '/route.js')) { |
| 108 | 0 | C('url_route_rules', require(CONF_PATH + '/route.js')); |
| 109 | } | |
| 110 | //common文件 | |
| 111 | 1 | if (isFile(COMMON_PATH + '/common.js')) { |
| 112 | 1 | require(COMMON_PATH + '/common.js'); |
| 113 | } | |
| 114 | //别名文件 | |
| 115 | 1 | if (isFile(COMMON_PATH + '/alias.js')) { |
| 116 | 0 | aliasImport(require(COMMON_PATH + '/alias.js')); |
| 117 | } | |
| 118 | 1 | this.loadTag(); |
| 119 | 1 | this.loadExtConfig(); |
| 120 | 1 | this.loadExtFiles(); |
| 121 | }, | |
| 122 | //加载标签行为 | |
| 123 | loadTag: function(){ | |
| 124 | 1 | 'use strict'; |
| 125 | //系统标签 | |
| 126 | 1 | var tag = require(THINK_PATH + '/Conf/tag.js'); |
| 127 | //用户行为标签 | |
| 128 | 1 | var tagFile = CONF_PATH + '/tag.js'; |
| 129 | 1 | if (!C('app_tag_on') || !isFile(tagFile)) { |
| 130 | 1 | C('tag', tag); |
| 131 | 1 | return; |
| 132 | } | |
| 133 | 0 | var mixTag = extend({}, tag); |
| 134 | 0 | var userTag = extend({}, require(tagFile)); |
| 135 | 0 | for(var key in userTag){ |
| 136 | 0 | var value = userTag[key]; |
| 137 | 0 | if (!value.length) { |
| 138 | 0 | continue; |
| 139 | } | |
| 140 | 0 | mixTag[key] = mixTag[key] || []; |
| 141 | 0 | if (isBoolean(value[0])) { |
| 142 | 0 | var flag = value.shift(); |
| 143 | 0 | if (flag) { //true为替换系统标签 |
| 144 | 0 | mixTag[key] = value; |
| 145 | }else{ //false为将自定义标签置为系统标签前面 | |
| 146 | 0 | mixTag[key] = value.concat(mixTag[key]); |
| 147 | } | |
| 148 | }else{// 默认将用户标签置为系统标签后面 | |
| 149 | 0 | mixTag[key] = mixTag[key].concat(value); |
| 150 | } | |
| 151 | } | |
| 152 | //行为标签 | |
| 153 | 0 | C('tag', mixTag); |
| 154 | }, | |
| 155 | //加载自定义外部文件 | |
| 156 | loadExtFiles: function(){ | |
| 157 | 1 | 'use strict'; |
| 158 | 1 | var files = C('load_ext_file'); |
| 159 | 1 | if (files) { |
| 160 | 1 | if (isString(files)) { |
| 161 | 0 | files = files.split(','); |
| 162 | } | |
| 163 | 1 | files.forEach(function(file){ |
| 164 | 0 | file = COMMON_PATH + '/' + file + '.js'; |
| 165 | 0 | if (isFile(file)) { |
| 166 | 0 | require(file); |
| 167 | } | |
| 168 | }); | |
| 169 | } | |
| 170 | }, | |
| 171 | //加载额外的配置 | |
| 172 | loadExtConfig: function(){ | |
| 173 | 1 | 'use strict'; |
| 174 | 1 | var files = C('load_ext_config'); |
| 175 | 1 | if (files) { |
| 176 | 1 | if (isString(files)) { |
| 177 | 0 | files = files.split(','); |
| 178 | } | |
| 179 | 1 | files.forEach(function(file){ |
| 180 | 0 | file = CONF_PATH + '/' + file + '.js'; |
| 181 | 0 | if (isFile(file)) { |
| 182 | 0 | C(require(file)); |
| 183 | } | |
| 184 | }); | |
| 185 | } | |
| 186 | }, | |
| 187 | //加载debug模式配置文件 | |
| 188 | loadDebugFiles: function(){ | |
| 189 | 0 | 'use strict'; |
| 190 | //加载debug模式下的配置 | |
| 191 | 0 | C(require(THINK_PATH + '/Conf/debug.js')); |
| 192 | //debug下自定义状态的配置 | |
| 193 | 0 | var status = C('app_status'); |
| 194 | 0 | if (status) { |
| 195 | 0 | if (isFile(CONF_PATH + '/' + status + '.js')) { |
| 196 | 0 | C(require(CONF_PATH + '/' + status + '.js')); |
| 197 | } | |
| 198 | }else{ | |
| 199 | 0 | if (isFile(CONF_PATH + '/debug.js')) { |
| 200 | 0 | C(require(CONF_PATH + '/debug.js')); |
| 201 | } | |
| 202 | } | |
| 203 | 0 | if (APP_MODE) { |
| 204 | 0 | var modeFiles = [ |
| 205 | THINK_PATH + '/Conf/mode.js', | |
| 206 | CONF_PATH + '/mode.js' | |
| 207 | ]; | |
| 208 | 0 | modeFiles.forEach(function(file){ |
| 209 | 0 | if (isFile(file)) { |
| 210 | 0 | var conf = require(file); |
| 211 | 0 | var key = APP_MODE + '_debug'; |
| 212 | 0 | if (conf[key]) { |
| 213 | 0 | C(conf[key]); |
| 214 | } | |
| 215 | } | |
| 216 | }); | |
| 217 | } | |
| 218 | }, | |
| 219 | /** | |
| 220 | * debug模式下一些特殊处理 | |
| 221 | * @return {[type]} [description] | |
| 222 | */ | |
| 223 | debug: function(){ | |
| 224 | 0 | 'use strict'; |
| 225 | 0 | this.loadDebugFiles(); |
| 226 | //清除require的缓存 | |
| 227 | 0 | if (C('clear_require_cache')) { |
| 228 | //这些文件不清除缓存 | |
| 229 | 0 | var retainFiles = C('debug_retain_files'); |
| 230 | 0 | var self = this; |
| 231 | 0 | setInterval(function(){ |
| 232 | 0 | var fn = function(item){ |
| 233 | //windows目录定界符为\ | |
| 234 | 0 | if (process.platform === 'win32') { |
| 235 | 0 | item = item.replace(/\//g, '\\'); |
| 236 | } | |
| 237 | 0 | if (file.indexOf(item) > -1) { |
| 238 | 0 | return true; |
| 239 | } | |
| 240 | }; | |
| 241 | 0 | for(var file in require.cache){ |
| 242 | 0 | var flag = retainFiles.some(fn); |
| 243 | 0 | if (!flag) { |
| 244 | 0 | delete require.cache[file]; |
| 245 | } | |
| 246 | } | |
| 247 | 0 | self.loadFiles(); |
| 248 | 0 | self.loadDebugFiles(); |
| 249 | }, 100); | |
| 250 | } | |
| 251 | }, | |
| 252 | /** | |
| 253 | * 记录当前进程的id | |
| 254 | * 记录在Runtime/Data/app.pid文件里 | |
| 255 | * @return {[type]} [description] | |
| 256 | */ | |
| 257 | logPid: function(){ | |
| 258 | 1 | 'use strict'; |
| 259 | 1 | if (C('log_process_pid') && cluster.isMaster) { |
| 260 | 0 | mkdir(DATA_PATH); |
| 261 | 0 | var pidFile = DATA_PATH + '/app.pid'; |
| 262 | 0 | fs.writeFileSync(pidFile, process.pid); |
| 263 | 0 | chmod(pidFile); |
| 264 | //进程退出时删除该文件 | |
| 265 | 0 | process.on('SIGTERM', function () { |
| 266 | 0 | if (fs.existsSync(pidFile)) { |
| 267 | 0 | fs.unlinkSync(pidFile); |
| 268 | } | |
| 269 | 0 | process.exit(0); |
| 270 | }); | |
| 271 | } | |
| 272 | }, | |
| 273 | /** | |
| 274 | * 合并autoload的path | |
| 275 | * @return {[type]} [description] | |
| 276 | */ | |
| 277 | mergeAutoloadPath: function(){ | |
| 278 | 1 | 'use strict'; |
| 279 | 1 | var file = '__CLASS__.js'; |
| 280 | 1 | var sysAutoloadPath = { |
| 281 | 'Behavior': [ | |
| 282 | LIB_PATH + '/Behavior/' + file, | |
| 283 | THINK_LIB_PATH + '/Behavior/' + file | |
| 284 | ], | |
| 285 | 'Model': [ | |
| 286 | LIB_PATH + '/Model/' + file, | |
| 287 | THINK_EXTEND_PATH + '/Model/' + file | |
| 288 | ], | |
| 289 | 'Controller': [ | |
| 290 | LIB_PATH + '/Controller/' + file, | |
| 291 | THINK_EXTEND_PATH + '/Controller/' + file | |
| 292 | ], | |
| 293 | 'Cache': [ | |
| 294 | LIB_PATH + '/Driver/Cache/' + file, | |
| 295 | THINK_LIB_PATH + '/Driver/Cache/' + file | |
| 296 | ], | |
| 297 | 'Db': [ | |
| 298 | LIB_PATH + '/Driver/Db/' + file, | |
| 299 | THINK_LIB_PATH + '/Driver/Db/' + file | |
| 300 | ], | |
| 301 | 'Template': [ | |
| 302 | LIB_PATH + '/Driver/Template/' + file, | |
| 303 | THINK_LIB_PATH + '/Driver/Template/' + file | |
| 304 | ], | |
| 305 | 'Socket': [ | |
| 306 | LIB_PATH + '/Driver/Socket/' + file, | |
| 307 | THINK_LIB_PATH + '/Driver/Socket/' + file | |
| 308 | ], | |
| 309 | 'Session': [ | |
| 310 | LIB_PATH + '/Driver/Session/' + file, | |
| 311 | THINK_LIB_PATH + '/Driver/Session/' + file | |
| 312 | ] | |
| 313 | }; | |
| 314 | 1 | var autoloadPath = C('autoload_path'); |
| 315 | 1 | for(var type in autoloadPath){ |
| 316 | 0 | var paths = autoloadPath[type]; |
| 317 | 0 | var override = false; |
| 318 | 0 | if (!isArray(paths)) { |
| 319 | 0 | paths = [paths]; |
| 320 | 0 | }else if (isBoolean(paths[0])) { |
| 321 | 0 | override = paths.shift(); |
| 322 | } | |
| 323 | 0 | if (override) { |
| 324 | 0 | sysAutoloadPath[type] = paths; |
| 325 | }else{ | |
| 326 | 0 | paths.push.apply(paths, sysAutoloadPath[type]); |
| 327 | 0 | sysAutoloadPath[type] = paths; |
| 328 | } | |
| 329 | } | |
| 330 | 1 | autoloadPaths = sysAutoloadPath; |
| 331 | }, | |
| 332 | //thinkRequire的自动加载 | |
| 333 | autoload: function(cls){ | |
| 334 | 46 | 'use strict'; |
| 335 | 46 | var filepath = ''; |
| 336 | 46 | var fn = function(item){ |
| 337 | 84 | item = item.replace(/__CLASS__/g, cls); |
| 338 | 84 | if (isFile(item)) { |
| 339 | 21 | filepath = item; |
| 340 | 21 | return true; |
| 341 | } | |
| 342 | }; | |
| 343 | 46 | for(var name in autoloadPaths){ |
| 344 | 272 | var length = name.length; |
| 345 | 272 | if (cls.substr(0 - length) === name) { |
| 346 | 44 | var list = autoloadPaths[name]; |
| 347 | 44 | list.some(fn); |
| 348 | 44 | if (filepath) { |
| 349 | 21 | if (!APP_DEBUG) { |
| 350 | 21 | aliasImport(cls, filepath); |
| 351 | } | |
| 352 | 21 | return filepath; |
| 353 | } | |
| 354 | } | |
| 355 | } | |
| 356 | } | |
| 357 | }; | |
| 358 |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * view | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Class(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | tVar: {}, | |
| 9 | init: function(http){ | |
| 10 | 8 | this.http = http; |
| 11 | }, | |
| 12 | /** | |
| 13 | * 给变量赋值 | |
| 14 | * @param {[type]} name [description] | |
| 15 | * @param {[type]} value [description] | |
| 16 | * @return {[type]} [description] | |
| 17 | */ | |
| 18 | assign: function(name, value){ | |
| 19 | 16 | if (name === undefined) { |
| 20 | 0 | return this.tVar; |
| 21 | } | |
| 22 | 16 | if (isString(name) && arguments.length === 1) { |
| 23 | 0 | return this.tVar[name]; |
| 24 | } | |
| 25 | 16 | if (isObject(name)) { |
| 26 | 0 | this.tVar = extend(this.tVar, name); |
| 27 | }else{ | |
| 28 | 16 | this.tVar[name] = value; |
| 29 | } | |
| 30 | }, | |
| 31 | /** | |
| 32 | * 获取变量的值 | |
| 33 | * @param {[type]} name [description] | |
| 34 | * @return {[type]} [description] | |
| 35 | */ | |
| 36 | get: function(name){ | |
| 37 | 0 | if (!name) { |
| 38 | 0 | return this.tVar; |
| 39 | } | |
| 40 | 0 | return this.tVar[name]; |
| 41 | }, | |
| 42 | /** | |
| 43 | * 输出模版文件内容 | |
| 44 | * @param {[type]} templateFile [description] | |
| 45 | * @param {[type]} charset [description] | |
| 46 | * @param {[type]} contentType [description] | |
| 47 | * @param {[type]} content [description] | |
| 48 | * @return {[type]} [description] | |
| 49 | */ | |
| 50 | display: function(templateFile, charset, contentType, content){ | |
| 51 | 1 | var self = this; |
| 52 | 1 | return tag('view_init', this.http).then(function(){ |
| 53 | 1 | return self.fetch(templateFile, content); |
| 54 | }).then(function(content){ | |
| 55 | 1 | self.render(content, charset, contentType); |
| 56 | 1 | return tag('view_end', self.http, content); |
| 57 | }).then(function(){ | |
| 58 | 1 | return self.http.end(); |
| 59 | }).catch(function(){ | |
| 60 | 0 | return self.http.end(); |
| 61 | }); | |
| 62 | }, | |
| 63 | /** | |
| 64 | * 渲染模版 | |
| 65 | * @param {[type]} content [description] | |
| 66 | * @param {[type]} charset [description] | |
| 67 | * @param {[type]} contentType [description] | |
| 68 | * @return {[type]} [description] | |
| 69 | */ | |
| 70 | render: function(content, charset, contentType){ | |
| 71 | 1 | if (!this.http.cthIsSend) { |
| 72 | 1 | if (charset === undefined) { |
| 73 | 1 | charset = C('encoding'); |
| 74 | } | |
| 75 | 1 | if (contentType === undefined) { |
| 76 | 1 | contentType = C('tpl_content_type'); |
| 77 | } | |
| 78 | 1 | this.http.setHeader('Content-Type', contentType + '; charset=' + charset); |
| 79 | } | |
| 80 | 1 | if (C('show_exec_time')) { |
| 81 | 0 | this.http.sendTime('Exec-Time'); |
| 82 | } | |
| 83 | 1 | this.http.echo(content || '', C('encoding')); |
| 84 | }, | |
| 85 | /** | |
| 86 | * 获取模版文件内容 | |
| 87 | * @param {[type]} templateFile [description] | |
| 88 | * @param {[type]} content [description] | |
| 89 | * @return {[type]} [description] | |
| 90 | */ | |
| 91 | fetch: function(templateFile, content){ | |
| 92 | 1 | var self = this; |
| 93 | 1 | var promise = getPromise(''); |
| 94 | 1 | if (content === undefined) { |
| 95 | 1 | promise = tag('view_template', this.http, templateFile).then(function(file){ |
| 96 | 1 | if (file && isFile(file)) { |
| 97 | 1 | templateFile = file; |
| 98 | } | |
| 99 | }); | |
| 100 | } | |
| 101 | 1 | return promise.then(function(){ |
| 102 | 1 | if (!templateFile) { |
| 103 | 0 | return ''; |
| 104 | } | |
| 105 | 1 | return tag('view_parse', self.http, { |
| 106 | 'var': self.tVar, | |
| 107 | 'file': templateFile, | |
| 108 | 'content': content | |
| 109 | }).then(function(content){ | |
| 110 | 1 | return tag('view_filter', self.http, content); |
| 111 | }); | |
| 112 | }).catch(function(err){ | |
| 113 | //输出模版解析异常 | |
| 114 | 0 | console.log(isError(err) ? err.stack : err); |
| 115 | }); | |
| 116 | } | |
| 117 | }; | |
| 118 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var fs = require('fs'); |
| 2 | /** | |
| 3 | * 基于文件的缓存 | |
| 4 | * @return {[type]} [description] | |
| 5 | */ | |
| 6 | 1 | module.exports = Cache(function(){ |
| 7 | 1 | 'use strict'; |
| 8 | 1 | return { |
| 9 | gcType: 'FileCache', | |
| 10 | init: function(options){ | |
| 11 | 15 | this.options = extend({ |
| 12 | cache_path: C('cache_path'), //缓存目录 | |
| 13 | cache_path_level: 2, //缓存子目录深度 | |
| 14 | cache_file_suffix: C('cache_file_suffix') //缓存文件后缀名 | |
| 15 | }, options); | |
| 16 | 15 | mkdir(this.options.cache_path); |
| 17 | 15 | this.gcType += ':' + this.options.cache_path; |
| 18 | ||
| 19 | 15 | this.super_('init', this.options); |
| 20 | }, | |
| 21 | /** | |
| 22 | * 存储的缓存文件 | |
| 23 | * @param {[type]} name [description] | |
| 24 | * @return {[type]} [description] | |
| 25 | */ | |
| 26 | getStoredFile: function(name){ | |
| 27 | 15 | name = md5(this.key || name); |
| 28 | 15 | var dir = name.split('').slice(0, this.options.cache_path_level).join('/'); |
| 29 | 15 | mkdir(this.options.cache_path + '/' + dir); |
| 30 | 15 | var path = this.options.cache_path + '/' + dir + '/' + name + this.options.cache_file_suffix; |
| 31 | 15 | return path; |
| 32 | }, | |
| 33 | /** | |
| 34 | * 获取缓存,返回promise | |
| 35 | * @param {[type]} name [description] | |
| 36 | * @return {[type]} [description] | |
| 37 | */ | |
| 38 | getData: function(name){ | |
| 39 | 8 | var filePath = this.getStoredFile(name); |
| 40 | 8 | if (!isFile(filePath)) { |
| 41 | 2 | return getPromise(); |
| 42 | } | |
| 43 | 6 | var deferred = getDefer(); |
| 44 | 6 | fs.exists(filePath, function(exists){ |
| 45 | 6 | if (!exists) { |
| 46 | 0 | return deferred.resolve(); |
| 47 | } | |
| 48 | 6 | fs.readFile(filePath, {encoding: 'utf8'}, function(error, content){ |
| 49 | 6 | if (error || !content) { |
| 50 | 0 | return deferred.resolve(); |
| 51 | } | |
| 52 | 6 | try{ |
| 53 | 6 | var data = JSON.parse(content); |
| 54 | 6 | if (Date.now() > data.expire) { |
| 55 | 2 | fs.unlink(filePath, function(){ |
| 56 | 2 | return deferred.resolve(); |
| 57 | }); | |
| 58 | }else{ | |
| 59 | 4 | deferred.resolve(data.data); |
| 60 | } | |
| 61 | }catch(e){ | |
| 62 | //异常时删除该文件 | |
| 63 | 0 | fs.unlink(filePath, function(){ |
| 64 | 0 | return deferred.resolve(); |
| 65 | }); | |
| 66 | } | |
| 67 | }); | |
| 68 | }); | |
| 69 | 6 | return deferred.promise; |
| 70 | }, | |
| 71 | get: function(name){ | |
| 72 | 7 | return this.getData(name).then(function(data){ |
| 73 | 7 | return (data || {})[name]; |
| 74 | }) | |
| 75 | }, | |
| 76 | setData: function(name, value, timeout){ | |
| 77 | 6 | if (isObject(name)) { |
| 78 | 0 | timeout = value; |
| 79 | } | |
| 80 | 6 | if (timeout === undefined) { |
| 81 | 3 | timeout = this.options.timeout; |
| 82 | } | |
| 83 | 6 | var filePath = this.getStoredFile(name); |
| 84 | 6 | var data = { |
| 85 | data: isObject(name) ? name : getObject(name, value), | |
| 86 | expire: Date.now() + timeout * 1000, | |
| 87 | timeout: timeout | |
| 88 | }; | |
| 89 | 6 | var deferred = getDefer(); |
| 90 | 6 | fs.writeFile(filePath, JSON.stringify(data), function(){ |
| 91 | //修改缓存文件权限,避免不同账号下启动时可能会出现无权限的问题 | |
| 92 | 6 | chmod(filePath); |
| 93 | 6 | deferred.resolve(); |
| 94 | }) | |
| 95 | 6 | return deferred.promise; |
| 96 | }, | |
| 97 | /** | |
| 98 | * 设置缓存 | |
| 99 | * @param {[type]} name [description] | |
| 100 | * @param {[type]} value [description] | |
| 101 | * @param {[type]} expire [description] | |
| 102 | */ | |
| 103 | set: function(name, value, timeout){ | |
| 104 | 6 | return this.setData(name, value, timeout); |
| 105 | }, | |
| 106 | /** | |
| 107 | * 删除缓存 | |
| 108 | * @param {[type]} name [description] | |
| 109 | * @return {[type]} [description] | |
| 110 | */ | |
| 111 | rm: function(name){ | |
| 112 | 1 | var filePath = this.getStoredFile(name); |
| 113 | 1 | if (isFile(filePath)) { |
| 114 | 1 | var deferred = getDefer(); |
| 115 | 1 | fs.unlink(filePath, function(){ |
| 116 | 1 | deferred.resolve(); |
| 117 | }) | |
| 118 | 1 | return deferred.promise; |
| 119 | } | |
| 120 | 0 | return getPromise(); |
| 121 | }, | |
| 122 | /** | |
| 123 | * gc | |
| 124 | * @param {[type]} now [description] | |
| 125 | * @return {[type]} [description] | |
| 126 | */ | |
| 127 | gc: function(now, path){ | |
| 128 | 0 | path = path || this.options.cache_path; |
| 129 | 0 | var self = this; |
| 130 | 0 | var files = fs.readdirSync(path); |
| 131 | 0 | files.forEach(function(item){ |
| 132 | 0 | var filePath = path + '/' + item; |
| 133 | 0 | var stat = fs.statSync(filePath); |
| 134 | 0 | if (stat.isDirectory()) { |
| 135 | 0 | self.gc(now, filePath); |
| 136 | 0 | }else if (stat.isFile()) { |
| 137 | 0 | var data = getFileContent(filePath); |
| 138 | 0 | try{ |
| 139 | 0 | data = JSON.parse(data); |
| 140 | 0 | if (now > data.expire) { |
| 141 | 0 | fs.unlink(filePath, function(){}); |
| 142 | } | |
| 143 | }catch(e){} | |
| 144 | } | |
| 145 | }); | |
| 146 | } | |
| 147 | }; | |
| 148 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var memcache = thinkRequire('MemcacheSocket'); |
| 2 | 1 | module.exports = Cache(function(){ |
| 3 | 1 | 'use strict'; |
| 4 | 1 | var instance = null; |
| 5 | 1 | return { |
| 6 | namePrefix: '__thinkjs__', | |
| 7 | init: function(options){ | |
| 8 | 0 | this.super_('init', options); |
| 9 | 0 | if (!instance) { |
| 10 | 0 | instance = memcache(C('memcache_port'), C('memcache_host')); |
| 11 | } | |
| 12 | 0 | this.handle = instance; |
| 13 | }, | |
| 14 | get: function(name){ | |
| 15 | 0 | return this.handle.get(this.namePrefix + name).then(function(value){ |
| 16 | 0 | return value ? JSON.parse(value) : value; |
| 17 | }) | |
| 18 | }, | |
| 19 | set: function(name, value, timeout){ | |
| 20 | 0 | timeout = timeout || this.options.timeout; |
| 21 | 0 | return this.handle.set(this.namePrefix + name, JSON.stringify(value), timeout); |
| 22 | }, | |
| 23 | rm: function(name){ | |
| 24 | 0 | return this.handle.delete(this.namePrefix + name); |
| 25 | } | |
| 26 | }; | |
| 27 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * mysql数据库 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | var mysqlSocket = thinkRequire('MysqlSocket'); |
| 6 | 1 | module.exports = Db(function(){ |
| 7 | 1 | 'use strict'; |
| 8 | 1 | var keyReg = /[,\'\"\*\(\)`.\s]/; |
| 9 | 1 | return { |
| 10 | init: function(config){ | |
| 11 | 1 | this.super_('init'); |
| 12 | 1 | if (config) { |
| 13 | 1 | this.config = config; |
| 14 | } | |
| 15 | 1 | this.lastInsertId = 0; |
| 16 | //查询等待 | |
| 17 | 1 | this.queryWaiting = {}; |
| 18 | }, | |
| 19 | /** | |
| 20 | * 连接数据库 | |
| 21 | * @param {[type]} config [description] | |
| 22 | * @param {[type]} linknum [description] | |
| 23 | * @return {[type]} [description] | |
| 24 | */ | |
| 25 | connect: function(){ | |
| 26 | 177 | if (!this.linkId) { |
| 27 | 1 | this.linkId = mysqlSocket(this.config); |
| 28 | } | |
| 29 | 177 | return this.linkId; |
| 30 | }, | |
| 31 | /** | |
| 32 | * 查询一条sql | |
| 33 | * @param string str | |
| 34 | * @return promise | |
| 35 | */ | |
| 36 | query: function(str){ | |
| 37 | 155 | this.queryStr = str; |
| 38 | 155 | var self = this; |
| 39 | 155 | if (!(str in this.queryWaiting)) { |
| 40 | 155 | this.queryWaiting[str] = []; |
| 41 | 155 | return this.connect().query(str).then(function(data){ |
| 42 | 155 | process.nextTick(function(){ |
| 43 | 155 | self.queryWaiting[str].forEach(function(deferred){ |
| 44 | 0 | deferred.resolve(data); |
| 45 | }); | |
| 46 | 155 | delete self.queryWaiting[str]; |
| 47 | }) | |
| 48 | 155 | return data; |
| 49 | }); | |
| 50 | }else{ | |
| 51 | 0 | var deferred = getDefer(); |
| 52 | 0 | this.queryWaiting[str].push(deferred); |
| 53 | 0 | return deferred.promise; |
| 54 | } | |
| 55 | }, | |
| 56 | /** | |
| 57 | * 执行一条sql, 返回影响的行数 | |
| 58 | * @param {[type]} str [description] | |
| 59 | * @return {[type]} [description] | |
| 60 | */ | |
| 61 | execute: function(str){ | |
| 62 | 22 | this.queryStr = str; |
| 63 | 22 | var self = this; |
| 64 | 22 | return this.connect().query(str).then(function(data){ |
| 65 | 22 | self.lastInsertId = data.insertId; |
| 66 | 22 | return data.affectedRows || 0; |
| 67 | }); | |
| 68 | }, | |
| 69 | /** | |
| 70 | * 获取数据表字段信息 | |
| 71 | * @param string tableName 数据表名 | |
| 72 | * @return promise 返回一个promise | |
| 73 | */ | |
| 74 | getFields: function(tableName){ | |
| 75 | 4 | var sql = 'SHOW COLUMNS FROM ' + this.parseKey(tableName); |
| 76 | 4 | return this.query(sql).then(function(data){ |
| 77 | 4 | var ret = {}; |
| 78 | 4 | data.forEach(function(item){ |
| 79 | 52 | ret[item.Field] = { |
| 80 | 'name': item.Field, | |
| 81 | 'type': item.Type, | |
| 82 | 'notnull': item.Null === '', | |
| 83 | 'default': item.Default, | |
| 84 | 'primary': item.Key === 'PRI', | |
| 85 | 'unique': item.Key === 'UNI', | |
| 86 | 'autoinc': item.Extra.toLowerCase() === 'auto_increment' | |
| 87 | }; | |
| 88 | }); | |
| 89 | 4 | return ret; |
| 90 | }); | |
| 91 | }, | |
| 92 | /** | |
| 93 | * 获取数据库的表信息 | |
| 94 | * @param {[type]} dbName [description] | |
| 95 | * @return {[type]} [description] | |
| 96 | */ | |
| 97 | getTables: function(dbName){ | |
| 98 | 0 | var sql = 'SHOW TABLES'; |
| 99 | 0 | if (dbName) { |
| 100 | 0 | sql += ' FROM ' + dbName; |
| 101 | } | |
| 102 | 0 | return this.query(sql).then(function(data){ |
| 103 | 0 | return data.map(function(item){ |
| 104 | 0 | for(var key in item){ |
| 105 | 0 | return item[key]; |
| 106 | } | |
| 107 | }); | |
| 108 | }); | |
| 109 | }, | |
| 110 | /** | |
| 111 | * 关闭连接 | |
| 112 | * @return {[type]} [description] | |
| 113 | */ | |
| 114 | close: function(){ | |
| 115 | 0 | if (this.linkId) { |
| 116 | 0 | this.linkId.close(); |
| 117 | 0 | this.linkId = null; |
| 118 | } | |
| 119 | }, | |
| 120 | /** | |
| 121 | * 解析key | |
| 122 | * @param {[type]} key [description] | |
| 123 | * @return {[type]} [description] | |
| 124 | */ | |
| 125 | parseKey: function(key){ | |
| 126 | 345 | key = (key || '').trim(); |
| 127 | 345 | if (!keyReg.test(key)) { |
| 128 | 324 | key = '`' + key + '`'; |
| 129 | } | |
| 130 | 345 | return key; |
| 131 | }, | |
| 132 | /** | |
| 133 | * 获取最后插入的id | |
| 134 | * @return {[type]} [description] | |
| 135 | */ | |
| 136 | getLastInsertId: function(){ | |
| 137 | 6 | return this.lastInsertId; |
| 138 | } | |
| 139 | }; | |
| 140 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * DbSession | |
| 3 | * 需要在数据库中建立对应的数据表 | |
| 4 | * | |
| 5 | * DROP TABLE IF EXISTS `think_session`; | |
| 6 | CREATE TABLE `think_session` ( | |
| 7 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, | |
| 8 | `key` varchar(255) NOT NULL DEFAULT '', | |
| 9 | `data` text, | |
| 10 | `expire` bigint(11) NOT NULL, | |
| 11 | PRIMARY KEY (`id`), | |
| 12 | UNIQUE KEY `cookie` (`key`), | |
| 13 | KEY `expire` (`expire`) | |
| 14 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |
| 15 | * | |
| 16 | * | |
| 17 | * @return {[type]} [description] | |
| 18 | */ | |
| 19 | 1 | module.exports = Cache(function(){ |
| 20 | 1 | 'use strict'; |
| 21 | 1 | return { |
| 22 | /** | |
| 23 | * gc类型 | |
| 24 | * @type {String} | |
| 25 | */ | |
| 26 | gcType: 'DbSession', | |
| 27 | /** | |
| 28 | * [init description] | |
| 29 | * @param {[type]} options [description] | |
| 30 | * @return {[type]} [description] | |
| 31 | */ | |
| 32 | init: function(options){ | |
| 33 | 0 | this.super_('init', options); |
| 34 | 0 | this.key = this.options.cookie; |
| 35 | 0 | this.data = {}; |
| 36 | 0 | this.initData(); |
| 37 | }, | |
| 38 | /** | |
| 39 | * 初始化数据 | |
| 40 | * @return {[type]} [description] | |
| 41 | */ | |
| 42 | initData: function(){ | |
| 43 | 0 | if (!this.promise) { |
| 44 | 0 | var model = D('Session'); |
| 45 | 0 | var self = this; |
| 46 | 0 | this.promise = model.where({key: this.key}).find().then(function(data){ |
| 47 | 0 | self.data = {}; |
| 48 | 0 | if (isEmpty(data)) { |
| 49 | 0 | return model.add({ |
| 50 | key: self.key, | |
| 51 | expire: Date.now() + self.options.timeout * 1000 | |
| 52 | }); | |
| 53 | } | |
| 54 | 0 | if (Date.now() > data.expire) { |
| 55 | 0 | return; |
| 56 | } | |
| 57 | 0 | self.data = JSON.parse(data.data || '{}'); |
| 58 | }); | |
| 59 | } | |
| 60 | 0 | return this.promise; |
| 61 | }, | |
| 62 | /** | |
| 63 | * 获取 | |
| 64 | * @param {[type]} name [description] | |
| 65 | * @return {[type]} [description] | |
| 66 | */ | |
| 67 | get: function(name){ | |
| 68 | 0 | var self = this; |
| 69 | 0 | return this.initData().then(function(){ |
| 70 | 0 | return self.data[name]; |
| 71 | }); | |
| 72 | }, | |
| 73 | /** | |
| 74 | * 设置 | |
| 75 | * @param {[type]} name [description] | |
| 76 | * @param {[type]} value [description] | |
| 77 | */ | |
| 78 | set: function(name, value){ | |
| 79 | 0 | var self = this; |
| 80 | 0 | return this.initData().then(function(){ |
| 81 | 0 | self.data[name] = value; |
| 82 | }); | |
| 83 | }, | |
| 84 | /** | |
| 85 | * 删除 | |
| 86 | * @param {[type]} name [description] | |
| 87 | * @return {[type]} [description] | |
| 88 | */ | |
| 89 | rm: function(name){ | |
| 90 | 0 | if (this.data) { |
| 91 | 0 | delete this.data[name]; |
| 92 | } | |
| 93 | 0 | return getPromise(); |
| 94 | }, | |
| 95 | /** | |
| 96 | * 将数据保存到数据库中 | |
| 97 | * @return {[type]} [description] | |
| 98 | */ | |
| 99 | flush: function(){ | |
| 100 | 0 | var model = D('Session'); |
| 101 | 0 | var self = this; |
| 102 | 0 | var data = { |
| 103 | expire: Date.now() + self.options.timeout * 1000, | |
| 104 | data: JSON.stringify(self.data) | |
| 105 | }; | |
| 106 | 0 | return this.initData().then(function(){ |
| 107 | 0 | return model.where({key: self.key}).update(data); |
| 108 | }); | |
| 109 | }, | |
| 110 | /** | |
| 111 | * [gc description] | |
| 112 | * @param {[type]} now [description] | |
| 113 | * @return {[type]} [description] | |
| 114 | */ | |
| 115 | gc: function(now){ | |
| 116 | 0 | return D('Session').where({ |
| 117 | expire: ['<', now] | |
| 118 | }).delete(); | |
| 119 | } | |
| 120 | }; | |
| 121 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 文件Session | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | ||
| 6 | 1 | var os = require('os'); |
| 7 | 1 | module.exports = Class(function(){ |
| 8 | 1 | 'use strict'; |
| 9 | 1 | return { |
| 10 | gcType: 'FileSession', | |
| 11 | /** | |
| 12 | * 差异化的init | |
| 13 | * @return {[type]} [description] | |
| 14 | */ | |
| 15 | init: function(options){ | |
| 16 | 1 | options = options || {}; |
| 17 | 1 | options.cache_path = C('session_path') || (os.tmpdir() + '/thinkjs'); |
| 18 | 1 | this.super_('init', options); |
| 19 | 1 | this.key = options.cookie; |
| 20 | }, | |
| 21 | initData: function(){ | |
| 22 | 10 | if (!this.promise) { |
| 23 | 1 | var self = this; |
| 24 | 1 | this.promise = this.getData().then(function(data){ |
| 25 | 1 | self.sessionData = data || {}; |
| 26 | }) | |
| 27 | } | |
| 28 | 10 | return this.promise; |
| 29 | }, | |
| 30 | get: function(name){ | |
| 31 | 5 | var self = this; |
| 32 | 5 | return this.initData().then(function(){ |
| 33 | 5 | return self.sessionData[name]; |
| 34 | }); | |
| 35 | }, | |
| 36 | set: function(name, value, timeout){ | |
| 37 | 5 | var self = this; |
| 38 | 5 | return this.initData().then(function(){ |
| 39 | 5 | self.sessionData[name] = value; |
| 40 | 5 | if (timeout) { |
| 41 | 0 | self.options.timeout = timeout; |
| 42 | } | |
| 43 | }); | |
| 44 | }, | |
| 45 | rm: function(){ | |
| 46 | 1 | this.sessionData = {}; |
| 47 | 1 | return getPromise(); |
| 48 | }, | |
| 49 | /** | |
| 50 | * 将数据写入到文件中 | |
| 51 | * @return {[type]} [description] | |
| 52 | */ | |
| 53 | flush: function(){ | |
| 54 | 0 | var self = this; |
| 55 | 0 | return this.initData().then(function(){ |
| 56 | 0 | return self.setData(self.sessionData); |
| 57 | }) | |
| 58 | } | |
| 59 | }; | |
| 60 | }, thinkRequire('FileCache')); |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var net = require('net'); |
| 2 | 1 | var EventEmitter = require('events').EventEmitter; |
| 3 | ||
| 4 | 1 | var CRLF = '\r\n'; //换行符 |
| 5 | 1 | var CRLF_LENGTH = CRLF.length; |
| 6 | 1 | var ERRORS = ['ERROR', 'NOT_FOUND', 'CLIENT_ERROR', 'SERVER_ERROR']; //错误 |
| 7 | 1 | var ERRORS_LENGTH = ERRORS.length; |
| 8 | ||
| 9 | //读取一行数据 | |
| 10 | 1 | function readLine(string){ |
| 11 | 0 | 'use strict'; |
| 12 | 0 | var pos = string.indexOf(CRLF); |
| 13 | 0 | if (pos > -1) { |
| 14 | 0 | return string.substr(0, pos); |
| 15 | } | |
| 16 | 0 | return string; |
| 17 | } | |
| 18 | /** | |
| 19 | * memcache类 | |
| 20 | * @return {[type]} [description] | |
| 21 | */ | |
| 22 | 1 | module.exports = Class(function(){ |
| 23 | 1 | 'use strict'; |
| 24 | 1 | return { |
| 25 | init: function(port, hostname){ | |
| 26 | 0 | EventEmitter.call(this); |
| 27 | 0 | this.port = port || 11211; |
| 28 | 0 | this.hostname = hostname || 'localhost'; |
| 29 | 0 | this.buffer = ''; |
| 30 | 0 | this.callbacks = []; //回调函数 |
| 31 | 0 | this.handle = null; //socket连接句柄 |
| 32 | }, | |
| 33 | /** | |
| 34 | * 建立连接 | |
| 35 | * @return {[type]} [description] | |
| 36 | */ | |
| 37 | connect: function(){ | |
| 38 | 0 | if (this.handle) { |
| 39 | 0 | return this; |
| 40 | } | |
| 41 | 0 | var self = this; |
| 42 | 0 | var deferred = getDefer(); |
| 43 | 0 | this.handle = net.createConnection(this.port, this.host); |
| 44 | 0 | this.handle.on('connect', function(){ |
| 45 | 0 | this.setTimeout(0); |
| 46 | 0 | this.setNoDelay(); |
| 47 | 0 | self.emit('connect'); |
| 48 | 0 | deferred.resolve(); |
| 49 | }); | |
| 50 | 0 | this.handle.on('data', function(data){ |
| 51 | 0 | self.buffer += data.toString(); |
| 52 | 0 | self.handleData(); |
| 53 | }); | |
| 54 | 0 | this.handle.on('end', function(){ |
| 55 | 0 | self.handle.end(); |
| 56 | 0 | self.handle = null; |
| 57 | }); | |
| 58 | 0 | this.handle.on('close', function(){ |
| 59 | 0 | self.handle = null; |
| 60 | 0 | self.emit('close'); |
| 61 | }); | |
| 62 | 0 | this.handle.on('timeout', function(){ |
| 63 | 0 | self.handle = null; |
| 64 | 0 | self.emit('timeout'); |
| 65 | }); | |
| 66 | 0 | this.handle.on('error', function(error){ |
| 67 | 0 | self.handle = null; |
| 68 | 0 | self.emit('error', error); |
| 69 | }); | |
| 70 | 0 | this.promise = deferred.promise; |
| 71 | 0 | return this; |
| 72 | }, | |
| 73 | /** | |
| 74 | * 处理接收的数据 | |
| 75 | * @return {[type]} [description] | |
| 76 | */ | |
| 77 | handleData: function(){ | |
| 78 | 0 | while(this.buffer.length > 0){ |
| 79 | 0 | var result = this.getHandleResult(this.buffer); |
| 80 | 0 | if(result === false){ |
| 81 | 0 | break; |
| 82 | } | |
| 83 | 0 | var value = result[0]; |
| 84 | 0 | var pos = result[1]; |
| 85 | 0 | var error = result[2]; |
| 86 | 0 | if (pos > this.buffer.length) { |
| 87 | 0 | break; |
| 88 | } | |
| 89 | 0 | this.buffer = this.buffer.substring(pos); |
| 90 | 0 | var callback = this.callbacks.shift(); |
| 91 | 0 | if (callback && callback.callback) { |
| 92 | 0 | callback.callback(error, value); |
| 93 | } | |
| 94 | } | |
| 95 | }, | |
| 96 | getHandleResult: function(buffer){ | |
| 97 | 0 | if (buffer.indexOf(CRLF) === -1) { |
| 98 | 0 | return false; |
| 99 | } | |
| 100 | 0 | for(var i = 0; i < ERRORS_LENGTH; i++){ |
| 101 | 0 | var item = ERRORS[i]; |
| 102 | 0 | if (buffer.indexOf(item) > -1) { |
| 103 | 0 | return this.handleError(buffer); |
| 104 | } | |
| 105 | } | |
| 106 | 0 | var callback = this.callbacks[0]; |
| 107 | 0 | if (callback && callback.type) { |
| 108 | 0 | return this['handle' + ucfirst(callback.type)](buffer); |
| 109 | } | |
| 110 | 0 | return false; |
| 111 | }, | |
| 112 | /** | |
| 113 | * 处理错误 | |
| 114 | * @param {[type]} buffer [description] | |
| 115 | * @return {[type]} [description] | |
| 116 | */ | |
| 117 | handleError: function(buffer){ | |
| 118 | 0 | var line = readLine(buffer); |
| 119 | 0 | return [null, line.length + CRLF_LENGTH, line]; |
| 120 | }, | |
| 121 | /** | |
| 122 | * 处理获取数据 | |
| 123 | * @param {[type]} buffer [description] | |
| 124 | * @return {[type]} [description] | |
| 125 | */ | |
| 126 | handleGet: function(buffer){ | |
| 127 | 0 | var value = null; |
| 128 | 0 | var end = 3; |
| 129 | 0 | var resultLen = 0; |
| 130 | 0 | var firstPos; |
| 131 | 0 | if (buffer.indexOf('END') === 0) { |
| 132 | 0 | return [value, end + CRLF_LENGTH]; |
| 133 | 0 | }else if (buffer.indexOf('VALUE') === 0 && buffer.indexOf('END') > -1) { |
| 134 | 0 | firstPos = buffer.indexOf(CRLF) + CRLF_LENGTH; |
| 135 | 0 | var endPos = buffer.indexOf('END'); |
| 136 | 0 | resultLen = endPos - firstPos - CRLF_LENGTH; |
| 137 | 0 | value = buffer.substr(firstPos, resultLen); |
| 138 | 0 | return [value, firstPos + parseInt(resultLen, 10) + CRLF_LENGTH + end + CRLF_LENGTH]; |
| 139 | }else{ | |
| 140 | 0 | firstPos = buffer.indexOf(CRLF) + CRLF_LENGTH; |
| 141 | 0 | resultLen = buffer.substr(0, firstPos).split(' ')[3]; |
| 142 | 0 | value = buffer.substr(firstPos, resultLen); |
| 143 | 0 | return [value, firstPos + parseInt(resultLen) + CRLF_LENGTH + end + CRLF_LENGTH]; |
| 144 | } | |
| 145 | }, | |
| 146 | /** | |
| 147 | * 处理简单数据 | |
| 148 | * @param {[type]} buffer [description] | |
| 149 | * @return {[type]} [description] | |
| 150 | */ | |
| 151 | handleSimple: function(buffer){ | |
| 152 | 0 | var line = readLine(buffer); |
| 153 | 0 | return [line, line.length + CRLF_LENGTH, null]; |
| 154 | }, | |
| 155 | /** | |
| 156 | * 版本号 | |
| 157 | * @param {[type]} buffer [description] | |
| 158 | * @return {[type]} [description] | |
| 159 | */ | |
| 160 | handleVersion: function(buffer){ | |
| 161 | 0 | var pos = buffer.indexOf(CRLF); |
| 162 | //8 is length of 'VERSION ' | |
| 163 | 0 | var value = buffer.substr(8, pos - 8); |
| 164 | 0 | return [value, pos + CRLF_LENGTH, null]; |
| 165 | }, | |
| 166 | /** | |
| 167 | * 查询 | |
| 168 | * @param {[type]} query [description] | |
| 169 | * @param {[type]} type [description] | |
| 170 | * @param {Function} callback [description] | |
| 171 | * @return {[type]} [description] | |
| 172 | */ | |
| 173 | query: function(query, type){ | |
| 174 | 0 | this.connect(); |
| 175 | 0 | var self = this; |
| 176 | 0 | var deferred = getDefer(); |
| 177 | 0 | var callback = function(error, value){ |
| 178 | 0 | return error ? deferred.reject(error) : deferred.resolve(value); |
| 179 | } | |
| 180 | 0 | this.promise.then(function(){ |
| 181 | 0 | self.callbacks.push({type: type, callback: callback}); |
| 182 | 0 | self.handle.write(query + CRLF); |
| 183 | }); | |
| 184 | 0 | return deferred.promise; |
| 185 | }, | |
| 186 | /** | |
| 187 | * 获取 | |
| 188 | * @param {[type]} key [description] | |
| 189 | * @param {Function} callback [description] | |
| 190 | * @return {[type]} [description] | |
| 191 | */ | |
| 192 | get: function(key){ | |
| 193 | 0 | return this.query('get ' + key, 'get'); |
| 194 | }, | |
| 195 | /** | |
| 196 | * 存储 | |
| 197 | * @return {[type]} [description] | |
| 198 | */ | |
| 199 | store: function(key, value, type, lifetime, flags){ | |
| 200 | 0 | lifetime = lifetime || 0; |
| 201 | 0 | flags = flags || 0; |
| 202 | 0 | var length = Buffer.byteLength(value.toString()); |
| 203 | 0 | var query = [type, key, flags, lifetime, length].join(' ') + CRLF + value; |
| 204 | 0 | return this.query(query, 'simple'); |
| 205 | }, | |
| 206 | /** | |
| 207 | * 删除 | |
| 208 | * @param {[type]} key [description] | |
| 209 | * @param {Function} callback [description] | |
| 210 | * @return {[type]} [description] | |
| 211 | */ | |
| 212 | delete: function(key){ | |
| 213 | 0 | return this.query('delete ' + key, 'simple'); |
| 214 | }, | |
| 215 | /** | |
| 216 | * 获取版本号 | |
| 217 | * @param {Function} callback [description] | |
| 218 | * @return {[type]} [description] | |
| 219 | */ | |
| 220 | version: function(){ | |
| 221 | 0 | return this.query('version', 'version'); |
| 222 | }, | |
| 223 | /** | |
| 224 | * 增长 | |
| 225 | * @param {[type]} key [description] | |
| 226 | * @param {[type]} step [description] | |
| 227 | * @param {Function} callback [description] | |
| 228 | * @return {[type]} [description] | |
| 229 | */ | |
| 230 | increment: function(key, step){ | |
| 231 | 0 | step = step || 1; |
| 232 | 0 | return this.query('incr ' + key + ' ' + step, 'simple'); |
| 233 | }, | |
| 234 | /** | |
| 235 | * 减少 | |
| 236 | * @param {[type]} key [description] | |
| 237 | * @param {[type]} step [description] | |
| 238 | * @param {Function} callback [description] | |
| 239 | * @return {[type]} [description] | |
| 240 | */ | |
| 241 | decrement: function(key, step){ | |
| 242 | 0 | step = step || 1; |
| 243 | 0 | return this.query('decr ' + key + ' ' + step, 'simple'); |
| 244 | }, | |
| 245 | /** | |
| 246 | * 关闭 | |
| 247 | * @return {[type]} [description] | |
| 248 | */ | |
| 249 | close: function(){ | |
| 250 | 0 | if (this.handle && this.handle.readyState === 'open') { |
| 251 | 0 | this.handle.end(); |
| 252 | 0 | this.handle = null; |
| 253 | } | |
| 254 | } | |
| 255 | } | |
| 256 | }, EventEmitter).extend(function(){ | |
| 257 | 1 | 'use strict'; |
| 258 | 1 | var result = {}; |
| 259 | 1 | ['set', 'add', 'replace', 'append', 'prepend'].forEach(function(item){ |
| 260 | 5 | result[item] = function(key, value, callback, lifetime, flags){ |
| 261 | 0 | return this.store(key, value, item, callback, lifetime, flags); |
| 262 | } | |
| 263 | }); | |
| 264 | 1 | return result; |
| 265 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * mysql socket | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | ||
| 6 | //暂时使用mysql库 | |
| 7 | 1 | var mysql = require('mysql'); |
| 8 | 1 | module.exports = Class(function(){ |
| 9 | 1 | 'use strict'; |
| 10 | 1 | return { |
| 11 | init: function(config){ | |
| 12 | 1 | this.handle = null; |
| 13 | 1 | this.config = config; |
| 14 | 1 | this.deferred = null; |
| 15 | 1 | this.tryTimes = 0; |
| 16 | }, | |
| 17 | /** | |
| 18 | * 建立数据库连接 | |
| 19 | * @return {[type]} [description] | |
| 20 | */ | |
| 21 | connect: function(){ | |
| 22 | 0 | if (this.handle) { |
| 23 | 0 | return this.deferred.promise; |
| 24 | } | |
| 25 | 0 | var self = this; |
| 26 | 0 | var deferred = getDefer(); |
| 27 | //创建连接 | |
| 28 | 0 | var connection = mysql.createConnection({ |
| 29 | host : this.config.hostname || 'localhost', | |
| 30 | user : this.config.username || 'root', | |
| 31 | password : this.config.password || '', | |
| 32 | database : this.config.database || '' | |
| 33 | }); | |
| 34 | //连接 | |
| 35 | 0 | connection.connect(function(err){ |
| 36 | //连接失败 | |
| 37 | 0 | if (err) { |
| 38 | 0 | deferred.reject(err); |
| 39 | 0 | self.close(); |
| 40 | }else{ | |
| 41 | 0 | deferred.resolve(); |
| 42 | } | |
| 43 | }); | |
| 44 | //错误时关闭当前连接 | |
| 45 | 0 | connection.on('error', function(){ |
| 46 | 0 | self.close(); |
| 47 | }); | |
| 48 | //PROTOCOL_CONNECTION_LOST | |
| 49 | 0 | connection.on('end', function(){ |
| 50 | 0 | self.close(); |
| 51 | }) | |
| 52 | //连接句柄 | |
| 53 | 0 | this.handle = connection; |
| 54 | //把上一次的promise reject | |
| 55 | 0 | if (this.deferred) { |
| 56 | 0 | this.deferred.reject(new Error('connection closed')); |
| 57 | } | |
| 58 | 0 | this.deferred = deferred; |
| 59 | 0 | return this.deferred.promise; |
| 60 | }, | |
| 61 | /** | |
| 62 | * 查询sql语句,返回一个promise | |
| 63 | * @param {[type]} sql [description] | |
| 64 | * @return {[type]} [description] | |
| 65 | */ | |
| 66 | query: function(sql){ | |
| 67 | 0 | if (APP_DEBUG) { |
| 68 | 0 | console.log('sql: ' + sql); |
| 69 | } | |
| 70 | 0 | var self = this; |
| 71 | 0 | return this.connect().then(function(){ |
| 72 | 0 | var deferred = getDefer(); |
| 73 | 0 | self.handle.query(sql, function(err, rows){ |
| 74 | 0 | if (err) { |
| 75 | //当数据量非常大时,可能会出现连接丢失,这里进行重连 | |
| 76 | 0 | if (err.code === 'PROTOCOL_CONNECTION_LOST' && self.tryTimes < 3) { |
| 77 | 0 | self.tryTimes++; |
| 78 | 0 | self.close(); |
| 79 | 0 | return self.query(sql); |
| 80 | } | |
| 81 | 0 | return deferred.reject(err); |
| 82 | } | |
| 83 | 0 | self.tryTimes = 0; |
| 84 | 0 | return deferred.resolve(rows || []); |
| 85 | }); | |
| 86 | 0 | return deferred.promise; |
| 87 | }); | |
| 88 | }, | |
| 89 | /** | |
| 90 | * 关闭连接 | |
| 91 | * @return {[type]} [description] | |
| 92 | */ | |
| 93 | close: function(){ | |
| 94 | 0 | if (this.handle) { |
| 95 | 0 | this.handle.destroy(); |
| 96 | 0 | this.handle = null; |
| 97 | } | |
| 98 | } | |
| 99 | }; | |
| 100 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * ejs | |
| 3 | * https://github.com/visionmedia/ejs | |
| 4 | * @type {[type]} | |
| 5 | */ | |
| 6 | 1 | var ejs = require('ejs'); |
| 7 | 1 | module.exports = { |
| 8 | fetch: function(templateFile, tVar){ | |
| 9 | 1 | 'use strict'; |
| 10 | 1 | var content = getFileContent(templateFile); |
| 11 | 1 | var conf = extend({ |
| 12 | filename: templateFile, | |
| 13 | cache: true | |
| 14 | }, C('tpl_engine_config')); | |
| 15 | 1 | return ejs.compile(content, conf)(tVar); |
| 16 | } | |
| 17 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * REST Controller | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Controller(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | __before: function(){ | |
| 9 | ||
| 10 | }, | |
| 11 | __call: function(){ | |
| 12 | 0 | this.end('method is not allowed'); |
| 13 | } | |
| 14 | } | |
| 15 | }) |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 高级模型 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Model(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | //关联类型 | |
| 8 | 1 | global.HAS_ONE = 1; |
| 9 | 1 | global.BELONGS_TO = 2; |
| 10 | 1 | global.HAS_MANY = 3; |
| 11 | 1 | global.MANY_TO_MANY = 4; |
| 12 | ||
| 13 | //post的操作类型 | |
| 14 | 1 | var ADD = 'ADD'; |
| 15 | 1 | var UPDATE = 'UPDATE'; |
| 16 | 1 | var DELETE = 'DELETE'; |
| 17 | ||
| 18 | //get时不同的type对应的回调 | |
| 19 | 1 | var mapTypeGetFn = { |
| 20 | 1: '_getHasOneRelation', | |
| 21 | 2: '_getBelongsToRelation', | |
| 22 | 3: '_getHasManyRelation', | |
| 23 | 4: '_getManyToManyRelation' | |
| 24 | }; | |
| 25 | ||
| 26 | //post时不同的type对应的回调 | |
| 27 | 1 | var mapTypePostFn = { |
| 28 | 1: '_postHasOneRelation', | |
| 29 | 2: '_postBelongsToRelation', | |
| 30 | 3: '_postHasManyRelation', | |
| 31 | 4: '_postManyToManyRelation' | |
| 32 | }; | |
| 33 | ||
| 34 | 1 | return { |
| 35 | /** | |
| 36 | * 关联定义 | |
| 37 | * 数据格式: | |
| 38 | * 'Profile': { | |
| 39 | type: 1, | |
| 40 | model: 'Profile', | |
| 41 | name: 'Profile', | |
| 42 | key: 'id', | |
| 43 | fKey: 'user_id', | |
| 44 | field: 'id,name', | |
| 45 | where: 'name=xx', | |
| 46 | order: '', | |
| 47 | limit: '' | |
| 48 | * } | |
| 49 | * @type {Object} | |
| 50 | */ | |
| 51 | relation: {}, | |
| 52 | /** | |
| 53 | * 本次使用的关联名称,默认是全部使用 | |
| 54 | * @type {Boolean} | |
| 55 | */ | |
| 56 | _relationName: true, | |
| 57 | /** | |
| 58 | * 只读字段 | |
| 59 | * @type {String} | |
| 60 | */ | |
| 61 | readonlyField: '', | |
| 62 | /** | |
| 63 | * 保存时对数据进行校验 | |
| 64 | * @type {Boolean} | |
| 65 | */ | |
| 66 | _validateField: true, | |
| 67 | /** | |
| 68 | * 字段类型 | |
| 69 | * @type {Object} | |
| 70 | */ | |
| 71 | fieldType: {}, | |
| 72 | /** | |
| 73 | * 设置本次使用的relation | |
| 74 | * @param {[type]} name [description] | |
| 75 | */ | |
| 76 | setRelation: function(name, value){ | |
| 77 | 0 | if (isObject(name) || !isEmpty(value)) { |
| 78 | 0 | var obj = isObject(name) ? name : getObject(name, value); |
| 79 | 0 | extend(this.relation, obj); |
| 80 | 0 | return this; |
| 81 | } | |
| 82 | 0 | if (isString(name)) { |
| 83 | 0 | name = name.split(','); |
| 84 | } | |
| 85 | 0 | this._relationName = name; |
| 86 | 0 | return this; |
| 87 | }, | |
| 88 | /** | |
| 89 | * find后置操作 | |
| 90 | * @param {[type]} data [description] | |
| 91 | * @return {[type]} [description] | |
| 92 | */ | |
| 93 | _afterFind: function(data, parsedOptions){ | |
| 94 | 0 | return this.getRelation(data, parsedOptions); |
| 95 | }, | |
| 96 | /** | |
| 97 | * select后置操作 | |
| 98 | * @param {[type]} data [description] | |
| 99 | * @return {[type]} [description] | |
| 100 | */ | |
| 101 | _afterSelect: function(data, parsedOptions){ | |
| 102 | 0 | return this.getRelation(data, parsedOptions); |
| 103 | }, | |
| 104 | /** | |
| 105 | * 获取关联的数据 | |
| 106 | * @param {[type]} data [description] | |
| 107 | * @param Boolean isDataList 是否是数据列表 | |
| 108 | * @return {[type]} | |
| 109 | */ | |
| 110 | getRelation: function(data, parsedOptions){ | |
| 111 | 0 | if (isEmpty(data) || isEmpty(this.relation) || isEmpty(this._relationName)) { |
| 112 | 0 | return data; |
| 113 | } | |
| 114 | 0 | var self = this; |
| 115 | 0 | var promises = Object.keys(this.relation).map(function(key){ |
| 116 | 0 | var mapName, mapType, model, mapKey, mapfKey; |
| 117 | 0 | var value = self.relation[key]; |
| 118 | 0 | if (!isObject(value)) { |
| 119 | 0 | value = {type: value}; |
| 120 | } | |
| 121 | 0 | mapName = value.name || key; |
| 122 | //如果不在开启的relation内,则直接返回 | |
| 123 | 0 | if (self._relationName !== true && self._relationName.indexOf(mapName) === -1) { |
| 124 | 0 | return; |
| 125 | } | |
| 126 | 0 | mapType = value.type || HAS_ONE; |
| 127 | 0 | mapKey = value.key || self.getPk(); |
| 128 | 0 | mapfKey = value.fKey || (self.name.toLowerCase() + '_id'); |
| 129 | 0 | model = D(value.model || key); |
| 130 | 0 | model.where(value.where).cache(parsedOptions.cache).field(value.field).order(value.order).limit(value.limit); |
| 131 | //调用不同的类型解析 | |
| 132 | 0 | return self[mapTypeGetFn[mapType]](data, value, { |
| 133 | model: model, | |
| 134 | mapName: mapName, | |
| 135 | mapKey: mapKey, | |
| 136 | mapfKey: mapfKey | |
| 137 | }, parsedOptions); | |
| 138 | }); | |
| 139 | 0 | return Promise.all(promises).then(function(){ |
| 140 | 0 | return data; |
| 141 | }); | |
| 142 | }, | |
| 143 | _getHasOneRelation: function(data, value, mapOptions){ | |
| 144 | 0 | var self = this; |
| 145 | 0 | var where = self.parseRelationWhere(data, mapOptions.mapKey, mapOptions.mapfKey); |
| 146 | 0 | if (where === false) { |
| 147 | 0 | return {}; |
| 148 | } | |
| 149 | 0 | mapOptions.model.where(where); |
| 150 | 0 | return mapOptions.model.select().then(function(mapData){ |
| 151 | 0 | return self.parseRelationData(data, mapData, mapOptions.mapName, mapOptions.mapKey, mapOptions.mapfKey); |
| 152 | }); | |
| 153 | }, | |
| 154 | _getBelongsToRelation: function(data, value, mapOptions){ | |
| 155 | 0 | var self = this; |
| 156 | 0 | var mapKey, mapfKey; |
| 157 | 0 | return mapOptions.model.getTableFields().then(function(){ |
| 158 | 0 | mapKey = mapOptions.model.getModelName().toLowerCase() + '_id'; |
| 159 | 0 | mapfKey = mapOptions.model.getPk(); |
| 160 | 0 | var where = self.parseRelationWhere(data, mapKey, mapfKey); |
| 161 | 0 | if (where === false) { |
| 162 | 0 | return {}; |
| 163 | } | |
| 164 | 0 | mapOptions.model.where(where); |
| 165 | 0 | return mapOptions.model.select().then(function(mapData){ |
| 166 | 0 | return self.parseRelationData(data, mapData, mapOptions.mapName, mapKey, mapfKey); |
| 167 | }) | |
| 168 | }) | |
| 169 | }, | |
| 170 | _getHasManyRelation: function(data, value, mapOptions){ | |
| 171 | 0 | var self = this; |
| 172 | 0 | var where = self.parseRelationWhere(data, mapOptions.mapKey, mapOptions.mapfKey); |
| 173 | 0 | if (where === false) { |
| 174 | 0 | return []; |
| 175 | } | |
| 176 | 0 | mapOptions.model.where(where); |
| 177 | 0 | return mapOptions.model.select().then(function(mapData){ |
| 178 | 0 | return self.parseRelationData(data, mapData, mapOptions.mapName, mapOptions.mapKey, mapOptions.mapfKey, true); |
| 179 | }); | |
| 180 | }, | |
| 181 | _getManyToManyRelation: function(data, value, mapOptions, parsedOptions){ | |
| 182 | 0 | var self = this; |
| 183 | 0 | return mapOptions.model.getTableFields().then(function(){ |
| 184 | 0 | var where = self.parseRelationWhere(data, mapOptions.mapKey, mapOptions.mapfKey); |
| 185 | 0 | if (where === false) { |
| 186 | 0 | return []; |
| 187 | } | |
| 188 | 0 | var whereStr = self.db.parseWhere(where); |
| 189 | //关联的实体表和关系表联合查询 | |
| 190 | 0 | var sql = 'SELECT b.%s, a.%s FROM %s as a, %s as b %s AND a.%s=b.%s %s'; |
| 191 | 0 | var queryData = [ |
| 192 | value.field || '*', | |
| 193 | mapOptions.mapfKey, | |
| 194 | value.rTable || self.getRelationTableName(mapOptions.model), | |
| 195 | mapOptions.model.getTableName(), | |
| 196 | whereStr || 'WHERE ', | |
| 197 | value.rfKey || (mapOptions.model.getModelName().toLowerCase() + '_id'), | |
| 198 | mapOptions.model.getPk(), | |
| 199 | value.where ? (' AND ' + value.where) : '' | |
| 200 | ]; | |
| 201 | 0 | return self.parseSql(sql, queryData).then(function(sql){ |
| 202 | 0 | return self.db.select(sql, parsedOptions.cache); |
| 203 | }).then(function(mapData){ | |
| 204 | 0 | return self.parseRelationData(data, mapData, mapOptions.mapName, mapOptions.mapKey, mapOptions.mapfKey, true); |
| 205 | }); | |
| 206 | }); | |
| 207 | }, | |
| 208 | /** | |
| 209 | * 多对多关系下,获取对应的关联表 | |
| 210 | * @return {[type]} [description] | |
| 211 | */ | |
| 212 | getRelationTableName: function(model){ | |
| 213 | 0 | var table = [ |
| 214 | this.tablePrefix, | |
| 215 | this.tableName || this.name, | |
| 216 | '_', | |
| 217 | model.getModelName() | |
| 218 | ].join(''); | |
| 219 | 0 | return table.toLowerCase(); |
| 220 | }, | |
| 221 | /** | |
| 222 | * 多堆垛关系下,回去对应关联表的模型 | |
| 223 | * @param {[type]} model [description] | |
| 224 | * @return {[type]} [description] | |
| 225 | */ | |
| 226 | getRelationModel: function(model){ | |
| 227 | 0 | var name = ucfirst(this.tableName || this.name) + ucfirst(model.getModelName()); |
| 228 | 0 | return D(name); |
| 229 | }, | |
| 230 | /** | |
| 231 | * 解析relation的where条件 | |
| 232 | * @param {[type]} data [description] | |
| 233 | * @param {[type]} mapKey [description] | |
| 234 | * @param {[type]} mapfKey [description] | |
| 235 | * @return {[type]} [description] | |
| 236 | */ | |
| 237 | parseRelationWhere: function(data, mapKey, mapfKey){ | |
| 238 | 0 | if (isArray(data)) { |
| 239 | 0 | var keys = {}; |
| 240 | 0 | data.forEach(function(item){ |
| 241 | 0 | keys[item[mapKey]] = 1; |
| 242 | }) | |
| 243 | 0 | var value = Object.keys(keys); |
| 244 | 0 | if (value.length === 0) { |
| 245 | 0 | return false; |
| 246 | } | |
| 247 | 0 | return getObject(mapfKey, ['IN', value]); |
| 248 | } | |
| 249 | 0 | return getObject(mapfKey, data[mapKey]); |
| 250 | }, | |
| 251 | /** | |
| 252 | * 解析查询后的数据 | |
| 253 | * @param {[type]} data [description] | |
| 254 | * @param {[type]} mapData [description] | |
| 255 | * @param {[type]} mapName [description] | |
| 256 | * @param {[type]} mapKey [description] | |
| 257 | * @param {[type]} mapfKey [description] | |
| 258 | * @param {Boolean} isArrMap [description] | |
| 259 | * @return {[type]} [description] | |
| 260 | */ | |
| 261 | parseRelationData: function(data, mapData, mapName, mapKey, mapfKey, isArrMap){ | |
| 262 | 0 | if (isArray(data)) { |
| 263 | //提前初始化,防止mapData为空导致data里的数据没有初始化的情况 | |
| 264 | 0 | data.forEach(function(item, i){ |
| 265 | 0 | data[i][mapName] = isArrMap ? [] : {}; |
| 266 | }); | |
| 267 | 0 | mapData.forEach(function(mapItem){ |
| 268 | 0 | data.forEach(function(item, i){ |
| 269 | 0 | if (mapItem[mapfKey] !== item[mapKey]) { |
| 270 | 0 | return; |
| 271 | } | |
| 272 | 0 | if (isArrMap) { |
| 273 | 0 | data[i][mapName].push(mapItem); |
| 274 | }else{ | |
| 275 | 0 | data[i][mapName] = mapItem; |
| 276 | } | |
| 277 | }); | |
| 278 | }); | |
| 279 | }else{ | |
| 280 | 0 | data[mapName] = isArrMap ? (mapData || []) : (mapData[0] || {}); |
| 281 | } | |
| 282 | 0 | return data; |
| 283 | }, | |
| 284 | /** | |
| 285 | * 添加后置操作 | |
| 286 | * @param {[type]} data [description] | |
| 287 | * @param {[type]} parsedOptions [description] | |
| 288 | * @return {[type]} [description] | |
| 289 | */ | |
| 290 | _afterAdd: function(data, parsedOptions){ | |
| 291 | 0 | return this.postRelation(ADD, data, parsedOptions); |
| 292 | }, | |
| 293 | /** | |
| 294 | * 删除后置操作 | |
| 295 | * @param {[type]} data [description] | |
| 296 | * @param {[type]} parsedOptions [description] | |
| 297 | * @return {[type]} [description] | |
| 298 | */ | |
| 299 | _afterDelete: function(data, parsedOptions){ | |
| 300 | 0 | return this.postRelation(DELETE, data, parsedOptions); |
| 301 | }, | |
| 302 | /** | |
| 303 | * 更新前置操作 | |
| 304 | * @param {[type]} data [description] | |
| 305 | * @param {[type]} parsedOptions [description] | |
| 306 | * @return {[type]} [description] | |
| 307 | */ | |
| 308 | _beforeUpdate: function(data){ | |
| 309 | //只读字段处理 | |
| 310 | 0 | if (!isEmpty(this.readonlyField)) { |
| 311 | 0 | if (isString(this.readonlyField)) { |
| 312 | 0 | this.readonlyField = this.readonlyField.split(','); |
| 313 | } | |
| 314 | 0 | this.readonlyField.forEach(function(field){ |
| 315 | 0 | delete data[field]; |
| 316 | }); | |
| 317 | } | |
| 318 | 0 | return data; |
| 319 | }, | |
| 320 | /** | |
| 321 | * 更新后置操作 | |
| 322 | * @param {[type]} data [description] | |
| 323 | * @param {[type]} parsedOptions [description] | |
| 324 | * @return {[type]} [description] | |
| 325 | */ | |
| 326 | _afterUpdate: function(data, parsedOptions){ | |
| 327 | 0 | return this.postRelation(UPDATE, data, parsedOptions); |
| 328 | }, | |
| 329 | /** | |
| 330 | * 提交类关联操作 | |
| 331 | * @param {[type]} postType [description] | |
| 332 | * @param {[type]} data [description] | |
| 333 | * @param {[type]} parsedOptions [description] | |
| 334 | * @return {[type]} [description] | |
| 335 | */ | |
| 336 | postRelation: function(postType, data, parsedOptions){ | |
| 337 | 0 | if (isEmpty(data) || isEmpty(this.relation) || isEmpty(this._relationName)) { |
| 338 | 0 | return data; |
| 339 | } | |
| 340 | 0 | var self = this; |
| 341 | 0 | var promises = []; |
| 342 | 0 | Object.keys(this.relation).forEach(function(key){ |
| 343 | 0 | var promise, mapName, mapType, model, mapKey, mapfKey, mapData; |
| 344 | 0 | var value = self.relation[key]; |
| 345 | 0 | if (!isObject(value)) { |
| 346 | 0 | value = {type: value}; |
| 347 | } | |
| 348 | 0 | mapName = value.name || key; |
| 349 | //如果没有开启对应的relation,则直接返回 | |
| 350 | 0 | if (self._relationName !== true && self._relationName.indexOf(mapName) === -1) { |
| 351 | 0 | return; |
| 352 | } | |
| 353 | 0 | mapData = data[mapName]; |
| 354 | //如果没有对应的数据,则直接返回 | |
| 355 | 0 | if (isEmpty(mapData) && postType !== DELETE) { |
| 356 | 0 | return; |
| 357 | } | |
| 358 | 0 | mapKey = value.key || self.getPk(); |
| 359 | 0 | if (isEmpty(data[mapKey])) { |
| 360 | 0 | return; |
| 361 | } | |
| 362 | 0 | mapType = value.type || HAS_ONE; |
| 363 | 0 | mapfKey = value.fKey || (self.name.toLowerCase() + '_id'); |
| 364 | 0 | model = D(value.model || key); |
| 365 | 0 | model.where(value.where); |
| 366 | //调用不同的类型解析 | |
| 367 | 0 | promise = self[mapTypePostFn[mapType]](data, value, { |
| 368 | model: model, | |
| 369 | mapName: mapName, | |
| 370 | mapKey: mapKey, | |
| 371 | mapfKey: mapfKey, | |
| 372 | mapData: mapData, | |
| 373 | type: postType | |
| 374 | }, parsedOptions); | |
| 375 | ||
| 376 | 0 | promises.push(promise); |
| 377 | }); | |
| 378 | 0 | return Promise.all(promises).then(function(){ |
| 379 | 0 | return data; |
| 380 | }); | |
| 381 | }, | |
| 382 | /** | |
| 383 | * 一对一提交 | |
| 384 | * @param {[type]} data [description] | |
| 385 | * @param {[type]} value [description] | |
| 386 | * @param {[type]} mapOptions [description] | |
| 387 | * @param {[type]} parsedOptions [description] | |
| 388 | * @return {[type]} [description] | |
| 389 | */ | |
| 390 | _postHasOneRelation: function(data, value, mapOptions){ | |
| 391 | 0 | var promise = null; |
| 392 | 0 | var where; |
| 393 | 0 | switch(mapOptions.type){ |
| 394 | case ADD: | |
| 395 | 0 | mapOptions.mapData[mapOptions.mapfKey] = data[mapOptions.mapKey]; |
| 396 | 0 | promise = mapOptions.model.add(mapOptions.mapData); |
| 397 | 0 | break; |
| 398 | case DELETE: | |
| 399 | 0 | where = getObject(mapOptions.mapfKey, data[mapOptions.mapKey]); |
| 400 | 0 | promise = mapOptions.model.where(where).delete(); |
| 401 | 0 | break; |
| 402 | case UPDATE: | |
| 403 | 0 | where = getObject(mapOptions.mapfKey, data[mapOptions.mapKey]); |
| 404 | 0 | promise = mapOptions.model.where(where).update(mapOptions.mapData); |
| 405 | 0 | break; |
| 406 | default: | |
| 407 | 0 | break; |
| 408 | } | |
| 409 | 0 | return promise; |
| 410 | }, | |
| 411 | _postBelongsToRelation: function(data){ | |
| 412 | 0 | return data; |
| 413 | }, | |
| 414 | /** | |
| 415 | * 一对多提交 | |
| 416 | * @param {[type]} data [description] | |
| 417 | * @param {[type]} value [description] | |
| 418 | * @param {[type]} mapOptions [description] | |
| 419 | * @param {[type]} parsedOptions [description] | |
| 420 | * @return {[type]} [description] | |
| 421 | */ | |
| 422 | _postHasManyRelation: function(data, value, mapOptions){ | |
| 423 | 0 | var type = mapOptions.type; |
| 424 | 0 | var mapData = mapOptions.mapData; |
| 425 | 0 | var model = mapOptions.model; |
| 426 | 0 | var promise; |
| 427 | 0 | if (!isArray(mapData)) { |
| 428 | 0 | mapData = [mapData]; |
| 429 | } | |
| 430 | 0 | switch(type){ |
| 431 | case ADD: | |
| 432 | 0 | mapData = mapData.map(function(item){ |
| 433 | 0 | item[mapOptions.mapfKey] = data[mapOptions.mapKey]; |
| 434 | }); | |
| 435 | 0 | promise = model.addAll(mapData); |
| 436 | 0 | break; |
| 437 | case UPDATE: | |
| 438 | 0 | promise = model.getTableFields().then(function(){ |
| 439 | 0 | var promises = []; |
| 440 | 0 | var pk = model.getPk(); |
| 441 | 0 | mapData.forEach(function(item){ |
| 442 | 0 | var pro; |
| 443 | 0 | if (item[pk]) { |
| 444 | 0 | pro = model.update(item); |
| 445 | }else{ | |
| 446 | 0 | item[mapOptions.mapfKey] = data[mapOptions.mapKey]; |
| 447 | 0 | pro = model.add(item); |
| 448 | } | |
| 449 | 0 | promises.push(pro); |
| 450 | }); | |
| 451 | 0 | return Promise.all(promises); |
| 452 | }); | |
| 453 | 0 | break; |
| 454 | case DELETE: | |
| 455 | 0 | var where = getObject(mapOptions.mapfKey, data[mapOptions.mapKey]); |
| 456 | 0 | promise = model.where(where).delete(); |
| 457 | 0 | break; |
| 458 | } | |
| 459 | 0 | return promise; |
| 460 | }, | |
| 461 | /** | |
| 462 | * 多对多提交 | |
| 463 | * @param Object data [description] | |
| 464 | * @param object value [description] | |
| 465 | * @param {[type]} mapOptions [description] | |
| 466 | * @param {[type]} parsedOptions [description] | |
| 467 | * @return {[type]} [description] | |
| 468 | */ | |
| 469 | _postManyToManyRelation: function(data, value, mapOptions){ | |
| 470 | 0 | var self = this; |
| 471 | 0 | var model = mapOptions.model; |
| 472 | 0 | var promise = model.getTableFields(); |
| 473 | 0 | var rfKey = value.rfKey || (model.getModelName().toLowerCase() + '_id'); |
| 474 | //var relationTable = value.rTable || self.getRelationTableName(model); | |
| 475 | 0 | var where; |
| 476 | 0 | var type = mapOptions.type; |
| 477 | 0 | var mapData = mapOptions.mapData; |
| 478 | 0 | var relationModel = self.getRelationModel(model); |
| 479 | 0 | if (type === DELETE || type === UPDATE) { |
| 480 | 0 | where = getObject(mapOptions.mapfKey, data[mapOptions.mapKey]); |
| 481 | 0 | promise = promise.then(function(){ |
| 482 | 0 | return relationModel.where(where).delete(); |
| 483 | }); | |
| 484 | } | |
| 485 | 0 | if (type === ADD || type === UPDATE) { |
| 486 | 0 | promise = promise.then(function(){ |
| 487 | 0 | if (!isArray(mapData)) { |
| 488 | 0 | mapData = isString(mapData) ? mapData.split(',') : [mapData]; |
| 489 | } | |
| 490 | 0 | var firstItem = mapData[0]; |
| 491 | //关系数据 | |
| 492 | 0 | if (isNumberString(firstItem) || (isObject(firstItem) && (rfKey in firstItem))) { |
| 493 | //生成要更新的数据 | |
| 494 | 0 | var postData = mapData.map(function(item){ |
| 495 | 0 | var key = [mapOptions.mapfKey, rfKey]; |
| 496 | 0 | var val = [data[mapOptions.mapKey], item[rfKey] || item]; |
| 497 | 0 | return getObject(key, val); |
| 498 | }); | |
| 499 | 0 | return relationModel.addAll(postData); |
| 500 | }else{ //实体数据 | |
| 501 | 0 | var unqiueField = model.getUniqueField(); |
| 502 | 0 | if (!unqiueField) { |
| 503 | 0 | return getPromise(model.getTableName() + ' table has no unqiue field', true); |
| 504 | } | |
| 505 | 0 | return self._getRalationAddIds(mapData, model, unqiueField).then(function(ids){ |
| 506 | 0 | var postData = ids.map(function(id){ |
| 507 | 0 | var key = [mapOptions.mapfKey, rfKey]; |
| 508 | 0 | var val = [data[mapOptions.mapKey], id]; |
| 509 | 0 | return getObject(key, val); |
| 510 | }); | |
| 511 | 0 | return relationModel.addAll(postData); |
| 512 | }); | |
| 513 | } | |
| 514 | }); | |
| 515 | } | |
| 516 | 0 | return promise; |
| 517 | }, | |
| 518 | /** | |
| 519 | * 插入数据,并获取插入的id集合 | |
| 520 | * @param {[type]} dataList [description] | |
| 521 | * @param {[type]} model [description] | |
| 522 | * @param {[type]} unqiueField [description] | |
| 523 | * @return {[type]} [description] | |
| 524 | */ | |
| 525 | _getRalationAddIds: function(dataList, model, unqiueField){ | |
| 526 | 0 | var promises = []; |
| 527 | 0 | var ids = []; |
| 528 | 0 | dataList.forEach(function(item){ |
| 529 | 0 | if (!isObject(item)) { |
| 530 | 0 | item = getObject(unqiueField, item); |
| 531 | } | |
| 532 | 0 | var value = item[unqiueField]; |
| 533 | 0 | if (!value) { |
| 534 | 0 | return true; |
| 535 | } | |
| 536 | 0 | var where = getObject(unqiueField, value); |
| 537 | 0 | var promise = model.where(where).field(model.getPk()).find().then(function(data){ |
| 538 | 0 | if (isEmpty(data)) { |
| 539 | 0 | return model.add(item).then(function(insertId){ |
| 540 | 0 | ids.push(insertId); |
| 541 | }); | |
| 542 | }else{ | |
| 543 | 0 | ids.push(data[model.getPk()]); |
| 544 | } | |
| 545 | }); | |
| 546 | 0 | promises.push(promise); |
| 547 | }); | |
| 548 | 0 | return Promise.all(promises).then(function(){ |
| 549 | 0 | return ids; |
| 550 | }); | |
| 551 | }, | |
| 552 | /** | |
| 553 | * 设置是否对数据进行校验 | |
| 554 | * @param {[type]} validate [description] | |
| 555 | * @return {[type]} [description] | |
| 556 | */ | |
| 557 | validate: function(validate){ | |
| 558 | 0 | this._validateField = validate; |
| 559 | } | |
| 560 | }; | |
| 561 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 行为类 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | module.exports = Class(function(){ |
| 6 | 1 | 'use strict'; |
| 7 | 1 | return { |
| 8 | options: {}, //行为选项 | |
| 9 | http: null, | |
| 10 | init: function(http){ | |
| 11 | 8 | this.http = http; |
| 12 | 8 | for(var name in this.options){ |
| 13 | 12 | if (C(name) !== undefined) { |
| 14 | 10 | this.options[name] = C(name); |
| 15 | } | |
| 16 | } | |
| 17 | }, | |
| 18 | run: function(){ | |
| 19 | ||
| 20 | } | |
| 21 | }; | |
| 22 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 缓存基类 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | /** | |
| 6 | * 缓存数据 | |
| 7 | * @type {Object} | |
| 8 | */ | |
| 9 | 1 | var cacheData = {}; |
| 10 | /** | |
| 11 | * 定时器 | |
| 12 | * @type {Number} | |
| 13 | */ | |
| 14 | 1 | var gcTimer = {}; |
| 15 | /** | |
| 16 | * 清除已经过期的Cache | |
| 17 | * @return {[type]} [description] | |
| 18 | */ | |
| 19 | 1 | var gc = function(instance){ |
| 20 | 27 | 'use strict'; |
| 21 | 27 | if (APP_DEBUG || APP_MODE === 'cli' || gcTimer[instance.gcType]) { |
| 22 | 27 | return; |
| 23 | } | |
| 24 | 0 | gcTimer[instance.gcType] = setInterval(function(){ |
| 25 | 0 | var hour = (new Date()).getHours(); |
| 26 | 0 | if (C('cache_gc_hour').indexOf(hour) === -1) { |
| 27 | 0 | return; |
| 28 | } | |
| 29 | 0 | var now = Date.now(); |
| 30 | 0 | if (instance.gc) { |
| 31 | 0 | console.log('gc clean: ', instance.gcType, (new Date()).toUTCString()); |
| 32 | 0 | instance.gc(now); |
| 33 | } | |
| 34 | }, 3600 * 1000); | |
| 35 | }; | |
| 36 | ||
| 37 | 1 | module.exports = Class(function(){ |
| 38 | 1 | 'use strict'; |
| 39 | 1 | return { |
| 40 | /** | |
| 41 | * gc的类型,用于定时器类型判断 | |
| 42 | * @type {String} | |
| 43 | */ | |
| 44 | gcType: 'Cache', | |
| 45 | /** | |
| 46 | * 初始化 | |
| 47 | * @param {[type]} options [description] | |
| 48 | * @return {[type]} [description] | |
| 49 | */ | |
| 50 | init: function(options){ | |
| 51 | 27 | options = options || {}; |
| 52 | 27 | if (options.cacheData) { |
| 53 | 0 | this.cacheData = options.cacheData; |
| 54 | }else{ | |
| 55 | 27 | this.cacheData = cacheData; |
| 56 | } | |
| 57 | 27 | if (options.gcType) { |
| 58 | 0 | this.gcType = options.gcType; |
| 59 | } | |
| 60 | 27 | if (!options.timeout) { |
| 61 | 20 | options.timeout = C('cache_timeout') |
| 62 | } | |
| 63 | 27 | this.options = options; |
| 64 | //操作的key | |
| 65 | 27 | this.key = ''; |
| 66 | //是否更新expire值 | |
| 67 | 27 | this.updateExpire = false; |
| 68 | 27 | gc(this); |
| 69 | }, | |
| 70 | /** | |
| 71 | * 获取缓存值,返回一个promise | |
| 72 | * @param {[type]} name [description] | |
| 73 | * @return {[type]} [description] | |
| 74 | */ | |
| 75 | get: function(name){ | |
| 76 | 5 | var key = this.key || name; |
| 77 | 5 | if (!(key in this.cacheData)) { |
| 78 | 0 | return getPromise(); |
| 79 | } | |
| 80 | 5 | var value = this.cacheData[key]; |
| 81 | 5 | if (Date.now() > value.expire) { |
| 82 | 2 | delete this.cacheData[key]; |
| 83 | 2 | return getPromise(); |
| 84 | } | |
| 85 | 3 | if (this.updateExpire) { |
| 86 | 0 | this.cacheData[key].expire = Date.now() + value.timeout * 1000; |
| 87 | } | |
| 88 | 3 | return getPromise(value.data[name]); |
| 89 | }, | |
| 90 | /** | |
| 91 | * 设置缓存值 | |
| 92 | * @param {[type]} name [description] | |
| 93 | * @param {[type]} value [description] | |
| 94 | */ | |
| 95 | set: function(name, value, timeout){ | |
| 96 | 6 | if (timeout === undefined) { |
| 97 | 0 | timeout = this.options.timeout; |
| 98 | } | |
| 99 | 6 | var key = this.key || name; |
| 100 | 6 | if (key in this.cacheData) { |
| 101 | 0 | this.cacheData[key].data[name] = value; |
| 102 | }else{ | |
| 103 | 6 | this.cacheData[key] = { |
| 104 | data: getObject(name, value), | |
| 105 | timeout: timeout, | |
| 106 | expire: Date.now() + timeout * 1000 | |
| 107 | }; | |
| 108 | } | |
| 109 | 6 | return getPromise(); |
| 110 | }, | |
| 111 | /** | |
| 112 | * 移除缓存值 | |
| 113 | * @param {[type]} name [description] | |
| 114 | * @return {[type]} [description] | |
| 115 | */ | |
| 116 | rm: function(name){ | |
| 117 | 1 | var key = this.key || name; |
| 118 | 1 | if (key in this.cacheData) { |
| 119 | 1 | delete this.cacheData[key].data[name]; |
| 120 | } | |
| 121 | 1 | return getPromise(); |
| 122 | }, | |
| 123 | /** | |
| 124 | * gc | |
| 125 | * @param {[type]} now [description] | |
| 126 | * @return {[type]} [description] | |
| 127 | */ | |
| 128 | gc: function(now){ | |
| 129 | 0 | for(var key in this.cacheData){ |
| 130 | 0 | var item = this.cacheData[key] || {}; |
| 131 | 0 | if (now > item.expire) { |
| 132 | 0 | delete this.cacheData[key]; |
| 133 | } | |
| 134 | } | |
| 135 | } | |
| 136 | }; | |
| 137 | }); |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * cookie操作 | |
| 3 | * @type {Object} | |
| 4 | */ | |
| 5 | 1 | module.exports = { |
| 6 | /** | |
| 7 | * 解析 | |
| 8 | * @param {[type]} str [description] | |
| 9 | * @return {[type]} [description] | |
| 10 | */ | |
| 11 | parse: function(str){ | |
| 12 | 17 | 'use strict'; |
| 13 | 17 | var data = {}; |
| 14 | 17 | str.split(/; */).forEach(function(item) { |
| 15 | 34 | var pos = item.indexOf('='); |
| 16 | 34 | if (pos === -1) { |
| 17 | 5 | return; |
| 18 | } | |
| 19 | 29 | var key = item.substr(0, pos).trim(); |
| 20 | 29 | var val = item.substr(pos + 1).trim(); |
| 21 | 29 | if ('"' === val[0]) { |
| 22 | 2 | val = val.slice(1, -1); |
| 23 | } | |
| 24 | // only assign once | |
| 25 | 29 | if (undefined === data[key]) { |
| 26 | 29 | try { |
| 27 | 29 | data[key] = decodeURIComponent(val); |
| 28 | } catch (e) { | |
| 29 | 2 | data[key] = val; |
| 30 | } | |
| 31 | } | |
| 32 | }); | |
| 33 | 17 | return data; |
| 34 | }, | |
| 35 | /** | |
| 36 | * 格式化 | |
| 37 | * @param {[type]} name [description] | |
| 38 | * @param {[type]} val [description] | |
| 39 | * @param {[type]} options [description] | |
| 40 | * @return {[type]} [description] | |
| 41 | */ | |
| 42 | stringify: function(name, value, options){ | |
| 43 | 11 | 'use strict'; |
| 44 | 11 | options = options || {}; |
| 45 | 11 | var item = [name + '=' + encodeURIComponent(value)]; |
| 46 | 11 | if (options.maxage) { |
| 47 | 1 | item.push('Max-Age=' + options.maxage); |
| 48 | } | |
| 49 | 11 | if (options.domain) { |
| 50 | 1 | item.push('Domain=' + options.domain); |
| 51 | } | |
| 52 | 11 | if (options.path) { |
| 53 | 1 | item.push('Path=' + options.path); |
| 54 | } | |
| 55 | 11 | var expires = options.expires; |
| 56 | 11 | if (expires){ |
| 57 | 2 | if (!isDate(expires)) { |
| 58 | 1 | expires = new Date(expires); |
| 59 | } | |
| 60 | 2 | item.push('Expires=' + expires.toUTCString()); |
| 61 | } | |
| 62 | 11 | if (options.httponly) { |
| 63 | 1 | item.push('HttpOnly'); |
| 64 | } | |
| 65 | 11 | if (options.secure) { |
| 66 | 1 | item.push('Secure'); |
| 67 | } | |
| 68 | 11 | return item.join('; '); |
| 69 | } | |
| 70 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * 过滤器 | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | var Filter = module.exports = { |
| 6 | /** | |
| 7 | * 分页 | |
| 8 | * @param {[type]} value [description] | |
| 9 | * @return {[type]} [description] | |
| 10 | */ | |
| 11 | page: function(value){ | |
| 12 | 6 | 'use strict'; |
| 13 | 6 | return this.id(value) || 1; |
| 14 | }, | |
| 15 | /** | |
| 16 | * xxx asc,yyy desc | |
| 17 | * @return {[type]} [description] | |
| 18 | */ | |
| 19 | order: function(value){ | |
| 20 | 6 | 'use strict'; |
| 21 | 6 | if (isString(value)) { |
| 22 | 6 | value = value.split(','); |
| 23 | } | |
| 24 | 6 | if (!isArray(value)) { |
| 25 | 0 | return ''; |
| 26 | } | |
| 27 | 6 | return value.filter(function(item){ |
| 28 | 8 | item = item.trim().split(' '); |
| 29 | 8 | var field = item[0]; |
| 30 | 8 | var type = item[1]; |
| 31 | 8 | if (/^(ASC|DESC)$/i.test(type) && /^[\w]+$/.test(field)) { |
| 32 | 6 | return field + ' ' + type; |
| 33 | } | |
| 34 | }).join(','); | |
| 35 | }, | |
| 36 | /** | |
| 37 | * 大于0 | |
| 38 | * @return {[type]} [description] | |
| 39 | */ | |
| 40 | id: function(value){ | |
| 41 | 13 | 'use strict'; |
| 42 | 13 | value = parseInt(value + '', 10); |
| 43 | 13 | if (value > 0) { |
| 44 | 6 | return value; |
| 45 | } | |
| 46 | 7 | return 0; |
| 47 | }, | |
| 48 | /** | |
| 49 | * id列表 | |
| 50 | * @return {[type]} [description] | |
| 51 | */ | |
| 52 | ids: function(value, split){ | |
| 53 | 5 | 'use strict'; |
| 54 | 5 | if (isNumber(value)) { |
| 55 | 2 | value = this.id(value); |
| 56 | 2 | if (value) { |
| 57 | 1 | return [value]; |
| 58 | } | |
| 59 | 1 | return []; |
| 60 | } | |
| 61 | 3 | if (isString(value)) { |
| 62 | 2 | value = value.split(split || ','); |
| 63 | } | |
| 64 | 3 | if (!isArray(value)) { |
| 65 | 1 | return []; |
| 66 | } | |
| 67 | 2 | var ret = []; |
| 68 | 2 | for(var i = 0, length = value.length; i < length; i++){ |
| 69 | 3 | var item = (value[i] + '').trim(); |
| 70 | 3 | item = parseInt(item, 10); |
| 71 | 3 | if (item > 0) { |
| 72 | 3 | ret.push(item); |
| 73 | } | |
| 74 | } | |
| 75 | 2 | return ret; |
| 76 | }, | |
| 77 | /** | |
| 78 | * 是否在一个中 | |
| 79 | * @param {[type]} value [description] | |
| 80 | * @param {[type]} arr [description] | |
| 81 | * @return {[type]} [description] | |
| 82 | */ | |
| 83 | in: function(value, arr){ | |
| 84 | 0 | 'use strict'; |
| 85 | 0 | if (!isArray(arr)) { |
| 86 | 0 | arr = [arr]; |
| 87 | } | |
| 88 | 0 | if(arr.indexOf(value) > -1){ |
| 89 | 0 | return value; |
| 90 | } | |
| 91 | 0 | return ''; |
| 92 | }, | |
| 93 | /** | |
| 94 | * 将字符串切割为数组 | |
| 95 | * @param {[type]} value [description] | |
| 96 | * @param {[type]} split [description] | |
| 97 | * @return {[type]} [description] | |
| 98 | */ | |
| 99 | strs: function(value, split){ | |
| 100 | 0 | 'use strict'; |
| 101 | 0 | if (isString(value)) { |
| 102 | 0 | value = value.split(split || ','); |
| 103 | } | |
| 104 | 0 | if (!isArray(value)) { |
| 105 | 0 | return []; |
| 106 | } | |
| 107 | 0 | return value.filter(function(item){ |
| 108 | 0 | return (item + '').trim(); |
| 109 | }); | |
| 110 | } | |
| 111 | }; | |
| 112 | /** | |
| 113 | * 调用一个过滤器 | |
| 114 | * @param {[type]} data [description] | |
| 115 | * @param {[type]} type [description] | |
| 116 | * @return {[type]} [description] | |
| 117 | */ | |
| 118 | 1 | Filter.filter = function(value, type){ |
| 119 | 22 | 'use strict'; |
| 120 | 22 | var fn = Filter[type]; |
| 121 | 22 | if (typeof fn === 'function') { |
| 122 | 22 | var args = [].slice.call(arguments, 2); |
| 123 | 22 | args.unshift(value); |
| 124 | 22 | return Filter[type].apply(Filter, args); |
| 125 | } | |
| 126 | 0 | return false; |
| 127 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var crypto = require('crypto'); |
| 2 | /** | |
| 3 | * 生成uid | |
| 4 | * @param int length | |
| 5 | * @return string | |
| 6 | */ | |
| 7 | 1 | var uid = function(length){ |
| 8 | 0 | 'use strict'; |
| 9 | 0 | var ratio = Math.log(64) / Math.log(256); |
| 10 | 0 | var numbytes = Math.ceil(length * ratio); |
| 11 | 0 | var str = crypto.randomBytes(numbytes).toString('base64').slice(0, length); |
| 12 | 0 | return str.replace(/\+/g, '_').replace(/\//g, '-'); |
| 13 | }; | |
| 14 | /** | |
| 15 | * 生成cookie签名 | |
| 16 | * @param string val | |
| 17 | * @param string secret | |
| 18 | * @return string | |
| 19 | */ | |
| 20 | 1 | var cookieSign = function(val, secret){ |
| 21 | 0 | 'use strict'; |
| 22 | 0 | secret = crypto.createHmac('sha256', secret).update(val).digest('base64'); |
| 23 | 0 | secret = secret.replace(/\=+$/, ''); |
| 24 | 0 | return val + '.' + secret; |
| 25 | }; | |
| 26 | /** | |
| 27 | * 解析cookie签名 | |
| 28 | * @param {[type]} val | |
| 29 | * @param {[type]} secret | |
| 30 | * @return {[type]} | |
| 31 | */ | |
| 32 | 1 | var cookieUnsign = function(val, secret){ |
| 33 | 0 | 'use strict'; |
| 34 | 0 | var str = val.slice(0, val.lastIndexOf('.')); |
| 35 | 0 | return cookieSign(str, secret) === val ? str : false; |
| 36 | }; | |
| 37 | ||
| 38 | 1 | var Session = module.exports = Cache(function(){ |
| 39 | 1 | 'use strict'; |
| 40 | 1 | return { |
| 41 | init: function(options){ | |
| 42 | 0 | this.super_('init', options); |
| 43 | 0 | this.key = this.options.cookie; |
| 44 | 0 | this.updateExpire = true; |
| 45 | } | |
| 46 | }; | |
| 47 | }); | |
| 48 | ||
| 49 | 1 | Session.start = function(http){ |
| 50 | 11 | 'use strict'; |
| 51 | 11 | if (http.session) { |
| 52 | 10 | return http.session; |
| 53 | } | |
| 54 | 1 | var name = C('session_name'); |
| 55 | //是否使用签名 | |
| 56 | 1 | var secret = C('session_sign'); |
| 57 | 1 | var cookie = http.cookie[name]; |
| 58 | 1 | if (cookie && secret) { |
| 59 | 0 | cookie = cookieUnsign(cookie, secret); |
| 60 | } | |
| 61 | 1 | var session_cookie = cookie; |
| 62 | 1 | if (!cookie) { |
| 63 | 0 | cookie = uid(32); |
| 64 | 0 | session_cookie = cookie; |
| 65 | 0 | if (secret) { |
| 66 | 0 | cookie = cookieSign(cookie, secret); |
| 67 | } | |
| 68 | 0 | http.setCookie(name, cookie, C('session_options')); |
| 69 | } | |
| 70 | 1 | var type = C('session_type'); |
| 71 | 1 | if (!type && APP_DEBUG) { |
| 72 | 0 | type = 'File'; |
| 73 | 0 | console.log("in debug mode, session can't use memory type for storage, convert to File type"); |
| 74 | } | |
| 75 | //使用cluster的时候,不能使用内存来缓存Session | |
| 76 | 1 | if (!type && C('use_cluster')) { |
| 77 | 0 | type = 'File'; |
| 78 | 0 | console.log("in cluster mode, session can't use memory type for storage, convert to File type") |
| 79 | } | |
| 80 | 1 | name = type + 'Session'; |
| 81 | //session类 | |
| 82 | 1 | var session = http.session = thinkRequire(name)({ |
| 83 | cookie: session_cookie, | |
| 84 | timeout: C('session_timeout') | |
| 85 | }); | |
| 86 | //afterend时刷新缓存 | |
| 87 | 1 | http.on('afterEnd', function(){ |
| 88 | //刷新session | |
| 89 | 0 | return session.flush && session.flush(); |
| 90 | }) | |
| 91 | 1 | return cookie; |
| 92 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | /** | |
| 2 | * Valid | |
| 3 | * @return {[type]} [description] | |
| 4 | */ | |
| 5 | 1 | var net = require('net'); |
| 6 | ||
| 7 | 1 | var Valid = { |
| 8 | /** | |
| 9 | * 长度区域 | |
| 10 | * @param {[type]} min [description] | |
| 11 | * @param {[type]} max [description] | |
| 12 | * @return {[type]} [description] | |
| 13 | */ | |
| 14 | length: function(value, min, max){ | |
| 15 | 4 | 'use strict'; |
| 16 | 4 | min = min | 0; |
| 17 | 4 | var length = (value + '').length; |
| 18 | 4 | if (length < min) { |
| 19 | 1 | return false; |
| 20 | } | |
| 21 | 3 | if (max && length > max) { |
| 22 | 1 | return false; |
| 23 | } | |
| 24 | 2 | return true; |
| 25 | }, | |
| 26 | /** | |
| 27 | * 必填 | |
| 28 | * @return {[type]} [description] | |
| 29 | */ | |
| 30 | required: function(value){ | |
| 31 | 5 | 'use strict'; |
| 32 | 5 | return (value + '').length > 0; |
| 33 | }, | |
| 34 | /** | |
| 35 | * 自定义正则校验 | |
| 36 | * @param {[type]} reg [description] | |
| 37 | * @return {[type]} [description] | |
| 38 | */ | |
| 39 | regexp: function(value, reg){ | |
| 40 | 17 | 'use strict'; |
| 41 | 17 | return reg.test(value); |
| 42 | }, | |
| 43 | /** | |
| 44 | * 邮箱 | |
| 45 | * @return {[type]} [description] | |
| 46 | */ | |
| 47 | email: function(value){ | |
| 48 | 4 | 'use strict'; |
| 49 | 4 | var reg = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/; |
| 50 | 4 | return this.regexp(value, reg); |
| 51 | }, | |
| 52 | /** | |
| 53 | * 时间戳 | |
| 54 | * @return {[type]} [description] | |
| 55 | */ | |
| 56 | time: function(value){ | |
| 57 | 2 | 'use strict'; |
| 58 | 2 | var reg = /^[1-5]\d{12}$/; |
| 59 | 2 | return this.regexp(value, reg); |
| 60 | }, | |
| 61 | /** | |
| 62 | * 中文名 | |
| 63 | * @return {[type]} [description] | |
| 64 | */ | |
| 65 | cnname: function(value){ | |
| 66 | 3 | 'use strict'; |
| 67 | 3 | var reg = /^[\u4e00-\u9fa5\u3002\u2022]{2,32}$/; |
| 68 | 3 | return this.regexp(value, reg); |
| 69 | }, | |
| 70 | /** | |
| 71 | * 身份证号码 | |
| 72 | * @return {[type]} [description] | |
| 73 | */ | |
| 74 | idnumber: function(value){ | |
| 75 | 3 | 'use strict'; |
| 76 | 3 | if (/^\d{15}$/.test(value)) { |
| 77 | 1 | return true; |
| 78 | } | |
| 79 | 2 | if ((/^\d{17}[0-9xX]$/).test(value)) { |
| 80 | 1 | var vs = '1,0,x,9,8,7,6,5,4,3,2'.split(','), |
| 81 | ps = '7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2'.split(','), | |
| 82 | ss = value.toLowerCase().split(''), | |
| 83 | r = 0; | |
| 84 | 1 | for (var i = 0; i < 17; i++) { |
| 85 | 17 | r += ps[i] * ss[i]; |
| 86 | } | |
| 87 | 1 | var isOk = (vs[r % 11] === ss[17]); |
| 88 | 1 | return isOk; |
| 89 | } | |
| 90 | 1 | return false; |
| 91 | }, | |
| 92 | /** | |
| 93 | * 手机号 | |
| 94 | * @return {[type]} [description] | |
| 95 | */ | |
| 96 | mobile: function(value){ | |
| 97 | 2 | 'use strict'; |
| 98 | 2 | var reg = /^(13|15|18|14|17)\d{9}$/; |
| 99 | 2 | return this.regexp(value, reg); |
| 100 | }, | |
| 101 | /** | |
| 102 | * 邮编 | |
| 103 | * @return {[type]} [description] | |
| 104 | */ | |
| 105 | zipcode: function(value){ | |
| 106 | 2 | 'use strict'; |
| 107 | 2 | var reg = /^\d{6}$/; |
| 108 | 2 | return this.regexp(value, reg); |
| 109 | }, | |
| 110 | /** | |
| 111 | * 2次值是否一致 | |
| 112 | * @param {[type]} field [description] | |
| 113 | * @return {[type]} [description] | |
| 114 | */ | |
| 115 | confirm: function(value, cvalue){ | |
| 116 | 2 | 'use strict'; |
| 117 | 2 | return value === cvalue; |
| 118 | }, | |
| 119 | /** | |
| 120 | * url | |
| 121 | * @return {[type]} [description] | |
| 122 | */ | |
| 123 | url: function(value){ | |
| 124 | 2 | 'use strict'; |
| 125 | 2 | var reg = /^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/; |
| 126 | 2 | return this.regexp(value, reg); |
| 127 | }, | |
| 128 | /** | |
| 129 | * 整数 | |
| 130 | * @param {[type]} o [description] | |
| 131 | * @return {[type]} [description] | |
| 132 | */ | |
| 133 | int: function(value){ | |
| 134 | 3 | 'use strict'; |
| 135 | 3 | var val = parseInt(value, 10); |
| 136 | 3 | if (isNaN(val)) { |
| 137 | 1 | return false; |
| 138 | } | |
| 139 | 2 | return (val + '').length === value.length; |
| 140 | }, | |
| 141 | /** | |
| 142 | * 浮点数 | |
| 143 | * @return {[type]} [description] | |
| 144 | */ | |
| 145 | float: function(value){ | |
| 146 | 2 | 'use strict'; |
| 147 | 2 | return isNumberString(value); |
| 148 | }, | |
| 149 | /** | |
| 150 | * 整数范围 | |
| 151 | * @param {[type]} min [description] | |
| 152 | * @param {[type]} max [description] | |
| 153 | * @return {[type]} [description] | |
| 154 | */ | |
| 155 | range: function(value, min, max){ | |
| 156 | 4 | 'use strict'; |
| 157 | 4 | value = parseInt(value, 10); |
| 158 | 4 | min = min | 0; |
| 159 | 4 | if (isNaN(value) || value < min) { |
| 160 | 1 | return false; |
| 161 | } | |
| 162 | 3 | if (max && value > max) { |
| 163 | 1 | return false; |
| 164 | } | |
| 165 | 2 | return true; |
| 166 | }, | |
| 167 | /** | |
| 168 | * ip4校验 | |
| 169 | * @return {[type]} [description] | |
| 170 | */ | |
| 171 | ip4: function(value){ | |
| 172 | 2 | 'use strict'; |
| 173 | 2 | return net.isIPv4(value); |
| 174 | }, | |
| 175 | /** | |
| 176 | * ip6校验 | |
| 177 | * @return {[type]} [description] | |
| 178 | */ | |
| 179 | ip6: function(value){ | |
| 180 | 1 | 'use strict'; |
| 181 | 1 | return net.isIPv6(value); |
| 182 | }, | |
| 183 | /** | |
| 184 | * ip校验 | |
| 185 | * @return {[type]} [description] | |
| 186 | */ | |
| 187 | ip: function(value){ | |
| 188 | 1 | 'use strict'; |
| 189 | 1 | return net.isIP(value); |
| 190 | }, | |
| 191 | /** | |
| 192 | * 日期校验 | |
| 193 | * @return {[type]} [description] | |
| 194 | */ | |
| 195 | date: function(value){ | |
| 196 | 2 | 'use strict'; |
| 197 | 2 | var reg = /^\d{4}-\d{1,2}-\d{1,2}$/; |
| 198 | 2 | return this.regexp(value, reg); |
| 199 | }, | |
| 200 | /** | |
| 201 | * 在一个范围内 | |
| 202 | * @param {[type]} value [description] | |
| 203 | * @param {[type]} arr [description] | |
| 204 | * @return {[type]} [description] | |
| 205 | */ | |
| 206 | in: function(value, arr){ | |
| 207 | 1 | 'use strict'; |
| 208 | 1 | return arr.indexOf(value) > -1; |
| 209 | } | |
| 210 | }; | |
| 211 | /** | |
| 212 | * data格式 | |
| 213 | * [{ | |
| 214 | * value: xxx, | |
| 215 | * name: '', | |
| 216 | * valid: ['required', 'range'], | |
| 217 | * range_args: [], | |
| 218 | * msg:{ | |
| 219 | * required: '', | |
| 220 | * range: '' | |
| 221 | * } | |
| 222 | * },{ | |
| 223 | * value: xxx, | |
| 224 | * name: '', | |
| 225 | * valid: ['required', 'range'], | |
| 226 | * range_args: [], | |
| 227 | * msg:{ | |
| 228 | * required: '', | |
| 229 | * range: '' | |
| 230 | * } | |
| 231 | * }] | |
| 232 | * @param {[type]} data [description] | |
| 233 | * @return {[type]} [description] | |
| 234 | */ | |
| 235 | 1 | module.exports = function(data){ |
| 236 | 50 | 'use strict'; |
| 237 | 50 | if (!data) { |
| 238 | 1 | return true; |
| 239 | } | |
| 240 | 49 | if (!isArray(data)) { |
| 241 | 48 | data = [data]; |
| 242 | } | |
| 243 | 49 | var result = {}; |
| 244 | 49 | data.forEach(function(item){ |
| 245 | 50 | var valid = item.valid; |
| 246 | 50 | if (!isArray(valid)) { |
| 247 | 50 | valid = [valid]; |
| 248 | } | |
| 249 | 50 | valid.some(function(validItem){ |
| 250 | 50 | var flag = true; |
| 251 | //自定义检测方法 | |
| 252 | 50 | if (isFunction(validItem)) { |
| 253 | 4 | flag = validItem(item.value, item); |
| 254 | 4 | if (isString(flag)) { |
| 255 | 3 | result[item.name] = flag; |
| 256 | 3 | flag = false; |
| 257 | } | |
| 258 | 46 | }else if(!isFunction(Valid[validItem])){ |
| 259 | 1 | throw new Error(validItem + ' is not valid'); |
| 260 | }else{ | |
| 261 | 45 | var args = item[validItem + '_args'] || []; |
| 262 | 45 | if (!isArray(args)) { |
| 263 | 3 | args = [args]; |
| 264 | } | |
| 265 | 45 | args = [item.value].concat(args); |
| 266 | 45 | flag = Valid[validItem].apply(Valid, args); |
| 267 | 45 | if (flag === false) { |
| 268 | 25 | var msg = (isObject(item.msg) ? item.msg[validItem] : item.msg) || ''; |
| 269 | 25 | msg = msg.replace('{name}', item.name).replace('{value}', item.value); |
| 270 | 25 | result[item.name] = msg; |
| 271 | } | |
| 272 | } | |
| 273 | 49 | return !flag; |
| 274 | }); | |
| 275 | }); | |
| 276 | 48 | return result; |
| 277 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var thinkHttp = thinkRequire('Http'); |
| 2 | 1 | var url = require('url'); |
| 3 | 1 | var websocket = require('websocket').server; |
| 4 | 1 | var querystring = require('querystring'); |
| 5 | ||
| 6 | 1 | var WebSocket = module.exports = Class(function(){ |
| 7 | 1 | 'use strict'; |
| 8 | /** | |
| 9 | * socket初始化id | |
| 10 | * @type {Number} | |
| 11 | */ | |
| 12 | 1 | var socketId = 1000; |
| 13 | 1 | return { |
| 14 | init: function(httpServer, app){ | |
| 15 | 0 | this.httpServer = httpServer; |
| 16 | 0 | this.app = app; |
| 17 | }, | |
| 18 | /** | |
| 19 | * 检测origin是否合法 | |
| 20 | * @param {[type]} origin [description] | |
| 21 | * @return {[type]} [description] | |
| 22 | */ | |
| 23 | originIsAllowed: function(origin){ | |
| 24 | 0 | var allowOrigins = C('websocket_allow_origin'); |
| 25 | 0 | if (!allowOrigins) { |
| 26 | 0 | return true; |
| 27 | } | |
| 28 | 0 | var info = url.parse(origin); |
| 29 | 0 | var hostname = info.hostname; |
| 30 | 0 | if (isString(allowOrigins) && allowOrigins === hostname) { |
| 31 | 0 | return true; |
| 32 | 0 | }else if (isArray(allowOrigins) && allowOrigins.indexOf(hostname) > -1) { |
| 33 | 0 | return true; |
| 34 | 0 | }else if (isFunction(allowOrigins)) { |
| 35 | 0 | return allowOrigins(hostname, info); |
| 36 | } | |
| 37 | 0 | return false; |
| 38 | }, | |
| 39 | /** | |
| 40 | * 选择子协议 | |
| 41 | * @param {[type]} protocolFullCaseMap [description] | |
| 42 | * @return {[type]} [description] | |
| 43 | */ | |
| 44 | getSubProtocal: function(protocolFullCaseMap){ | |
| 45 | 0 | var selectedProtocal = C('websocket_sub_protocal'); |
| 46 | 0 | if (isFunction(selectedProtocal)) { |
| 47 | 0 | var subProtocals = Object.values(protocolFullCaseMap); |
| 48 | 0 | selectedProtocal = selectedProtocal(subProtocals); |
| 49 | } | |
| 50 | 0 | return selectedProtocal; |
| 51 | }, | |
| 52 | /** | |
| 53 | * 建立连接处理 | |
| 54 | * @param {[type]} request [description] | |
| 55 | * @return {[type]} [description] | |
| 56 | */ | |
| 57 | openHandle: function(request, protocal){ | |
| 58 | 0 | var req = request.httpRequest; |
| 59 | 0 | if (req.url === '/') { |
| 60 | 0 | return getPromise([]); |
| 61 | } | |
| 62 | 0 | var deferred = getDefer(); |
| 63 | 0 | var fn = function(){}; |
| 64 | 0 | var res = {setHeader: fn, end: fn, write: fn}; |
| 65 | 0 | var self = this; |
| 66 | 0 | thinkHttp(req, res).run(function(http){ |
| 67 | 0 | http.websocket = request.socket; |
| 68 | //子协议 | |
| 69 | 0 | http.websocket_sub_protocal = protocal; |
| 70 | 0 | self.app.listener(http).then(function(){ |
| 71 | 0 | deferred.resolve({ |
| 72 | cookie: Object.values(http._cookie), | |
| 73 | http: http | |
| 74 | }); | |
| 75 | }).catch(function(err){ | |
| 76 | 0 | deferred.reject(err); |
| 77 | }) | |
| 78 | }); | |
| 79 | 0 | return deferred.promise; |
| 80 | }, | |
| 81 | /** | |
| 82 | * 消息处理 | |
| 83 | * @return {[type]} [description] | |
| 84 | */ | |
| 85 | messageHandle: function(message, connection, app, type){ | |
| 86 | 0 | if (type !== 'utf8') { |
| 87 | 0 | connection.socket.send(WebSocket.ERROR_MESSAGE.TYPE_ERROR, message + ' is not valid json'); |
| 88 | 0 | return; |
| 89 | } | |
| 90 | //解析数据 | |
| 91 | 0 | try{ |
| 92 | 0 | message = JSON.parse(message); |
| 93 | }catch(e){ | |
| 94 | 0 | connection.socket.send(WebSocket.ERROR_MESSAGE.INVALID_JSON, message + ' is not valid json'); |
| 95 | 0 | return; |
| 96 | } | |
| 97 | 0 | if (message.jsonrpc !== '2.0') { |
| 98 | 0 | connection.socket.send(WebSocket.ERROR_MESSAGE.INVALID_JSONRPC, 'data.jsonrpc must be 2.0'); |
| 99 | 0 | return; |
| 100 | } | |
| 101 | 0 | var method = message.method + ''; |
| 102 | 0 | if (!method) { |
| 103 | 0 | connection.socket.send(WebSocket.ERROR_MESSAGE.INVALID_METHOD, 'data.method is not valid'); |
| 104 | 0 | return; |
| 105 | } | |
| 106 | 0 | var pars = message.params; |
| 107 | 0 | var headers = {}; |
| 108 | 0 | if (isObject(message.params.headers)) { |
| 109 | 0 | headers = message.params.headers; |
| 110 | 0 | pars = message.params.data; |
| 111 | } | |
| 112 | 0 | if (isObject(pars)) { |
| 113 | 0 | method += (method.indexOf('?') > -1 ? '&' : '?') + querystring.stringify(pars) |
| 114 | } | |
| 115 | 0 | var self = this; |
| 116 | 0 | var data = { |
| 117 | host: '', | |
| 118 | url: method, | |
| 119 | headers: headers, | |
| 120 | write: function(data, encoding, errMsg){ | |
| 121 | 0 | var pars = self.getRPCData(JSON.parse(data), errMsg); |
| 122 | 0 | pars.id = message.id; |
| 123 | 0 | connection.send(JSON.stringify(pars)); |
| 124 | }, | |
| 125 | end: function(data){ | |
| 126 | 0 | if (data) { |
| 127 | 0 | this.write(data); |
| 128 | } | |
| 129 | 0 | connection.close(); |
| 130 | } | |
| 131 | } | |
| 132 | 0 | var defaultHttp = thinkHttp.getDefaultHttp(data); |
| 133 | 0 | var httpInstance = thinkHttp(defaultHttp.req, defaultHttp.res); |
| 134 | //将websocket实例添加到http对象上 | |
| 135 | 0 | httpInstance.http.websocket = connection.socket; |
| 136 | 0 | httpInstance.run(app.listener); |
| 137 | }, | |
| 138 | /** | |
| 139 | * 获取rpc数据对象 | |
| 140 | * @param {[type]} data [description] | |
| 141 | * @param {[type]} errMsg [description] | |
| 142 | * @return {[type]} [description] | |
| 143 | */ | |
| 144 | getRPCData: function(data, errMsg){ | |
| 145 | 0 | var pars = {jsonrpc: '2.0'}; |
| 146 | 0 | if (errMsg) { |
| 147 | 0 | pars.error = {code: data, message: errMsg}; |
| 148 | }else{ | |
| 149 | 0 | pars.result = data; |
| 150 | } | |
| 151 | 0 | return pars; |
| 152 | }, | |
| 153 | run: function(){ | |
| 154 | 0 | var instance = new websocket({ |
| 155 | httpServer: this.httpServer, | |
| 156 | autoAcceptConnections: false | |
| 157 | }); | |
| 158 | 0 | var self = this; |
| 159 | 0 | instance.on('request', function(request){ |
| 160 | //检测origin | |
| 161 | 0 | if (!self.originIsAllowed(request.origin)) { |
| 162 | 0 | return request.reject(); |
| 163 | } | |
| 164 | 0 | var socket = request.socket; |
| 165 | 0 | socket.id = socketId++; |
| 166 | 0 | socket.activeTime = Date.now(); |
| 167 | //选择子协议 | |
| 168 | 0 | var protocal = self.getSubProtocal(request.protocolFullCaseMap); |
| 169 | 0 | return self.openHandle(request, protocal).then(function(data){ |
| 170 | 0 | var connection = socket.connection = request.accept(protocal, request.origin, data.cookie); |
| 171 | 0 | socket.close = function(){ |
| 172 | 0 | connection.close(); |
| 173 | } | |
| 174 | 0 | if (!socket.send) { |
| 175 | 0 | socket.send = function(data, errMsg){ |
| 176 | 0 | var pars = self.getRPCData(data, errMsg); |
| 177 | 0 | connection.send(JSON.stringify(pars)); |
| 178 | } | |
| 179 | } | |
| 180 | 0 | var messageHandle = C('websocket_message_handle'); |
| 181 | 0 | connection.on('message', function(message) { |
| 182 | 0 | socket.activeTime = Date.now(); |
| 183 | 0 | var data = message.type === 'utf8' ? message.utf8Data : message.binaryData; |
| 184 | 0 | if (isFunction(messageHandle)) { |
| 185 | 0 | messageHandle(data, connection, self.app, message.type); |
| 186 | }else{ | |
| 187 | 0 | self.messageHandle(data, connection, self.app, message.type); |
| 188 | } | |
| 189 | }); | |
| 190 | 0 | connection.on('close', function() { |
| 191 | 0 | data.http.emit('websocket.close'); |
| 192 | }); | |
| 193 | }).catch(function(err){ | |
| 194 | 0 | request.reject(err); |
| 195 | }) | |
| 196 | }) | |
| 197 | } | |
| 198 | } | |
| 199 | }); | |
| 200 | /** | |
| 201 | * 错误信息 | |
| 202 | * @type {Object} | |
| 203 | */ | |
| 204 | 1 | WebSocket.ERROR_MESSAGE = { |
| 205 | TYPE_ERROR: -100001, //数据类型错误 | |
| 206 | INVALID_JSON: -100002, //不是合法的json | |
| 207 | INVALID_JSONRPC: -100003, //不是jsonrpc数据格式 | |
| 208 | INVALID_METHOD: -100004 //请求方法不合法 | |
| 209 | } |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | var path = require('path'); |
| 2 | 1 | var fs = require('fs'); |
| 3 | ||
| 4 | //APP根目錄 | |
| 5 | 1 | global.APP_PATH = global.APP_PATH ? path.normalize(global.APP_PATH) : path.dirname(__dirname) + '/App'; |
| 6 | //RUNTIME目录 | |
| 7 | 1 | global.RUNTIME_PATH = global.RUNTIME_PATH ? path.normalize(global.RUNTIME_PATH) : global.APP_PATH + '/Runtime'; |
| 8 | //DEBUG模式 | |
| 9 | 1 | global.APP_DEBUG = global.APP_DEBUG || false; |
| 10 | //静态资源文件的根目录 | |
| 11 | 1 | global.RESOURCE_PATH = global.RESOURCE_PATH || ''; |
| 12 | //THINKJS的根目录 | |
| 13 | 1 | global.THINK_PATH = __dirname; |
| 14 | //默认为http模式 | |
| 15 | 1 | global.APP_MODE = global.APP_MODE || ''; |
| 16 | //命令行模式 | |
| 17 | 1 | if (process.argv[2]) { |
| 18 | 1 | APP_MODE = 'cli'; |
| 19 | } | |
| 20 | //从package.json文件里获取版本号 | |
| 21 | 1 | global.THINK_VERSION = JSON.parse(fs.readFileSync(global.THINK_PATH + '/../package.json', 'utf8')).version; |
| 22 | //启动 | |
| 23 | 1 | require(global.THINK_PATH + '/Lib/Core/Think.js').start(); |