support limit
This commit is contained in:
28
db/db.go
28
db/db.go
@@ -13,6 +13,7 @@ type Timetable struct {
|
|||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Status bool `json:"status"`
|
Status bool `json:"status"`
|
||||||
|
Limit int64 `json:"limit"`
|
||||||
Created time.Time `json:"created"`
|
Created time.Time `json:"created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ var (
|
|||||||
UpdateTakeCount *sql.Stmt
|
UpdateTakeCount *sql.Stmt
|
||||||
|
|
||||||
CheckTableStatus *sql.Stmt
|
CheckTableStatus *sql.Stmt
|
||||||
|
CheckLimit *sql.Stmt
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -60,7 +62,7 @@ func init() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(os.Args) > 1{
|
if len(os.Args) > 1 {
|
||||||
if os.Args[1] == "install" {
|
if os.Args[1] == "install" {
|
||||||
install()
|
install()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
@@ -68,7 +70,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GetAllTimetables, err = DB.Prepare(`
|
GetAllTimetables, err = DB.Prepare(`
|
||||||
select id, name, status
|
select id, name, status, "limit"
|
||||||
from timetables
|
from timetables
|
||||||
order by status desc, created desc
|
order by status desc, created desc
|
||||||
|
|
||||||
@@ -77,8 +79,8 @@ order by status desc, created desc
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
CreateNewTimetable, err = DB.Prepare(`
|
CreateNewTimetable, err = DB.Prepare(`
|
||||||
insert into timetables (name)
|
insert into timetables (name, "limit")
|
||||||
values ($1)
|
values ($1, $2)
|
||||||
returning id
|
returning id
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -93,7 +95,8 @@ where id = $1
|
|||||||
}
|
}
|
||||||
GetTimeSlotsByTimetable, err = DB.Prepare(`
|
GetTimeSlotsByTimetable, err = DB.Prepare(`
|
||||||
select t.id, t."name" ,t."time" ,t.take, t.capacity, t2."name", t2.status,
|
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
|
from timeslots t
|
||||||
join timetables t2 on t.ttid = t2.id
|
join timetables t2 on t.ttid = t2.id
|
||||||
left outer join (
|
left outer join (
|
||||||
@@ -137,8 +140,8 @@ where tsid = $1 and username = $2
|
|||||||
}
|
}
|
||||||
UpdateTimetableStatus, err = DB.Prepare(`
|
UpdateTimetableStatus, err = DB.Prepare(`
|
||||||
update timetables
|
update timetables
|
||||||
set status = $1
|
set status = $1, "limit" = $2
|
||||||
where id = $2
|
where id = $3
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -171,4 +174,15 @@ select status from timetables where id = $1
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ func install() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Exec(`
|
_, err = tx.Exec(`
|
||||||
CREATE TABLE timetables (
|
CREATE TABLE timetables (
|
||||||
id serial primary key,
|
id serial primary key,
|
||||||
"name" text NOT NULL,
|
"name" text NOT NULL,
|
||||||
|
"limit" integer NOT NULL DEFAULT 1,
|
||||||
status bool NOT NULL DEFAULT false,
|
status bool NOT NULL DEFAULT false,
|
||||||
created timestamp NOT NULL DEFAULT now()
|
created timestamp NOT NULL DEFAULT now()
|
||||||
);
|
);
|
||||||
|
|||||||
26
main.go
26
main.go
@@ -72,7 +72,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
s := &db.Timetable{}
|
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)
|
timetables = append(timetables, s)
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
@@ -83,6 +83,7 @@ func main() {
|
|||||||
api.POST("/timetables", auth, func(c *gin.Context) {
|
api.POST("/timetables", auth, func(c *gin.Context) {
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Name string `json:"newTimeTableName"`
|
Name string `json:"newTimeTableName"`
|
||||||
|
Limit int64 `json:"newLimit"`
|
||||||
}
|
}
|
||||||
req := &Request{}
|
req := &Request{}
|
||||||
err := c.ShouldBindBodyWith(req, binding.JSON)
|
err := c.ShouldBindBodyWith(req, binding.JSON)
|
||||||
@@ -90,7 +91,7 @@ func main() {
|
|||||||
c.AbortWithError(400, err)
|
c.AbortWithError(400, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
row := db.CreateNewTimetable.QueryRow(req.Name)
|
row := db.CreateNewTimetable.QueryRow(req.Name, req.Limit)
|
||||||
var id int64
|
var id int64
|
||||||
err = row.Scan(&id)
|
err = row.Scan(&id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -130,9 +131,10 @@ func main() {
|
|||||||
timeslots := make([]*db.TimeSlot, 0)
|
timeslots := make([]*db.TimeSlot, 0)
|
||||||
var timetableName string
|
var timetableName string
|
||||||
var timetableStatus bool
|
var timetableStatus bool
|
||||||
|
var timetableLimit int64
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
s := &db.TimeSlot{}
|
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 {
|
if err != nil {
|
||||||
c.AbortWithError(400, err)
|
c.AbortWithError(400, err)
|
||||||
return
|
return
|
||||||
@@ -143,6 +145,7 @@ func main() {
|
|||||||
"timeslots": timeslots,
|
"timeslots": timeslots,
|
||||||
"timetableName": timetableName,
|
"timetableName": timetableName,
|
||||||
"timetableStatus": timetableStatus,
|
"timetableStatus": timetableStatus,
|
||||||
|
"timetableLimit": timetableLimit,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -256,7 +259,7 @@ func main() {
|
|||||||
c.AbortWithError(400, err)
|
c.AbortWithError(400, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = db.UpdateTimetableStatus.Exec(req.Status, timetableID)
|
_, err = db.UpdateTimetableStatus.Exec(req.Status, req.Limit, timetableID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithError(400, err)
|
c.AbortWithError(400, err)
|
||||||
return
|
return
|
||||||
@@ -291,6 +294,21 @@ func main() {
|
|||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return
|
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)
|
updateCountStmt := tx.Stmt(db.UpdateTakeCount)
|
||||||
if req.Username[0] == '!' {
|
if req.Username[0] == '!' {
|
||||||
untakeStmt := tx.Stmt(db.UserUntakeTimeslot)
|
untakeStmt := tx.Stmt(db.UserUntakeTimeslot)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const Home = () => {
|
|||||||
|
|
||||||
const [timetables, setTimetables] = React.useState([]);
|
const [timetables, setTimetables] = React.useState([]);
|
||||||
const [newTimetableName, setNewTimetableName] = React.useState("");
|
const [newTimetableName, setNewTimetableName] = React.useState("");
|
||||||
|
const [newLimit, setNewLimit] = React.useState(null);
|
||||||
const navigator = useNavigate();
|
const navigator = useNavigate();
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
@@ -48,6 +49,18 @@ const Home = () => {
|
|||||||
}}
|
}}
|
||||||
placeholder="班表名"
|
placeholder="班表名"
|
||||||
/>
|
/>
|
||||||
|
<TextField
|
||||||
|
value={newLimit}
|
||||||
|
onChange={(event) => {
|
||||||
|
let num = parseInt(event.target.value)
|
||||||
|
if (num <= 0) {
|
||||||
|
num = 1;
|
||||||
|
}
|
||||||
|
setNewLimit(num);
|
||||||
|
}}
|
||||||
|
placeholder="每人选择上限"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -55,6 +68,7 @@ const Home = () => {
|
|||||||
await post("/timetables", {
|
await post("/timetables", {
|
||||||
token,
|
token,
|
||||||
newTimetableName,
|
newTimetableName,
|
||||||
|
newLimit,
|
||||||
});
|
});
|
||||||
await fetchData();
|
await fetchData();
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -18,10 +18,11 @@ import { UsernameContext } from "./Login";
|
|||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { get, post, put } from "../fetches";
|
import { get, post, put } from "../fetches";
|
||||||
|
|
||||||
const AdminComponent = ({ refresh, timetableStatus }) => {
|
const AdminComponent = ({ refresh, timetableStatus, timetableLimit }) => {
|
||||||
const { username, token } = React.useContext(UsernameContext);
|
const { username, token } = React.useContext(UsernameContext);
|
||||||
const [newName, setNewName] = React.useState("");
|
const [newName, setNewName] = React.useState("");
|
||||||
const [newTime, setNewTime] = React.useState("");
|
const [newTime, setNewTime] = React.useState("");
|
||||||
|
const [newLimit, setNewLimit] = React.useState(timetableLimit);
|
||||||
const [newCapacity, setNewCapacity] = React.useState(1);
|
const [newCapacity, setNewCapacity] = React.useState(1);
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
let { timetableID } = params;
|
let { timetableID } = params;
|
||||||
@@ -34,17 +35,11 @@ const AdminComponent = ({ refresh, timetableStatus }) => {
|
|||||||
});
|
});
|
||||||
await refresh();
|
await refresh();
|
||||||
};
|
};
|
||||||
const handleOpen = async () => {
|
const handleChangeStatus = async (status, limit) => {
|
||||||
await put(`/timetables/${timetableID}`, {
|
await put(`/timetables/${timetableID}`, {
|
||||||
token,
|
token,
|
||||||
status: true,
|
limit,
|
||||||
});
|
status,
|
||||||
await refresh();
|
|
||||||
};
|
|
||||||
const handleClose = async () => {
|
|
||||||
await put(`/timetables/${timetableID}`, {
|
|
||||||
token,
|
|
||||||
status: false,
|
|
||||||
});
|
});
|
||||||
await refresh();
|
await refresh();
|
||||||
};
|
};
|
||||||
@@ -85,15 +80,47 @@ const AdminComponent = ({ refresh, timetableStatus }) => {
|
|||||||
添加
|
添加
|
||||||
</Button>
|
</Button>
|
||||||
{timetableStatus && (
|
{timetableStatus && (
|
||||||
<Button variant="contained" color="warning" onClick={handleClose}>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="warning"
|
||||||
|
onClick={() => {
|
||||||
|
handleChangeStatus(false, timetableLimit);
|
||||||
|
}}
|
||||||
|
>
|
||||||
暂停报名
|
暂停报名
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{!timetableStatus && (
|
{!timetableStatus && (
|
||||||
<Button variant="contained" color="success" onClick={handleOpen}>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="success"
|
||||||
|
onClick={() => {
|
||||||
|
handleChangeStatus(true, timetableLimit);
|
||||||
|
}}
|
||||||
|
>
|
||||||
开放报名
|
开放报名
|
||||||
</Button>
|
</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>
|
</Stack>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -103,6 +130,7 @@ const Timetable = () => {
|
|||||||
const [timeslots, setTimeslots] = React.useState([]);
|
const [timeslots, setTimeslots] = React.useState([]);
|
||||||
const [timetableName, setTimetableName] = React.useState("");
|
const [timetableName, setTimetableName] = React.useState("");
|
||||||
const [timetableStatus, setTimetableStatus] = React.useState(true);
|
const [timetableStatus, setTimetableStatus] = React.useState(true);
|
||||||
|
const [timetableLimit, setTimetableLimit] = React.useState(null);
|
||||||
const { username } = React.useContext(UsernameContext);
|
const { username } = React.useContext(UsernameContext);
|
||||||
const navigator = useNavigate();
|
const navigator = useNavigate();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -115,6 +143,7 @@ const Timetable = () => {
|
|||||||
setTimeslots(data.timeslots);
|
setTimeslots(data.timeslots);
|
||||||
setTimetableName(data.timetableName);
|
setTimetableName(data.timetableName);
|
||||||
setTimetableStatus(data.timetableStatus);
|
setTimetableStatus(data.timetableStatus);
|
||||||
|
setTimetableLimit(data.timetableLimit);
|
||||||
};
|
};
|
||||||
const handleTakeSlot = async (slotID, status) => {
|
const handleTakeSlot = async (slotID, status) => {
|
||||||
if (username === "admin") {
|
if (username === "admin") {
|
||||||
@@ -129,7 +158,7 @@ const Timetable = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
refresh()
|
refresh();
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
refresh();
|
refresh();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@@ -150,9 +179,13 @@ const Timetable = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Typography variant="h4">{timetableName}</Typography>
|
<Typography variant="h4">{timetableName}</Typography>
|
||||||
|
|
||||||
<AdminComponent refresh={refresh} timetableStatus={timetableStatus} />
|
<AdminComponent
|
||||||
|
refresh={refresh}
|
||||||
|
timetableStatus={timetableStatus}
|
||||||
|
timetableLimit={timetableLimit}
|
||||||
|
/>
|
||||||
|
|
||||||
<Typography>列表实时更新中</Typography>
|
<Typography>列表实时更新中,每人报名限制:{timetableLimit}</Typography>
|
||||||
<LinearProgress />
|
<LinearProgress />
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table>
|
<Table>
|
||||||
|
|||||||
Reference in New Issue
Block a user