feat: SimApiBaseModel 实体基类(对齐 C# Models/SimApiBaseModel)

- _id 默认 UUID v4、_createdAt/_updatedAt 自动当前时间
- mapData 反射式字段映射(忽略字段/白名单两种重载)
- updateTime 刷新更新时间
This commit is contained in:
2026-08-16 22:46:48 +08:00
parent d68a31077a
commit 9af7871114
+120
View File
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2025 SimcuTeam. All rights reserved.
* 移植自 C# 项目 SimApiE:\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)
}
}
}
}
}