github.com/erda-project/erda-infra@v1.0.9/providers/mysql/v2/plugins/fields/soft_delete.go (about)

     1  // Copyright (c) 2021 Terminus, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fields
    16  
    17  import (
    18  	"database/sql"
    19  	"database/sql/driver"
    20  	"encoding/json"
    21  	"reflect"
    22  	"strings"
    23  	"time"
    24  
    25  	"gorm.io/gorm"
    26  	"gorm.io/gorm/clause"
    27  	"gorm.io/gorm/schema"
    28  )
    29  
    30  var zero = time.Unix(0, 0)
    31  
    32  // DeletedAt on delete: the field value will be set to the current datetime.
    33  // on query: the record will be not returned if it is soft-deleted.
    34  // if you want to find the record which is soft-deleted, or you want
    35  // to delete the record forever, you can use db.Unscoped().Find(&users),
    36  // and db.Unscoped().Delete(&order).
    37  type DeletedAt sql.NullTime
    38  
    39  // Scan implements the Scanner interface.
    40  func (n *DeletedAt) Scan(value interface{}) error {
    41  	return (*sql.NullTime)(n).Scan(value)
    42  }
    43  
    44  // Value implements the driver Valuer interface.
    45  func (n DeletedAt) Value() (driver.Value, error) {
    46  	if !n.Valid {
    47  		return nil, nil
    48  	}
    49  	return n.Time, nil
    50  }
    51  
    52  // MustValue returns zero timestamp if the value is null;
    53  // returns time value if the value is not null.
    54  // if the value is null returns
    55  func (n DeletedAt) MustValue() time.Time {
    56  	if n.Valid {
    57  		return n.Time
    58  	}
    59  	return zero
    60  }
    61  
    62  // MarshalJSON .
    63  func (n DeletedAt) MarshalJSON() ([]byte, error) {
    64  	if n.Valid {
    65  		return json.Marshal(n.Time)
    66  	}
    67  	return json.Marshal(nil)
    68  }
    69  
    70  // UnmarshalJSON .
    71  func (n *DeletedAt) UnmarshalJSON(b []byte) error {
    72  	bs := string(b)
    73  	if strings.EqualFold(bs, "null") {
    74  		n.Valid = false
    75  		return nil
    76  	}
    77  	err := json.Unmarshal(b, &n.Time)
    78  	n.Valid = err == nil
    79  	return err
    80  }
    81  
    82  // QueryClauses .
    83  func (DeletedAt) QueryClauses(f *schema.Field) []clause.Interface {
    84  	return []clause.Interface{SoftDeleteQueryClause{Field: f}}
    85  }
    86  
    87  // SoftDeleteQueryClause .
    88  type SoftDeleteQueryClause struct {
    89  	Field *schema.Field
    90  }
    91  
    92  // Name .
    93  func (sd SoftDeleteQueryClause) Name() string {
    94  	return ""
    95  }
    96  
    97  // Build .
    98  func (sd SoftDeleteQueryClause) Build(clause.Builder) {
    99  }
   100  
   101  // MergeClause .
   102  func (sd SoftDeleteQueryClause) MergeClause(*clause.Clause) {
   103  }
   104  
   105  // ModifyStatement .
   106  func (sd SoftDeleteQueryClause) ModifyStatement(stmt *gorm.Statement) {
   107  	if _, ok := stmt.Clauses["soft_delete_enabled"]; !ok && !stmt.Statement.Unscoped {
   108  		if c, ok := stmt.Clauses["WHERE"]; ok {
   109  			if where, ok := c.Expression.(clause.Where); ok && len(where.Exprs) >= 1 {
   110  				for _, expr := range where.Exprs {
   111  					if orCond, ok := expr.(clause.OrConditions); ok && len(orCond.Exprs) == 1 {
   112  						where.Exprs = []clause.Expression{clause.And(where.Exprs...)}
   113  						c.Expression = where
   114  						stmt.Clauses["WHERE"] = c
   115  						break
   116  					}
   117  				}
   118  			}
   119  		}
   120  
   121  		stmt.AddClause(clause.Where{Exprs: []clause.Expression{
   122  			clause.Or(
   123  				clause.Lte{Column: clause.Column{Table: clause.CurrentTable, Name: sd.Field.DBName}, Value: zero},
   124  				clause.Eq{Column: clause.Column{Table: clause.CurrentTable, Name: sd.Field.DBName}, Value: nil},
   125  			),
   126  		}})
   127  		stmt.Clauses["soft_delete_enabled"] = clause.Clause{}
   128  	}
   129  }
   130  
   131  // UpdateClauses .
   132  func (DeletedAt) UpdateClauses(f *schema.Field) []clause.Interface {
   133  	return []clause.Interface{SoftDeleteUpdateClause{Field: f}}
   134  }
   135  
   136  // SoftDeleteUpdateClause .
   137  type SoftDeleteUpdateClause struct {
   138  	Field *schema.Field
   139  }
   140  
   141  // Name .
   142  func (sd SoftDeleteUpdateClause) Name() string {
   143  	return ""
   144  }
   145  
   146  // Build .
   147  func (sd SoftDeleteUpdateClause) Build(clause.Builder) {
   148  }
   149  
   150  // MergeClause .
   151  func (sd SoftDeleteUpdateClause) MergeClause(*clause.Clause) {
   152  }
   153  
   154  // ModifyStatement .
   155  func (sd SoftDeleteUpdateClause) ModifyStatement(stmt *gorm.Statement) {
   156  	if stmt.SQL.Len() == 0 && !stmt.Statement.Unscoped {
   157  		SoftDeleteQueryClause(sd).ModifyStatement(stmt)
   158  	}
   159  }
   160  
   161  // DeleteClauses .
   162  func (DeletedAt) DeleteClauses(f *schema.Field) []clause.Interface {
   163  	return []clause.Interface{SoftDeleteDeleteClause{Field: f}}
   164  }
   165  
   166  // SoftDeleteDeleteClause .
   167  type SoftDeleteDeleteClause struct {
   168  	Field *schema.Field
   169  }
   170  
   171  // Name .
   172  func (sd SoftDeleteDeleteClause) Name() string {
   173  	return ""
   174  }
   175  
   176  // Build .
   177  func (sd SoftDeleteDeleteClause) Build(clause.Builder) {
   178  }
   179  
   180  // MergeClause .
   181  func (sd SoftDeleteDeleteClause) MergeClause(*clause.Clause) {
   182  }
   183  
   184  // ModifyStatement .
   185  func (sd SoftDeleteDeleteClause) ModifyStatement(stmt *gorm.Statement) {
   186  	if stmt.SQL.Len() == 0 && !stmt.Statement.Unscoped {
   187  		curTime := stmt.DB.NowFunc()
   188  		stmt.AddClause(clause.Set{{Column: clause.Column{Name: sd.Field.DBName}, Value: curTime}})
   189  		stmt.SetColumn(sd.Field.DBName, curTime, true)
   190  
   191  		if stmt.Schema != nil {
   192  			_, queryValues := schema.GetIdentityFieldValuesMap(stmt.Context, stmt.ReflectValue, stmt.Schema.PrimaryFields)
   193  			column, values := schema.ToQueryValues(stmt.Table, stmt.Schema.PrimaryFieldDBNames, queryValues)
   194  
   195  			if len(values) > 0 {
   196  				stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})
   197  			}
   198  
   199  			if stmt.ReflectValue.CanAddr() && stmt.Dest != stmt.Model && stmt.Model != nil {
   200  				_, queryValues = schema.GetIdentityFieldValuesMap(stmt.Context, reflect.ValueOf(stmt.Model), stmt.Schema.PrimaryFields)
   201  				column, values = schema.ToQueryValues(stmt.Table, stmt.Schema.PrimaryFieldDBNames, queryValues)
   202  
   203  				if len(values) > 0 {
   204  					stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})
   205  				}
   206  			}
   207  		}
   208  
   209  		SoftDeleteQueryClause(sd).ModifyStatement(stmt)
   210  		stmt.AddClauseIfNotExists(clause.Update{})
   211  		stmt.Build(stmt.DB.Callback().Update().Clauses...)
   212  	}
   213  }
   214  
   215  // CreateClauses .
   216  func (DeletedAt) CreateClauses(f *schema.Field) []clause.Interface {
   217  	return []clause.Interface{SoftDeleteCreateClause{Field: f}}
   218  }
   219  
   220  // SoftDeleteCreateClause .
   221  type SoftDeleteCreateClause struct {
   222  	Field *schema.Field
   223  }
   224  
   225  // Name .
   226  func (sd SoftDeleteCreateClause) Name() string {
   227  	return ""
   228  }
   229  
   230  // Build .
   231  func (sd SoftDeleteCreateClause) Build(clause.Builder) {
   232  }
   233  
   234  // MergeClause .
   235  func (sd SoftDeleteCreateClause) MergeClause(*clause.Clause) {
   236  }
   237  
   238  // ModifyStatement .
   239  func (sd SoftDeleteCreateClause) ModifyStatement(stmt *gorm.Statement) {
   240  	if stmt.SQL.Len() == 0 && !stmt.Statement.Unscoped {
   241  		stmt.SetColumn(sd.Field.Name, DeletedAt{Time: zero, Valid: true})
   242  	}
   243  }