import db from "apigo.cloud/git/apigo/plugins/db" import redis from "apigo.cloud/git/apigo/plugins/redis" import logger from "logger" import console from "console"; /*{{$dao := . -}}*/ class DBConnector { constructor(dbName, redisName) { this._db = db.fetch(dbName) this._rd = redis.fetch(redisName) this._tx = db.Tx } query(sql, ...args) { if (this._tx) return this._tx.query(sql, ...args) return this._db.query(sql, ...args) } query1(sql, ...args) { if (this._tx) return this._tx.query1(sql, ...args) return this._db.query1(sql, ...args) } query11(sql, ...args) { if (this._tx) return this._tx.query11(sql, ...args) return this._db.query11(sql, ...args) } query1a(sql, ...args) { if (this._tx) return this._tx.query1a(sql, ...args) return this._db.query1a(sql, ...args) } exec(sql, ...args) { if (this._tx) return this._tx.exec(sql, ...args) return this._db.exec(sql, ...args) } insertTo(table, data) { if (this._tx) return this._tx.insert(table, data) return this._db.insert(table, data) } replaceTo(table, data) { if (this._tx) return this._tx.replace(table, data) return this._db.replace(table, data) } updateTo(table, data, sql, ...args) { if (this._tx) return this._tx.update(table, data, sql, ...args) return this._db.update(table, data, sql, ...args) } deleteFrom(table, where, ...args) { if (this._tx) return this._tx.delete(table, where, ...args) return this._db.delete(table, where, ...args) } begin(){ if(!this._tx) this._tx = this._db.begin() } end(success){ if(this._tx) this._tx.finish(success) this._tx = null } } class BaseTable { constructor(conn:DBConnector, options) { this._conn = conn this._dbName = options.dbName this._tableName = options.tableName this._fields = options.fields this._selectFields = options.selectFields this._validSetValue = options.validSetValue this._validField = options.validField this._validWhere = options.validWhere this._validSet = options.validSet this._versionField = options.versionField this._hasVersion = options.hasVersion this._autoGenerated = options.autoGenerated this._autoGeneratedOnUpdate = options.autoGeneratedOnUpdate } _fixData(data=null) { let version = this._hasVersion ? this._getVersion() : 0 if(data) { for (let f of this._autoGenerated) if (data[f] == null) delete data[f] for (let f of this._autoGeneratedOnUpdate) if (data[f] == null) delete data[f] if (this._validField && data[this._validField] === undefined) data[this._validField] = this._validSetValue if (this._hasVersion) data[this._versionField] = version } return version } _finishData(r, version) { if (this._hasVersion){ this._commitVersion(version) r.version = version } return r } _getVersion() { let version = 0 if(this._hasVersion) { if (this._conn._rd) { version = this._conn._rd.incr("_DATA_VERSION_"+this._tableName) if (version > 1) { this._conn._rd.setEX("_DATA_VERSION_DOING_"+this._tableName+"_" + version, 10, true) return version } this._conn._rd.del("_DATA_VERSION_"+this._tableName) version = 0 } else if(!globalThis._noRedisWarned) { globalThis._noRedisWarned = true logger.warn("use version but not configured redis", {db: "{{$dao.DBName}}", table: this._tableName}) } // no redis configured or first time will use version from db let maxVersion = parseInt(this._conn.query11("SELECT MAX(`"+this._versionField+"`) FROM `"+this._tableName+"`") || '0') version = maxVersion + 1 if (this._conn._rd) { this._conn._rd.mSet("_DATA_VERSION_"+this._tableName, version, "_DATA_MAX_VERSION_"+this._tableName, version) this._conn._rd.setEX("_DATA_VERSION_DOING_"+this._tableName+"_" + version, 10, true) } } return version } _commitVersion(version) { if(this._conn._rd){ this._conn._rd.del("_DATA_VERSION_DOING_"+this._tableName+"_"+version) let seqVersion = parseInt(this._conn._rd.get("_DATA_VERSION_"+this._tableName) || '0') let currentMaxVersion = parseInt(this._conn._rd.get("_DATA_MAX_VERSION_"+this._tableName) || '0') for(let i=currentMaxVersion; i<=seqVersion; i++){ if(this._conn._rd.exists("_DATA_VERSION_DOING_"+this._tableName+"_"+i)) break this._conn._rd.set("_DATA_MAX_VERSION_"+this._tableName, i) } } } insert(data) { let version = this._fixData(data) return this._finishData(this._conn.insertTo(this._tableName, data), version) } replace(data) { let version = this._fixData(data) return this._finishData(this._conn.replaceTo(this._tableName, data), version) } updateBy(data, where, ...args) { let version = this._fixData(data) return this._finishData(this._conn.updateTo(this._tableName, data, where, ...args), version) } } class BaseItem { constructor(table: BaseTable, data) { this._table = table this._data = data || {} this._changed = false } _set(key, value){ if(this._data[key] === value) return this._changed = true this._data[key] = value } _get(key, value){ return this._data[key] } getData(){ return this._data } getJson(){ return JSON.stringify(this._data) } } class BaseList extends Array { constructor(table: BaseTable, list:Array) { super() this._table = table this._list = list || [] } getData(){ return this._list } getJson(){ return JSON.stringify(this._list) } getDataBy(...fields){ let list = [] for(let item of this._list){ let o = {} for(let k in item) if(fields.indexOf(k)!==-1) o[k] = item[k] list.push(o) } return list } } class BaseQuery { constructor(table: BaseTable) { this._table = table this._validWhere = table._validWhere this._sql = "" this._fields = table._selectFields this._where = "" this._extraSql = "" this._extraArgs = [] this._args = [] this._leftJoins = [] this._leftJoinArgs = [] this._result = null this._lastSql = "" this._lastSqlArgs = [] } _parseFields(fields, table) { let fieldArr = fields if(typeof fields !== 'object' || !(fields instanceof Array)) { if(typeof fields !== 'string') fields = fields.toString() if (fields==="" || fields.indexOf('(') !== -1 || fields.indexOf('`') !== -1) return fields fieldArr = fields.split(",") } for(let i=0; i 0){ leftJoinsStr = " " + this._leftJoins.join(" ") this._args.unshift(...this._leftJoinArgs) validWhere = validWhere.replace(/ AND /g, " AND `"+this._table._tableName+"`.") } if(this._where==="" && validWhere.startsWith(" AND ")) validWhere = validWhere.substring(5) if(this._extraArgs) this._args.push(...this._extraArgs) return {sql:"SELECT " + fields + " FROM `"+this._table._tableName+"`" + leftJoinsStr + " WHERE " + this._where + validWhere + this._extraSql, args:this._args} } sql(sql, ...args){ this._sql = sql this._args = args return this } fields(fields){ this._fields = this._parseFields(fields, "") return this } appendFields(fields){ if(this._fields) this._fields += ", " this._fields += this._parseFields(fields, "") return this } where(where, ...args){ this._where = where this._args = args return this } in(field, ...values){ if(field.indexOf("`")===-1) field = "`"+field+"`" this._where = field+" IN "+db.inKeys(values.length) this._args = values return this } and(where, ...args){ if(this._where) this._where += " AND " this._where += where this._args = this._args.push(...args) return this } or(where, ...args){ if(this._where) this._where += " OR " this._where += where this._args = this._args.push(...args) return this } andIn(field, ...values){ if(field.indexOf("`")===-1) field = "`"+field+"`" if(this._where) this._where += " AND " this._where += field + " IN "+db.inKeys(values.length) this._args = this._args.push(...values) return this } orIn(field, ...values){ if(field.indexOf("`")===-1) field = "`"+field+"`" if(this._where) this._where += " OR " this._where += field + " IN "+db.inKeys(values.length) this._args = this._args.push(...values) return this } orderBy(orderBy){ this._extraSql += " ORDER BY " + orderBy return this } groupBy(groupBy){ this._extraSql += " GROUP BY " + groupBy return this } limit(start, num){ if(num===undefined){ num = start start = 0 } this._extraSql += " LIMIT ?,?" this._extraArgs.push(start, num) return this } having(where, ...args){ this._extraSql += " HAVING "+where this._extraArgs = this._extraArgs.push(...args) return this } extra(sql, ...args){ this._extraSql += sql this._extraArgs = this._extraArgs.push(...args) return this } leftJoin(joinTable, fields, on, ...args){ if(this._fields.indexOf("`"+this._table._tableName+"`.")===-1){ this._fields = "`"+this._table._tableName+"`."+this._fields.replace(/`, `/g, "`, `"+this._table._tableName+"`.`") } if(fields) this._fields += ", "+this._parseFields(fields, joinTable) this._leftJoins = this._leftJoins.push("LEFT JOIN `" + joinTable + "` ON " + on) this._leftJoinArgs = this._leftJoinArgs.push(...args) return this } query(){ let {sql,args} = this._parse("") this._lastSql = sql this._lastSqlArgs = args this._result = this._table._conn.query(sql, ...args).result return this } count(){ let {sql,args} = this._parse("COUNT") this._lastSql = sql this._lastSqlArgs = args return parseInt(this._conn.query11(sql, ...args) || '0') } queryByPage(start, num){ this.limit(start, num) return this.query() } first(){ let list = this.list() if(list.length > 0) return list[0] return null } list(){ if(this._result===null) this.query() return this._result } getSql(){ return this._lastSql } getSqlArgs(){ return this._lastSqlArgs } } /*{{- range $t := .Tables }}*/ class /*{{$t.TableName}}*/Table extends BaseTable { constructor(conn:DBConnector) { //**//let hasVersion = {{$t.HasVersion}} super(conn, {dbName: "{{$dao.DBName}}", tableName: "{{$t.TableName}}", fields: [ /*{{- range $f := .Fields -}}*/ //**//{name:'{{$f.Name}}',default:{{$f.Default}}}, /*{{- end -}}*/ ], selectFields: "{{$t.SelectFields}}", validSetValue: "{{$t.ValidFieldConfig.ValidSetValue}}", validField: "{{$t.ValidField}}", validWhere: "{{$t.ValidWhere}}", validSet: "{{$t.ValidSet}}", versionField: "{{$t.VersionField}}", hasVersion: hasVersion, autoGenerated: [/*{{- range $v := .AutoGenerated -}}*/'{{$v}}', /*{{- end -}}*/], autoGeneratedOnUpdate: [/*{{- range $v := .AutoGeneratedOnUpdate -}}*/'{{$v}}', /*{{- end -}}*/]}) } new(data = {}) { return data === null ? null : new /*{{$t.TableName}}*/Item(this, data) } newQuery() {return new /*{{$t.TableName}}*/Query(this)} /*{{range $k := .UniqueKeys -}}*/ getBy/*{{$k.Name}}*/(/*{{$k.Args}}*/) { return this.new( this._conn.query1("SELECT {{$t.SelectFields}} FROM `/*{{$t.TableName}}*/` WHERE {{$k.Where}}{{$t.ValidWhere}}", /*{{$k.Args}}*/) ) } /*{{- end }}*/ /*{{ if $t.PrimaryKey -}}*/ get(/*{{$t.PrimaryKey.Args}}*/) {return this.new( this._conn.query1("SELECT /*{{$t.SelectFields}}*/ FROM `/*{{$t.TableName}}*/` WHERE /*{{$t.PrimaryKey.Where}}*//*{{$t.ValidWhere}}", /*{{$t.PrimaryKey.Args}}*/))} getWithFields(fields,/*{{$t.PrimaryKey.Args}}*/) {return this.new(this._conn.query1("SELECT "+fields+" FROM `/*{{$t.TableName}}*/` WHERE /*{{$t.PrimaryKey.Where}}*//*{{$t.ValidWhere}}", /*{{$t.PrimaryKey.Args}}*/))} /*{{ if .ValidSet -}}*/ getWithInvalid(/*{{$t.PrimaryKey.Args}}*/) {return this.new(this._conn.query1("SELECT /*{{$t.SelectFields}}*/ FROM `/*{{$t.TableName}}*/` WHERE /*{{$t.PrimaryKey.Where}}", /*{{$t.PrimaryKey.Args}}*/))} /*{{- end }}*/ update(data, /*{{$t.PrimaryKey.Args}}*/) { let version = this._fixData(data) let r = this.updateTo(this._tableName, data, "{{$t.PrimaryKey.Where}}", /*{{$t.PrimaryKey.Args}}*/) return this._finishData(r, version) } /*{{- end }}*/ /*{{- if .InvalidSet }}*/ enable(/*{{$t.PrimaryKey.Args}}*/) { /*{{- if .HasVersion }}*/ let version = this._getVersion() let r = this._conn.exec("UPDATE `/*{{$t.TableName}}*/` set /*{{$t.ValidSet}}*/, `/*{{$t.VersionField}}*/`=? WHERE /*{{$t.PrimaryKey.Where}}", version, /*{{$t.PrimaryKey.Args}}*/) this._commitVersion(version) r.version = version return r /*{{- else }}*/ return this._conn.exec("UPDATE `/*{{$t.TableName}}*/` set /*{{$t.ValidSet}}*/ WHERE /*{{$t.PrimaryKey.Where}}", /*{{$t.PrimaryKey.Args}}*/) /*{{- end }}*/ } disable(/*{{$t.PrimaryKey.Args}}*/){ /*{{- if .HasVersion }}*/ let version = this._getVersion() let r = this._conn.exec("UPDATE `/*{{$t.TableName}}*/` set /*{{$t.InvalidSet}}*/, `/*{{$t.VersionField}}*/`=? WHERE /*{{$t.PrimaryKey.Where}}", version, /*{{$t.PrimaryKey.Args}}*/) this._commitVersion(version) r.version = version return r /*{{- else }}*/ return this._conn.exec("UPDATE `/*{{$t.TableName}}*/` set /*{{$t.InvalidSet}}*/ WHERE /*{{$t.PrimaryKey.Where}}", /*{{$t.PrimaryKey.Args}}*/) /*{{- end }}*/ } /*{{- else }}*/ delete(/*{{$t.PrimaryKey.Args}}*/) {return this._conn.exec("DELETE FROM `/*{{$t.TableName}}*/` WHERE /*{{$t.PrimaryKey.Where}}", /*{{$t.PrimaryKey.Args}}*/)} deleteBy(where, ...args) {return this.deleteFrom(this._tableName, where, ...args)} /*{{- end }}*/ /*{{- range $extCode := $t.ExtTableCode }}*/ /*{{$extCode}}*/ /*{{- end }}*/ } class /*{{$t.TableName}}*/Item extends BaseItem { constructor(table: /*{{$t.TableName}}*/Table, data) { if(!data) data = {} super(table, data) this._table = table } /*{{- range $f := .Fields }}*/ set /*{{$f.Name}}*/(value){this._set('{{$f.Name}}', value)} get /*{{$f.Name}}*/(){return this._get('{{$f.Name}}')} /*{{- end }}*/ /*{{- if .PrimaryKey }}*/ save(){ if(!this._changed) return null let r = this._table.replace(this.getData()) this._changed = false return r } /*{{- if .InvalidSet }}*/ enable(){return this._table.enable(/*{{.PrimaryKey.ItemArgs}}*/)} disable(){return this._table.disable(/*{{.PrimaryKey.ItemArgs}}*/)} /*{{- else }}*/ delete(){return this._table.delete(/*{{.PrimaryKey.ItemArgs}}*/)} /*{{- end }}*/ /*{{- end }}*/ /*{{- range $extCode := $t.ExtItemCode }}*/ /*{{$extCode}}*/ /*{{- end }}*/ } class /*{{$t.TableName}}*/List extends BaseList { constructor(table:/*{{$t.TableName}}*/Table, list) { if(!list) list = [] super(table, list) this._table = table for(let item of list) if(item) this.push(new /*{{$t.TableName}}*/Item(this._table, item)) } /*{{- if .PrimaryKey }}*/ save(){ let result = {changes:0, version:0, sql:[], sqlArgs:[]} for(let item of this){ let r = item.save() if(r){ result.changes += r.changes result.version = r.version result.sql.push(r.sql) result.sqlArgs.push(r.sqlArgs) } } return result } /*{{- end }}*/ } class /*{{$t.TableName}}*/Query extends BaseQuery { constructor(table:/*{{$t.TableName}}*/Table) { super(table) this._table = table } first(): /*{{$t.TableName}}*/Item { let item = super.first() return !item ? null : new /*{{$t.TableName}}*/Item(this._table, item) } list(): /*{{$t.TableName}}*/List { let list = super.list() return new /*{{$t.TableName}}*/List(this._table, list) } /*{{range $idx := .IndexKeys}}*/ by/*{{$idx.Name}}*/(/*{{$idx.Args}}*/){return this.where("{{$idx.Where}}", /*{{$idx.Args}}*/)} and/*{{$idx.Name}}*/(/*{{$idx.Args}}*/){ if(this._where) this._where += " AND " this._where += "{{$idx.Where}}" this._args = this._args.push(/*{{$idx.Args}}*/) return this } or/*{{$idx.Name}}*/(/*{{$idx.Args}}*/){ if(this._where) this._where += " OR " this._where += "{{$idx.Where}}" this._args = this._args.push(/*{{$idx.Args}}*/) return this } /*{{- end }}*/ /*{{- if .ValidSet }}*/ queryWithValid(){ let {sql,args} = this._parse("ALL") this._lastSql = sql this._lastSqlArgs = args this._result = this._table._conn.query(sql, ...args).result return this } /*{{- end }}*/ /*{{- if .ValidSet }}*/ countAll(){ let {sql,args} = this._parse("COUNT_ALL") this._lastSql = sql this._lastSqlArgs = args return parseInt(this._table._conn.query11(sql, ...args) || '0') } /*{{- end }}*/ /*{{- if .ValidSet }}*/ queryWithValidByPage(start, num){ this.limit(start, num) return this.queryWithValid() } /*{{- end }}*/ /*{{- if .HasVersion }}*/ /*{{- if .ValidSet }}*/ queryByVersion(minVersion, maxVersion, limit, withInvalid){ if(minVersion > 0) withInvalid = true let parseTag = withInvalid ? "ALL_VERSION" : "VERSION" /*{{- else }}*/ //**//queryByVersion(minVersion, maxVersion, limit){ //**//let parseTag = "VERSION" /*{{- end }}*/ if(maxVersion===0){ if(this._table._conn._rd){ maxVersion = parseInt(this._table._conn._rd.GET("_DATA_MAX_VERSION_"+this._table._tableName) || '0') } else if(!globalThis._noRedisWarned) { globalThis._noRedisWarned = true logger.warn("use version but not configured redis", {db:"{{.DBName}}", table:"{{.TableName}}"}) } if(maxVersion===0){ maxVersion = parseInt(this._table._conn.query11("SELECT MAX(`/*{{.VersionField}}*/`) FROM `/*{{.TableName}}*/`") || '0') this._lastSql = "SELECT MAX(`/*{{.VersionField}}*/`) FROM `/*{{.TableName}}*/`" this._lastSqlArgs = [] } } this.and("`version` BETWEEN ? AND ?", minVersion+1, maxVersion ) if(limit > 0){ this.orderBy("`/*{{.VersionField}}*/`") this.limit(0, limit) } let {sql,args} = this._parse(parseTag) this._lastSql = sql this._lastSqlArgs = args this._result = this._table._conn.query(sql, ...args).result this.maxVersion = maxVersion return this } /*{{- end }}*/ } /*{{- end }}*/ class /*{{$dao.DBName}}*/Dao { _tables = {} constructor(dbName, redisName) {this._conn = new DBConnector(dbName, redisName)} _getTable(tableName, tableType) { let tb = this._tables[tableName] if(!tb){ tb = new tableType(this._conn) this._tables[tableName] = tb } return tb } begin() {this._conn.begin()} end(success) {this._conn.end(success)} /*{{- range $t := .Tables }}*/ get /*{{$t.TableName}}*/(): /*{{$t.TableName}}*/Table {return this._getTable('{{$t.TableName}}', /*{{$t.TableName}}*/Table)} /*{{- end }}*/ } export { /*{{- $dao.DBName}}*/Dao /*{{- range $t := .Tables -}}*/, /*{{$t.TableName}}*/Table, /*{{$t.TableName}}*/Query, /*{{$t.TableName}}*/Item, /*{{$t.TableName}}*/List /*{{- end -}}*/ }