home.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. package daytask
  2. import (
  3. "app/commons/model/entity"
  4. "app/commons/services"
  5. "fmt"
  6. "strings"
  7. "time"
  8. "github.com/gin-gonic/gin"
  9. )
  10. // TaskWithCategory 带分类信息的任务响应结构
  11. type TaskWithCategory struct {
  12. entity.DtTask
  13. CategoryName string `json:"categoryName"`
  14. CategoryIcon string `json:"categoryIcon"`
  15. Platform string `json:"platform"`
  16. }
  17. // HomeIndex 首页数据
  18. func (s *Server) HomeIndex(c *gin.Context) {
  19. ctx := s.FromContext(c)
  20. db := s.DB()
  21. // 获取Banner列表
  22. banners := make([]*entity.DtBanner, 0)
  23. db.Model(&entity.DtBanner{}).
  24. Where("status = ? AND position = ?", 1, "home").
  25. Order("sort DESC, id DESC").
  26. Limit(5).
  27. Find(&banners)
  28. // 获取任务分类
  29. categories := make([]*entity.DtTaskCategory, 0)
  30. db.Model(&entity.DtTaskCategory{}).
  31. Where("status = ?", 1).
  32. Order("sort ASC").
  33. Find(&categories)
  34. // 获取推荐任务
  35. recommendTasks := make([]*entity.DtTask, 0)
  36. // 尝试使用 is_recommend 字段,如果字段不存在则降级处理
  37. if err := db.Model(&entity.DtTask{}).Where("status IN (?) AND is_recommend = ?", []int8{entity.TaskStatusOnline, entity.TaskStatusPrepare}, 1).
  38. Order("sort DESC, id DESC").Limit(10).Find(&recommendTasks).Error; err != nil {
  39. if isColumnNotExistError(err) {
  40. // 字段不存在,使用降级查询(不筛选推荐)
  41. db.Model(&entity.DtTask{}).Where("status IN (?)", []int8{entity.TaskStatusOnline, entity.TaskStatusPrepare}).
  42. Order("sort DESC, id DESC").Limit(10).Find(&recommendTasks)
  43. }
  44. }
  45. // 获取普通任务
  46. normalTasks := make([]*entity.DtTask, 0)
  47. // 尝试使用 is_top 字段,如果字段不存在则降级处理
  48. if err := db.Model(&entity.DtTask{}).Where("status IN (?) AND remain_count > 0", []int8{entity.TaskStatusOnline, entity.TaskStatusPrepare}).
  49. Order("is_top DESC, created_at DESC").Limit(20).Find(&normalTasks).Error; err != nil {
  50. if isColumnNotExistError(err) {
  51. // 字段不存在,使用降级查询(不按置顶排序)
  52. db.Model(&entity.DtTask{}).Where("status IN (?) AND remain_count > 0", []int8{entity.TaskStatusOnline, entity.TaskStatusPrepare}).
  53. Order("created_at DESC").Limit(20).Find(&normalTasks)
  54. }
  55. }
  56. ctx.OK(gin.H{
  57. "banners": banners,
  58. "categories": categories,
  59. "recommendTasks": recommendTasks,
  60. "normalTasks": normalTasks,
  61. })
  62. }
  63. // HomeBanner Banner列表
  64. func (s *Server) HomeBanner(c *gin.Context) {
  65. ctx := s.FromContext(c)
  66. db := s.DB()
  67. position := c.DefaultQuery("position", "home")
  68. banners := make([]*entity.DtBanner, 0)
  69. db.Model(&entity.DtBanner{}).
  70. Where("status = ? AND position = ?", 1, position).
  71. Order("sort DESC, id DESC").
  72. Find(&banners)
  73. ctx.OK(banners)
  74. }
  75. // HomeHall 大厅数据
  76. func (s *Server) HomeHall(c *gin.Context) {
  77. ctx := s.FromContext(c)
  78. db := s.DB()
  79. // 平台发放收益统计
  80. var totalReward float64
  81. db.Model(&entity.DtUserTask{}).
  82. Where("status = ?", entity.UserTaskStatusCompleted).
  83. Select("COALESCE(SUM(reward_amount), 0)").
  84. Scan(&totalReward)
  85. // 已完成任务数
  86. var completedCount int64
  87. db.Model(&entity.DtUserTask{}).
  88. Where("status = ?", entity.UserTaskStatusCompleted).
  89. Count(&completedCount)
  90. // 任务列表
  91. paging := &services.Pagination{
  92. Current: ctx.QueryInt64("current", 1),
  93. Size: ctx.QueryInt64("size", 20),
  94. }
  95. tasks := make([]*entity.DtTask, 0)
  96. query := db.Model(&entity.DtTask{}).
  97. Where("status IN (?) AND remain_count > 0", []int8{entity.TaskStatusOnline, entity.TaskStatusPrepare})
  98. query.Count(&paging.Total)
  99. paging.Computer()
  100. // 尝试使用 is_top 和 is_recommend 字段,如果字段不存在则降级处理
  101. fallbackQuery := db.Model(&entity.DtTask{}).Where("status IN (?) AND remain_count > 0", []int8{entity.TaskStatusOnline, entity.TaskStatusPrepare})
  102. if err := query.Order("is_top DESC, is_recommend DESC, created_at DESC").
  103. Offset(int(paging.Start)).
  104. Limit(int(paging.Size)).
  105. Find(&tasks).Error; err != nil {
  106. if isColumnNotExistError(err) {
  107. // 字段不存在,使用全新查询(不按置顶和推荐排序)
  108. fallbackQuery.Order("created_at DESC").
  109. Offset(int(paging.Start)).
  110. Limit(int(paging.Size)).
  111. Find(&tasks)
  112. }
  113. }
  114. // 获取所有分类信息并构建映射
  115. categoryMap := make(map[int64]*entity.DtTaskCategory)
  116. if len(tasks) > 0 {
  117. categoryIds := make([]int64, 0)
  118. for _, task := range tasks {
  119. categoryIds = append(categoryIds, task.CategoryId)
  120. }
  121. categories := make([]*entity.DtTaskCategory, 0)
  122. db.Where("id IN ?", categoryIds).Find(&categories)
  123. for _, cat := range categories {
  124. categoryMap[cat.Id] = cat
  125. }
  126. }
  127. // 构建带分类信息的任务列表
  128. tasksWithCategory := make([]*TaskWithCategory, 0, len(tasks))
  129. for _, task := range tasks {
  130. twc := &TaskWithCategory{
  131. DtTask: *task,
  132. }
  133. if cat, ok := categoryMap[task.CategoryId]; ok {
  134. twc.CategoryName = cat.Name
  135. twc.CategoryIcon = cat.Icon
  136. twc.Platform = cat.Platform
  137. }
  138. tasksWithCategory = append(tasksWithCategory, twc)
  139. }
  140. ctx.OK(gin.H{
  141. "totalReward": totalReward,
  142. "completedCount": completedCount,
  143. "tasks": tasksWithCategory,
  144. "paging": paging,
  145. })
  146. }
  147. // TaskCategories 任务分类列表
  148. func (s *Server) TaskCategories(c *gin.Context) {
  149. ctx := s.FromContext(c)
  150. db := s.DB()
  151. categories := make([]*entity.DtTaskCategory, 0)
  152. db.Model(&entity.DtTaskCategory{}).
  153. Where("status = ?", 1).
  154. Order("sort ASC").
  155. Find(&categories)
  156. ctx.OK(categories)
  157. }
  158. // TaskList 任务列表
  159. func (s *Server) TaskList(c *gin.Context) {
  160. ctx := s.FromContext(c)
  161. db := s.DB()
  162. categoryId := ctx.QueryInt64("categoryId", 0)
  163. keyword := c.Query("keyword")
  164. paging := &services.Pagination{
  165. Current: ctx.QueryInt64("current", 1),
  166. Size: ctx.QueryInt64("size", 20),
  167. }
  168. query := db.Model(&entity.DtTask{}).
  169. Where("status IN (?) AND remain_count > 0", []int8{entity.TaskStatusOnline, entity.TaskStatusPrepare})
  170. if categoryId > 0 {
  171. query = query.Where("category_id = ?", categoryId)
  172. }
  173. if keyword != "" {
  174. query = query.Where("title LIKE ?", "%"+keyword+"%")
  175. }
  176. query.Count(&paging.Total)
  177. paging.Computer()
  178. tasks := make([]*entity.DtTask, 0)
  179. // 尝试使用 is_top 和 is_recommend 字段,如果字段不存在则降级处理
  180. fallbackQuery := db.Model(&entity.DtTask{}).Where("status IN (?) AND remain_count > 0", []int8{entity.TaskStatusOnline, entity.TaskStatusPrepare})
  181. if categoryId > 0 {
  182. fallbackQuery = fallbackQuery.Where("category_id = ?", categoryId)
  183. }
  184. if keyword != "" {
  185. fallbackQuery = fallbackQuery.Where("title LIKE ?", "%"+keyword+"%")
  186. }
  187. if err := query.Order("is_top DESC, is_recommend DESC, created_at DESC").
  188. Offset(int(paging.Start)).
  189. Limit(int(paging.Size)).
  190. Find(&tasks).Error; err != nil {
  191. if isColumnNotExistError(err) {
  192. // 字段不存在,使用全新查询(不按置顶和推荐排序)
  193. fallbackQuery.Order("created_at DESC").
  194. Offset(int(paging.Start)).
  195. Limit(int(paging.Size)).
  196. Find(&tasks)
  197. }
  198. }
  199. // 获取所有分类信息并构建映射
  200. categoryMap := make(map[int64]*entity.DtTaskCategory)
  201. if len(tasks) > 0 {
  202. categoryIds := make([]int64, 0)
  203. for _, task := range tasks {
  204. categoryIds = append(categoryIds, task.CategoryId)
  205. }
  206. categories := make([]*entity.DtTaskCategory, 0)
  207. db.Where("id IN ?", categoryIds).Find(&categories)
  208. for _, cat := range categories {
  209. categoryMap[cat.Id] = cat
  210. }
  211. }
  212. // 构建带分类信息的任务列表
  213. tasksWithCategory := make([]*TaskWithCategory, 0, len(tasks))
  214. for _, task := range tasks {
  215. twc := &TaskWithCategory{
  216. DtTask: *task,
  217. }
  218. if cat, ok := categoryMap[task.CategoryId]; ok {
  219. twc.CategoryName = cat.Name
  220. twc.CategoryIcon = cat.Icon
  221. twc.Platform = cat.Platform
  222. }
  223. tasksWithCategory = append(tasksWithCategory, twc)
  224. }
  225. ctx.OK(gin.H{
  226. "list": tasksWithCategory,
  227. "paging": paging,
  228. })
  229. }
  230. // TaskDetail 任务详情
  231. func (s *Server) TaskDetail(c *gin.Context) {
  232. ctx := s.FromContext(c)
  233. db := s.DB()
  234. taskId := ctx.QueryInt64("id", 0)
  235. if taskId == 0 {
  236. ctx.Fail("task_id_required")
  237. return
  238. }
  239. // 获取任务
  240. task := &entity.DtTask{}
  241. if err := db.Where("id = ? AND status IN (?)", taskId, []int8{entity.TaskStatusOnline, entity.TaskStatusPrepare}).First(task).Error; err != nil {
  242. ctx.Fail("task_not_found")
  243. return
  244. }
  245. // 获取任务分类
  246. category := &entity.DtTaskCategory{}
  247. db.Where("id = ?", task.CategoryId).First(category)
  248. // 获取任务步骤
  249. steps := make([]*entity.DtTaskStep, 0)
  250. db.Model(&entity.DtTaskStep{}).
  251. Where("task_id = ?", taskId).
  252. Order("step_no ASC, id ASC").
  253. Find(&steps)
  254. // 获取审核样例
  255. examples := make([]*entity.DtTaskExample, 0)
  256. db.Model(&entity.DtTaskExample{}).
  257. Where("task_id = ?", taskId).
  258. Order("id ASC").
  259. Find(&examples)
  260. // 获取用户领取状态
  261. var apply *entity.DtUserTask
  262. userId := ctx.UserId()
  263. if userId > 0 {
  264. userTask := &entity.DtUserTask{}
  265. if err := db.Where("user_id = ? AND task_id = ?", userId, taskId).
  266. Order("id DESC").First(userTask).Error; err == nil {
  267. // 如果任务已完成,需要检查是否可以再次领取
  268. if userTask.Status == entity.UserTaskStatusCompleted {
  269. canReClaim := true
  270. today := time.Now().Format("2006-01-02")
  271. // 检查每日限制
  272. if task.DailyLimit > 0 {
  273. var todayCount int64
  274. db.Model(&entity.DtUserTask{}).
  275. Where("user_id = ? AND task_id = ? AND DATE(FROM_UNIXTIME(created_at)) = ?",
  276. userId, taskId, today).
  277. Count(&todayCount)
  278. if int(todayCount) >= task.DailyLimit {
  279. canReClaim = false
  280. }
  281. }
  282. // 检查总限制
  283. if canReClaim && task.TotalLimit > 0 {
  284. var totalCount int64
  285. db.Model(&entity.DtUserTask{}).
  286. Where("user_id = ? AND task_id = ? AND status != ?",
  287. userId, taskId, entity.UserTaskStatusAbandoned).
  288. Count(&totalCount)
  289. if int(totalCount) >= task.TotalLimit {
  290. canReClaim = false
  291. }
  292. }
  293. if canReClaim {
  294. apply = nil
  295. } else {
  296. apply = userTask
  297. }
  298. } else {
  299. // 其他状态(进行中、待审核等)直接返回
  300. apply = userTask
  301. }
  302. }
  303. }
  304. ctx.OK(gin.H{
  305. "task": task,
  306. "category": category,
  307. "steps": steps,
  308. "examples": examples,
  309. "apply": apply,
  310. })
  311. }
  312. // ConfigGet 获取配置
  313. func (s *Server) ConfigGet(c *gin.Context) {
  314. ctx := s.FromContext(c)
  315. db := s.DB()
  316. key := c.Query("key")
  317. group := c.Query("group")
  318. if key != "" {
  319. // 获取单个配置
  320. config := &entity.DtConfig{}
  321. if err := db.Where("`key` = ?", key).First(config).Error; err != nil {
  322. ctx.Fail("config_not_found")
  323. return
  324. }
  325. ctx.OK(config.Value)
  326. return
  327. }
  328. if group != "" {
  329. // 获取分组配置
  330. configs := make([]*entity.DtConfig, 0)
  331. db.Where("`group` = ?", group).Order("sort ASC").Find(&configs)
  332. result := make(map[string]string)
  333. for _, cfg := range configs {
  334. result[cfg.Key] = cfg.Value
  335. }
  336. ctx.OK(result)
  337. return
  338. }
  339. ctx.Fail("key_or_group_required")
  340. }
  341. // CustomerService 客服配置
  342. func (s *Server) CustomerService(c *gin.Context) {
  343. ctx := s.FromContext(c)
  344. db := s.DB()
  345. services := make([]*entity.DtCustomerService, 0)
  346. db.Model(&entity.DtCustomerService{}).
  347. Where("status = ?", 1).
  348. Order("sort ASC").
  349. Find(&services)
  350. ctx.OK(services)
  351. }
  352. // OAuthConfig 获取OAuth配置(公开接口,只返回客户端需要的信息)
  353. func (s *Server) OAuthConfig(c *gin.Context) {
  354. ctx := s.FromContext(c)
  355. db := s.DB()
  356. result := make(map[string]string)
  357. // 获取 Google Client ID
  358. var googleConfig entity.DtConfig
  359. if err := db.Where("`key` = ?", entity.ConfigKeyGoogleClientId).First(&googleConfig).Error; err == nil {
  360. result["googleClientId"] = googleConfig.Value
  361. }
  362. // 获取 Zalo App ID
  363. var zaloConfig entity.DtConfig
  364. if err := db.Where("`key` = ?", entity.ConfigKeyZaloAppId).First(&zaloConfig).Error; err == nil {
  365. result["zaloAppId"] = zaloConfig.Value
  366. }
  367. // 获取 Telegram Bot Name
  368. var telegramConfig entity.DtConfig
  369. if err := db.Where("`key` = ?", entity.ConfigKeyTelegramBotName).First(&telegramConfig).Error; err == nil {
  370. result["telegramBotName"] = telegramConfig.Value
  371. } else {
  372. fmt.Printf("Telegram config error: %v, key: %s\n", err, entity.ConfigKeyTelegramBotName)
  373. }
  374. // 获取 Telegram Bot ID
  375. var telegramIdConfig entity.DtConfig
  376. if err := db.Where("`key` = ?", entity.ConfigKeyTelegramBotId).First(&telegramIdConfig).Error; err == nil {
  377. result["telegramBotId"] = telegramIdConfig.Value
  378. }
  379. // 获取 TikTok Client Key
  380. var tiktokConfig entity.DtConfig
  381. if err := db.Where("`key` = ?", entity.ConfigKeyTiktokClientKey).First(&tiktokConfig).Error; err == nil {
  382. result["tiktokClientKey"] = tiktokConfig.Value
  383. }
  384. // 获取 Facebook App ID
  385. var facebookConfig entity.DtConfig
  386. if err := db.Where("`key` = ?", entity.ConfigKeyFacebookAppId).First(&facebookConfig).Error; err == nil {
  387. result["facebookAppId"] = facebookConfig.Value
  388. }
  389. ctx.OK(result)
  390. }
  391. // isColumnNotExistError 检查是否是列不存在的错误
  392. func isColumnNotExistError(err error) bool {
  393. if err == nil {
  394. return false
  395. }
  396. errStr := err.Error()
  397. // MySQL 错误 1054 表示列不存在
  398. return strings.Contains(errStr, "1054") || strings.Contains(errStr, "Unknown column")
  399. }