fighting.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "gadmin/config"
  6. "gadmin/internal/admin/consts"
  7. "gadmin/internal/admin/forms"
  8. "gadmin/internal/gorm/model"
  9. "gadmin/internal/gorm/query"
  10. "gadmin/utility/serializer"
  11. "github.com/gin-gonic/gin"
  12. "github.com/sirupsen/logrus"
  13. "github.com/xuri/excelize/v2"
  14. "io/ioutil"
  15. "os"
  16. "strings"
  17. "sync"
  18. "time"
  19. )
  20. var Fighting = new(sFighting)
  21. type sFighting struct{}
  22. func (s *sFighting) unionModel(ctx *gin.Context, where string) (unionALL string, err error) {
  23. type Table struct {
  24. Value string
  25. }
  26. dbName := config.GetDBName(os.Getenv("MYSQL_DSN"))
  27. var sql = "SELECT TABLE_NAME as `value` FROM information_schema.`TABLES` WHERE TABLE_SCHEMA ='" + dbName + "' AND TABLE_NAME LIKE 'chapter_logs_user_details_%'"
  28. tx := config.DB.Raw(sql)
  29. var tables []string
  30. if err = tx.Scan(&tables).Error; err != nil {
  31. return
  32. }
  33. logrus.Warnf("tables:%+v", tables)
  34. field := "id,server_id,user_id,event_id,chapter_id,room_id,user_created_at,event_at,extra,token,difficulty,event_at_ns"
  35. for k, table := range tables {
  36. if k == 0 {
  37. unionALL = fmt.Sprintf("SELECT %s FROM %s %s", field, table, where)
  38. continue
  39. }
  40. unionALL += fmt.Sprintf(" UNION ALL SELECT %s FROM %s %s", field, table, where)
  41. }
  42. return unionALL, nil
  43. }
  44. func (s *sFighting) List(ctx *gin.Context, req forms.FightingListReq) serializer.Response {
  45. var (
  46. models forms.ListRes
  47. lists []model.ChapterLogsUserDetail
  48. offset int64 = 0
  49. hasWhere bool
  50. where string
  51. count int64
  52. masterWhere string
  53. )
  54. if req.UserId > 0 {
  55. if hasWhere {
  56. where += fmt.Sprintf(" and user_id = %v", req.UserId)
  57. } else {
  58. hasWhere = true
  59. where += fmt.Sprintf(" where user_id = %v", req.UserId)
  60. }
  61. }
  62. if req.Id > 0 {
  63. if hasWhere {
  64. where += fmt.Sprintf(" and id = %v", req.Id)
  65. } else {
  66. hasWhere = true
  67. where += fmt.Sprintf(" where id = %v", req.Id)
  68. }
  69. }
  70. if len(req.EventAt) == 2 {
  71. if hasWhere {
  72. where += fmt.Sprintf(" and event_at BETWEEN %v AND %v", int32(req.EventAt[0]), int32(req.EventAt[1]))
  73. } else {
  74. hasWhere = true
  75. where += fmt.Sprintf(" where event_at BETWEEN %v AND %v", int32(req.EventAt[0]), int32(req.EventAt[1]))
  76. }
  77. }
  78. if req.ChapterId > 0 {
  79. if hasWhere {
  80. where += fmt.Sprintf(" and chapter_id = %v", req.ChapterId)
  81. } else {
  82. hasWhere = true
  83. where += fmt.Sprintf(" where chapter_id = %v", req.ChapterId)
  84. }
  85. }
  86. if req.Difficulty > -1 {
  87. if hasWhere {
  88. where += fmt.Sprintf(" and difficulty = %v", req.Difficulty)
  89. } else {
  90. hasWhere = true
  91. where += fmt.Sprintf(" where difficulty = %v", req.Difficulty)
  92. }
  93. }
  94. unionALL, err := s.unionModel(ctx, where)
  95. if err != nil {
  96. return serializer.Err(consts.CodeParamErr, "查询出错 unionModel", err)
  97. }
  98. countSql := fmt.Sprintf("SELECT count(1) FROM(%s) as a", unionALL)
  99. getSql := fmt.Sprintf("SELECT * FROM(%s) as a", unionALL)
  100. // 排序
  101. masterWhere += " ORDER BY event_at DESC"
  102. // 分页
  103. req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage)
  104. config.DB.Raw(countSql + masterWhere).Scan(&count)
  105. if count > 0 {
  106. masterWhere += fmt.Sprintf(" LIMIT %v", req.PerPage)
  107. if offset > 0 {
  108. masterWhere += fmt.Sprintf(" OFFSET %v", offset)
  109. }
  110. config.DB.Raw(getSql + masterWhere).Scan(&lists)
  111. }
  112. models.List = lists
  113. models.Page = req.Page
  114. models.PerPage = req.PerPage
  115. models.PageCount = (count + req.PerPage - 1) / req.PerPage
  116. return serializer.Suc(models)
  117. }
  118. func (s *sFighting) GetExtra(extraPath string) serializer.Response {
  119. // 处理从ocs获取extra逻辑
  120. if strings.Contains(extraPath, "{") {
  121. return serializer.Suc(extraPath)
  122. }
  123. if response, err := config.GetCOSClient().Object.Get(context.Background(), extraPath, nil); err != nil {
  124. return serializer.Err(consts.CodeParamErr, "从cos中获取extra发生错误", err)
  125. } else {
  126. extra, err := ioutil.ReadAll(response.Body)
  127. response.Body.Close()
  128. if err != nil {
  129. return serializer.Err(consts.CodeParamErr, "读取cos数据出错", err)
  130. } else {
  131. return serializer.Suc(string(extra))
  132. }
  133. }
  134. }
  135. func (s *sFighting) Export(ctx *gin.Context, req forms.FightingExportReq) {
  136. var (
  137. lists []model.ChapterLogsUserDetail
  138. hasWhere bool
  139. where string
  140. masterWhere string
  141. )
  142. if req.UserId > 0 {
  143. if hasWhere {
  144. where += fmt.Sprintf(" and user_id = %v", req.UserId)
  145. } else {
  146. hasWhere = true
  147. where += fmt.Sprintf(" where user_id = %v", req.UserId)
  148. }
  149. }
  150. if req.Id > 0 {
  151. if hasWhere {
  152. where += fmt.Sprintf(" and id = %v", req.Id)
  153. } else {
  154. hasWhere = true
  155. where += fmt.Sprintf(" where id = %v", req.Id)
  156. }
  157. }
  158. // 导出必须选择一个时间段,且不能大于一天
  159. if hasWhere {
  160. where += fmt.Sprintf(" and event_at BETWEEN %v AND %v", int32(req.EventStamp[0]), int32(req.EventStamp[1]))
  161. } else {
  162. hasWhere = true
  163. where += fmt.Sprintf(" where event_at BETWEEN %v AND %v", int32(req.EventStamp[0]), int32(req.EventStamp[1]))
  164. }
  165. if req.ChapterId > 0 {
  166. if hasWhere {
  167. where += fmt.Sprintf(" and chapter_id = %v", req.ChapterId)
  168. } else {
  169. hasWhere = true
  170. where += fmt.Sprintf(" where chapter_id = %v", req.ChapterId)
  171. }
  172. }
  173. if req.Difficulty > -1 {
  174. if hasWhere {
  175. where += fmt.Sprintf(" and difficulty = %v", req.Difficulty)
  176. } else {
  177. hasWhere = true
  178. where += fmt.Sprintf(" where difficulty = %v", req.Difficulty)
  179. }
  180. }
  181. unionALL, err := s.unionModel(ctx, where)
  182. if err != nil {
  183. logrus.Warnf("Export 查询出错 unionModel: err:%+v", err)
  184. return
  185. }
  186. //countSql := fmt.Sprintf("SELECT count(1) FROM(%s) as a", unionALL)
  187. getSql := fmt.Sprintf("SELECT * FROM(%s) as a", unionALL)
  188. // 排序
  189. masterWhere += " ORDER BY event_at DESC"
  190. config.DB.Raw(getSql + masterWhere).Scan(&lists)
  191. // 多协程去cos请求数据
  192. numWorkers := 100
  193. var wg sync.WaitGroup
  194. if numWorkers > len(lists) {
  195. numWorkers = len(lists)
  196. }
  197. // 计算每个worker负责处理的元素范围
  198. itemsPerWorker := len(lists) / numWorkers
  199. extraItems := len(lists) % numWorkers
  200. // 分配任务给每个worker
  201. for i := 0; i < numWorkers; i++ {
  202. startIndex := i * itemsPerWorker
  203. endIndex := startIndex + itemsPerWorker
  204. // 处理最后一个worker的情况
  205. if i == numWorkers-1 {
  206. endIndex += extraItems
  207. }
  208. wg.Add(1)
  209. go func(start, end int) {
  210. defer wg.Done()
  211. for j := start; j < end; j++ {
  212. if strings.Contains(`lists[j].Extra`, "{") {
  213. continue
  214. }
  215. if response, err := config.GetCOSClient().Object.Get(context.Background(), lists[j].Extra, nil); err != nil {
  216. logrus.Warnf("从cos中获取extra发生错误 err:%+v", err)
  217. continue
  218. } else {
  219. extra, err := ioutil.ReadAll(response.Body)
  220. response.Body.Close()
  221. if err != nil {
  222. logrus.Warnf("读取cos数据出错 err:%+v", err)
  223. continue
  224. } else {
  225. lists[j].Extra = string(extra)
  226. }
  227. }
  228. }
  229. }(startIndex, endIndex)
  230. }
  231. // 等待所有goroutines完成
  232. wg.Wait()
  233. f := excelize.NewFile()
  234. f.SetColWidth("Sheet1", "G", "G", 150)
  235. f.SetColWidth("Sheet1", "H", "H", 25)
  236. // 创建一个工作表
  237. f.SetCellValue("Sheet1", "A1", "ID")
  238. f.SetCellValue("Sheet1", "B1", "玩家ID")
  239. f.SetCellValue("Sheet1", "C1", "埋点ID")
  240. f.SetCellValue("Sheet1", "D1", "关卡ID")
  241. f.SetCellValue("Sheet1", "E1", "房间ID")
  242. f.SetCellValue("Sheet1", "F1", "困难度")
  243. f.SetCellValue("Sheet1", "G1", "其他参数")
  244. f.SetCellValue("Sheet1", "H1", "埋点触发时间")
  245. for i, v := range lists {
  246. f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), v.ID)
  247. f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), v.UserID)
  248. f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), v.EventID)
  249. f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), v.ChapterID)
  250. f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), v.RoomID)
  251. if v.Difficulty == 0 {
  252. f.SetCellValue("Sheet1", fmt.Sprintf("F%d", i+2), "简单")
  253. } else {
  254. f.SetCellValue("Sheet1", fmt.Sprintf("F%d", i+2), "困难")
  255. }
  256. f.SetCellValue("Sheet1", fmt.Sprintf("G%d", i+2), v.Extra)
  257. f.SetCellValue("Sheet1", fmt.Sprintf("H%d", i+2), time.Unix(int64(v.EventAt), 0).Format("2006-01-02 15:04:05"))
  258. }
  259. // 设置工作簿的默认工作表
  260. f.SetActiveSheet(1)
  261. ctx.Header("Content-Type", "application/vnd.ms-excel")
  262. ctx.Header("Content-Disposition", fmt.Sprintf("attachment;filename=玩家(%v)战斗数据导出_%s.xlsx", req.UserId, time.Now().Format("20060102150405")))
  263. f.WriteTo(ctx.Writer)
  264. }
  265. func (s *sFighting) Export2(ctx *gin.Context, req forms.FightingExportReq) {
  266. var (
  267. q = query.Use(config.DB).ChapterLogsUserDetail
  268. m = q.WithContext(ctx)
  269. models forms.ListRes
  270. lists []model.ChapterLogsUserDetail
  271. )
  272. if req.UserId > 0 {
  273. m = m.Where(q.UserID.Eq(req.UserId))
  274. }
  275. if req.Id > 0 {
  276. m = m.Where(q.ID.Eq(req.Id))
  277. }
  278. if len(req.EventStamp) == 2 {
  279. m = m.Where(q.EventAt.Between(int32(req.EventStamp[0]), int32(req.EventStamp[1])))
  280. }
  281. if req.ChapterId > 0 {
  282. m = m.Where(q.ChapterID.Eq(req.ChapterId))
  283. }
  284. if req.Difficulty > -1 {
  285. m = m.Where(q.Difficulty.Eq(req.Difficulty))
  286. }
  287. //m = m.Order(q.ID.Desc())
  288. if err := m.Scan(&lists); err != nil {
  289. return
  290. }
  291. models.List = lists
  292. f := excelize.NewFile()
  293. //f.SetColWidth("Sheet1", "C", "C", 12)
  294. //f.SetColWidth("Sheet1", "D", "D", 25)
  295. f.SetColWidth("Sheet1", "G", "G", 150)
  296. f.SetColWidth("Sheet1", "H", "H", 25)
  297. // 创建一个工作表
  298. f.SetCellValue("Sheet1", "A1", "ID")
  299. f.SetCellValue("Sheet1", "B1", "玩家ID")
  300. f.SetCellValue("Sheet1", "C1", "埋点ID")
  301. f.SetCellValue("Sheet1", "D1", "关卡ID")
  302. f.SetCellValue("Sheet1", "E1", "房间ID")
  303. f.SetCellValue("Sheet1", "F1", "困难度")
  304. f.SetCellValue("Sheet1", "G1", "其他参数")
  305. f.SetCellValue("Sheet1", "H1", "埋点触发时间")
  306. for i, v := range lists {
  307. f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), v.ID)
  308. f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), v.UserID)
  309. f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), v.EventID)
  310. f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), v.ChapterID)
  311. f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), v.RoomID)
  312. if v.Difficulty == 0 {
  313. f.SetCellValue("Sheet1", fmt.Sprintf("F%d", i+2), "简单")
  314. } else {
  315. f.SetCellValue("Sheet1", fmt.Sprintf("F%d", i+2), "困难")
  316. }
  317. f.SetCellValue("Sheet1", fmt.Sprintf("G%d", i+2), v.Extra)
  318. f.SetCellValue("Sheet1", fmt.Sprintf("H%d", i+2), time.Unix(int64(v.EventAt), 0).Format("2006-01-02 15:04:05"))
  319. }
  320. // 设置工作簿的默认工作表
  321. f.SetActiveSheet(1)
  322. // content, _ := gocsv.MarshalString(&data)
  323. ctx.Header("Content-Type", "application/vnd.ms-excel")
  324. ctx.Header("Content-Disposition", fmt.Sprintf("attachment;filename=玩家(%v)战斗数据导出_%s.xlsx", req.UserId, time.Now().Format("20060102150405")))
  325. f.WriteTo(ctx.Writer)
  326. }