feat: SimApiBaseModel 实体基类(对齐 C# Models/SimApiBaseModel)
- _id 默认 UUID v4、_createdAt/_updatedAt 自动当前时间 - mapData 反射式字段映射(忽略字段/白名单两种重载) - updateTime 刷新更新时间
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simapi.models
|
||||
|
||||
import std.collection.*
|
||||
import std.reflect.*
|
||||
import std.time.*
|
||||
import simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 实体基类(对齐 C# Models/SimApiBaseModel)。
|
||||
* 提供:
|
||||
* - Id(默认 Guid)、CreatedAt / UpdatedAt(默认当前时间)
|
||||
* - MapData:反射式字段映射(源 → 目标,同名 + 同类型;可忽略字段/白名单)
|
||||
* - UpdateTime:更新 UpdatedAt
|
||||
*/
|
||||
public open class SimApiBaseModel {
|
||||
public var _id: String = SimApiUtil.newGuid()
|
||||
public var _updatedAt: DateTime = DateTime.now()
|
||||
public var _createdAt: DateTime = DateTime.now()
|
||||
|
||||
/// MapData 默认忽略的字段(Id / CreatedAt / UpdatedAt)
|
||||
protected var _mapperIgnoreField: Array<String> = ["_id", "_createdAt", "_updatedAt"]
|
||||
|
||||
/// UpdateTime 更新的字段名
|
||||
protected var _updatedTimeField: String = "_updatedAt"
|
||||
|
||||
public init() {}
|
||||
|
||||
/**
|
||||
* 反射映射:把 source 的同名同类型非忽略字段赋值到 this(对齐 C# MapData(source, mapAll))。
|
||||
* @param source 源对象。
|
||||
* @param mapAll 为 true 时连忽略字段(Id/CreatedAt/UpdatedAt)也映射。
|
||||
*/
|
||||
public func mapData(source: Any, mapAll!: Bool = false): Unit {
|
||||
let sourceProps = collectProps(TypeInfo.of(source))
|
||||
let targetProps = collectProps(TypeInfo.of(this))
|
||||
for (sp in sourceProps) {
|
||||
if (mapAll || !_mapperIgnoreField.contains(sp.name)) {
|
||||
copyProp(targetProps, sp, source, this)
|
||||
}
|
||||
}
|
||||
updateTime()
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射映射:仅映射白名单字段(对齐 C# MapData(source, mapFields))。
|
||||
* @param source 源对象。
|
||||
* @param mapFields 白名单字段名。
|
||||
*/
|
||||
public func mapData(source: Any, mapFields: Array<String>): Unit {
|
||||
let sourceProps = collectProps(TypeInfo.of(source))
|
||||
let targetProps = collectProps(TypeInfo.of(this))
|
||||
for (sp in sourceProps) {
|
||||
if (mapFields.contains(sp.name)) {
|
||||
copyProp(targetProps, sp, source, this)
|
||||
}
|
||||
}
|
||||
updateTime()
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 UpdatedAt 为当前时间(对齐 C# UpdateTime)。
|
||||
*/
|
||||
public func updateTime(): Unit {
|
||||
let targetProps = collectProps(TypeInfo.of(this))
|
||||
for (tp in targetProps) {
|
||||
if (tp.name == _updatedTimeField) {
|
||||
tp.setValue(this, DateTime.now())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 内部实现 =====
|
||||
|
||||
/// 把源属性 sp 的值复制到目标对象(要求目标存在同名同类型属性)
|
||||
private static func copyProp(targetProps: ArrayList<InstancePropertyInfo>, sp: InstancePropertyInfo,
|
||||
source: Any, target: Any): Unit {
|
||||
for (tp in targetProps) {
|
||||
if (tp.name == sp.name && tp.typeInfo == sp.typeInfo) {
|
||||
tp.setValue(target, sp.getValue(source))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 收集类型(含继承链)的 public 实例属性,子类同名覆盖父类
|
||||
private static func collectProps(typeInfo: TypeInfo): ArrayList<InstancePropertyInfo> {
|
||||
let result = ArrayList<InstancePropertyInfo>()
|
||||
collectPropsRecursive(typeInfo, result)
|
||||
result
|
||||
}
|
||||
|
||||
private static func collectPropsRecursive(typeInfo: TypeInfo, result: ArrayList<InstancePropertyInfo>): Unit {
|
||||
if (let ct: ClassTypeInfo <- typeInfo) {
|
||||
if (let Some(superType) <- ct.superClass) {
|
||||
if (superType != TypeInfo.of<Object>()) {
|
||||
collectPropsRecursive(superType, result)
|
||||
}
|
||||
}
|
||||
for (p in ct.instanceProperties) {
|
||||
var replaced = false
|
||||
for ((index, existing) in result |> enumerate) {
|
||||
if (existing.name == p.name) {
|
||||
result[index] = p
|
||||
replaced = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!replaced) {
|
||||
result.add(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user