component.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package db
  2. import (
  3. "context"
  4. dbFacade "f1-game/internal/component/db/facade"
  5. dbMigrator "f1-game/internal/component/db/migrator"
  6. dbQueue "f1-game/internal/component/db/queue"
  7. "fmt"
  8. cstring "github.com/cherry-game/cherry/extend/string"
  9. clog "github.com/cherry-game/cherry/logger"
  10. cmongo "github.com/cherry-game/components/mongo"
  11. "go.mongodb.org/mongo-driver/v2/bson"
  12. "go.mongodb.org/mongo-driver/v2/mongo"
  13. "go.mongodb.org/mongo-driver/v2/mongo/options"
  14. )
  15. type Component struct {
  16. *cmongo.Component
  17. name string
  18. db *mongo.Database
  19. tables []dbFacade.Table
  20. migratorScripts []dbMigrator.IScript
  21. dbQueue *dbQueue.TableQueue
  22. onLoadFuncs []func()
  23. }
  24. func (p *Component) Name() string {
  25. return p.name
  26. }
  27. func New(name string) *Component {
  28. return &Component{
  29. name: name,
  30. Component: cmongo.NewComponent(),
  31. }
  32. }
  33. func (p *Component) Init() {
  34. p.Component.Init()
  35. p.db = p.loadMongo("mongo_id")
  36. if p.db == nil {
  37. panic("db id not found.")
  38. }
  39. p.checkIndexes()
  40. p.initMigrator()
  41. p.initDBQueue()
  42. clog.Infof("[db_id = %s] database is init.", p.db.Name())
  43. for _, fn := range p.onLoadFuncs {
  44. if fn != nil {
  45. fn()
  46. }
  47. }
  48. }
  49. // 添加映射表
  50. func (p *Component) AddTable(table ...dbFacade.Table) {
  51. p.tables = append(p.tables, table...)
  52. }
  53. // 添加DB脚本
  54. func (p *Component) AddScript(script ...dbMigrator.IScript) {
  55. p.migratorScripts = append(p.migratorScripts, script...)
  56. }
  57. // 初始化db脚本
  58. func (p *Component) initMigrator() {
  59. if len(p.migratorScripts) > 0 {
  60. migrator := &dbMigrator.Migrator{}
  61. migrator.Registers(p.migratorScripts)
  62. migrator.Run(p.App().NodeID(), p.db, p.tables)
  63. }
  64. }
  65. // 初始化db队列
  66. func (p *Component) initDBQueue() {
  67. queueConfig := dbQueue.QueueConfig{}
  68. if err := queueConfig.Load(); err != nil {
  69. clog.Panic(err)
  70. }
  71. p.dbQueue = dbQueue.NewTableQueue(p.db)
  72. for _, table := range p.tables {
  73. if !table.EnableQueue() {
  74. continue
  75. }
  76. opts := queueConfig.BuildOpts(table.TableName())
  77. p.dbQueue.AddTable(table, opts...)
  78. }
  79. p.dbQueue.Start()
  80. }
  81. // 检查每个Collection的索引
  82. func (p *Component) checkIndexes() {
  83. // 构建 获取索引的函数
  84. findIndexFunc := func(collection *mongo.Collection) map[string]bool {
  85. indexMaps := map[string]bool{}
  86. // 读取collection的索引列表
  87. cursor, err := collection.Indexes().List(context.TODO())
  88. if err != nil {
  89. clog.Fatal(err)
  90. }
  91. defer cursor.Close(context.TODO())
  92. for cursor.Next(context.TODO()) {
  93. var index bson.M
  94. if err := cursor.Decode(&index); err != nil {
  95. clog.Fatal(err)
  96. }
  97. indexKey := cstring.ToString(index["name"])
  98. if indexKey != "_id_" {
  99. indexMaps[indexKey] = true
  100. }
  101. }
  102. return indexMaps
  103. }
  104. // 构建 创建索引的函数包装
  105. createIndexFunc := func(collection *mongo.Collection, indexObject mongo.IndexModel) {
  106. indexName, err := collection.Indexes().CreateOne(context.TODO(), indexObject)
  107. if err != nil {
  108. clog.Fatalf("Create index error. [table = %s, err = %v]", collection.Name(), err)
  109. } else {
  110. clog.Infof("Create index. [table = %s, indexName = %s]", collection.Name(), indexName)
  111. }
  112. }
  113. buildIndexNameFunc := func(d bson.D) string {
  114. var (
  115. key string
  116. size = len(d)
  117. )
  118. for i, v := range d {
  119. key += fmt.Sprintf("%s_%v", v.Key, v.Value)
  120. if i+1 < size {
  121. key += "_"
  122. }
  123. }
  124. return key
  125. }
  126. for _, table := range p.tables {
  127. tableIndexes := table.Indexes()
  128. if len(tableIndexes) < 1 {
  129. continue
  130. }
  131. collection := p.Collection(table.TableName())
  132. // 获取表已创建的索引
  133. indexMaps := findIndexFunc(collection)
  134. if len(indexMaps) < 1 {
  135. // 全量创建已定义的索引
  136. for _, indexObject := range tableIndexes {
  137. createIndexFunc(collection, indexObject)
  138. }
  139. } else {
  140. // 创建新增的索引
  141. for _, indexObject := range tableIndexes {
  142. bsonD := indexObject.Keys.(bson.D)
  143. key := buildIndexNameFunc(bsonD)
  144. if !indexMaps[key] {
  145. createIndexFunc(collection, indexObject)
  146. }
  147. }
  148. }
  149. }
  150. }
  151. func (p *Component) DBQueue() *dbQueue.TableQueue {
  152. return p.dbQueue
  153. }
  154. // 设置加载函数(组件初始化后执行)
  155. func (p *Component) SetOnLoadFunc(fn ...func()) {
  156. p.onLoadFuncs = append(p.onLoadFuncs, fn...)
  157. }
  158. func (p *Component) OnStop() {
  159. p.dbQueue.Stop()
  160. }
  161. // 根据节点配置获取mongo db实
  162. func (p *Component) loadMongo(id string) *mongo.Database {
  163. dbID := p.App().Settings().GetConfig("mongo_id_list").GetString(id)
  164. return p.Component.GetDb(dbID)
  165. }
  166. // 获取db对象
  167. func (p *Component) DB() *mongo.Database {
  168. return p.db
  169. }
  170. // 获取集合对象
  171. func (p *Component) Collection(name string, opts ...options.Lister[options.CollectionOptions]) *mongo.Collection {
  172. return p.db.Collection(name, opts...)
  173. }
  174. // 查找一个对象
  175. func (p *Component) FindOne(retTable dbFacade.Table, filter bson.M, opts ...options.Lister[options.FindOneOptions]) error {
  176. if len(opts) < 1 {
  177. opts = append(opts, options.FindOne())
  178. }
  179. return p.Collection(retTable.TableName()).FindOne(context.TODO(), filter, opts...).Decode(retTable)
  180. }
  181. // 分页
  182. func Pagination[T dbFacade.Table](collection *mongo.Collection, ctx context.Context, filter bson.M, projection bson.M, sortOpts bson.D,
  183. pageNum, pageSize int32, isTotal bool) ([]T, int64, error) {
  184. if pageNum < 1 {
  185. pageNum = 1
  186. }
  187. if pageSize < 1 {
  188. pageSize = 1
  189. }
  190. if len(sortOpts) < 1 {
  191. sortOpts = bson.D{{
  192. Key: "_id",
  193. Value: 1,
  194. }}
  195. }
  196. if filter == nil {
  197. filter = bson.M{}
  198. }
  199. if projection == nil {
  200. projection = bson.M{}
  201. }
  202. var (
  203. skipCount = (pageNum - 1) * pageSize
  204. findOpts = options.Find().SetSkip(int64(skipCount)).SetLimit(int64(pageSize)).SetSort(sortOpts).SetProjection(projection)
  205. )
  206. cursor, err := collection.Find(ctx, filter, findOpts)
  207. if err != nil {
  208. return nil, 0, err
  209. }
  210. defer cursor.Close(ctx)
  211. var results []T
  212. for cursor.Next(ctx) {
  213. var item T
  214. err := cursor.Decode(&item)
  215. if err != nil {
  216. return nil, 0, err
  217. }
  218. results = append(results, item)
  219. }
  220. if err := cursor.Err(); err != nil {
  221. return nil, 0, err
  222. }
  223. var total int64 = 0
  224. if isTotal {
  225. total, err = collection.CountDocuments(ctx, filter)
  226. if err != nil {
  227. return nil, 0, err
  228. }
  229. }
  230. return results, total, nil
  231. }