説明なし
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

mongodb_factory.go 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. package mongodb
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "fmt"
  6. "log"
  7. "sync"
  8. "time"
  9. "git.x2erp.com/qdy/go-base/config/subconfigs"
  10. "git.x2erp.com/qdy/go-base/logger"
  11. "go.mongodb.org/mongo-driver/bson"
  12. "go.mongodb.org/mongo-driver/mongo"
  13. "go.mongodb.org/mongo-driver/mongo/options"
  14. "go.mongodb.org/mongo-driver/mongo/readpref"
  15. )
  16. // MongoDBFactory MongoDB工厂(全局单例模式)
  17. type MongoDBFactory struct {
  18. client *mongo.Client
  19. db *mongo.Database
  20. config *subconfigs.MongoDBConfig
  21. //mu sync.RWMutex // 添加读写锁保护
  22. }
  23. var (
  24. instanceMongodb *MongoDBFactory
  25. instanceMongodbOnce sync.Once
  26. initErr error
  27. )
  28. // GetMongoDBFactory 获取MongoDB工厂单例
  29. func GetMongoDBFactory(config *subconfigs.MongoDBConfig) *MongoDBFactory {
  30. instanceMongodbOnce.Do(func() {
  31. if config == nil {
  32. log.Fatal("配置未初始化,请先在yaml进行配置")
  33. }
  34. // 设置默认值
  35. if config.Timeout == 0 {
  36. config.Timeout = 20 * time.Second
  37. }
  38. if config.MaxPoolSize == 0 {
  39. config.MaxPoolSize = 100
  40. }
  41. if config.MinPoolSize == 0 {
  42. config.MinPoolSize = 10
  43. }
  44. if config.AuthSource == "" {
  45. config.AuthSource = "admin"
  46. }
  47. // 验证配置
  48. if config.URI == "" {
  49. initErr = fmt.Errorf("mongodb URI must be configured")
  50. return
  51. }
  52. if config.Database == "" {
  53. initErr = fmt.Errorf("mongodb database name must be configured")
  54. return
  55. }
  56. log.Printf("Creating MongoDB connection...")
  57. // 创建客户端选项
  58. clientOptions := options.Client().
  59. ApplyURI(config.URI).
  60. SetConnectTimeout(config.Timeout).
  61. SetSocketTimeout(config.Timeout).
  62. SetServerSelectionTimeout(config.Timeout).
  63. SetMaxPoolSize(config.MaxPoolSize).
  64. SetMinPoolSize(config.MinPoolSize).
  65. SetMaxConnIdleTime(5 * time.Minute).
  66. SetHeartbeatInterval(10 * time.Second)
  67. // 设置认证
  68. if config.Username != "" && config.Password != "" {
  69. clientOptions.SetAuth(options.Credential{
  70. Username: config.Username,
  71. Password: config.Password,
  72. AuthSource: config.AuthSource,
  73. })
  74. }
  75. // 设置SSL
  76. if config.SSL {
  77. clientOptions.SetTLSConfig(&tls.Config{
  78. InsecureSkipVerify: false,
  79. })
  80. }
  81. // 创建上下文
  82. ctx, cancel := context.WithTimeout(context.Background(), config.Timeout)
  83. defer cancel()
  84. log.Printf(" context.WithTimeout ... successfully.")
  85. // 连接MongoDB
  86. client, err := mongo.Connect(ctx, clientOptions)
  87. if err != nil {
  88. initErr = fmt.Errorf("failed to connect to MongoDB: %v", err)
  89. return
  90. }
  91. log.Printf(" mongo.connect ... successfully.")
  92. // // 测试连接
  93. // if err := client.Ping(ctx, nil); err != nil {
  94. // initErr = fmt.Errorf("failed to ping MongoDB: %v", err)
  95. // return
  96. // }
  97. // 获取数据库
  98. database := client.Database(config.Database)
  99. log.Printf("MongoDBFactory is successfully created.\n")
  100. instanceMongodb = &MongoDBFactory{
  101. client: client,
  102. db: database,
  103. config: config,
  104. }
  105. })
  106. if initErr != nil {
  107. //logger.Errorf("MongoDBFactory is error:'%v'", initErr)
  108. log.Fatalf("MongoDBFactory is error:'%v'", initErr)
  109. //return nil
  110. }
  111. return instanceMongodb
  112. }
  113. // ========== MongoDBFactory 实例方法 ==========
  114. // GetClient 获取MongoDB客户端
  115. func (f *MongoDBFactory) GetClient() *mongo.Client {
  116. return f.client
  117. }
  118. // GetDatabase 获取数据库
  119. func (f *MongoDBFactory) GetDatabase() *mongo.Database {
  120. return f.db
  121. }
  122. // GetCollection 获取集合(类似获取数据库连接)
  123. func (f *MongoDBFactory) GetCollection(collectionName string) *mongo.Collection {
  124. return f.db.Collection(collectionName)
  125. }
  126. // Close 关闭MongoDB连接
  127. func (f *MongoDBFactory) Close() {
  128. if f.client != nil {
  129. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  130. defer cancel()
  131. err := f.client.Disconnect(ctx)
  132. if err != nil {
  133. logger.Errorf("failed to disconnect MongoDB: %v", err)
  134. //return fmt.Errorf("failed to disconnect MongoDB: %v", err)
  135. }
  136. log.Printf("MongoDB connection closed gracefully\n")
  137. f.client = nil
  138. f.db = nil
  139. }
  140. }
  141. // GetConfig 获取配置信息
  142. func (f *MongoDBFactory) GetConfig() subconfigs.MongoDBConfig {
  143. return *f.config
  144. }
  145. // TestConnection 测试连接
  146. func (f *MongoDBFactory) TestConnection() error {
  147. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  148. defer cancel()
  149. return f.client.Ping(ctx, readpref.Primary())
  150. }
  151. // ========== 快捷操作方法(增强版) ==========
  152. // InsertOne 插入单个文档,返回是否成功
  153. func (f *MongoDBFactory) InsertOne(collectionName string, document interface{}) bool {
  154. collection := f.GetCollection(collectionName)
  155. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  156. defer cancel()
  157. _, err := collection.InsertOne(ctx, document)
  158. if err != nil {
  159. logger.Errorf("插入文档失败,集合:%s,错误:%v", collectionName, err)
  160. return false
  161. }
  162. return true
  163. }
  164. // InsertOneWithResult 插入单个文档并返回结果
  165. func (f *MongoDBFactory) InsertOneWithResult(collectionName string, document interface{}) (*mongo.InsertOneResult, bool) {
  166. collection := f.GetCollection(collectionName)
  167. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  168. defer cancel()
  169. result, err := collection.InsertOne(ctx, document)
  170. if err != nil {
  171. logger.Errorf("插入文档失败,集合:%s,错误:%v", collectionName, err)
  172. return nil, false
  173. }
  174. return result, true
  175. }
  176. // InsertMany 插入多个文档,返回是否成功
  177. func (f *MongoDBFactory) InsertMany(collectionName string, documents []interface{}) bool {
  178. collection := f.GetCollection(collectionName)
  179. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  180. defer cancel()
  181. _, err := collection.InsertMany(ctx, documents)
  182. if err != nil {
  183. logger.Errorf("批量插入文档失败,集合:%s,错误:%v", collectionName, err)
  184. return false
  185. }
  186. return true
  187. }
  188. // InsertManyWithResult 插入多个文档并返回结果
  189. func (f *MongoDBFactory) InsertManyWithResult(collectionName string, documents []interface{}) (*mongo.InsertManyResult, bool) {
  190. collection := f.GetCollection(collectionName)
  191. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  192. defer cancel()
  193. result, err := collection.InsertMany(ctx, documents)
  194. if err != nil {
  195. logger.Errorf("批量插入文档失败,集合:%s,错误:%v", collectionName, err)
  196. return nil, false
  197. }
  198. return result, true
  199. }
  200. // FindOne 查询单个文档,返回解码后的对象
  201. func (f *MongoDBFactory) FindOne(collectionName string, filter interface{}, result interface{}) error {
  202. collection := f.GetCollection(collectionName)
  203. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  204. defer cancel()
  205. err := collection.FindOne(ctx, filter).Decode(result)
  206. if err != nil {
  207. if err != mongo.ErrNoDocuments {
  208. logger.Errorf("查询文档失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  209. }
  210. return err
  211. }
  212. return nil
  213. }
  214. // FindOneAndDecode 查询单个文档并自动解码(简化版)
  215. func (f *MongoDBFactory) FindOneAndDecode(collectionName string, filter interface{}) (interface{}, error) {
  216. collection := f.GetCollection(collectionName)
  217. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  218. defer cancel()
  219. var result map[string]interface{}
  220. err := collection.FindOne(ctx, filter).Decode(&result)
  221. if err != nil {
  222. if err != mongo.ErrNoDocuments {
  223. logger.Errorf("查询文档失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  224. }
  225. return nil, err
  226. }
  227. return result, nil
  228. }
  229. // Find 查询多个文档,返回对象数组
  230. func (f *MongoDBFactory) Find(collectionName string, filter interface{}, results interface{}, opts ...*options.FindOptions) error {
  231. collection := f.GetCollection(collectionName)
  232. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  233. defer cancel()
  234. cursor, err := collection.Find(ctx, filter, opts...)
  235. if err != nil {
  236. logger.Errorf("查询文档失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  237. return err
  238. }
  239. defer cursor.Close(ctx)
  240. // 解码到传入的切片指针中
  241. if err = cursor.All(ctx, results); err != nil {
  242. logger.Errorf("解码查询结果失败,集合:%s,错误:%v", collectionName, err)
  243. return err
  244. }
  245. return nil
  246. }
  247. // FindAll 查询所有文档,返回对象数组
  248. func (f *MongoDBFactory) FindAll(collectionName string, results interface{}) error {
  249. return f.Find(collectionName, bson.M{}, results)
  250. }
  251. // UpdateOneWithResult 更新单个文档并返回结果
  252. func (f *MongoDBFactory) UpdateOneWithResult(collectionName string, filter interface{}, update interface{}) (*mongo.UpdateResult, bool) {
  253. collection := f.GetCollection(collectionName)
  254. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  255. defer cancel()
  256. result, err := collection.UpdateOne(ctx, filter, update)
  257. if err != nil {
  258. logger.Errorf("更新文档失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  259. return nil, false
  260. }
  261. return result, true
  262. }
  263. // CountDocuments 统计文档数量,返回数量
  264. func (f *MongoDBFactory) CountDocuments(collectionName string, filter interface{}) (int64, error) {
  265. collection := f.GetCollection(collectionName)
  266. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  267. defer cancel()
  268. count, err := collection.CountDocuments(ctx, filter)
  269. if err != nil {
  270. logger.Errorf("统计文档数量失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  271. return 0, err
  272. }
  273. return count, nil
  274. }
  275. // Aggregate 聚合查询,返回对象数组
  276. func (f *MongoDBFactory) Aggregate(collectionName string, pipeline interface{}, results interface{}) error {
  277. collection := f.GetCollection(collectionName)
  278. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  279. defer cancel()
  280. cursor, err := collection.Aggregate(ctx, pipeline)
  281. if err != nil {
  282. logger.Errorf("聚合查询失败,集合:%s,管道:%v,错误:%v", collectionName, pipeline, err)
  283. return err
  284. }
  285. defer cursor.Close(ctx)
  286. if err = cursor.All(ctx, results); err != nil {
  287. logger.Errorf("解码聚合结果失败,集合:%s,错误:%v", collectionName, err)
  288. return err
  289. }
  290. return nil
  291. }
  292. // FindOneAndUpdate 查找并更新,返回更新后的文档
  293. func (f *MongoDBFactory) FindOneAndUpdate(collectionName string, filter interface{}, update interface{}, result interface{}) error {
  294. collection := f.GetCollection(collectionName)
  295. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  296. defer cancel()
  297. err := collection.FindOneAndUpdate(ctx, filter, update).Decode(result)
  298. if err != nil {
  299. if err != mongo.ErrNoDocuments {
  300. logger.Errorf("查找并更新失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  301. }
  302. return err
  303. }
  304. return nil
  305. }
  306. // FindOneAndDelete 查找并删除,返回删除的文档
  307. func (f *MongoDBFactory) FindOneAndDelete(collectionName string, filter interface{}, result interface{}) error {
  308. collection := f.GetCollection(collectionName)
  309. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  310. defer cancel()
  311. err := collection.FindOneAndDelete(ctx, filter).Decode(result)
  312. if err != nil {
  313. if err != mongo.ErrNoDocuments {
  314. logger.Errorf("查找并删除失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  315. }
  316. return err
  317. }
  318. return nil
  319. }
  320. // BulkWrite 批量写入操作,返回是否成功
  321. func (f *MongoDBFactory) BulkWrite(collectionName string, operations []mongo.WriteModel) bool {
  322. collection := f.GetCollection(collectionName)
  323. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  324. defer cancel()
  325. _, err := collection.BulkWrite(ctx, operations)
  326. if err != nil {
  327. logger.Errorf("批量写入操作失败,集合:%s,错误:%v", collectionName, err)
  328. return false
  329. }
  330. return true
  331. }
  332. // CreateIndex 创建索引,返回是否成功
  333. func (f *MongoDBFactory) CreateIndex(collectionName string, keys interface{}, opts ...*options.IndexOptions) bool {
  334. collection := f.GetCollection(collectionName)
  335. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  336. defer cancel()
  337. indexModel := mongo.IndexModel{
  338. Keys: keys,
  339. }
  340. if len(opts) > 0 && opts[0] != nil {
  341. indexModel.Options = opts[0]
  342. }
  343. _, err := collection.Indexes().CreateOne(ctx, indexModel)
  344. if err != nil {
  345. logger.Errorf("创建索引失败,集合:%s,键:%v,错误:%v", collectionName, keys, err)
  346. return false
  347. }
  348. return true
  349. }
  350. // FindWithPagination 分页查询,返回对象数组
  351. func (f *MongoDBFactory) FindWithPagination(
  352. collectionName string,
  353. filter interface{},
  354. skip, limit int64,
  355. sort interface{},
  356. results interface{},
  357. ) error {
  358. //collection := f.GetCollection(collectionName)
  359. //ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  360. //defer cancel()
  361. findOptions := options.Find().
  362. SetSkip(skip).
  363. SetLimit(limit)
  364. if sort != nil {
  365. findOptions.SetSort(sort)
  366. }
  367. return f.Find(collectionName, filter, results, findOptions)
  368. }
  369. // FindOneByID 根据ID查询文档
  370. func (f *MongoDBFactory) FindOneByID(collectionName string, id interface{}, result interface{}) error {
  371. return f.FindOne(collectionName, bson.M{"_id": id}, result)
  372. }
  373. // Exists 检查文档是否存在
  374. func (f *MongoDBFactory) Exists(collectionName string, filter interface{}) (bool, error) {
  375. count, err := f.CountDocuments(collectionName, filter)
  376. if err != nil {
  377. return false, err
  378. }
  379. return count > 0, nil
  380. }
  381. // FindAndCount 查询文档并返回总数(用于分页场景)
  382. func (f *MongoDBFactory) FindAndCount(
  383. collectionName string,
  384. filter interface{},
  385. skip, limit int64,
  386. sort interface{},
  387. results interface{},
  388. ) (int64, error) {
  389. // 查询数据
  390. err := f.FindWithPagination(collectionName, filter, skip, limit, sort, results)
  391. if err != nil {
  392. return 0, err
  393. }
  394. // 查询总数
  395. total, err := f.CountDocuments(collectionName, filter)
  396. if err != nil {
  397. return 0, err
  398. }
  399. return total, nil
  400. }
  401. // UpdateOne 更新单个文档,返回是否执行成功和影响记录数
  402. func (f *MongoDBFactory) UpdateOne(collectionName string, filter interface{}, update interface{}) (bool, int64) {
  403. collection := f.GetCollection(collectionName)
  404. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  405. defer cancel()
  406. result, err := collection.UpdateOne(ctx, filter, update)
  407. if err != nil {
  408. logger.Errorf("更新文档失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  409. return false, 0
  410. }
  411. // 返回执行成功 和 实际更新的文档数
  412. return true, result.ModifiedCount
  413. }
  414. // UpdateOneWithMatch 更新单个文档,只有匹配到文档才返回成功
  415. func (f *MongoDBFactory) UpdateOneWithMatch(collectionName string, filter interface{}, update interface{}) bool {
  416. success, modifiedCount := f.UpdateOne(collectionName, filter, update)
  417. return success && modifiedCount > 0
  418. }
  419. // UpdateMany 更新多个文档,返回是否执行成功和影响记录数
  420. func (f *MongoDBFactory) UpdateMany(collectionName string, filter interface{}, update interface{}) (bool, int64) {
  421. collection := f.GetCollection(collectionName)
  422. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  423. defer cancel()
  424. result, err := collection.UpdateMany(ctx, filter, update)
  425. if err != nil {
  426. logger.Errorf("批量更新文档失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  427. return false, 0
  428. }
  429. return true, result.ModifiedCount
  430. }
  431. // DeleteOne 删除单个文档,返回是否执行成功和删除的记录数
  432. func (f *MongoDBFactory) DeleteOne(collectionName string, filter interface{}) (bool, int64) {
  433. collection := f.GetCollection(collectionName)
  434. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  435. defer cancel()
  436. result, err := collection.DeleteOne(ctx, filter)
  437. if err != nil {
  438. logger.Errorf("删除文档失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  439. return false, 0
  440. }
  441. // 返回执行成功 和 实际删除的文档数
  442. return true, result.DeletedCount
  443. }
  444. // DeleteOneWithMatch 删除单个文档,只有删除了文档才返回成功
  445. func (f *MongoDBFactory) DeleteOneWithMatch(collectionName string, filter interface{}) bool {
  446. success, deletedCount := f.DeleteOne(collectionName, filter)
  447. return success && deletedCount > 0
  448. }
  449. // DeleteMany 删除多个文档,返回是否执行成功和删除的记录数
  450. func (f *MongoDBFactory) DeleteMany(collectionName string, filter interface{}) (bool, int64) {
  451. collection := f.GetCollection(collectionName)
  452. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  453. defer cancel()
  454. result, err := collection.DeleteMany(ctx, filter)
  455. if err != nil {
  456. logger.Errorf("批量删除文档失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  457. return false, 0
  458. }
  459. return true, result.DeletedCount
  460. }
  461. // UpsertOne 更新或插入文档(upsert操作)
  462. func (f *MongoDBFactory) UpsertOne(collectionName string, filter interface{}, update interface{}) (bool, interface{}) {
  463. collection := f.GetCollection(collectionName)
  464. ctx, cancel := context.WithTimeout(context.Background(), f.config.Timeout)
  465. defer cancel()
  466. // 设置 upsert 选项
  467. opts := options.Update().SetUpsert(true)
  468. result, err := collection.UpdateOne(ctx, filter, update, opts)
  469. if err != nil {
  470. logger.Errorf("Upsert操作失败,集合:%s,过滤条件:%v,错误:%v", collectionName, filter, err)
  471. return false, result
  472. }
  473. // 返回: 成功状态, 匹配数, 修改数, 插入数
  474. return true, result
  475. }
  476. // UpdateOneByID 根据ID更新文档
  477. func (f *MongoDBFactory) UpdateOneByID(collectionName string, id interface{}, update interface{}) (bool, int64) {
  478. return f.UpdateOne(collectionName, bson.M{"_id": id}, update)
  479. }
  480. // UpdateOneByIDWithMatch 根据ID更新文档,只有匹配到才返回成功
  481. func (f *MongoDBFactory) UpdateOneByIDWithMatch(collectionName string, id interface{}, update interface{}) bool {
  482. success, modifiedCount := f.UpdateOneByID(collectionName, id, update)
  483. return success && modifiedCount > 0
  484. }
  485. // DeleteOneByID 根据ID删除文档
  486. func (f *MongoDBFactory) DeleteOneByID(collectionName string, id interface{}) (bool, int64) {
  487. return f.DeleteOne(collectionName, bson.M{"_id": id})
  488. }
  489. // DeleteOneByIDWithMatch 根据ID删除文档,只有删除了才返回成功
  490. func (f *MongoDBFactory) DeleteOneByIDWithMatch(collectionName string, id interface{}) bool {
  491. success, deletedCount := f.DeleteOneByID(collectionName, id)
  492. return success && deletedCount > 0
  493. }