package domain import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "regexp" "strings" ) type Color struct { Code string `json:"code"` Name string `json:"name"` Hex string `json:"hex"` Location string `json:"location"` Available bool `json:"available"` } type Pattern struct { ID string `json:"id"` Title string `json:"title"` Category string `json:"category"` Width int `json:"width"` Height int `json:"height"` Cells []*string `json:"cells"` Colors []Color `json:"colors"` BeadCount int `json:"beadCount"` OwnerID string `json:"ownerId,omitempty"` Visibility string `json:"visibility"` CreatedAt string `json:"createdAt"` StoreID string `json:"storeId"` Demo bool `json:"demo"` } type User struct { ID string `json:"id"` Name string `json:"name"` Role string `json:"role"` StoreID string `json:"storeId"` } type Store struct { ID string `json:"id"` Name string `json:"name"` Address string `json:"address"` Hours string `json:"hours"` Notice string `json:"notice"` LocalBaseURL string `json:"localBaseUrl"` PublicBaseURL string `json:"publicBaseUrl"` } type Table struct { Code string `json:"code"` Label string `json:"label"` Enabled bool `json:"enabled"` } type Visit struct { ID string `json:"id"` StoreID string `json:"storeId"` TableCode string `json:"tableCode"` Status string `json:"status"` } type Request struct { ID string `json:"id"` StoreID string `json:"storeId"` UserID string `json:"userId"` TableCode string `json:"tableCode"` Kind string `json:"kind"` Note string `json:"note"` Status string `json:"status"` PatternID string `json:"patternId,omitempty"` CreatedAt string `json:"createdAt"` UpdatedAt string `json:"updatedAt"` } var hexColor = regexp.MustCompile(`^#[0-9a-fA-F]{6}$`) func ValidateColors(cs []Color) error { seen := map[string]bool{} if len(cs) == 0 || len(cs) > 512 { return fmt.Errorf("色板需包含 1–512 色") } for _, c := range cs { if strings.TrimSpace(c.Code) == "" || len(c.Code) > 32 || seen[c.Code] || !hexColor.MatchString(c.Hex) { return fmt.Errorf("色号重复或颜色格式错误") } seen[c.Code] = true } return nil } func (p *Pattern) Validate() error { if strings.TrimSpace(p.Title) == "" || len(p.Title) > 200 || p.Width < 1 || p.Height < 1 || p.Width > 200 || p.Height > 200 || len(p.Cells) != p.Width*p.Height { return fmt.Errorf("图纸名称或尺寸无效(最大 200×200)") } if p.Visibility != "public" && p.Visibility != "private" { return fmt.Errorf("可见性无效") } if err := ValidateColors(p.Colors); err != nil { return err } cs := map[string]bool{} for _, c := range p.Colors { cs[c.Code] = true } p.BeadCount = 0 for _, c := range p.Cells { if c != nil { if !cs[*c] { return fmt.Errorf("网格含未知色号") } p.BeadCount++ } } return nil } func Transition(role, old, next string, own bool) bool { if role == "admin" || role == "staff" { return old == "pending" && next == "accepted" || old == "accepted" && next == "completed" } return own && old == "pending" && next == "cancelled" } func Fingerprint(kind, note, pattern string) string { b, _ := json.Marshal([]string{kind, note, pattern}) h := sha256.Sum256(b) return hex.EncodeToString(h[:]) } func CanRead(p Pattern, u User) bool { return p.Visibility == "public" || p.OwnerID == u.ID && u.ID != "" || (u.Role == "admin" || u.Role == "staff") && p.StoreID == u.StoreID }