Files
wangjia 7a1d8465e5 feat(backend): 意见反馈接口(提交+图片上传+admin列表)
- model/feedback.go: Feedback(多租户 shop_id,images 以 JSON 存)+ StringSlice 类型
- handler/feedback.go: Submit(认证,shop/user 从 JWT,去规范化 username/shop_code)、
  UploadImage(复用 product_image 压缩逻辑,存 /images/feedback/<shop>/)、
  List(SuperAdminOnly,分页过滤)、UpdateStatus(new/handled)
- router 注册 /feedback、/feedback/images、/admin/feedback;main AutoMigrate;schema.sql 加表
- feedback_test 覆盖提交/校验/权限/列表

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 21:31:46 +08:00

59 lines
1.9 KiB
Go

package model
import (
"database/sql/driver"
"encoding/json"
"fmt"
"time"
)
// StringSlice 以 JSON 数组形式存储的字符串切片(用于图片 URL 列表)
type StringSlice []string
func (s StringSlice) Value() (driver.Value, error) {
if s == nil {
return "[]", nil
}
b, err := json.Marshal(s)
return string(b), err
}
func (s *StringSlice) Scan(value interface{}) error {
if value == nil {
*s = nil
return nil
}
var b []byte
switch v := value.(type) {
case string:
b = []byte(v)
case []byte:
b = v
default:
return fmt.Errorf("cannot scan %T into StringSlice", value)
}
if len(b) == 0 {
*s = nil
return nil
}
return json.Unmarshal(b, s)
}
// Feedback 用户意见反馈(bug / 功能建议):文字 + 附图。多租户按 ShopID 隔离。
type Feedback struct {
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
CreatedAt time.Time `json:"created_at"`
ShopID uint64 `gorm:"index;not null" json:"shop_id"`
ShopCode string `gorm:"size:50" json:"shop_code"`
UserID uint64 `gorm:"index" json:"user_id"`
Username string `gorm:"size:50" json:"username"`
Role string `gorm:"size:20" json:"role"`
Type string `gorm:"size:20;not null;index" json:"type"` // bug / suggestion
Content string `gorm:"type:text" json:"content"`
Images StringSlice `gorm:"type:json" json:"images"`
AppVersion string `gorm:"size:30" json:"app_version"`
Platform string `gorm:"size:20" json:"platform"`
ClientIP string `gorm:"size:60" json:"client_ip"`
Status string `gorm:"size:20;not null;default:new;index" json:"status"` // new / handled
}