synthesis.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. package service
  2. import (
  3. "context"
  4. "math"
  5. "time"
  6. mhayaTime "github.com/mhaya/extend/time"
  7. "github.com/mhaya/game/game_cluster/internal/code"
  8. "github.com/mhaya/game/game_cluster/internal/constant"
  9. "github.com/mhaya/game/game_cluster/internal/mdb"
  10. "github.com/mhaya/game/game_cluster/internal/mdb/eventmodels"
  11. "github.com/mhaya/game/game_cluster/internal/mdb/models"
  12. "github.com/mhaya/game/game_cluster/nodes/webadmin/common"
  13. "github.com/mhaya/game/game_cluster/nodes/webadmin/entity"
  14. "github.com/mhaya/game/game_cluster/nodes/webadmin/model"
  15. mhayaLogger "github.com/mhaya/logger"
  16. "github.com/spf13/cast"
  17. "go.mongodb.org/mongo-driver/bson"
  18. "go.mongodb.org/mongo-driver/bson/primitive"
  19. "go.mongodb.org/mongo-driver/mongo"
  20. "go.mongodb.org/mongo-driver/mongo/options"
  21. "gorm.io/gorm"
  22. )
  23. type Synthesis struct {
  24. db *mongo.Database
  25. }
  26. func NewSynthesis() *Synthesis {
  27. return &Synthesis{
  28. db: mdb.MDB,
  29. }
  30. }
  31. // 综合统计
  32. func (s *Synthesis) Overview(req entity.OverviewReq) (*entity.OverviewResp, *code.Result) {
  33. page, pageSize := checkPageParam(req.Page, req.Size)
  34. // 构建查询条件
  35. filter := bson.M{}
  36. if req.UserName != "" {
  37. filter["userName"] = req.UserName
  38. }
  39. if req.OpenId != "" {
  40. filter["openId"] = req.OpenId
  41. }
  42. // 设置分页选项
  43. findOptions := options.Find()
  44. findOptions.SetSkip(int64((page - 1) * pageSize))
  45. findOptions.SetLimit(int64(pageSize))
  46. findOptions.SetSort(bson.D{{"createTime", -1}})
  47. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  48. defer cancel()
  49. collection := mdb.MDB.Collection(constant.CNamePlayer)
  50. // 查询数据
  51. var results []*entity.OverviewDetail
  52. cursor, err := collection.Find(ctx, filter, findOptions)
  53. if err != nil {
  54. mhayaLogger.Warnf("Overview Find error:%v", err)
  55. return nil, common.NewResult(code.InternalError)
  56. }
  57. defer cursor.Close(ctx)
  58. // 解析结果
  59. for cursor.Next(ctx) {
  60. var result entity.OverviewDetail
  61. if err := cursor.Decode(&result); err != nil {
  62. mhayaLogger.Warnf("Overview Decode error:%v", err)
  63. return nil, common.NewResult(code.InternalError)
  64. }
  65. results = append(results, &result)
  66. }
  67. if err := cursor.Err(); err != nil {
  68. mhayaLogger.Warnf("Overview cursor error:%v", err)
  69. return nil, common.NewResult(code.InternalError)
  70. }
  71. // TODO 综合统计相关信息
  72. // TODO 获取总数total
  73. var count int64
  74. return &entity.OverviewResp{
  75. Details: results,
  76. Total: count,
  77. }, nil
  78. }
  79. // 统计用户相关信息
  80. func (s *Synthesis) UserList(req entity.UserListReq) (*entity.UserListResp, *code.Result) {
  81. page, pageSize := checkPageParam(req.Page, req.Size)
  82. // 构建查询条件
  83. filter := bson.M{}
  84. if req.UserName != "" {
  85. filter["userName"] = req.UserName
  86. }
  87. if req.OpenId != "" {
  88. filter["openId"] = req.OpenId
  89. }
  90. // 设置分页选项
  91. findOptions := options.Find()
  92. findOptions.SetSkip(int64((page - 1) * pageSize))
  93. findOptions.SetLimit(int64(pageSize))
  94. findOptions.SetSort(bson.D{{"createTime", -1}})
  95. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  96. defer cancel()
  97. collection := mdb.MDB.Collection(constant.CNamePlayer)
  98. // 查询数据
  99. var results []*entity.UserListDetail
  100. cursor, err := collection.Find(ctx, filter, findOptions)
  101. if err != nil {
  102. mhayaLogger.Warnf("UserList Find error:%v", err)
  103. return nil, common.NewResult(code.InternalError)
  104. }
  105. defer cursor.Close(ctx)
  106. // 解析结果
  107. for cursor.Next(ctx) {
  108. var result entity.UserListDetail
  109. if err := cursor.Decode(&result); err != nil {
  110. mhayaLogger.Warnf("UserList Decode error:%v", err)
  111. return nil, common.NewResult(code.InternalError)
  112. }
  113. results = append(results, &result)
  114. }
  115. if err := cursor.Err(); err != nil {
  116. mhayaLogger.Warnf("UserList cursor error:%v", err)
  117. return nil, common.NewResult(code.InternalError)
  118. }
  119. // TODO UserList 统计用户相关信息
  120. // TODO 获取总数total
  121. var count int64
  122. return &entity.UserListResp{
  123. Details: results,
  124. Total: count,
  125. }, nil
  126. }
  127. func (s *Synthesis) FindMDBUserLogDaily(req entity.UserLogDailyReq) (*entity.UserLogDailyResp, *code.Result) {
  128. page, pageSize := checkPageParam(req.Page, req.Size)
  129. // 定义上下文
  130. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  131. defer cancel()
  132. // 指定集合
  133. collection := mdb.MDB.Collection(constant.CNamePlayerDailyRecord)
  134. // 构建查询条件 - 如果查询值为空那就不添加查询条件
  135. filter := bson.M{}
  136. if req.StartTime != 0 {
  137. filter["daily"] = bson.M{
  138. "$gte": req.StartTime,
  139. "$lte": req.EndTime,
  140. }
  141. }
  142. if req.Platform != "" && req.Platform != "all" {
  143. filter["platform"] = req.Platform
  144. }
  145. if req.Channel != "" {
  146. filter["channel"] = req.Channel
  147. }
  148. // 计算总数
  149. count, err := collection.CountDocuments(ctx, filter)
  150. if err != nil {
  151. mhayaLogger.Warnf("FindMDBUserLogDaily CountDocuments error:%v", err)
  152. return nil, common.NewResult(code.InternalError)
  153. }
  154. // 执行查询
  155. opts := options.Find()
  156. opts.SetSkip(int64((page - 1) * pageSize))
  157. opts.SetLimit(int64(pageSize))
  158. opts.SetSort(bson.D{{"daily", -1}})
  159. cursor, err := collection.Find(ctx, filter, opts)
  160. if err != nil {
  161. mhayaLogger.Warnf("FindMDBUserLogDaily Find error:%v", err)
  162. return nil, common.NewResult(code.InternalError)
  163. }
  164. defer cursor.Close(ctx)
  165. // 解析查询结果
  166. var results []*entity.UserLogDailyDetail
  167. for cursor.Next(ctx) {
  168. var result *entity.UserLogDailyDetail
  169. err := cursor.Decode(&result)
  170. if err != nil {
  171. mhayaLogger.Warnf("FindMDBUserLogDaily Decode error:%v", err)
  172. return nil, common.NewResult(code.InternalError)
  173. }
  174. results = append(results, result)
  175. }
  176. // 同一天的数据全部放到platform= ALl 且数据累加
  177. // 如果没有platform=all 的那就新增一个 同一个时间段只能有一个 platform=all
  178. // 将同一天的数据累加到platform=all的记录中
  179. allPlatformRecordMap := make(map[int64]*entity.UserLogDailyDetail)
  180. for _, result := range results {
  181. allPlatformRecordMap[result.Timestamp] = &entity.UserLogDailyDetail{
  182. Timestamp: result.Timestamp,
  183. Platform: "all",
  184. }
  185. }
  186. for _, v := range results {
  187. if v.Timestamp == allPlatformRecordMap[v.Timestamp].Timestamp {
  188. allPlatformRecordMap[v.Timestamp].Registered += v.Registered
  189. allPlatformRecordMap[v.Timestamp].LoggedIn += v.LoggedIn
  190. allPlatformRecordMap[v.Timestamp].NewActive += v.NewActive
  191. allPlatformRecordMap[v.Timestamp].OldActive += v.OldActive
  192. allPlatformRecordMap[v.Timestamp].TotalPoints += v.TotalPoints
  193. allPlatformRecordMap[v.Timestamp].UProduced += v.UProduced
  194. allPlatformRecordMap[v.Timestamp].UCashout += v.UCashout
  195. allPlatformRecordMap[v.Timestamp].NewLogin += v.NewLogin
  196. allPlatformRecordMap[v.Timestamp].OldLogin += v.OldLogin
  197. }
  198. }
  199. // 替换原有结果
  200. for _, record := range allPlatformRecordMap {
  201. results = append(results, record)
  202. }
  203. return &entity.UserLogDailyResp{
  204. Details: results,
  205. Total: count,
  206. }, nil
  207. }
  208. // FindWithdrawal 根据请求查询提现记录
  209. func (s *Synthesis) FindWithdrawal(req entity.UserWithdrawalReq) (*entity.UserWithdrawalResp, *code.Result) {
  210. page, pageSize := checkPageParam(req.Page, req.Size)
  211. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  212. defer cancel()
  213. collection := mdb.MDB.Collection(constant.CNameCashOutRecord)
  214. // 构建过滤器
  215. filter := bson.M{}
  216. if req.UserName != "" {
  217. filter["userName"] = req.UserName
  218. }
  219. if req.NickName != "" {
  220. filter["nickName"] = req.NickName
  221. }
  222. if req.ID != "" {
  223. filter["_id"], _ = primitive.ObjectIDFromHex(req.ID)
  224. }
  225. if req.Address != "" {
  226. filter["address"] = req.Address
  227. }
  228. if req.State > 0 {
  229. filter["state"] = req.State
  230. }
  231. if req.Withdrawal > 0 {
  232. filter["withdrawal"] = req.Withdrawal
  233. }
  234. if req.StartTime > 0 && req.EndTime > 0 && req.StartTime <= req.EndTime {
  235. filter["createAt"] = bson.M{
  236. "$gte": req.StartTime,
  237. "$lte": req.EndTime,
  238. }
  239. }
  240. if req.AmountMin > 0 && req.AmountMax > 0 && req.AmountMin <= req.AmountMax {
  241. filter["amount"] = bson.M{
  242. "$gte": req.AmountMin,
  243. "$lte": req.AmountMax,
  244. }
  245. }
  246. if req.AfterAmountMin > 0 && req.AfterAmountMax > 0 && req.AfterAmountMin <= req.AfterAmountMax {
  247. filter["after_amount"] = bson.M{
  248. "$gte": req.AfterAmountMin,
  249. "$lte": req.AfterAmountMax,
  250. }
  251. }
  252. // 设置分页选项
  253. findOptions := options.Find()
  254. findOptions.SetSkip(int64((page - 1) * pageSize))
  255. findOptions.SetLimit(int64(pageSize))
  256. findOptions.SetSort(bson.D{{"createAt", -1}})
  257. // 获取总数total
  258. count, err := collection.CountDocuments(ctx, filter)
  259. if err != nil {
  260. mhayaLogger.Warnf("FindWithdrawal CountDocuments error:%v", err)
  261. return nil, common.NewResult(code.InternalError)
  262. }
  263. // 查询数据
  264. var results []*entity.UserWithdrawalDetail
  265. cursor, err := collection.Find(ctx, filter, findOptions)
  266. if err != nil {
  267. mhayaLogger.Warnf("FindWithdrawal Find error:%v", err)
  268. return nil, common.NewResult(code.InternalError)
  269. }
  270. defer cursor.Close(ctx)
  271. // 解析结果
  272. for cursor.Next(ctx) {
  273. var result entity.UserWithdrawalDetail
  274. if err := cursor.Decode(&result); err != nil {
  275. mhayaLogger.Warnf("FindWithdrawal Decode error:%v", err)
  276. return nil, common.NewResult(code.InternalError)
  277. }
  278. results = append(results, &result)
  279. }
  280. if err := cursor.Err(); err != nil {
  281. mhayaLogger.Warnf("FindWithdrawal cursor error:%v", err)
  282. return nil, common.NewResult(code.InternalError)
  283. }
  284. return &entity.UserWithdrawalResp{
  285. Details: results,
  286. Total: count,
  287. }, nil
  288. }
  289. // 导出提现记录
  290. func (s *Synthesis) WithdrawalExport(req entity.UserWithdrawalExportReq) (*entity.UserWithdrawalResp, *code.Result) {
  291. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  292. defer cancel()
  293. collection := mdb.MDB.Collection(constant.CNameCashOutRecord)
  294. // 构建过滤器
  295. filter := bson.M{}
  296. if req.UserName != "" {
  297. filter["userName"] = req.UserName
  298. }
  299. if req.NickName != "" {
  300. filter["nickName"] = req.NickName
  301. }
  302. if req.ID != "" {
  303. filter["_id"], _ = primitive.ObjectIDFromHex(req.ID)
  304. }
  305. if req.Address != "" {
  306. filter["address"] = req.Address
  307. }
  308. if req.State > 0 {
  309. filter["state"] = req.State
  310. }
  311. if req.Withdrawal > 0 {
  312. filter["withdrawal"] = req.Withdrawal
  313. }
  314. if req.StartTime > 0 && req.EndTime > 0 && req.StartTime <= req.EndTime {
  315. filter["createAt"] = bson.M{
  316. "$gte": req.StartTime,
  317. "$lte": req.EndTime,
  318. }
  319. }
  320. if req.AmountMin > 0 && req.AmountMax > 0 && req.AmountMin <= req.AmountMax {
  321. filter["amount"] = bson.M{
  322. "$gte": req.AmountMin,
  323. "$lte": req.AmountMax,
  324. }
  325. }
  326. if req.AfterAmountMin > 0 && req.AfterAmountMax > 0 && req.AfterAmountMin <= req.AfterAmountMax {
  327. filter["after_amount"] = bson.M{
  328. "$gte": req.AfterAmountMin,
  329. "$lte": req.AfterAmountMax,
  330. }
  331. }
  332. findOptions := options.Find()
  333. findOptions.SetSort(bson.D{{"createAt", -1}})
  334. // 获取总数total
  335. count, err := collection.CountDocuments(ctx, filter)
  336. if err != nil {
  337. mhayaLogger.Warnf("WithdrawalExportData CountDocuments error:%v", err)
  338. return nil, common.NewResult(code.InternalError)
  339. }
  340. // 查询数据
  341. var results []*entity.UserWithdrawalDetail
  342. cursor, err := collection.Find(ctx, filter, findOptions)
  343. if err != nil {
  344. mhayaLogger.Warnf("WithdrawalExportData Find error:%v", err)
  345. return nil, common.NewResult(code.InternalError)
  346. }
  347. defer cursor.Close(ctx)
  348. // 解析结果
  349. for cursor.Next(ctx) {
  350. var result entity.UserWithdrawalDetail
  351. if err := cursor.Decode(&result); err != nil {
  352. mhayaLogger.Warnf("WithdrawalExportData Decode error:%v", err)
  353. return nil, common.NewResult(code.InternalError)
  354. }
  355. results = append(results, &result)
  356. }
  357. if err := cursor.Err(); err != nil {
  358. mhayaLogger.Warnf("WithdrawalExportData cursor error:%v", err)
  359. return nil, common.NewResult(code.InternalError)
  360. }
  361. return &entity.UserWithdrawalResp{
  362. Details: results,
  363. Total: count,
  364. }, nil
  365. }
  366. // WithdrawalStatus 更新提现状态
  367. func (s *Synthesis) WithdrawalStatus(req entity.UserWithdrawalStatus) *code.Result {
  368. // ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  369. // defer cancel()
  370. collection := mdb.MDB.Collection(constant.CNameCashOutRecord)
  371. // 更新条件
  372. updateCondition := bson.M{"userName": req.UserName}
  373. // 更新内容
  374. updateContent := bson.M{"$set": bson.M{"status": req.Status}}
  375. // 设置更新选项
  376. updateOptions := options.Update() // 设置 upsert 选项
  377. // 执行更新操作
  378. _, err := collection.UpdateOne(context.TODO(), updateCondition, updateContent, updateOptions)
  379. if err != nil {
  380. mhayaLogger.Warnf("WithdrawalStatus UpdateOne error:%v", err)
  381. return common.NewResult(code.InternalError)
  382. }
  383. return nil
  384. }
  385. // WithdrawalStatusBatch 更新提现状态
  386. func (s *Synthesis) WithdrawalStatusBatch(req entity.UserWithdrawalStatusBatch) *code.Result {
  387. collection := mdb.MDB.Collection(constant.CNameCashOutRecord)
  388. if len(req.ID) == 0 {
  389. return common.NewResult(code.ParamError)
  390. }
  391. for _, id := range req.ID {
  392. objID := primitive.ObjectID{}
  393. objID, _ = primitive.ObjectIDFromHex(id)
  394. updateCondition := bson.M{"_id": objID}
  395. updateContent := bson.M{}
  396. withdrawal := models.CashOutRecord{}
  397. err := collection.FindOne(context.TODO(), updateCondition).Decode(&withdrawal)
  398. if err != nil {
  399. mhayaLogger.Warnf("WithdrawalStatusBatch FindOne error:%v", err)
  400. continue
  401. }
  402. if req.Withdrawal != 0 {
  403. if withdrawal.Status == 1 {
  404. updateContent = bson.M{"$set": bson.M{"withdrawal": req.Withdrawal}}
  405. } else {
  406. continue
  407. }
  408. }
  409. if req.Status > 0 {
  410. if withdrawal.Status != 0 {
  411. continue
  412. }
  413. updateContent = bson.M{"$set": bson.M{"status": req.Status}}
  414. }
  415. updateOptions := options.Update().SetUpsert(true)
  416. _, err = collection.UpdateOne(context.TODO(), updateCondition, updateContent, updateOptions)
  417. if err != nil {
  418. mhayaLogger.Warnf("WithdrawalStatusBatch UpdateOne error:%v", err)
  419. continue
  420. }
  421. }
  422. return nil
  423. }
  424. // FindUserCountryCount 查询用户国家分布
  425. //
  426. // 返回值为 UserCountryResp 的切片和错误。
  427. func (s *Synthesis) FindUserCountryCount() (*entity.UserCountryResp, *code.Result) {
  428. // 选择数据库和集合
  429. collection := mdb.MDB.Collection(constant.CNamePlayerCountryByIPStat)
  430. // 定义聚合管道
  431. // 定义聚合管道
  432. pipeline := []bson.D{
  433. {
  434. {Key: "$project", Value: bson.D{
  435. {Key: "playerRegisterCountry", Value: bson.D{{Key: "$objectToArray", Value: "$playerRegisterCountry"}}},
  436. }},
  437. },
  438. {
  439. {Key: "$unwind", Value: "$playerRegisterCountry"},
  440. },
  441. {
  442. {Key: "$group", Value: bson.D{
  443. {Key: "_id", Value: "$playerRegisterCountry.k"},
  444. {Key: "totalValue", Value: bson.D{{Key: "$sum", Value: "$playerRegisterCountry.v"}}},
  445. }},
  446. },
  447. {
  448. {Key: "$project", Value: bson.D{
  449. {Key: "_id", Value: 0},
  450. {Key: "countryKey", Value: "$_id"},
  451. {Key: "totalValue", Value: 1},
  452. }},
  453. },
  454. }
  455. // 执行聚合查询
  456. cursor, err := collection.Aggregate(context.TODO(), pipeline)
  457. if err != nil {
  458. mhayaLogger.Warnf("FindUserCountryCount Aggregate error:%v", err)
  459. return nil, common.NewResult(code.InternalError)
  460. }
  461. defer cursor.Close(context.TODO())
  462. // 遍历查询结果
  463. var results []bson.M
  464. if err := cursor.All(context.TODO(), &results); err != nil {
  465. mhayaLogger.Warnf("FindUserCountryCount All error:%v", err)
  466. return nil, common.NewResult(code.InternalError)
  467. }
  468. var totalIPCount int64
  469. var data []*entity.UserCountryDetail
  470. // 将结果转换为 UserCountryDetail
  471. for _, r := range results {
  472. var resp entity.UserCountryDetail
  473. resp.Country = r["countryKey"].(string)
  474. resp.IPCount = int(r["totalValue"].(int32))
  475. totalIPCount += int64(resp.IPCount)
  476. data = append(data, &resp)
  477. }
  478. for _, v := range data {
  479. // 保留小数点后两位
  480. v.Percentage = math.Round(float64(v.IPCount)/float64(totalIPCount)*10000) / 100
  481. }
  482. // 根据阈值过滤结果
  483. otherCount := 0
  484. otherPercentage := 0.00
  485. filteredResults := make([]*entity.UserCountryDetail, 0)
  486. threshold := 1.00
  487. for _, r := range data {
  488. if r.Percentage >= threshold {
  489. filteredResults = append(filteredResults, r)
  490. // 保留小数点后两位
  491. r.Percentage = math.Round(r.Percentage*100) / 100
  492. otherPercentage += r.Percentage
  493. } else {
  494. otherCount += r.IPCount
  495. }
  496. }
  497. // 将其他国家添加到过滤后的结果中
  498. if otherCount > 0 {
  499. p := 100.00 - math.Round(otherPercentage*100)/100
  500. filteredResults = append(filteredResults, &entity.UserCountryDetail{
  501. Country: "other",
  502. IPCount: otherCount,
  503. Percentage: math.Round(p*100) / 100,
  504. })
  505. }
  506. return &entity.UserCountryResp{
  507. Details: filteredResults,
  508. }, nil
  509. }
  510. // FindUserRetention UserRetentionResp 用户留存率
  511. // 1. 获取指定日期范围内的注册用户数量
  512. // 2. 获取指定日期范围内的活跃用户数量
  513. // 3. 计算留存率
  514. // 4. 返回结果
  515. func (s *Synthesis) FindUserRetention(req entity.UserRetentionReq) (*entity.UserRetentionResp, *code.Result) {
  516. playerPreserve := models.GetPlayerPreserve(req.StartTime, req.EndTime)
  517. // 查询数据
  518. var results []*entity.UserRetentionDetail
  519. for key, v := range playerPreserve {
  520. var retention entity.Retention
  521. for _, vv := range v {
  522. if vv.ID == 1 {
  523. retention.Day1 = entity.DayRetention{
  524. LoggedIn: vv.Ratio,
  525. LoginDate: key,
  526. }
  527. }
  528. if vv.ID == 3 {
  529. retention.Day3 = entity.DayRetention{
  530. LoggedIn: vv.Ratio,
  531. LoginDate: key,
  532. }
  533. }
  534. if vv.ID == 7 {
  535. retention.Day7 = entity.DayRetention{
  536. LoggedIn: vv.Ratio,
  537. LoginDate: key,
  538. }
  539. }
  540. if vv.ID == 14 {
  541. retention.Day14 = entity.DayRetention{
  542. LoggedIn: vv.Ratio,
  543. LoginDate: key,
  544. }
  545. }
  546. if vv.ID == 30 {
  547. retention.Day30 = entity.DayRetention{
  548. LoggedIn: vv.Ratio,
  549. LoginDate: key,
  550. }
  551. }
  552. }
  553. results = append(results, &entity.UserRetentionDetail{
  554. RegistrationDate: key,
  555. RetentionData: retention,
  556. })
  557. }
  558. return &entity.UserRetentionResp{
  559. Details: results,
  560. }, nil
  561. }
  562. // FindUserLevel 用户等级统计
  563. func (s *Synthesis) FindUserLevel() (*entity.UserLevelCountResp, *code.Result) {
  564. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  565. defer cancel()
  566. collection := mdb.MDB.Collection(constant.CNamePlayerLevelStat)
  567. // 查询所有文档
  568. cursor, err := collection.Find(ctx, bson.M{})
  569. if err != nil {
  570. mhayaLogger.Warnf("FindUserLevel Find error:%v", err)
  571. return nil, common.NewResult(code.InternalError)
  572. }
  573. defer cursor.Close(ctx)
  574. var results []*entity.UserLevelCountDetail
  575. var reData []*models.PlayerLevelStat
  576. for cursor.Next(ctx) {
  577. var result models.PlayerLevelStat
  578. if err := cursor.Decode(&result); err != nil {
  579. mhayaLogger.Warnf("FindUserLevel Decode error:%v", err)
  580. return nil, common.NewResult(code.InternalError)
  581. }
  582. reData = append(reData, &result)
  583. }
  584. if err := cursor.Err(); err != nil {
  585. mhayaLogger.Warnf("FindUserLevel cursor error:%v", err)
  586. return nil, common.NewResult(code.InternalError)
  587. }
  588. dataMap := make(map[string]int)
  589. for _, v := range reData {
  590. for key, v1 := range v.ServerLevel {
  591. dataMap[key] += v1
  592. }
  593. }
  594. for k, v := range dataMap {
  595. results = append(results, &entity.UserLevelCountDetail{
  596. Level: cast.ToInt(k),
  597. UserCount: v,
  598. })
  599. }
  600. return &entity.UserLevelCountResp{
  601. Details: results,
  602. }, nil
  603. }
  604. func (s *Synthesis) InsertRecord(param model.UserOperationLog) {
  605. mhayaLogger.Warnf("InsertRecord param:%#v", param)
  606. record := new(model.UserOperationLog)
  607. collection := mdb.MDB.Collection(record.TableName())
  608. insertData := bson.M{}
  609. insertData["user_name"] = param.Username
  610. insertData["role_id"] = param.RoleId
  611. insertData["url"] = param.Path
  612. insertData["method"] = param.Method
  613. insertData["status_code"] = param.StatusCode
  614. insertData["dur"] = param.Dur
  615. insertData["client_ip"] = param.ClientIP
  616. insertData["error_message"] = param.ErrorMessage
  617. insertData["created_at"] = mhayaTime.Now().Unix()
  618. _, err := collection.InsertOne(context.Background(), insertData)
  619. if err != nil {
  620. mhayaLogger.Warnf("InsertRecord InsertOne error:%v", err)
  621. return
  622. }
  623. }
  624. func (s *Synthesis) RecordList(req entity.RecordListReq) (*entity.RecordListResp, *code.Result) {
  625. page, pageSize := checkPageParam(req.Page, req.Size)
  626. var records *[]eventmodels.BackendOperationEventContent
  627. // TODO 筛选条件
  628. where := &eventmodels.BackendOperationEventContent{
  629. UserBasic: eventmodels.UserBasic{
  630. UserName: req.UserName,
  631. },
  632. EventBasic: eventmodels.EventBasic{
  633. ServerId: "",
  634. IsSuccess: false,
  635. CreateAt: 0,
  636. },
  637. RoleId: req.RoleId,
  638. Path: "/v1/admin/user/log",
  639. Method: "",
  640. StatusCode: 0,
  641. Dur: 0,
  642. ClientIP: "",
  643. ErrorMessage: "",
  644. }
  645. db := mdb.LogstashDB.Model(&eventmodels.BackendOperationEventContent{}).Where(where).Order("create_at")
  646. pages := Paginate(db, page, pageSize)
  647. err := db.Scopes(pages.Limit).Find(&records).Error
  648. if err != nil && err != gorm.ErrRecordNotFound {
  649. mhayaLogger.Warnf("RecordList Find error:%v", err)
  650. return nil, common.NewResult(code.InternalError)
  651. }
  652. var results []*entity.RecordListDetail
  653. for _, v := range *records {
  654. results = append(results, &entity.RecordListDetail{
  655. Username: v.UserName,
  656. RoleId: v.RoleId,
  657. Path: v.Path,
  658. Method: v.Method,
  659. StatusCode: v.StatusCode,
  660. Dur: v.Dur,
  661. ClientIP: v.ClientIP,
  662. ErrorMessage: v.ErrorMessage,
  663. CreatedAt: v.CreateAt,
  664. })
  665. }
  666. return &entity.RecordListResp{
  667. Details: results,
  668. Total: pages.Count,
  669. }, nil
  670. }
  671. // 转盘统计
  672. func (s *Synthesis) Turntable(req entity.TurntableReq) (*entity.TurntableResp, *code.Result) {
  673. page, pageSize := checkPageParam(req.Page, req.Size)
  674. // 构建查询条件
  675. filter := bson.M{}
  676. if req.UserName != "" {
  677. filter["userName"] = req.UserName
  678. }
  679. if req.OpenId != "" {
  680. filter["openId"] = req.OpenId
  681. }
  682. // 设置分页选项
  683. findOptions := options.Find()
  684. findOptions.SetSkip(int64((page - 1) * pageSize))
  685. findOptions.SetLimit(int64(pageSize))
  686. findOptions.SetSort(bson.D{{"createTime", -1}})
  687. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  688. defer cancel()
  689. collection := mdb.MDB.Collection(constant.CNamePlayer)
  690. // 查询数据
  691. var results []*entity.TurntableDetail
  692. cursor, err := collection.Find(ctx, filter, findOptions)
  693. if err != nil {
  694. mhayaLogger.Warnf("Turntable Find error:%v", err)
  695. return nil, common.NewResult(code.InternalError)
  696. }
  697. defer cursor.Close(ctx)
  698. // 解析结果
  699. for cursor.Next(ctx) {
  700. var result entity.TurntableDetail
  701. if err := cursor.Decode(&result); err != nil {
  702. mhayaLogger.Warnf("Turntable Decode error:%v", err)
  703. return nil, common.NewResult(code.InternalError)
  704. }
  705. results = append(results, &result)
  706. }
  707. if err := cursor.Err(); err != nil {
  708. mhayaLogger.Warnf("Turntable cursor error:%v", err)
  709. return nil, common.NewResult(code.InternalError)
  710. }
  711. // TODO 转盘统计相关信息
  712. // TODO 获取总数total
  713. var count int64
  714. return &entity.TurntableResp{
  715. Details: results,
  716. Total: count,
  717. }, nil
  718. }
  719. // 资产统计
  720. func (s *Synthesis) Assets(req entity.AssetsReq) (*entity.AssetsResp, *code.Result) {
  721. page, pageSize := checkPageParam(req.Page, req.Size)
  722. // 构建查询条件
  723. filter := bson.M{}
  724. if req.UserName != "" {
  725. filter["userName"] = req.UserName
  726. }
  727. if req.OpenId != "" {
  728. filter["openId"] = req.OpenId
  729. }
  730. // 设置分页选项
  731. findOptions := options.Find()
  732. findOptions.SetSkip(int64((page - 1) * pageSize))
  733. findOptions.SetLimit(int64(pageSize))
  734. findOptions.SetSort(bson.D{{"createTime", -1}})
  735. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  736. defer cancel()
  737. collection := mdb.MDB.Collection(constant.CNamePlayer)
  738. // 查询数据
  739. var results []*entity.AssetsDetail
  740. cursor, err := collection.Find(ctx, filter, findOptions)
  741. if err != nil {
  742. mhayaLogger.Warnf("Assets Find error:%v", err)
  743. return nil, common.NewResult(code.InternalError)
  744. }
  745. defer cursor.Close(ctx)
  746. // 解析结果
  747. for cursor.Next(ctx) {
  748. var result entity.AssetsDetail
  749. if err := cursor.Decode(&result); err != nil {
  750. mhayaLogger.Warnf("Assets Decode error:%v", err)
  751. return nil, common.NewResult(code.InternalError)
  752. }
  753. results = append(results, &result)
  754. }
  755. if err := cursor.Err(); err != nil {
  756. mhayaLogger.Warnf("Assets cursor error:%v", err)
  757. return nil, common.NewResult(code.InternalError)
  758. }
  759. // TODO Assets 统计相关信息
  760. // TODO 获取总数total
  761. var count int64
  762. return &entity.AssetsResp{
  763. Details: results,
  764. Total: count,
  765. }, nil
  766. }
  767. // 资产变动记录
  768. func (s *Synthesis) AssetsRecord(req entity.AssetsRecordReq) (*entity.AssetsRecordResp, *code.Result) {
  769. page, pageSize := checkPageParam(req.Page, req.Size)
  770. // 构建查询条件
  771. filter := bson.M{}
  772. if req.UserName != "" {
  773. filter["userName"] = req.UserName
  774. }
  775. if req.OpenId != "" {
  776. filter["openId"] = req.OpenId
  777. }
  778. // 设置分页选项
  779. findOptions := options.Find()
  780. findOptions.SetSkip(int64((page - 1) * pageSize))
  781. findOptions.SetLimit(int64(pageSize))
  782. findOptions.SetSort(bson.D{{"createTime", -1}})
  783. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  784. defer cancel()
  785. collection := mdb.MDB.Collection(constant.CNamePlayer)
  786. // 查询数据
  787. var results []*entity.AssetsRecordDetail
  788. cursor, err := collection.Find(ctx, filter, findOptions)
  789. if err != nil {
  790. mhayaLogger.Warnf("AssetsRecord Find error:%v", err)
  791. return nil, common.NewResult(code.InternalError)
  792. }
  793. defer cursor.Close(ctx)
  794. // 解析结果
  795. for cursor.Next(ctx) {
  796. var result entity.AssetsRecordDetail
  797. if err := cursor.Decode(&result); err != nil {
  798. mhayaLogger.Warnf("AssetsRecord Decode error:%v", err)
  799. return nil, common.NewResult(code.InternalError)
  800. }
  801. results = append(results, &result)
  802. }
  803. if err := cursor.Err(); err != nil {
  804. mhayaLogger.Warnf("AssetsRecord cursor error:%v", err)
  805. return nil, common.NewResult(code.InternalError)
  806. }
  807. // TODO 资产变动记录相关信息
  808. // TODO 获取总数total
  809. var count int64
  810. return &entity.AssetsRecordResp{
  811. Details: results,
  812. Total: count,
  813. }, nil
  814. }
  815. // 邀请统计
  816. func (s *Synthesis) Invite(req entity.InviteReq) (*entity.InviteResp, *code.Result) {
  817. page, pageSize := checkPageParam(req.Page, req.Size)
  818. // 构建查询条件
  819. filter := bson.M{}
  820. if req.UserName != "" {
  821. filter["userName"] = req.UserName
  822. }
  823. if req.OpenId != "" {
  824. filter["openId"] = req.OpenId
  825. }
  826. // 设置分页选项
  827. findOptions := options.Find()
  828. findOptions.SetSkip(int64((page - 1) * pageSize))
  829. findOptions.SetLimit(int64(pageSize))
  830. findOptions.SetSort(bson.D{{"createTime", -1}})
  831. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  832. defer cancel()
  833. collection := mdb.MDB.Collection(constant.CNamePlayer)
  834. // 查询数据
  835. var results []*entity.InviteDetail
  836. cursor, err := collection.Find(ctx, filter, findOptions)
  837. if err != nil {
  838. mhayaLogger.Warnf("Invite Find error:%v", err)
  839. return nil, common.NewResult(code.InternalError)
  840. }
  841. defer cursor.Close(ctx)
  842. // 解析结果
  843. for cursor.Next(ctx) {
  844. var result entity.InviteDetail
  845. if err := cursor.Decode(&result); err != nil {
  846. mhayaLogger.Warnf("Invite Decode error:%v", err)
  847. return nil, common.NewResult(code.InternalError)
  848. }
  849. results = append(results, &result)
  850. }
  851. if err := cursor.Err(); err != nil {
  852. mhayaLogger.Warnf("Invite cursor error:%v", err)
  853. return nil, common.NewResult(code.InternalError)
  854. }
  855. // TODO 邀请统计相关信息
  856. // TODO 获取总数total
  857. var count int64
  858. return &entity.InviteResp{
  859. Details: results,
  860. Total: count,
  861. }, nil
  862. }
  863. // 活跃统计
  864. func (s *Synthesis) Active(req entity.ActiveReq) (*entity.ActiveResp, *code.Result) {
  865. page, pageSize := checkPageParam(req.Page, req.Size)
  866. // 构建查询条件
  867. filter := bson.M{}
  868. if req.UserName != "" {
  869. filter["userName"] = req.UserName
  870. }
  871. if req.OpenId != "" {
  872. filter["openId"] = req.OpenId
  873. }
  874. // 设置分页选项
  875. findOptions := options.Find()
  876. findOptions.SetSkip(int64((page - 1) * pageSize))
  877. findOptions.SetLimit(int64(pageSize))
  878. findOptions.SetSort(bson.D{{"createTime", -1}})
  879. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  880. defer cancel()
  881. collection := mdb.MDB.Collection(constant.CNamePlayer)
  882. // 查询数据
  883. var results []*entity.ActiveDetail
  884. cursor, err := collection.Find(ctx, filter, findOptions)
  885. if err != nil {
  886. mhayaLogger.Warnf("Active Find error:%v", err)
  887. return nil, common.NewResult(code.InternalError)
  888. }
  889. defer cursor.Close(ctx)
  890. // 解析结果
  891. for cursor.Next(ctx) {
  892. var result entity.ActiveDetail
  893. if err := cursor.Decode(&result); err != nil {
  894. mhayaLogger.Warnf("Active Decode error:%v", err)
  895. return nil, common.NewResult(code.InternalError)
  896. }
  897. results = append(results, &result)
  898. }
  899. if err := cursor.Err(); err != nil {
  900. mhayaLogger.Warnf("Active cursor error:%v", err)
  901. return nil, common.NewResult(code.InternalError)
  902. }
  903. // TODO Active 活跃统计相关信息
  904. // TODO 获取总数total
  905. var count int64
  906. return &entity.ActiveResp{
  907. Details: results,
  908. Total: count,
  909. }, nil
  910. }
  911. // 任务完成度统计
  912. func (s *Synthesis) TaskCompletion(req entity.TaskCompletionReq) (*entity.TaskCompletionResp, *code.Result) {
  913. page, pageSize := checkPageParam(req.Page, req.Size)
  914. // 构建查询条件
  915. filter := bson.M{}
  916. if req.UserName != "" {
  917. filter["userName"] = req.UserName
  918. }
  919. if req.OpenId != "" {
  920. filter["openId"] = req.OpenId
  921. }
  922. // 设置分页选项
  923. findOptions := options.Find()
  924. findOptions.SetSkip(int64((page - 1) * pageSize))
  925. findOptions.SetLimit(int64(pageSize))
  926. findOptions.SetSort(bson.D{{"createTime", -1}})
  927. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  928. defer cancel()
  929. collection := mdb.MDB.Collection(constant.CNamePlayer)
  930. // 查询数据
  931. var results []*entity.TaskCompletionDetail
  932. cursor, err := collection.Find(ctx, filter, findOptions)
  933. if err != nil {
  934. mhayaLogger.Warnf("TaskCompletion Find error:%v", err)
  935. return nil, common.NewResult(code.InternalError)
  936. }
  937. defer cursor.Close(ctx)
  938. // 解析结果
  939. for cursor.Next(ctx) {
  940. var result entity.TaskCompletionDetail
  941. if err := cursor.Decode(&result); err != nil {
  942. mhayaLogger.Warnf("TaskCompletion Decode error:%v", err)
  943. return nil, common.NewResult(code.InternalError)
  944. }
  945. results = append(results, &result)
  946. }
  947. if err := cursor.Err(); err != nil {
  948. mhayaLogger.Warnf("TaskCompletion cursor error:%v", err)
  949. return nil, common.NewResult(code.InternalError)
  950. }
  951. // TODO 任务完成度统计相关信息
  952. // TODO 获取总数total
  953. var count int64
  954. return &entity.TaskCompletionResp{
  955. Details: results,
  956. Total: count,
  957. }, nil
  958. }
  959. // 用户行为检测
  960. func (s *Synthesis) BehaviorMonitoring(req entity.BehaviorMonitoringReq) (*entity.BehaviorMonitoringResp, *code.Result) {
  961. page, pageSize := checkPageParam(req.Page, req.Size)
  962. // 构建查询条件
  963. filter := bson.M{}
  964. if req.UserName != "" {
  965. filter["userName"] = req.UserName
  966. }
  967. if req.OpenId != "" {
  968. filter["openId"] = req.OpenId
  969. }
  970. // 设置分页选项
  971. findOptions := options.Find()
  972. findOptions.SetSkip(int64((page - 1) * pageSize))
  973. findOptions.SetLimit(int64(pageSize))
  974. findOptions.SetSort(bson.D{{"createTime", -1}})
  975. ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
  976. defer cancel()
  977. collection := mdb.MDB.Collection(constant.CNamePlayer)
  978. // 查询数据
  979. var results []*entity.BehaviorMonitoringDetail
  980. cursor, err := collection.Find(ctx, filter, findOptions)
  981. if err != nil {
  982. mhayaLogger.Warnf("BehaviorMonitoring Find error:%v", err)
  983. return nil, common.NewResult(code.InternalError)
  984. }
  985. defer cursor.Close(ctx)
  986. // 解析结果
  987. for cursor.Next(ctx) {
  988. var result entity.BehaviorMonitoringDetail
  989. if err := cursor.Decode(&result); err != nil {
  990. mhayaLogger.Warnf("BehaviorMonitoring Decode error:%v", err)
  991. return nil, common.NewResult(code.InternalError)
  992. }
  993. results = append(results, &result)
  994. }
  995. if err := cursor.Err(); err != nil {
  996. mhayaLogger.Warnf("BehaviorMonitoring cursor error:%v", err)
  997. return nil, common.NewResult(code.InternalError)
  998. }
  999. // TODO 用户行为检测相关信息
  1000. // TODO 获取总数total
  1001. var count int64
  1002. return &entity.BehaviorMonitoringResp{
  1003. Details: results,
  1004. Total: count,
  1005. }, nil
  1006. }