commonBIzService.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package base
  2. import (
  3. "fmt"
  4. "github.com/samber/lo"
  5. "go_server/base/config"
  6. "go_server/base/core"
  7. "go_server/global"
  8. "go_server/utils"
  9. "gorm.io/gorm"
  10. "reflect"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. // 业务库公共服务
  16. type BizCommonService struct {
  17. DbAlias string
  18. }
  19. var lock sync.RWMutex
  20. func (s *BizCommonService) mustAlias() string {
  21. lock.RLock()
  22. defer lock.RUnlock()
  23. if s.DbAlias == "" {
  24. return global.DefaultAlias()
  25. }
  26. return s.DbAlias
  27. }
  28. func (s *BizCommonService) SetDbAlias(alias string) {
  29. lock.Lock()
  30. defer lock.Unlock()
  31. s.DbAlias = alias
  32. }
  33. func (s *BizCommonService) DB() *gorm.DB {
  34. db, err := global.BizDBByAlias(s.mustAlias())
  35. if err != nil {
  36. core.Log.Errorf("获取业务数据库失败, alias: %s, err: %v", s.mustAlias(), err)
  37. return nil
  38. }
  39. return db
  40. }
  41. func (s *BizCommonService) GetColumnComment(dbAlias string, tableName string) (data []Column, err error) {
  42. var entities []Column
  43. sql := `
  44. SELECT
  45. c.COLUMN_NAME column_name,
  46. c.DATA_TYPE data_type,
  47. CASE c.DATA_TYPE
  48. WHEN 'longtext' THEN c.CHARACTER_MAXIMUM_LENGTH
  49. WHEN 'varchar' THEN c.CHARACTER_MAXIMUM_LENGTH
  50. WHEN 'double' THEN CONCAT_WS(',', c.NUMERIC_PRECISION, c.NUMERIC_SCALE)
  51. WHEN 'decimal' THEN CONCAT_WS(',', c.NUMERIC_PRECISION, c.NUMERIC_SCALE)
  52. WHEN 'int' THEN c.NUMERIC_PRECISION
  53. WHEN 'bigint' THEN c.NUMERIC_PRECISION
  54. ELSE ''
  55. END AS data_type_long,
  56. c.COLUMN_COMMENT column_comment,
  57. CASE WHEN kcu.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS primary_key,
  58. c.ORDINAL_POSITION
  59. FROM
  60. INFORMATION_SCHEMA.COLUMNS c
  61. LEFT JOIN
  62. INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
  63. ON
  64. c.TABLE_SCHEMA = kcu.TABLE_SCHEMA
  65. AND c.TABLE_NAME = kcu.TABLE_NAME
  66. AND c.COLUMN_NAME = kcu.COLUMN_NAME
  67. AND kcu.CONSTRAINT_NAME = 'PRIMARY'
  68. WHERE
  69. c.TABLE_NAME = ?
  70. AND c.TABLE_SCHEMA = ?
  71. ORDER BY
  72. c.ORDINAL_POSITION;`
  73. db, err := global.BizDBByAlias(s.mustAlias())
  74. if err != nil {
  75. return entities, err
  76. }
  77. core.Log.Infof("dbName:%s tableName:%s", db.Migrator().CurrentDatabase(), tableName)
  78. err = db.Raw(sql, tableName, db.Migrator().CurrentDatabase()).Scan(&entities).Error
  79. return entities, err
  80. }
  81. // 反射获得结构体字段与备注信息 获取json gorm.comment
  82. type ClmInfo struct {
  83. Field string `json:"field"`
  84. Json string `json:"json"`
  85. Comment string `json:"comment"`
  86. }
  87. func (s *BizCommonService) GetColumnCommentFromStruct(anyStruct any) []ClmInfo {
  88. t := reflect.TypeOf(anyStruct)
  89. var fieldInfoList []ClmInfo
  90. for i := 0; i < t.NumField(); i++ {
  91. field := t.Field(i)
  92. // 处理嵌套结构体
  93. //if field.Anonymous && field.Type.Kind() == reflect.Struct {
  94. // // 递归处理嵌套结构体的字段
  95. // nestedFields := s.GetColumnCommentFromStruct(reflect.New(field.Type).Elem().Interface())
  96. // fieldInfoList = append(fieldInfoList, nestedFields...)
  97. // continue
  98. //}
  99. // 处理嵌套结构体(包括指针类型)
  100. if field.Anonymous {
  101. fieldType := field.Type
  102. // 如果是指针类型,获取其指向的类型
  103. if fieldType.Kind() == reflect.Ptr {
  104. fieldType = fieldType.Elem()
  105. }
  106. // 如果是结构体类型,递归处理
  107. if fieldType.Kind() == reflect.Struct {
  108. // 创建该类型的零值实例进行递归
  109. nestedFields := s.GetColumnCommentFromStruct(reflect.New(fieldType).Elem().Interface())
  110. fieldInfoList = append(fieldInfoList, nestedFields...)
  111. continue
  112. }
  113. }
  114. // 获取json标签值
  115. jsonTag := field.Tag.Get("json")
  116. // 获取gorm标签中的comment值
  117. gormTag := field.Tag.Get("gorm")
  118. comment := s.extractCommentFromGormTag(gormTag)
  119. // 添加到结果列表
  120. fieldInfoList = append(fieldInfoList, ClmInfo{
  121. Field: field.Name,
  122. Json: jsonTag,
  123. Comment: comment,
  124. })
  125. }
  126. return fieldInfoList
  127. }
  128. func (s *BizCommonService) extractCommentFromGormTag(gormTag string) string {
  129. pairs := strings.Split(gormTag, ";")
  130. for _, pair := range pairs {
  131. if strings.HasPrefix(pair, "comment:") {
  132. return strings.TrimPrefix(pair, "comment:")
  133. }
  134. }
  135. return ""
  136. }
  137. // 通用CSV导出方法
  138. // 表名 传入需要导出字段名 表字段信息
  139. func ExportCsv[T any](db *gorm.DB, fields []string, colInfo []ClmInfo) (string, error) {
  140. core.Log.Infof("申请导出字段:%+v", fields)
  141. allRecords, err := GetMore[T](db)
  142. if err != nil {
  143. return "", err
  144. }
  145. core.Log.Infof("导出数据总量:%d", len(allRecords))
  146. if len(allRecords) == 0 {
  147. return "", nil
  148. }
  149. var csvHeard []string
  150. var csvCols []string
  151. for _, cls := range colInfo {
  152. if lo.Contains(fields, cls.Field) {
  153. csvHeard = append(csvHeard, cls.Comment)
  154. csvCols = append(csvCols, cls.Field)
  155. }
  156. }
  157. core.Log.Infof("导出表头:%+v", csvHeard)
  158. core.Log.Infof("可导出csvCols字段:%+v", csvCols)
  159. var csvData [][]string
  160. for _, item := range allRecords {
  161. var row []string
  162. val := reflect.ValueOf(item)
  163. if val.Kind() == reflect.Ptr {
  164. val = val.Elem()
  165. }
  166. for _, fieldName := range csvCols {
  167. fieldVal := val.FieldByName(fieldName)
  168. if !fieldVal.IsValid() {
  169. // 如果直接字段找不到,尝试从嵌套结构体查找
  170. for i := 0; i < val.NumField(); i++ {
  171. field := val.Type().Field(i)
  172. if field.Anonymous {
  173. nestedVal := val.Field(i)
  174. // 处理嵌套结构体指针
  175. if nestedVal.Kind() == reflect.Ptr {
  176. if nestedVal.IsNil() {
  177. continue
  178. }
  179. nestedVal = nestedVal.Elem()
  180. }
  181. if nestedVal.IsValid() {
  182. nestedField := nestedVal.FieldByName(fieldName)
  183. if nestedField.IsValid() {
  184. fieldVal = nestedField
  185. break
  186. }
  187. }
  188. }
  189. }
  190. }
  191. var strVal string
  192. if fieldVal.IsValid() {
  193. strVal = fmt.Sprintf("%v", fieldVal.Interface())
  194. }
  195. row = append(row, strVal)
  196. }
  197. csvData = append(csvData, row)
  198. }
  199. filename := fmt.Sprintf("export_%s.csv", time.Now().Format("0102150405"))
  200. viewPath := utils.ToPath(config.EnvConf().File.Path, config.EnvConf().File.ProxyPath, filename)
  201. storePath := utils.ToPath(config.EnvConf().File.StorePath, filename)
  202. _, err = config.ExportCsv(csvHeard, csvData, storePath)
  203. if err != nil {
  204. return "", err
  205. }
  206. return viewPath, nil
  207. }