7a1d8465e5
- 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>
59 lines
1.9 KiB
Go
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
|
|
}
|