You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
6.4 KiB
316 lines
6.4 KiB
package fly
|
|
|
|
import (
|
|
"database/sql"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/daodao97/fly/interval/util"
|
|
)
|
|
|
|
type Model interface {
|
|
PrimaryKey() string
|
|
Select(opt ...Option) (rows *Rows)
|
|
SelectOne(opt ...Option) *Row
|
|
Count(opt ...Option) (count int64, err error)
|
|
Insert(record interface{}) (lastId int64, err error)
|
|
Update(record interface{}, opt ...Option) (ok bool, err error)
|
|
Delete(opt ...Option) (ok bool, err error)
|
|
Exec(query string, args ...interface{}) (sql.Result, error)
|
|
Query(query string, args ...interface{}) (*sql.Rows, error)
|
|
}
|
|
|
|
type model struct {
|
|
connection string
|
|
database string
|
|
table string
|
|
fakeDelKey string
|
|
primaryKey string
|
|
columnHook map[string]HookData
|
|
columnValidator []Valid
|
|
hasOne []HasOpts
|
|
hasMany []HasOpts
|
|
options *Options
|
|
client *sql.DB
|
|
saveZero bool
|
|
enableValidator bool
|
|
err error
|
|
}
|
|
|
|
func New(table string, baseOpt ...With) *model {
|
|
m := &model{
|
|
connection: "default",
|
|
primaryKey: "id",
|
|
table: table,
|
|
}
|
|
|
|
if table == "" {
|
|
m.err = errors.New("table name is empty")
|
|
return m
|
|
}
|
|
|
|
for _, v := range baseOpt {
|
|
v(m)
|
|
}
|
|
|
|
if m.client == nil {
|
|
m.client, m.err = db(m.connection)
|
|
}
|
|
m.enableValidator = true
|
|
return m
|
|
}
|
|
|
|
func (m *model) PrimaryKey() string {
|
|
return m.primaryKey
|
|
}
|
|
|
|
func (m *model) Select(opt ...Option) (rows *Rows) {
|
|
var kv []interface{}
|
|
var err error
|
|
defer dbLog("Select", time.Now(), &err, &kv)
|
|
|
|
if m.err != nil {
|
|
err = m.err
|
|
return &Rows{Err: m.err}
|
|
}
|
|
opts := new(Options)
|
|
opt = append(opt, table(m.table), database(m.database))
|
|
if m.fakeDelKey != "" {
|
|
opt = append(opt, WhereEq(m.fakeDelKey, 0))
|
|
}
|
|
for _, o := range opt {
|
|
o(opts)
|
|
}
|
|
|
|
_sql, args := SelectBuilder(opt...)
|
|
res, err := query(m.client, _sql, args...)
|
|
kv = append(kv, "sql:", _sql, "args:", args)
|
|
if err != nil {
|
|
return &Rows{Err: err}
|
|
}
|
|
|
|
for _, has := range m.hasOne {
|
|
res, err = m.hasOneData(res, has)
|
|
if err != nil {
|
|
return &Rows{Err: err}
|
|
}
|
|
}
|
|
|
|
for _, has := range m.hasMany {
|
|
res, err = m.hasManyData(res, has)
|
|
if err != nil {
|
|
return &Rows{Err: err}
|
|
}
|
|
}
|
|
|
|
for k, v := range m.columnHook {
|
|
for i, r := range res {
|
|
for field, val := range r.Data {
|
|
if k == field {
|
|
overVal, err1 := v.Output(res[i].Data, val)
|
|
if err1 != nil {
|
|
err = err1
|
|
return &Rows{Err: err}
|
|
}
|
|
res[i].Data[field] = overVal
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return &Rows{List: res, Err: err}
|
|
}
|
|
|
|
func (m *model) SelectOne(opt ...Option) *Row {
|
|
opt = append(opt, Limit(1))
|
|
rows := m.Select(opt...)
|
|
if rows.Err != nil {
|
|
return &Row{Err: rows.Err}
|
|
}
|
|
if len(rows.List) == 0 {
|
|
return &Row{}
|
|
}
|
|
return &rows.List[0]
|
|
}
|
|
|
|
func (m *model) Count(opt ...Option) (count int64, err error) {
|
|
opt = append(opt, table(m.table), AggregateCount("*"))
|
|
var result struct {
|
|
Count int64
|
|
}
|
|
err = m.SelectOne(opt...).Binding(&result)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return result.Count, nil
|
|
}
|
|
|
|
func (m *model) Insert(record interface{}) (lastId int64, err error) {
|
|
if m.err != nil {
|
|
return 0, m.err
|
|
}
|
|
|
|
var kv []interface{}
|
|
defer dbLog("Insert", time.Now(), &err, &kv)
|
|
|
|
_record, err := util.DecodeToMap(record, m.saveZero)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
_record, err = m.hookInput(_record)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if m.enableValidator {
|
|
for _, v := range m.columnValidator {
|
|
err = v(NewValidOpt(withRow(_record), WithModel(m)))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
}
|
|
|
|
delete(_record, m.primaryKey)
|
|
if len(_record) == 0 {
|
|
return 0, errors.New("empty record to insert")
|
|
}
|
|
|
|
ks, vs := m.recordToKV(_record)
|
|
_sql, args := InsertBuilder(table(m.table), Field(ks...), Value(vs...))
|
|
kv = append(kv, "sql:", _sql, "args:", vs)
|
|
|
|
result, err := exec(m.client, _sql, args...)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return result.LastInsertId()
|
|
}
|
|
|
|
func (m *model) Update(record interface{}, opt ...Option) (ok bool, err error) {
|
|
if m.err != nil {
|
|
return false, m.err
|
|
}
|
|
|
|
var kv []interface{}
|
|
defer dbLog("Update", time.Now(), &err, &kv)
|
|
|
|
_record, err := util.DecodeToMap(record, m.saveZero)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if id, ok := _record[m.primaryKey]; ok {
|
|
kv = append(kv, "id:", id)
|
|
opt = append(opt, WhereEq(m.primaryKey, id))
|
|
}
|
|
|
|
_record, err = m.hookInput(_record)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
delete(_record, m.primaryKey)
|
|
if len(_record) == 0 {
|
|
return false, errors.New("empty record to update")
|
|
}
|
|
|
|
if m.enableValidator {
|
|
for _, v := range m.columnValidator {
|
|
err = v(NewValidOpt(withRow(_record), WithModel(m)))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
}
|
|
|
|
ks, vs := m.recordToKV(_record)
|
|
opt = append(opt, table(m.table), Field(ks...), Value(vs...))
|
|
|
|
_sql, args := UpdateBuilder(opt...)
|
|
kv = append(kv, "sql:", _sql, "args:", vs)
|
|
|
|
result, err := exec(m.client, _sql, args...)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
effect, err := result.RowsAffected()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return effect >= int64(0), nil
|
|
}
|
|
|
|
func (m *model) Delete(opt ...Option) (ok bool, err error) {
|
|
if len(opt) == 0 {
|
|
return false, errors.New("danger, delete query must with some condition")
|
|
}
|
|
|
|
if m.err != nil {
|
|
return false, m.err
|
|
}
|
|
|
|
opt = append(opt, table(m.table))
|
|
if m.fakeDelKey != "" {
|
|
m.enableValidator = false
|
|
defer func() {
|
|
m.enableValidator = true
|
|
}()
|
|
return m.Update(map[string]interface{}{m.fakeDelKey: 1}, opt...)
|
|
}
|
|
|
|
var kv []interface{}
|
|
defer dbLog("Delete", time.Now(), &err, &kv)
|
|
|
|
_sql, args := DeleteBuilder(opt...)
|
|
kv = append(kv, "slq:", _sql, "args:", args)
|
|
|
|
result, err := exec(m.client, _sql, args...)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
effect, err := result.RowsAffected()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return effect > int64(0), nil
|
|
}
|
|
|
|
func (m *model) Exec(query string, args ...interface{}) (sql.Result, error) {
|
|
return m.client.Exec(query, args...)
|
|
}
|
|
|
|
func (m *model) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
|
return m.client.Query(query, args...)
|
|
}
|
|
|
|
func (m *model) hookInput(record map[string]interface{}) (map[string]interface{}, error) {
|
|
for k, v := range m.columnHook {
|
|
for field, val := range record {
|
|
if k == field {
|
|
overVal, err := v.Input(record, val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
record[field] = overVal
|
|
}
|
|
}
|
|
}
|
|
return record, nil
|
|
}
|
|
|
|
func (m *model) recordToKV(record map[string]interface{}) (ks []string, vs []interface{}) {
|
|
for k, v := range record {
|
|
ks = append(ks, k)
|
|
vs = append(vs, v)
|
|
}
|
|
|
|
return ks, vs
|
|
}
|