feedbackControllers.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. * Feedback API List
  3. * /feedback/submit - POST 提交问题反馈或需求
  4. * /feedback/history - GET 查询提交历史
  5. * /feedback/upload - POST 上传附件到 Plane (返回 assetId)
  6. * /feedback/asset/:id - DELETE 删除已上传的附件
  7. */
  8. package controllers
  9. import (
  10. "fwh-toolbox-server/dto"
  11. "fwh-toolbox-server/middleware"
  12. "fwh-toolbox-server/services"
  13. "github.com/gofiber/fiber/v2"
  14. "go.uber.org/zap"
  15. )
  16. // 提交反馈
  17. // /feedback/submit
  18. func SubmitFeedback(c *fiber.Ctx) error {
  19. var body dto.FeedbackSubmitDTO
  20. if err := c.BodyParser(&body); err != nil {
  21. zap.L().Warn("Invalid request body", zap.String("err", err.Error()))
  22. return Fail(c, "Invalid request body")
  23. }
  24. // 参数校验
  25. if body.Title == "" {
  26. return Fail(c, "标题不能为空")
  27. }
  28. if body.Type != "bug" && body.Type != "feature" {
  29. return Fail(c, "类型必须是 bug 或 feature")
  30. }
  31. // 从 JWT 中获取用户名
  32. userName := middleware.AuthGetDisplayName(c)
  33. result, err := services.SubmitFeedback(body, userName)
  34. if err != nil {
  35. return Error(c, err.Error())
  36. }
  37. return Success(c, result)
  38. }
  39. // 查询提交历史
  40. // /feedback/history
  41. func GetFeedbackHistory(c *fiber.Ctx) error {
  42. // 从 JWT 中获取用户名
  43. userName := ""
  44. if name, ok := c.Locals("userName").(string); ok && name != "" {
  45. userName = name
  46. }
  47. result, err := services.GetFeedbackHistory(userName)
  48. if err != nil {
  49. return Error(c, err.Error())
  50. }
  51. return Success(c, result)
  52. }
  53. // 上传附件到 Plane(立即上传,返回 assetId 供后续提交使用)
  54. // /feedback/upload
  55. func UploadFeedbackAsset(c *fiber.Ctx) error {
  56. file, err := c.FormFile("file")
  57. if err != nil {
  58. return Fail(c, "请选择文件")
  59. }
  60. // 限制文件大小 10MB
  61. if file.Size > 10*1024*1024 {
  62. return Fail(c, "文件大小不能超过 10MB")
  63. }
  64. // 读取文件内容
  65. src, err := file.Open()
  66. if err != nil {
  67. return Fail(c, "文件读取失败")
  68. }
  69. defer src.Close()
  70. fileData := make([]byte, file.Size)
  71. if _, err := src.Read(fileData); err != nil {
  72. return Fail(c, "文件读取失败")
  73. }
  74. // 获取 Content-Type
  75. contentType := file.Header.Get("Content-Type")
  76. if contentType == "" {
  77. contentType = "application/octet-stream"
  78. }
  79. result, err := services.UploadFeedbackAsset(file.Filename, contentType, file.Size, fileData)
  80. if err != nil {
  81. return Error(c, err.Error())
  82. }
  83. // 返回 assetId 和 assetUrl
  84. return Success(c, fiber.Map{
  85. "assetId": result.AssetID,
  86. "assetUrl": result.AssetURL,
  87. })
  88. }
  89. // 获取反馈详情(含描述内容)
  90. // /feedback/detail/:id
  91. func GetFeedbackDetail(c *fiber.Ctx) error {
  92. issueId := c.Params("id")
  93. if issueId == "" {
  94. return Fail(c, "issue id 不能为空")
  95. }
  96. result, err := services.GetFeedbackDetail(issueId)
  97. if err != nil {
  98. return Error(c, err.Error())
  99. }
  100. return Success(c, result)
  101. }
  102. // 代理获取附件文件(从 Plane 下载后转发给前端)
  103. // /feedback/asset/view/:id
  104. func ViewFeedbackAsset(c *fiber.Ctx) error {
  105. assetId := c.Params("id")
  106. if assetId == "" {
  107. return Fail(c, "asset id 不能为空")
  108. }
  109. contentType, data, err := services.ProxyFeedbackAsset(assetId)
  110. if err != nil {
  111. return Error(c, err.Error())
  112. }
  113. c.Set("Content-Type", contentType)
  114. c.Set("Cache-Control", "private, max-age=3600")
  115. return c.Send(data)
  116. }
  117. // 删除已上传的附件
  118. // /feedback/asset/:id
  119. func DeleteFeedbackAsset(c *fiber.Ctx) error {
  120. assetId := c.Params("id")
  121. if assetId == "" {
  122. return Fail(c, "asset id 不能为空")
  123. }
  124. err := services.DeleteFeedbackAsset(assetId)
  125. if err != nil {
  126. return Error(c, err.Error())
  127. }
  128. return Success[any](c, nil, "删除成功")
  129. }