support limit

This commit is contained in:
2022-11-23 18:08:04 +08:00
parent bfe739e442
commit f4cb36dbee
5 changed files with 108 additions and 28 deletions

View File

@@ -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)
}
}

View File

@@ -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()
);

28
main.go
View File

@@ -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)

View File

@@ -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="班表名"
/>
<TextField
value={newLimit}
onChange={(event) => {
let num = parseInt(event.target.value)
if (num <= 0) {
num = 1;
}
setNewLimit(num);
}}
placeholder="每人选择上限"
type="number"
/>
<Button
variant="contained"
color="secondary"
@@ -55,6 +68,7 @@ const Home = () => {
await post("/timetables", {
token,
newTimetableName,
newLimit,
});
await fetchData();
}}

View File

@@ -18,10 +18,11 @@ import { UsernameContext } from "./Login";
import { useNavigate, useParams } from "react-router-dom";
import { get, post, put } from "../fetches";
const AdminComponent = ({ refresh, timetableStatus }) => {
const AdminComponent = ({ refresh, timetableStatus, timetableLimit }) => {
const { username, token } = React.useContext(UsernameContext);
const [newName, setNewName] = React.useState("");
const [newTime, setNewTime] = React.useState("");
const [newLimit, setNewLimit] = React.useState(timetableLimit);
const [newCapacity, setNewCapacity] = React.useState(1);
const params = useParams();
let { timetableID } = params;
@@ -34,17 +35,11 @@ const AdminComponent = ({ refresh, timetableStatus }) => {
});
await refresh();
};
const handleOpen = async () => {
const handleChangeStatus = async (status, limit) => {
await put(`/timetables/${timetableID}`, {
token,
status: true,
});
await refresh();
};
const handleClose = async () => {
await put(`/timetables/${timetableID}`, {
token,
status: false,
limit,
status,
});
await refresh();
};
@@ -85,15 +80,47 @@ const AdminComponent = ({ refresh, timetableStatus }) => {
添加
</Button>
{timetableStatus && (
<Button variant="contained" color="warning" onClick={handleClose}>
<Button
variant="contained"
color="warning"
onClick={() => {
handleChangeStatus(false, timetableLimit);
}}
>
暂停报名
</Button>
)}
{!timetableStatus && (
<Button variant="contained" color="success" onClick={handleOpen}>
<Button
variant="contained"
color="success"
onClick={() => {
handleChangeStatus(true, timetableLimit);
}}
>
开放报名
</Button>
)}
<TextField
value={newLimit}
onChange={(event) => {
let number = parseInt(event.target.value);
if (number <= 0) {
number = 1;
}
setNewLimit(number);
}}
type="number"
/>
<Button
variant="contained"
color="primary"
onClick={() => {
handleChangeStatus(timetableStatus, newLimit);
}}
>
修改报名限制
</Button>
</Stack>
)
);
@@ -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 = () => {
</Button>
<Typography variant="h4">{timetableName}</Typography>
<AdminComponent refresh={refresh} timetableStatus={timetableStatus} />
<AdminComponent
refresh={refresh}
timetableStatus={timetableStatus}
timetableLimit={timetableLimit}
/>
<Typography>列表实时更新中</Typography>
<Typography>列表实时更新中每人报名限制{timetableLimit}</Typography>
<LinearProgress />
<TableContainer>
<Table>