mirror of
https://github.com/ZSCNetSupportDept/scheduler.git
synced 2025-10-28 20:45:05 +08:00
1
This commit is contained in:
26
Dockerfile
Normal file
26
Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# 构建阶段
|
||||||
|
FROM golang:1.23 AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制源码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建二进制文件
|
||||||
|
RUN go build -o /app/scheduler
|
||||||
|
|
||||||
|
# 运行阶段
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制构建好的二进制文件
|
||||||
|
COPY --from=builder ./scheduler ./scheduler
|
||||||
|
|
||||||
|
RUN mkdir -p config
|
||||||
|
|
||||||
|
EXPOSE 25005
|
||||||
|
|
||||||
|
USER nonroot:nonroot
|
||||||
|
|
||||||
|
CMD ["./scheduler","--config","config/config.yaml"]
|
||||||
@@ -1,33 +1,36 @@
|
|||||||
<html data-theme="light">
|
<html data-theme="light">
|
||||||
<head>
|
<head>
|
||||||
<!-- <link rel="stylesheet" href="https://cdn.jsdmirror.com/npm/@picocss/pico@2/css/pico.min.css"> -->
|
<!-- <link rel="stylesheet" href="https://cdn.jsdmirror.com/npm/@picocss/pico@2/css/pico.min.css"> -->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div>
|
||||||
<h1 align=center>今日值班表</h1>
|
<h1 align="center">今日值班表</h1>
|
||||||
<div align=center>
|
<div align="center">
|
||||||
<button hx-get="/api/getAssignment" hx-swap="outerHTML"> 点我!</button>
|
<button hx-get="/api/getAssignment" hx-swap="outerHTML">点我!</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<script src="/htmx.min.js"></script>
|
<script src="/htmx.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
margin:auto
|
margin: auto;
|
||||||
}
|
}
|
||||||
th, td {
|
th,
|
||||||
|
td {
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
td:first-child, th:first-child {
|
td:first-child,
|
||||||
|
th:first-child {
|
||||||
background-color: #b3d9f7;
|
background-color: #b3d9f7;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
td:nth-child(2), th:nth-child(2) {
|
td:nth-child(2),
|
||||||
background-color: #55ffaa
|
th:nth-child(2) {
|
||||||
|
background-color: #55ffaa;
|
||||||
}
|
}
|
||||||
.ZoneHead {
|
.ZoneHead {
|
||||||
display: inline-block; /* 让颜色块和文字在同一行显示 */
|
display: inline-block; /* 让颜色块和文字在同一行显示 */
|
||||||
@@ -44,22 +47,22 @@
|
|||||||
background-color: #ffa0c9; /* 设置颜色块的背景颜色 */
|
background-color: #ffa0c9; /* 设置颜色块的背景颜色 */
|
||||||
vertical-align: middle; /* 让颜色块垂直居中 */
|
vertical-align: middle; /* 让颜色块垂直居中 */
|
||||||
}
|
}
|
||||||
.SwitchOrRepay{
|
.SwitchOrRepay {
|
||||||
display: inline-block; /* 让颜色块和文字在同一行显示 */
|
display: inline-block; /* 让颜色块和文字在同一行显示 */
|
||||||
width: 1em; /* 设置颜色块的宽度为1个字的宽度 */
|
width: 1em; /* 设置颜色块的宽度为1个字的宽度 */
|
||||||
height: 1em; /* 设置颜色块的高度为1个字的高度 */
|
height: 1em; /* 设置颜色块的高度为1个字的高度 */
|
||||||
background-color: #fff6b5; /* 设置颜色块的背景颜色 */
|
background-color: #fff6b5; /* 设置颜色块的背景颜色 */
|
||||||
vertical-align: middle; /* 让颜色块垂直居中 */
|
vertical-align: middle; /* 让颜色块垂直居中 */
|
||||||
}
|
}
|
||||||
.Volunteering{
|
.Volunteering {
|
||||||
display: inline-block; /* 让颜色块和文字在同一行显示 */
|
display: inline-block; /* 让颜色块和文字在同一行显示 */
|
||||||
width: 1em; /* 设置颜色块的宽度为1个字的宽度 */
|
width: 1em; /* 设置颜色块的宽度为1个字的宽度 */
|
||||||
height: 1em; /* 设置颜色块的高度为1个字的高度 */
|
height: 1em; /* 设置颜色块的高度为1个字的高度 */
|
||||||
background-color: #c0ff85; /* 设置颜色块的背景颜色 */
|
background-color: #c0ff85; /* 设置颜色块的背景颜色 */
|
||||||
vertical-align: middle; /* 让颜色块垂直居中 */
|
vertical-align: middle; /* 让颜色块垂直居中 */
|
||||||
}
|
}
|
||||||
.table_notes{
|
.table_notes {
|
||||||
align-items:left
|
align-items: left;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
document.getElementById('getAssignment').addEventListener('click', function () {
|
document.getElementById("getAssignment").addEventListener("click", function () {
|
||||||
dateInput = document.getElementById('calendar').value;
|
dateInput = document.getElementById("calendar").value;
|
||||||
|
|
||||||
if (!dateInput) {
|
if (!dateInput) {
|
||||||
dateInput = getToday()
|
dateInput = getToday();
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `/api/getAssignment?date=${dateInput}`;
|
const url = `/api/getAssignment?date=${dateInput}`;
|
||||||
|
|
||||||
fetch(url)
|
fetch(url)
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('网络响应失败');
|
throw new Error("网络响应失败");
|
||||||
}
|
}
|
||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then((data) => {
|
||||||
const responseDiv = document.getElementById('response');
|
const responseDiv = document.getElementById("response");
|
||||||
responseDiv.innerHTML = ''; // 清除旧内容
|
responseDiv.innerHTML = ""; // 清除旧内容
|
||||||
|
|
||||||
const table = document.createElement('table');
|
const table = document.createElement("table");
|
||||||
|
|
||||||
data.forEach(subArray => {
|
data.forEach((subArray) => {
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement("tr");
|
||||||
|
|
||||||
subArray.forEach(item => {
|
subArray.forEach((item) => {
|
||||||
const cell = document.createElement('td');
|
const cell = document.createElement("td");
|
||||||
cell.textContent = item.Name || item.ID;
|
cell.textContent = item.Name || item.ID;
|
||||||
|
|
||||||
// 优先判断 Access 条件
|
// 优先判断 Access 条件
|
||||||
if (item.Access <=3) {
|
if (item.Access <= 3) {
|
||||||
cell.classList.add('cell_Moderator');
|
cell.classList.add("cell_Moderator");
|
||||||
} else if (item.Note === 1) {
|
} else if (item.Note === 1) {
|
||||||
cell.classList.add('cell_SwitchOrRepay');
|
cell.classList.add("cell_SwitchOrRepay");
|
||||||
} else if (item.Note === 2) {
|
} else if (item.Note === 2) {
|
||||||
cell.classList.add('cell_Volunteering');
|
cell.classList.add("cell_Volunteering");
|
||||||
}
|
}
|
||||||
|
|
||||||
row.appendChild(cell);
|
row.appendChild(cell);
|
||||||
@@ -41,10 +41,10 @@ document.getElementById('getAssignment').addEventListener('click', function () {
|
|||||||
|
|
||||||
table.appendChild(row);
|
table.appendChild(row);
|
||||||
});
|
});
|
||||||
const title =`<i><h5 align=center >${dateInput}网维值班表</h5></i>`
|
const title = `<i><h5 align=center >${dateInput}网维值班表</h5></i>`;
|
||||||
const titleContainer = document.createElement('div');
|
const titleContainer = document.createElement("div");
|
||||||
titleContainer.innerHTML = title
|
titleContainer.innerHTML = title;
|
||||||
responseDiv.appendChild(titleContainer)
|
responseDiv.appendChild(titleContainer);
|
||||||
// 插入表格
|
// 插入表格
|
||||||
responseDiv.appendChild(table);
|
responseDiv.appendChild(table);
|
||||||
|
|
||||||
@@ -55,13 +55,13 @@ document.getElementById('getAssignment').addEventListener('click', function () {
|
|||||||
<i class="table_notes"><span class="SwitchOrRepay"></span>换班/补班<br></i>
|
<i class="table_notes"><span class="SwitchOrRepay"></span>换班/补班<br></i>
|
||||||
<i class="table_notes"><span class="Volunteering"></span>蹭班<br></i>
|
<i class="table_notes"><span class="Volunteering"></span>蹭班<br></i>
|
||||||
`;
|
`;
|
||||||
const legendContainer = document.createElement('div');
|
const legendContainer = document.createElement("div");
|
||||||
legendContainer.innerHTML = legendHTML;
|
legendContainer.innerHTML = legendHTML;
|
||||||
responseDiv.appendChild(legendContainer);
|
responseDiv.appendChild(legendContainer);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
console.error('请求失败:', error);
|
console.error("请求失败:", error);
|
||||||
document.getElementById('response').innerHTML = '获取任务失败,请重试。';
|
document.getElementById("response").innerHTML = "获取任务失败,请重试。";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -69,15 +69,8 @@ function getToday() {
|
|||||||
const today = new Date();
|
const today = new Date();
|
||||||
|
|
||||||
const year = today.getFullYear();
|
const year = today.getFullYear();
|
||||||
const month = String(today.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要+1
|
const month = String(today.getMonth() + 1).padStart(2, "0"); // 月份从0开始,需要+1
|
||||||
const day = String(today.getDate()).padStart(2, '0');
|
const day = String(today.getDate()).padStart(2, "0");
|
||||||
|
|
||||||
return `${year}-${month}-${day}`;
|
return `${year}-${month}-${day}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"zsxyww.com/scheduler/config"
|
"zsxyww.com/scheduler/config"
|
||||||
"zsxyww.com/scheduler/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@@ -35,7 +34,7 @@ func connectSQLite() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if config.InitDB == true {
|
if config.InitDB == true {
|
||||||
Main.AutoMigrate(&model.Member{}, &model.Tweak{})
|
//Main.AutoMigrate(&model.Member{}, &model.Tweak{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +44,6 @@ func connectPGSQL() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if config.InitDB == true {
|
if config.InitDB == true {
|
||||||
Main.AutoMigrate(&model.Member{}, &model.Tweak{})
|
//Main.AutoMigrate(&model.Member{}, &model.Tweak{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,3 +8,7 @@
|
|||||||
为了让实习成员熟悉每一个片区,每两周都会轮换值班每位成员负责值班的的片区,片区的轮换遵守上面的规则,也就是说,女生只会在女生片区内轮换,男生则会在全部的片区内轮换。
|
为了让实习成员熟悉每一个片区,每两周都会轮换值班每位成员负责值班的的片区,片区的轮换遵守上面的规则,也就是说,女生只会在女生片区内轮换,男生则会在全部的片区内轮换。
|
||||||
|
|
||||||
一个学期值班的周数不确定,但是一般不会少于12周,即可以轮换到每个成员。
|
一个学期值班的周数不确定,但是一般不会少于12周,即可以轮换到每个成员。
|
||||||
|
## 问题
|
||||||
|
排班算法现在是试图平均分配成员,可能会优化为根据片区的单子数量加权分配
|
||||||
|
对于每两周换片区的问题,我简单地对当值成员列表进行循环移位来解决,好像和目前的算法有点冲突,未来准备写一个测试来覆盖
|
||||||
|
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -18,7 +18,7 @@ require (
|
|||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/pgx/v5 v5.7.5 // indirect
|
github.com/jackc/pgx/v5 v5.7.5
|
||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
@@ -34,7 +34,7 @@ require (
|
|||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.6
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
|
|||||||
@@ -3,21 +3,17 @@ package handler
|
|||||||
import (
|
import (
|
||||||
//"fmt"
|
//"fmt"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/gocarina/gocsv"
|
//"github.com/gocarina/gocsv"
|
||||||
"github.com/golang-module/carbon/v2"
|
"github.com/golang-module/carbon/v2"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
//"os"
|
||||||
"sync"
|
//"sync"
|
||||||
"zsxyww.com/scheduler/config"
|
"zsxyww.com/scheduler/config"
|
||||||
"zsxyww.com/scheduler/model"
|
"zsxyww.com/scheduler/model"
|
||||||
"zsxyww.com/scheduler/signals"
|
//"zsxyww.com/scheduler/signals"
|
||||||
)
|
)
|
||||||
|
|
||||||
var data *[7][]*model.Member
|
|
||||||
var mutex sync.RWMutex //lock for data
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// /api/getAssignment GET 获取当日值班表
|
// /api/getAssignment GET 获取当日值班表
|
||||||
// 接受参数date,是需要生成值班表的日期
|
// 接受参数date,是需要生成值班表的日期
|
||||||
func GetAssignment(i echo.Context) error {
|
func GetAssignment(i echo.Context) error {
|
||||||
@@ -29,51 +25,40 @@ func GetAssignment(i echo.Context) error {
|
|||||||
arg = carbon.Parse(date)
|
arg = carbon.Parse(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (carbon.Now().ToDateString() != signals.Table.GetLastUpdated().ToDateString()) || signals.Table.IsNeedUpdate() == true {
|
data, err := generateTable(arg)
|
||||||
|
|
||||||
mutex.Lock()
|
|
||||||
data, err = generateTable(arg)
|
|
||||||
mutex.Unlock()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
i.String(http.StatusInternalServerError, err.Error())
|
i.String(http.StatusInternalServerError, err.Error())
|
||||||
return echo.ErrInternalServerError
|
return echo.ErrInternalServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
//signals.Table.SetUpdated(carbon.Now())
|
|
||||||
//测试时注释掉上面的状态更新方便调试
|
|
||||||
}
|
|
||||||
mutex.RLock()
|
|
||||||
i.JSON(200, data)
|
i.JSON(200, data)
|
||||||
mutex.RUnlock()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据指定的时间来生成对应的值班表
|
// 根据指定的时间来生成对应的值班表
|
||||||
func generateTable(time carbon.Carbon) (*[7][]*model.Member, error) {
|
func generateTable(time carbon.Carbon) (*[7][]*model.Member, error) {
|
||||||
|
|
||||||
table := [7][]*model.Member{} //结果放入这里
|
table := [7][]*model.Member{} //结果放入这里
|
||||||
members := []*model.Member{} //包含所有成员信息的切片
|
members := []*model.Member{} //包含所有成员信息的切片
|
||||||
today := []*model.Member{} //今天值班的人
|
today := []*model.Member{} //今天值班的人
|
||||||
female := []*model.Member{} //今天的女生
|
female := []*model.Member{} //今天的女生
|
||||||
male := []*model.Member{} //今天的男生
|
male := []*model.Member{} //今天的男生
|
||||||
week, dayOfWeek := getWorkDay(time)
|
week, dayOfWeek := getWorkDay(time)
|
||||||
|
|
||||||
//检查传入时间有没有问题
|
//检查传入时间有没有问题
|
||||||
//TODO:这里好像有bug(对日期是否在值班时间内的判断部分),不过不怎么影响使用
|
//TODO:这里好像有bug(对日期是否在值班时间内的判断部分),不过不怎么影响使用
|
||||||
if (week < 0) || (week > config.Default.Business.Week) {
|
if (week < 0) || (week > config.Default.Business.Week) {
|
||||||
return nil, errors.New("日期错误,日期需要在本学期的值班日期内并且格式正确")
|
return nil, errors.New("日期错误,日期需要在本学期的值班日期内并且格式正确")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为了实现更换值班的片区,写的一个闭包切片访问器
|
// 切片访问函数,用来实现自动更换值班片区的功能
|
||||||
iter := func(array []*model.Member, i int) *model.Member {
|
iter := func(array []*model.Member, i int) *model.Member {
|
||||||
return array[(i+week)%len(array)]
|
return array[(i+week)%len(array)]
|
||||||
}
|
}
|
||||||
|
|
||||||
//读取csv文件
|
members = model.MemberList
|
||||||
err := readTableData(&members)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
//添加标题
|
//添加标题
|
||||||
table[0] = append(table[0], &model.Member{Name: "凤翔", Access: 7})
|
table[0] = append(table[0], &model.Member{Name: "凤翔", Access: 7})
|
||||||
table[1] = append(table[1], &model.Member{Name: "朝晖", Access: 7})
|
table[1] = append(table[1], &model.Member{Name: "朝晖", Access: 7})
|
||||||
@@ -98,32 +83,32 @@ func generateTable(time carbon.Carbon) (*[7][]*model.Member, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//为女生分配负责人
|
//将所有正式女生分配到女生片区
|
||||||
for i := 0; i < len(female); i++ {
|
for i := range female {
|
||||||
if a := iter(female, i); a.Access < model.FRESH { //是正式成员
|
if a := iter(female, i); a.Access < model.FRESH { //是正式成员
|
||||||
table[i%4] = append(table[i%4], a) //轮流分配到女生片区
|
table[i%4] = append(table[i%4], a) //轮流分配到女生片区
|
||||||
a.Arranged = true
|
a.Arranged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//为剩下的片区分配负责人
|
//将所有正式男生分配到所有片区(优先分配人少的片区)
|
||||||
for i := 0; i < len(male); i++ {
|
for i := range male {
|
||||||
if a := iter(male, i); a.Access < model.FRESH { //是正式成员
|
if a := iter(male, i); a.Access < model.FRESH { //是正式成员
|
||||||
table[fewest(table)] = append(table[fewest(table)], a)
|
table[fewest(table)] = append(table[fewest(table)], a)
|
||||||
a.Arranged = true
|
a.Arranged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//分配剩下的所有女生到女生片区
|
//分配剩下的所有女生到女生片区(优先分配人少的片区)
|
||||||
for i := 0; i < len(female); i++ {
|
for i := range female {
|
||||||
if a := iter(female, i); a.Arranged != true { //还没有安排
|
if a := iter(female, i); a.Arranged != true { //还没有安排
|
||||||
table[fewestF(table)] = append(table[fewestF(table)], a)
|
table[fewestF(table)] = append(table[fewestF(table)], a)
|
||||||
a.Arranged = true
|
a.Arranged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//分配剩下的所有男生
|
//分配剩下的所有男生(优先分配人少的片区)
|
||||||
for i := 0; i < len(male); i++ {
|
for i := range male {
|
||||||
if a := iter(male, i); a.Arranged == false { //还没有安排
|
if a := iter(male, i); a.Arranged == false { //还没有安排
|
||||||
table[fewest(table)] = append(table[fewest(table)], a)
|
table[fewest(table)] = append(table[fewest(table)], a)
|
||||||
a.Arranged = true
|
a.Arranged = true
|
||||||
@@ -133,24 +118,6 @@ func generateTable(time carbon.Carbon) (*[7][]*model.Member, error) {
|
|||||||
return &table, nil
|
return &table, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取csv文件
|
|
||||||
func readTableData(m *[]*model.Member) error {
|
|
||||||
data, err := os.OpenFile(config.Default.App.File, os.O_RDWR|os.O_CREATE, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer data.Close()
|
|
||||||
|
|
||||||
err = gocsv.UnmarshalFile(data, m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
//for index, member := range *m {
|
|
||||||
// fmt.Printf("%v:%v\n", index, member) // for debug concerns
|
|
||||||
//}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 找出人数最少的片区
|
// 找出人数最少的片区
|
||||||
func fewest(a [7][]*model.Member) int {
|
func fewest(a [7][]*model.Member) int {
|
||||||
b := min(len(a[0]), len(a[1]), len(a[2]), len(a[3]), len(a[4]), len(a[5]), len(a[6]))
|
b := min(len(a[0]), len(a[1]), len(a[2]), len(a[3]), len(a[4]), len(a[5]), len(a[6]))
|
||||||
|
|||||||
1
handler/init.go
Normal file
1
handler/init.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package handler
|
||||||
1
handler/tweaks.go
Normal file
1
handler/tweaks.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package handler
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
// CRUD的基础操作
|
|
||||||
package uo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"zsxyww.com/scheduler/database"
|
|
||||||
"zsxyww.com/scheduler/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 增加一项tweak
|
|
||||||
func AddTweak(in *model.Tweak) error {
|
|
||||||
result := db.Main.Create(in)
|
|
||||||
return result.Error
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除一项tweak
|
|
||||||
func DeleteTweak(in *model.Tweak) error {
|
|
||||||
if db.Main.Error != nil {
|
|
||||||
return db.Main.Error
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询一些tweak,通过IssueID
|
|
||||||
func GetTweakByIssueID(in *model.Tweak) (result []*model.Tweak, err error) {
|
|
||||||
if db.Main.Error != nil {
|
|
||||||
return nil, db.Main.Error
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询一些tweak,通过一个日期
|
|
||||||
func GetTweakByTime(in *model.Tweak) (result []*model.Tweak, err error) {
|
|
||||||
if db.Main.Error != nil {
|
|
||||||
return nil, db.Main.Error
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询一些tweak,通过一个工号
|
|
||||||
func GetTweakByID(in *model.Tweak) (result []*model.Tweak, err error) {
|
|
||||||
if db.Main.Error != nil {
|
|
||||||
return nil, db.Main.Error
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
18
main.go
18
main.go
@@ -2,10 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/gocarina/gocsv"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"os"
|
||||||
"zsxyww.com/scheduler/config"
|
"zsxyww.com/scheduler/config"
|
||||||
"zsxyww.com/scheduler/database"
|
"zsxyww.com/scheduler/database"
|
||||||
|
"zsxyww.com/scheduler/model"
|
||||||
"zsxyww.com/scheduler/route"
|
"zsxyww.com/scheduler/route"
|
||||||
"zsxyww.com/scheduler/templates"
|
"zsxyww.com/scheduler/templates"
|
||||||
)
|
)
|
||||||
@@ -17,6 +20,7 @@ func main() {
|
|||||||
|
|
||||||
app := echo.New()
|
app := echo.New()
|
||||||
register(app)
|
register(app)
|
||||||
|
csv()
|
||||||
|
|
||||||
listenAddress := fmt.Sprintf(":%d", config.Default.App.ListenPort)
|
listenAddress := fmt.Sprintf(":%d", config.Default.App.ListenPort)
|
||||||
|
|
||||||
@@ -31,3 +35,17 @@ func register(app *echo.Echo) {
|
|||||||
app.Renderer = renderer
|
app.Renderer = renderer
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 读取csv文件
|
||||||
|
func csv() {
|
||||||
|
data, err := os.OpenFile(config.Default.App.File, os.O_RDWR|os.O_CREATE, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer data.Close()
|
||||||
|
|
||||||
|
err = gocsv.UnmarshalFile(data, &model.MemberList)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,3 +20,5 @@ const GROUP = 3 //组长
|
|||||||
const FORMAL = 4 //正式成员
|
const FORMAL = 4 //正式成员
|
||||||
const FRESH = 5 //实习成员
|
const FRESH = 5 //实习成员
|
||||||
const PRE = 6 //前成员
|
const PRE = 6 //前成员
|
||||||
|
|
||||||
|
var MemberList []*Member
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package model
|
|||||||
import (
|
import (
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"time"
|
"time"
|
||||||
|
"zsxyww.com/scheduler/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 这个结构体是供数据库使用的表结构,换班补班蹭班的记录都会以这种方式储存
|
// 这个结构体是供数据库使用的表结构,换班补班蹭班的记录都会以这种方式储存
|
||||||
@@ -23,3 +24,40 @@ const (
|
|||||||
OP_ADMIN_ADD = 4
|
OP_ADMIN_ADD = 4
|
||||||
OP_ADMIN_SUB = 5
|
OP_ADMIN_SUB = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 增加一项tweak
|
||||||
|
func (t *Tweak) addTweak() error {
|
||||||
|
result := db.Main.Create(t)
|
||||||
|
return result.Error
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除一项tweak
|
||||||
|
func (t *Tweak) deleteTweak() error {
|
||||||
|
result := db.Main.Delete(t)
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询一些tweak,通过IssueID
|
||||||
|
func (t *Tweak) getTweakByIssueID() (result []*Tweak, err error) {
|
||||||
|
if db.Main.Error != nil {
|
||||||
|
return nil, db.Main.Error
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询一些tweak,通过一个日期
|
||||||
|
func (t *Tweak) getTweakByTime() (result []*Tweak, err error) {
|
||||||
|
if db.Main.Error != nil {
|
||||||
|
return nil, db.Main.Error
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询一些tweak,通过一个工号
|
||||||
|
func (t *Tweak) getTweakByID() (result []*Tweak, err error) {
|
||||||
|
if db.Main.Error != nil {
|
||||||
|
return nil, db.Main.Error
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user