Nenhuma descrição
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

save_dic_table.go 15KB

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