package server import ( "encoding/json" "errors" "github.com/gin-gonic/gin" "github.com/google/uuid" "net/url" "perler-beads-server/internal/domain" ) func (s *Server) visitCreate(c *gin.Context) { if !need(c, "") { return } var in struct { StoreID string `json:"storeId"` TableCode string `json:"tableCode"` } if !bind(c, &in) { return } var tables []domain.Table if s.get(c, "tables", in.StoreID, &tables) != nil { fail(c, errors.New("门店不存在")) return } enabled := false for _, t := range tables { if t.Code == in.TableCode && t.Enabled { enabled = true } } if !enabled { fail(c, errors.New("桌码未启用")) return } v := domain.Visit{ID: uuid.NewString(), StoreID: in.StoreID, TableCode: in.TableCode, Status: "active"} b, _ := json.Marshal(v) if _, e := s.DB.Exec(c, `INSERT INTO visits(user_id,body) VALUES($1,$2) ON CONFLICT(user_id) DO UPDATE SET body=EXCLUDED.body`, user(c).ID, b); e != nil { fail(c, e) return } ok(c, v) } func (s *Server) visit(c *gin.Context) (domain.Visit, error) { var b []byte var v domain.Visit e := s.DB.QueryRow(c, `SELECT body FROM visits WHERE user_id=$1`, user(c).ID).Scan(&b) if e == nil { e = json.Unmarshal(b, &v) } return v, e } func (s *Server) visitGet(c *gin.Context) { if !need(c, "") { return } v, e := s.visit(c) if e != nil || v.Status != "active" { ok(c, nil) return } ok(c, v) } func (s *Server) visitClose(c *gin.Context) { if !need(c, "") { return } if _, e := s.DB.Exec(c, `DELETE FROM visits WHERE user_id=$1`, user(c).ID); e != nil { fail(c, e) return } ok(c, nil) } func (s *Server) tableList(c *gin.Context) { if !need(c, "staff") { return } if storeID(c) != user(c).StoreID { reply(c, 401, "门店无权限", nil) return } var v []domain.Table if e := s.get(c, "tables", storeID(c), &v); e != nil { fail(c, e) return } ok(c, v) } func (s *Server) palettePut(c *gin.Context) { if !need(c, "admin") { return } var in struct { StoreID string `json:"storeId"` Colors []domain.Color `json:"colors"` } if !bind(c, &in) { return } if in.StoreID != user(c).StoreID { reply(c, 401, "门店无权限", nil) return } if e := domain.ValidateColors(in.Colors); e != nil { fail(c, e) return } if e := s.put(c, "palette", in.StoreID, in.StoreID, "", in.Colors); e != nil { fail(c, e) return } ok(c, in.Colors) } func (s *Server) tablePut(c *gin.Context) { if !need(c, "admin") { return } var in struct { StoreID string `json:"storeId"` Tables []domain.Table `json:"tables"` } if !bind(c, &in) { return } if in.StoreID != user(c).StoreID { reply(c, 401, "门店无权限", nil) return } seen := map[string]bool{} if len(in.Tables) > 200 { fail(c, errors.New("桌位超过限制")) return } for _, t := range in.Tables { if t.Code == "" || len(t.Code) > 64 || seen[t.Code] || t.Label == "" { fail(c, errors.New("桌码或名称无效/重复")) return } seen[t.Code] = true } if e := s.put(c, "tables", in.StoreID, in.StoreID, "", in.Tables); e != nil { fail(c, e) return } ok(c, in.Tables) } func endpointValid(v string) bool { if v == "" { return true } u, e := url.Parse(v) return e == nil && u.Scheme == "https" && u.Host != "" && u.User == nil && u.RawQuery == "" && u.Fragment == "" } func (s *Server) storePut(c *gin.Context) { if !need(c, "admin") { return } var v domain.Store if !bind(c, &v) { return } if v.ID != user(c).StoreID { reply(c, 401, "门店无权限", nil) return } if v.Name == "" || !endpointValid(v.LocalBaseURL) || !endpointValid(v.PublicBaseURL) { fail(c, errors.New("门店名称或 HTTPS 端点无效")) return } if e := s.put(c, "store", v.ID, v.ID, "", v); e != nil { fail(c, e) return } ok(c, v) }