설명 없음
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

save_dic_table.go 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package dicmanagement
  2. import (
  3. "context"
  4. "fmt"
  5. "regexp"
  6. "git.x2erp.com/qdy/go-base/ctx"
  7. "git.x2erp.com/qdy/go-base/logger"
  8. "git.x2erp.com/qdy/go-base/model/response"
  9. "git.x2erp.com/qdy/go-base/util"
  10. "git.x2erp.com/qdy/go-db/factory/database"
  11. "git.x2erp.com/qdy/go-svc-configure/internal/tables"
  12. "github.com/google/uuid"
  13. "github.com/jmoiron/sqlx"
  14. )
  15. // SaveDicTable 保存数据库表字典(主表+子表)
  16. func SaveDicTable(req *DicTableRequest, ctx context.Context, dbFactory *database.DBFactory, reqCtx *ctx.RequestContext) *response.QueryResult[DicTableDetail] {
  17. logger.Debug("SaveDicTable-开始保存数据库表字典")
  18. // 参数验证
  19. if err := validateDicTableRequest(req); err != nil {
  20. logger.ErrorC(reqCtx, fmt.Sprintf("参数验证失败: %v", err))
  21. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("参数验证失败: %v", err), reqCtx)
  22. }
  23. // 获取数据库连接并开始事务
  24. db := dbFactory.GetDB()
  25. tx, err := db.BeginTxx(ctx, nil)
  26. if err != nil {
  27. logger.ErrorC(reqCtx, fmt.Sprintf("开始事务失败: %v", err))
  28. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("开始事务失败: %v", err), reqCtx)
  29. }
  30. defer func() {
  31. if p := recover(); p != nil {
  32. tx.Rollback()
  33. panic(p)
  34. }
  35. }()
  36. // 获取当前用户
  37. creator := reqCtx.UserID
  38. if creator == "" {
  39. creator = "system"
  40. }
  41. // 1. 处理主表:检查是否存在 → 存在则更新,软删除则恢复,不存在则插入
  42. tableExists, err := checkTableExists(ctx, tx, req.TableID)
  43. if err != nil {
  44. tx.Rollback()
  45. logger.ErrorC(reqCtx, fmt.Sprintf("检查表存在性失败: %v", err))
  46. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("检查表存在性失败: %v", err), reqCtx)
  47. }
  48. var table tables.DicTableDB
  49. if tableExists {
  50. // 更新主表
  51. table, err = updateTable(ctx, tx, req, creator)
  52. if err != nil {
  53. tx.Rollback()
  54. logger.ErrorC(reqCtx, fmt.Sprintf("更新表失败: %v", err))
  55. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("更新表失败: %v", err), reqCtx)
  56. }
  57. logger.Debug(fmt.Sprintf("更新表成功: %s", req.TableID))
  58. } else {
  59. // 检查是否有软删除的记录
  60. softDeletedExists, err := checkSoftDeletedTableExists(ctx, tx, req.TableID)
  61. if err != nil {
  62. tx.Rollback()
  63. logger.ErrorC(reqCtx, fmt.Sprintf("检查软删除表存在性失败: %v", err))
  64. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("检查软删除表存在性失败: %v", err), reqCtx)
  65. }
  66. if softDeletedExists {
  67. // 恢复软删除的表
  68. table, err = restoreTable(ctx, tx, req, creator)
  69. if err != nil {
  70. tx.Rollback()
  71. logger.ErrorC(reqCtx, fmt.Sprintf("恢复软删除表失败: %v", err))
  72. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("恢复软删除表失败: %v", err), reqCtx)
  73. }
  74. logger.Debug(fmt.Sprintf("恢复软删除表成功: %s", req.TableID))
  75. } else {
  76. // 插入主表
  77. table, err = insertTable(ctx, tx, req, creator)
  78. if err != nil {
  79. tx.Rollback()
  80. logger.ErrorC(reqCtx, fmt.Sprintf("插入表失败: %v", err))
  81. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("插入表失败: %v", err), reqCtx)
  82. }
  83. logger.Debug(fmt.Sprintf("插入表成功: %s", req.TableID))
  84. }
  85. }
  86. // 2. 处理子表:删除此表的所有字段,然后插入新的记录
  87. // 首先软删除该表的所有字段记录
  88. err = deleteAllTableFields(ctx, tx, req.TableID)
  89. if err != nil {
  90. tx.Rollback()
  91. logger.ErrorC(reqCtx, fmt.Sprintf("删除表字段失败: %v", err))
  92. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("删除表字段失败: %v", err), reqCtx)
  93. }
  94. logger.Debug(fmt.Sprintf("已软删除表 %s 的所有字段记录", req.TableID))
  95. // 然后插入新的字段记录
  96. var fields []tables.DicTableFieldDB
  97. for _, fieldReq := range req.Fields {
  98. // 验证字段主键规则:fieldID 应该是 table_id + "." + field_name
  99. expectedFieldID := req.TableID + "." + fieldReq.FieldName
  100. if fieldReq.FieldID != expectedFieldID {
  101. tx.Rollback()
  102. logger.ErrorC(reqCtx, fmt.Sprintf("字段主键不符合规则: 期望 %s, 实际 %s", expectedFieldID, fieldReq.FieldID))
  103. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("字段主键不符合规则: 期望 %s, 实际 %s", expectedFieldID, fieldReq.FieldID), reqCtx)
  104. }
  105. // 确保字段的 tableID 与主表一致
  106. fieldReq.TableID = req.TableID
  107. // 插入字段
  108. field, err := insertTableField(ctx, tx, &fieldReq, creator)
  109. if err != nil {
  110. tx.Rollback()
  111. logger.ErrorC(reqCtx, fmt.Sprintf("插入表字段失败: %v", err))
  112. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("插入表字段失败: %v", err), reqCtx)
  113. }
  114. fields = append(fields, field)
  115. }
  116. logger.Debug(fmt.Sprintf("成功插入 %d 个表字段", len(fields)))
  117. // 提交事务
  118. if err := tx.Commit(); err != nil {
  119. logger.ErrorC(reqCtx, fmt.Sprintf("提交事务失败: %v", err))
  120. return util.CreateErrorResult[DicTableDetail](fmt.Sprintf("提交事务失败: %v", err), reqCtx)
  121. }
  122. logger.Debug(fmt.Sprintf("成功保存数据库表字典: %s, 包含 %d 个字段", req.TableID, len(fields)))
  123. // 构建返回结果
  124. detail := DicTableDetail{
  125. Table: table,
  126. Fields: fields,
  127. }
  128. return util.CreateSuccessResultData[DicTableDetail](detail, reqCtx)
  129. }
  130. // validateDicTableRequest 验证数据库表字典请求
  131. func validateDicTableRequest(req *DicTableRequest) error {
  132. if req.TableID == "" {
  133. return fmt.Errorf("表ID不能为空")
  134. }
  135. // 验证表ID格式:仅允许字母、数字、下划线
  136. match, _ := regexp.MatchString("^[a-zA-Z0-9_]+$", req.TableID)
  137. if !match {
  138. return fmt.Errorf("表ID只能包含字母、数字、下划线")
  139. }
  140. if req.TableType == "" {
  141. return fmt.Errorf("表类型不能为空")
  142. }
  143. // 验证表类型
  144. validTypes := map[string]bool{"实体表": true, "视图": true, "物化视图": true}
  145. if !validTypes[req.TableType] {
  146. return fmt.Errorf("表类型必须是'实体表'、'视图'或'物化视图'")
  147. }
  148. if req.Name == "" {
  149. return fmt.Errorf("表名称不能为空")
  150. }
  151. // 验证子表数据
  152. for i, field := range req.Fields {
  153. if field.FieldID == "" {
  154. return fmt.Errorf("第%d个字段的字段ID不能为空", i+1)
  155. }
  156. if field.FieldName == "" {
  157. return fmt.Errorf("第%d个字段的字段名称不能为空", i+1)
  158. }
  159. if field.FiledType == "" {
  160. return fmt.Errorf("第%d个字段的字段类型不能为空", i+1)
  161. }
  162. // 验证字段类型
  163. validFieldTypes := map[string]bool{"实际字段": true, "计算字段": true}
  164. if !validFieldTypes[field.FiledType] {
  165. return fmt.Errorf("第%d个字段的字段类型必须是'实际字段'或'计算字段'", i+1)
  166. }
  167. if field.DataType == "" {
  168. return fmt.Errorf("第%d个字段的数据类型不能为空", i+1)
  169. }
  170. // 验证数据类型
  171. validDataTypes := map[string]bool{"字符型": true, "数值型": true, "日期型": true, "布尔型": true}
  172. if !validDataTypes[field.DataType] {
  173. return fmt.Errorf("第%d个字段的数据类型必须是'字符型'、'数值型'、'日期型'或'布尔型'", i+1)
  174. }
  175. }
  176. return nil
  177. }
  178. // checkTableExists 检查表是否存在(仅活跃记录)
  179. func checkTableExists(ctx context.Context, tx *sqlx.Tx, tableID string) (bool, error) {
  180. var count int
  181. query := "SELECT COUNT(*) FROM dic_table WHERE table_id = ? AND deleted_at IS NULL"
  182. err := tx.GetContext(ctx, &count, query, tableID)
  183. return count > 0, err
  184. }
  185. // checkSoftDeletedTableExists 检查表是否被软删除
  186. func checkSoftDeletedTableExists(ctx context.Context, tx *sqlx.Tx, tableID string) (bool, error) {
  187. var count int
  188. query := "SELECT COUNT(*) FROM dic_table WHERE table_id = ? AND deleted_at IS NOT NULL"
  189. err := tx.GetContext(ctx, &count, query, tableID)
  190. return count > 0, err
  191. }
  192. // restoreTable 恢复软删除的表
  193. func restoreTable(ctx context.Context, tx *sqlx.Tx, req *DicTableRequest, creator string) (tables.DicTableDB, error) {
  194. query := `
  195. UPDATE dic_table
  196. SET deleted_at = NULL, table_type = ?, table_name = ?, description = ?, updated_at = CURRENT_TIMESTAMP
  197. WHERE table_id = ? AND deleted_at IS NOT NULL
  198. `
  199. _, err := tx.ExecContext(ctx, query,
  200. req.TableType,
  201. req.Name,
  202. req.Description,
  203. req.TableID,
  204. )
  205. if err != nil {
  206. return tables.DicTableDB{}, err
  207. }
  208. // 查询恢复后的记录
  209. var table tables.DicTableDB
  210. selectQuery := `
  211. SELECT id, table_id, table_type, table_name, description, creator, created_at, updated_at, deleted_at
  212. FROM dic_table
  213. WHERE table_id = ? AND deleted_at IS NULL
  214. `
  215. err = tx.GetContext(ctx, &table, selectQuery, req.TableID)
  216. return table, err
  217. }
  218. // insertTable 插入表主表
  219. func insertTable(ctx context.Context, tx *sqlx.Tx, req *DicTableRequest, creator string) (tables.DicTableDB, error) {
  220. // 生成ID
  221. id := uuid.New().String()
  222. query := `
  223. INSERT INTO dic_table (id, table_id, table_type, table_name, description, creator, created_at, updated_at)
  224. VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
  225. `
  226. logger.Debug(fmt.Sprintf("insertTable - 执行插入: query=%s, id=%s, table_id=%s, creator=%s", query, id, req.TableID, creator))
  227. _, err := tx.ExecContext(ctx, query,
  228. id,
  229. req.TableID,
  230. req.TableType,
  231. req.Name,
  232. req.Description,
  233. creator,
  234. )
  235. if err != nil {
  236. logger.Error(fmt.Sprintf("insertTable - 插入失败: %v", err))
  237. return tables.DicTableDB{}, err
  238. }
  239. // 查询刚插入的记录
  240. var table tables.DicTableDB
  241. selectQuery := `
  242. SELECT id, table_id, table_type, table_name, description, creator, created_at, updated_at, deleted_at
  243. FROM dic_table
  244. WHERE table_id = ? AND deleted_at IS NULL
  245. `
  246. err = tx.GetContext(ctx, &table, selectQuery, req.TableID)
  247. return table, err
  248. }
  249. // updateTable 更新表主表
  250. func updateTable(ctx context.Context, tx *sqlx.Tx, req *DicTableRequest, creator string) (tables.DicTableDB, error) {
  251. query := `
  252. UPDATE dic_table
  253. SET table_type = ?, table_name = ?, description = ?, updated_at = CURRENT_TIMESTAMP
  254. WHERE table_id = ? AND deleted_at IS NULL
  255. `
  256. _, err := tx.ExecContext(ctx, query,
  257. req.TableType,
  258. req.Name,
  259. req.Description,
  260. req.TableID,
  261. )
  262. if err != nil {
  263. return tables.DicTableDB{}, err
  264. }
  265. // 查询更新后的记录
  266. var table tables.DicTableDB
  267. selectQuery := `
  268. SELECT id, table_id, table_type, table_name, description, creator, created_at, updated_at, deleted_at
  269. FROM dic_table
  270. WHERE table_id = ? AND deleted_at IS NULL
  271. `
  272. err = tx.GetContext(ctx, &table, selectQuery, req.TableID)
  273. return table, err
  274. }
  275. // deleteAllTableFields 软删除表的所有字段记录
  276. func deleteAllTableFields(ctx context.Context, tx *sqlx.Tx, tableID string) error {
  277. query := "UPDATE dic_table_field SET deleted_at = CURRENT_TIMESTAMP WHERE table_id = ? AND deleted_at IS NULL"
  278. _, err := tx.ExecContext(ctx, query, tableID)
  279. return err
  280. }
  281. // insertTableField 插入表字段子表
  282. func insertTableField(ctx context.Context, tx *sqlx.Tx, fieldReq *DicTableFieldRequest, creator string) (tables.DicTableFieldDB, error) {
  283. // 生成ID
  284. id := uuid.New().String()
  285. query := `
  286. INSERT INTO dic_table_field (id, field_id, table_id, filed_type, data_type, field_name, field_name_cn, description, creator, created_at, updated_at)
  287. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
  288. `
  289. _, err := tx.ExecContext(ctx, query,
  290. id,
  291. fieldReq.FieldID,
  292. fieldReq.TableID,
  293. fieldReq.FiledType,
  294. fieldReq.DataType,
  295. fieldReq.FieldName,
  296. fieldReq.FieldNameCN,
  297. fieldReq.Description,
  298. creator,
  299. )
  300. if err != nil {
  301. return tables.DicTableFieldDB{}, err
  302. }
  303. // 查询刚插入的记录
  304. var field tables.DicTableFieldDB
  305. selectQuery := `
  306. SELECT id, field_id, table_id, filed_type, data_type, field_name, field_name_cn, description, creator, created_at, updated_at, deleted_at
  307. FROM dic_table_field
  308. WHERE field_id = ? AND deleted_at IS NULL
  309. `
  310. err = tx.GetContext(ctx, &field, selectQuery, fieldReq.FieldID)
  311. return field, err
  312. }