object.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. package domain
  2. import (
  3. "git.sxidc.com/go-framework/baize/framework/core/tag/check"
  4. "git.sxidc.com/go-tools/utils/reflectutils"
  5. "git.sxidc.com/go-tools/utils/strutils"
  6. "git.sxidc.com/go-tools/utils/template"
  7. "github.com/iancoleman/strcase"
  8. "github.com/pkg/errors"
  9. "reflect"
  10. )
  11. // Object 领域对象接口
  12. type Object interface {
  13. // DBSchema 返回针对领域的数据库的schema,一般不需要赋值,除非该领域对象与其他领域对象不在同一个schema
  14. DBSchema() string
  15. // DomainCNName 返回领域的中文名称
  16. DomainCNName() string
  17. // DomainCamelName 返回领域的大写驼峰式名称
  18. DomainCamelName() string
  19. }
  20. // HasField 校验领域对象中是否有某一个字段
  21. // 参数:
  22. // - object: 领域对象
  23. // - fieldName: 字段名
  24. // 返回值:
  25. // - 是否存在
  26. func HasField(object Object, fieldName string) bool {
  27. return hasField(object, fieldName)
  28. }
  29. // CheckField 校验领域对象某一字段的字段校验结果
  30. // 参数:
  31. // - result: 使用check.Struct返回的字段校验结果
  32. // - domainCNName: 领域中文名,可以使用DomainCNName()方法的返回值
  33. // - fieldName: 字段名
  34. // 返回值:
  35. // - 错误
  36. func CheckField(result check.Result, domainCNName string, fieldName string) error {
  37. err := result.CheckFields(fieldName)
  38. if err != nil {
  39. return errors.New(domainCNName + ": " + err.Error())
  40. }
  41. return nil
  42. }
  43. // CheckFields 校验领域对象多个字段的字段校验结果
  44. // 参数:
  45. // - result: 使用check.Struct返回的字段校验结果
  46. // - domainCNName: 领域中文名,可以使用DomainCNName()方法的返回值
  47. // - fieldNames: 多个字段名
  48. // 返回值:
  49. // - 错误
  50. func CheckFields(result check.Result, domainCNName string, fieldNames []string) error {
  51. err := result.CheckFields(fieldNames...)
  52. if err != nil {
  53. return errors.New(domainCNName + ":\n" + err.Error())
  54. }
  55. return nil
  56. }
  57. // SetField 设置领域对象对应字段的值
  58. // 类型参数:
  59. // - T: 字段值的类型
  60. // 参数:
  61. // - object: 领域对象
  62. // - fieldName: 要设置值的字段名
  63. // - value: 设置的值
  64. // 返回值:
  65. // - 错误
  66. func SetField[T any](object Object, fieldName string, value T) error {
  67. if object == nil {
  68. return errors.New("领域对象为nil")
  69. }
  70. fieldValue, err := getFieldValue(object, fieldName)
  71. if err != nil {
  72. return err
  73. }
  74. if !fieldValue.IsValid() || !fieldValue.CanSet() {
  75. return errors.New("领域对象" + fieldValue.Type().String() + "的字段" + fieldName + "无法赋值")
  76. }
  77. fieldValue.Set(reflect.ValueOf(value))
  78. return nil
  79. }
  80. // Field 获取领域对象对应字段的值
  81. // 类型参数:
  82. // - T: 字段值的类型
  83. // 参数:
  84. // - object: 领域对象
  85. // - fieldName: 要获取值的字段名
  86. // 返回值:
  87. // - 错误
  88. func Field[T any](object Object, fieldName string) (T, error) {
  89. zero := reflectutils.Zero[T]()
  90. if object == nil {
  91. return zero, errors.New("领域对象为nil")
  92. }
  93. fieldValue, err := getFieldValue(object, fieldName)
  94. if err != nil {
  95. return zero, err
  96. }
  97. if !fieldValue.IsValid() {
  98. return zero, errors.New("领域对象" + fieldValue.Type().String() + "的字段" + fieldName + "无法赋值")
  99. }
  100. retValue, ok := fieldValue.Interface().(T)
  101. if !ok {
  102. return zero, errors.New("转换字段类型失败")
  103. }
  104. return retValue, nil
  105. }
  106. // ToConcrete 将领域对象转换为具体类型
  107. // 类型参数:
  108. // - T: 要转换到的类型
  109. // 参数:
  110. // - object: 领域对象
  111. // 返回值:
  112. // - 转换出的类型
  113. // - 错误
  114. func ToConcrete[T Object](object Object) (T, error) {
  115. zero := reflectutils.Zero[T]()
  116. if object == nil {
  117. return zero, errors.New("领域对象为nil")
  118. }
  119. concrete, ok := object.(T)
  120. if !ok {
  121. return zero, errors.New("领域对象转化失败")
  122. }
  123. return concrete, nil
  124. }
  125. // TableName 基于领域对象生成表名,实际是将领域对象的驼峰式名称转换为蛇形复数形式,如classes
  126. // 参数:
  127. // - schema: 数据库的schema,如果领域对象DBSchema()方法返回值不为空,则取代schema参数
  128. // - object: 领域对象
  129. // 返回值:
  130. // - 表名
  131. func TableName(schema string, object Object) string {
  132. if strutils.IsStringNotEmpty(object.DBSchema()) {
  133. schema = object.DBSchema()
  134. }
  135. if strutils.IsStringEmpty(schema) {
  136. return template.Plural(strcase.ToSnake(template.Id(object.DomainCamelName())))
  137. } else {
  138. return schema + "." + template.Plural(strcase.ToSnake(template.Id(object.DomainCamelName())))
  139. }
  140. }
  141. // RelationTableName 生成两个领域对象的关联表名,实际是将两个领域对象的驼峰式名称转换为蛇形并使用and连接,如:class_and_student
  142. // 参数:
  143. // - schema: 数据库的schema,如果schema为空,但是两个领域对象的DBSchema()方法返回值不为空切一致,则取代schema参数
  144. // - left: 左领域对象
  145. // - right: 右领域对象
  146. // 返回值:
  147. // - 关联表名
  148. func RelationTableName(schema string, left Object, right Object) string {
  149. if strutils.IsStringNotEmpty(left.DBSchema()) && left.DBSchema() == right.DBSchema() {
  150. schema = left.DBSchema()
  151. }
  152. if strutils.IsStringEmpty(schema) {
  153. return strcase.ToSnake(template.Id(left.DomainCamelName())) + "_and_" + strcase.ToSnake(template.Id(right.DomainCamelName()))
  154. } else {
  155. return schema + "." + strcase.ToSnake(template.Id(left.DomainCamelName())) + "_and_" + strcase.ToSnake(template.Id(right.DomainCamelName()))
  156. }
  157. }
  158. // ColumnName 生成对应字段的列名,实际为字段名转换为蛇形,如StudentNum会转化为student_num
  159. // 参数:
  160. // - fieldName: 字段名
  161. // 返回值:
  162. // - 列名
  163. func ColumnName(fieldName string) string {
  164. return strcase.ToSnake(template.Id(fieldName))
  165. }
  166. // RelationColumnName 基于领域对象生成关联列名,实际为字段名转换为蛇形后加_id,如Student生成的关联列名为student_id
  167. // 参数:
  168. // - object: 领域对象
  169. // 返回值:
  170. // - 关联列名
  171. func RelationColumnName(object Object) string {
  172. return strcase.ToSnake(template.Id(object.DomainCamelName())) + "_id"
  173. }
  174. // RelativeDomainPath 基于领域对象生成领域URL路径,实际为字段名转换为左小写驼峰式前面加/,如Student生成的领域URL路径为/student
  175. // 参数:
  176. // - object: 领域对象
  177. // 返回值:
  178. // - 领域URL路径
  179. func RelativeDomainPath(object Object) string {
  180. return "/" + strcase.ToLowerCamel(template.Id(object.DomainCamelName()))
  181. }
  182. // SnakeDomainName 基于领域对象生成蛇形领域名称,如Student生成的蛇形领域名称为/student
  183. // 参数:
  184. // - object: 领域对象
  185. // 返回值:
  186. // - 蛇形领域名称
  187. func SnakeDomainName(object Object) string {
  188. return strcase.ToSnake(template.Id(object.DomainCamelName()))
  189. }
  190. func hasField(object Object, fieldName string) bool {
  191. if object == nil {
  192. return false
  193. }
  194. objectValue := reflect.ValueOf(object)
  195. if !reflectutils.IsValueStructOrStructPointer(objectValue) {
  196. return false
  197. }
  198. fieldValue := reflectutils.PointerValueElem(objectValue).FieldByName(fieldName)
  199. if !fieldValue.IsValid() {
  200. return false
  201. }
  202. return true
  203. }
  204. func getFieldValue(object Object, fieldName string) (*reflect.Value, error) {
  205. if object == nil {
  206. return nil, errors.New("领域对象为nil")
  207. }
  208. objectValue := reflect.ValueOf(object)
  209. if !reflectutils.IsValueStructOrStructPointer(objectValue) {
  210. return nil, errors.New("领域对象必须是结构或结构指针")
  211. }
  212. fieldValue := reflectutils.PointerValueElem(objectValue).FieldByName(fieldName)
  213. return &fieldValue, nil
  214. }