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 }