diff --git a/db/db.go b/db/db.go index a0afb1b..621844f 100644 --- a/db/db.go +++ b/db/db.go @@ -13,6 +13,7 @@ type Timetable struct { ID int64 `json:"id"` Name string `json:"name"` Status bool `json:"status"` + Limit int64 `json:"limit"` Created time.Time `json:"created"` } @@ -51,6 +52,7 @@ var ( UpdateTakeCount *sql.Stmt CheckTableStatus *sql.Stmt + CheckLimit *sql.Stmt ) func init() { @@ -60,7 +62,7 @@ func init() { log.Fatal(err) } - if len(os.Args) > 1{ + if len(os.Args) > 1 { if os.Args[1] == "install" { install() os.Exit(0) @@ -68,7 +70,7 @@ func init() { } GetAllTimetables, err = DB.Prepare(` -select id, name, status +select id, name, status, "limit" from timetables order by status desc, created desc @@ -77,8 +79,8 @@ order by status desc, created desc log.Fatal(err) } CreateNewTimetable, err = DB.Prepare(` -insert into timetables (name) -values ($1) +insert into timetables (name, "limit") +values ($1, $2) returning id `) if err != nil { @@ -93,7 +95,8 @@ where id = $1 } GetTimeSlotsByTimetable, err = DB.Prepare(` select t.id, t."name" ,t."time" ,t.take, t.capacity, t2."name", t2.status, - case sub.username when $2 then true else false end as success + case sub.username when $2 then true else false end as success, + t2."limit" from timeslots t join timetables t2 on t.ttid = t2.id left outer join ( @@ -137,8 +140,8 @@ where tsid = $1 and username = $2 } UpdateTimetableStatus, err = DB.Prepare(` update timetables -set status = $1 -where id = $2 +set status = $1, "limit" = $2 +where id = $3 `) if err != nil { log.Fatal(err) @@ -171,4 +174,15 @@ select status from timetables where id = $1 if err != nil { log.Fatal(err) } + CheckLimit, err = DB.Prepare(` +select count(t1.username) < t3."limit" as c +from takes t1 +join timeslots t2 on t1.tsid = t2.id +join timetables t3 on t2.ttid = t3.id +where t1.username = $1 and t3.id = $2 +group by t1.username, t3."limit" + `) + if err != nil { + log.Fatal(err) + } } diff --git a/db/install.go b/db/install.go index 51af79a..f50db4c 100644 --- a/db/install.go +++ b/db/install.go @@ -12,9 +12,10 @@ func install() { } _, err = tx.Exec(` - CREATE TABLE timetables ( +CREATE TABLE timetables ( id serial primary key, "name" text NOT NULL, + "limit" integer NOT NULL DEFAULT 1, status bool NOT NULL DEFAULT false, created timestamp NOT NULL DEFAULT now() ); diff --git a/main.go b/main.go index 0e16209..fc51c97 100644 --- a/main.go +++ b/main.go @@ -72,7 +72,7 @@ func main() { } for rows.Next() { s := &db.Timetable{} - rows.Scan(&s.ID, &s.Name, &s.Status) + rows.Scan(&s.ID, &s.Name, &s.Status, &s.Limit) timetables = append(timetables, s) } c.JSON(http.StatusOK, gin.H{ @@ -82,7 +82,8 @@ func main() { api.POST("/timetables", auth, func(c *gin.Context) { type Request struct { - Name string `json:"newTimeTableName"` + Name string `json:"newTimeTableName"` + Limit int64 `json:"newLimit"` } req := &Request{} err := c.ShouldBindBodyWith(req, binding.JSON) @@ -90,7 +91,7 @@ func main() { c.AbortWithError(400, err) return } - row := db.CreateNewTimetable.QueryRow(req.Name) + row := db.CreateNewTimetable.QueryRow(req.Name, req.Limit) var id int64 err = row.Scan(&id) if err != nil { @@ -130,9 +131,10 @@ func main() { timeslots := make([]*db.TimeSlot, 0) var timetableName string var timetableStatus bool + var timetableLimit int64 for rows.Next() { s := &db.TimeSlot{} - err = rows.Scan(&s.ID, &s.Name, &s.Time, &s.Take, &s.Capacity, &timetableName, &timetableStatus, &s.Success) + err = rows.Scan(&s.ID, &s.Name, &s.Time, &s.Take, &s.Capacity, &timetableName, &timetableStatus, &s.Success, &timetableLimit) if err != nil { c.AbortWithError(400, err) return @@ -143,6 +145,7 @@ func main() { "timeslots": timeslots, "timetableName": timetableName, "timetableStatus": timetableStatus, + "timetableLimit": timetableLimit, }) }) @@ -256,7 +259,7 @@ func main() { c.AbortWithError(400, err) return } - _, err = db.UpdateTimetableStatus.Exec(req.Status, timetableID) + _, err = db.UpdateTimetableStatus.Exec(req.Status, req.Limit, timetableID) if err != nil { c.AbortWithError(400, err) return @@ -291,6 +294,21 @@ func main() { tx.Rollback() return } + can := true + row, err := db.CheckLimit.Query(req.Username, timetableID) + for row.Next() { + err = row.Scan(&can) + } + if err != nil { + c.AbortWithError(400, err) + tx.Rollback() + return + } + if !can { + c.AbortWithError(401, errors.New("超出报名数量限制")) + tx.Rollback() + return + } updateCountStmt := tx.Stmt(db.UpdateTakeCount) if req.Username[0] == '!' { untakeStmt := tx.Stmt(db.UserUntakeTimeslot) diff --git a/web/src/components/Home.js b/web/src/components/Home.js index 5b0ae2c..9ae66c1 100644 --- a/web/src/components/Home.js +++ b/web/src/components/Home.js @@ -22,6 +22,7 @@ const Home = () => { const [timetables, setTimetables] = React.useState([]); const [newTimetableName, setNewTimetableName] = React.useState(""); + const [newLimit, setNewLimit] = React.useState(null); const navigator = useNavigate(); const fetchData = async () => { @@ -48,6 +49,18 @@ const Home = () => { }} placeholder="班表名" /> + { + let num = parseInt(event.target.value) + if (num <= 0) { + num = 1; + } + setNewLimit(num); + }} + placeholder="每人选择上限" + type="number" + /> {timetableStatus && ( - )} {!timetableStatus && ( - )} + { + let number = parseInt(event.target.value); + if (number <= 0) { + number = 1; + } + setNewLimit(number); + }} + type="number" + /> + ) ); @@ -103,6 +130,7 @@ const Timetable = () => { const [timeslots, setTimeslots] = React.useState([]); const [timetableName, setTimetableName] = React.useState(""); const [timetableStatus, setTimetableStatus] = React.useState(true); + const [timetableLimit, setTimetableLimit] = React.useState(null); const { username } = React.useContext(UsernameContext); const navigator = useNavigate(); const params = useParams(); @@ -115,6 +143,7 @@ const Timetable = () => { setTimeslots(data.timeslots); setTimetableName(data.timetableName); setTimetableStatus(data.timetableStatus); + setTimetableLimit(data.timetableLimit); }; const handleTakeSlot = async (slotID, status) => { if (username === "admin") { @@ -129,7 +158,7 @@ const Timetable = () => { } }; React.useEffect(() => { - refresh() + refresh(); const interval = setInterval(() => { refresh(); }, 1000); @@ -150,9 +179,13 @@ const Timetable = () => { {timetableName} - + - 列表实时更新中 + 列表实时更新中,每人报名限制:{timetableLimit}