synthesis.go 31 KB

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