From e568882c6f5ae365a883ed562f08b8eaba17cb72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9A=93=E6=9C=88=E5=BD=92=E5=B0=98?= Date: Mon, 11 Nov 2024 10:57:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9D=E5=A7=8B=E5=8C=96=E4=BB=93?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 83 +++++++++ data/config.json | 81 +++++++++ go.mod | 44 +++++ go.sum | 107 ++++++++++++ main.go | 83 +++++++++ models/building.go | 20 +++ models/campus.go | 19 +++ models/classname.go | 20 +++ models/classroom.go | 22 +++ models/course.go | 37 ++++ rsrc.syso | Bin 0 -> 459640 bytes utils/auth.go | 74 ++++++++ utils/config.go | 123 ++++++++++++++ utils/router.go | 402 ++++++++++++++++++++++++++++++++++++++++++++ 智享通.ico | Bin 0 -> 458870 bytes 15 files changed, 1115 insertions(+) create mode 100644 .gitignore create mode 100644 data/config.json create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 models/building.go create mode 100644 models/campus.go create mode 100644 models/classname.go create mode 100644 models/classroom.go create mode 100644 models/course.go create mode 100644 rsrc.syso create mode 100644 utils/auth.go create mode 100644 utils/config.go create mode 100644 utils/router.go create mode 100644 智享通.ico diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f92bea6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,83 @@ +tmp +.env +gohub +.DS_Store +.history + +# Golang # +###################### +# `go test -c` 生成的二进制文件 +*.test +# go coverage 工具 +*.out +*.prof +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +# 编译文件 # +################### +*.com +*.class +*.dll +*.exe +*.exe~ +*.o +*.so + +# 压缩包 # +############ +# Git 自带压缩,如果这些压缩包里有代码,建议解压后 commit +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# 日志文件和数据库 # +###################### +*.log +*.sqlite +*.db + +# 临时文件 # +###################### +tmp/ +.tmp/ + +# 系统生成文件 # +###################### +.DS_Store +.DS_Store? +.AppleDouble +.LSOverride +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +.TemporaryItems +.fseventsd +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# IDE 和编辑器 # +###################### +.idea/ +/go_build_* +out/ +.vscode/ +.vscode/settings.json +*.sublime* +__debug_bin +.project + +# 前端工具链 # +###################### +.sass-cache/* +node_modules/ \ No newline at end of file diff --git a/data/config.json b/data/config.json new file mode 100644 index 0000000..d00e257 --- /dev/null +++ b/data/config.json @@ -0,0 +1,81 @@ +{ + "host": "0.0.0.0", + "port": "8080", + "startDate": "2024-09-02", + "totalWeeks": 20, + "endDate": "2025-01-19", + "timeSetting": [ + { + "index": 1, + "start": "08:00", + "end": "08:45", + "time": 0 + }, + { + "index": 2, + "start": "08:50", + "end": "09:35", + "time": 0 + }, + { + "index": 3, + "start": "09:55", + "end": "10:40", + "time": 0 + }, + { + "index": 4, + "start": "10:45", + "end": "11:30", + "time": 0 + }, + { + "index": 5, + "start": "11:35", + "end": "12:20", + "time": 0 + }, + { + "index": 6, + "start": "14:00", + "end": "14:45", + "time": 1 + }, + { + "index": 7, + "start": "14:50", + "end": "15:35", + "time": 1 + }, + { + "index": 8, + "start": "15:55", + "end": "16:40", + "time": 1 + }, + { + "index": 9, + "start": "16:45", + "end": "17:30", + "time": 1 + }, + { + "index": 10, + "start": "19:00", + "end": "19:45", + "time": 2 + }, + { + "index": 11, + "start": "19:50", + "end": "20:35", + "time": 2 + }, + { + "index": 12, + "start": "20:40", + "end": "21:25", + "time": 2 + } + ] +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bf43402 --- /dev/null +++ b/go.mod @@ -0,0 +1,44 @@ +module zhixiangtong + +go 1.21 + +require ( + github.com/fatih/color v1.18.0 + github.com/gin-gonic/gin v1.10.0 + github.com/google/uuid v1.6.0 + gorm.io/driver/sqlite v1.5.6 + gorm.io/gorm v1.25.11 +) + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c1de6cd --- /dev/null +++ b/go.sum @@ -0,0 +1,107 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= +gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= +gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= +gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..884076e --- /dev/null +++ b/main.go @@ -0,0 +1,83 @@ +package main + +import ( + "fmt" + "github.com/fatih/color" + "github.com/gin-gonic/gin" + "log" + "os" + "runtime" + "syscall" + "unsafe" + "zhixiangtong/utils" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + setConsoleTitle = kernel32.NewProc("SetConsoleTitleW") + setConsoleMode = kernel32.NewProc("SetConsoleMode") +) + +const ( + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 +) + +// enableVirtualTerminalProcessing 启用 Windows 的虚拟终端处理 +func enableVirtualTerminalProcessing(fd uintptr) { + var mode uint32 + handle := syscall.Handle(fd) + syscall.GetConsoleMode(handle, &mode) + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING + setConsoleMode.Call(uintptr(handle), uintptr(mode)) +} + +// 设置终端标题 +func setTerminalTitle(title string) { + utf16Title, _ := syscall.UTF16PtrFromString(title) + setConsoleTitle.Call(uintptr(unsafe.Pointer(utf16Title))) +} + +func main() { + // 设置终端标题 + setTerminalTitle("智享通") + if os.Getenv("TERM") == "" { + // 在 Windows 平台上启用虚拟终端处理,以支持颜色输出 + enableVirtualTerminalProcessing(os.Stdout.Fd()) + } + // 初始化配置 + dataDir := "data" + // 确保 data 目录存在 + if _, err := os.Stat(dataDir); os.IsNotExist(err) { + err := os.Mkdir(dataDir, os.ModePerm) + if err != nil { + log.Fatalf("无法创建 data 目录:%v", err) + } + } + config, err := utils.InitConfig(dataDir) + if err != nil { + log.Fatalf("初始化配置失败:%v", err) + } + + // 初始化数据库 + db, err := utils.InitDatabase(dataDir) + if err != nil { + log.Fatalf("初始化数据库失败:%v", err) + } + // 如果是 Windows 平台,强制启用颜色支持 + if runtime.GOOS == "windows" { + color.NoColor = false + } + // 设置为生产模式 + gin.SetMode(gin.ReleaseMode) + // 配置路由 + router := utils.SetupRouter(db, config) + + // 启动服务 + addr := fmt.Sprintf("%s:%s", config.Host, config.Port) + log.Printf("服务正在运行在 %s", addr) + err = router.Run(addr) + if err != nil { + log.Fatalf("服务框架启动失败 %v", err) + return + } +} diff --git a/models/building.go b/models/building.go new file mode 100644 index 0000000..b4920db --- /dev/null +++ b/models/building.go @@ -0,0 +1,20 @@ +package models + +import ( + "github.com/google/uuid" + "gorm.io/gorm" +) + +// Building 教学楼模型 +type Building struct { + ID string `gorm:"type:uuid;primaryKey" json:"id"` + Name string `gorm:"type:varchar(255);not null" json:"name"` + Campus string `gorm:"type:varchar(255);not null" json:"campus"` // 教学楼所属校区名称 + CampusID string `gorm:"type:uuid;not null" json:"campus_id"` // 教学楼所属校区ID(外键) +} + +// BeforeCreate 在创建Building之前生成UUID +func (building *Building) BeforeCreate(tx *gorm.DB) (err error) { + building.ID = uuid.New().String() + return +} diff --git a/models/campus.go b/models/campus.go new file mode 100644 index 0000000..8e3da2e --- /dev/null +++ b/models/campus.go @@ -0,0 +1,19 @@ +// Package models +package models + +import ( + "github.com/google/uuid" + "gorm.io/gorm" +) + +// Campus 校区模型 +type Campus struct { + ID string `gorm:"type:uuid;primaryKey" json:"id"` + Name string `gorm:"type:varchar(255);not null" json:"name"` +} + +// BeforeCreate 在创建Campus之前生成UUID +func (campus *Campus) BeforeCreate(tx *gorm.DB) (err error) { + campus.ID = uuid.New().String() + return +} diff --git a/models/classname.go b/models/classname.go new file mode 100644 index 0000000..a98c40d --- /dev/null +++ b/models/classname.go @@ -0,0 +1,20 @@ +package models + +import ( + "github.com/google/uuid" + "gorm.io/gorm" +) + +// Classname 班级表 +type Classname struct { + ID string `gorm:"type:uuid;primaryKey" json:"id"` + Name string `gorm:"type:varchar(255);not null" json:"name"` + Campus string `gorm:"type:varchar(255);not null" json:"campus"` // 教学楼所属校区名称 + CampusID string `gorm:"type:uuid;not null" json:"campus_id"` // 教学楼所属校区ID(外键) +} + +// BeforeCreate 在创建Classname之前生成UUID +func (classname *Classname) BeforeCreate(tx *gorm.DB) (err error) { + classname.ID = uuid.New().String() + return +} diff --git a/models/classroom.go b/models/classroom.go new file mode 100644 index 0000000..0dcf1ff --- /dev/null +++ b/models/classroom.go @@ -0,0 +1,22 @@ +package models + +import ( + "github.com/google/uuid" + "gorm.io/gorm" +) + +// Classroom 教室模型 +type Classroom struct { + ID string `gorm:"type:uuid;primaryKey" json:"id"` + Name string `gorm:"type:varchar(255);not null" json:"name"` + Build string `gorm:"type:varchar(255);not null" json:"build"` + BuildID string `gorm:"type:uuid;not null" json:"build_id"` + CampusID string `gorm:"type:uuid;not null" json:"campus_id"` // 校区ID + Campus string `gorm:"type:varchar(255);not null" json:"campus"` // 校区名称 +} + +// BeforeCreate 在创建Classroom之前生成UUID +func (classroom *Classroom) BeforeCreate(tx *gorm.DB) (err error) { + classroom.ID = uuid.New().String() + return +} diff --git a/models/course.go b/models/course.go new file mode 100644 index 0000000..4625553 --- /dev/null +++ b/models/course.go @@ -0,0 +1,37 @@ +package models + +import ( + "time" + + "github.com/google/uuid" + "gorm.io/gorm" +) + +// Course 课程模型 +type Course struct { + ID string `gorm:"type:uuid;primaryKey;column:id" json:"id"` // 课程ID + ClassName string `gorm:"type:varchar(255);not null;column:classname" json:"classname"` // 班级名称 + ClassNameID string `gorm:"type:uuid;not null;column:classname_id" json:"classname_id"` // 班级ID + Course string `gorm:"type:varchar(255);not null;column:course" json:"course"` // 课程名称 + Teacher string `gorm:"type:varchar(255);not null;column:teacher" json:"teacher"` // 授课老师 + Classroom string `gorm:"type:varchar(255);not null;column:classroom" json:"classroom"` // 教室名称 + ClassroomID string `gorm:"type:uuid;not null;column:classroom_id" json:"classroom_id"` // 教室ID + Week int `gorm:"not null;column:week" json:"week"` // 周次 + Day int `gorm:"not null;column:day" json:"day"` // 星期几 + ClassTime int `gorm:"not null;column:classTime" json:"classTime"` // 第几节课 + StartTime time.Time `gorm:"type:datetime;not null;column:startTime" json:"startTime"` // 开始时间 + StartStamp float64 `gorm:"not null;column:startStamp" json:"startStamp"` // 开始时间戳 + EndTime time.Time `gorm:"type:datetime;not null;column:endTime" json:"endTime"` // 结束时间 + EndStamp float64 `gorm:"not null;column:endStamp" json:"endStamp"` // 结束时间戳 + CampusID string `gorm:"type:uuid;not null;column:campus_id" json:"campus_id"` // 校区ID + Campus string `gorm:"type:varchar(255);not null;column:campus" json:"campus"` // 校区名称 + BuildID string `gorm:"type:uuid;not null;column:build_id" json:"build_id"` // 教学楼ID + Build string `gorm:"type:varchar(255);not null;column:build" json:"build"` // 教学楼名称 + Status int `gorm:"not null;column:status" json:"status"` // 状态 +} + +// BeforeCreate 在创建Course之前生成UUID +func (course *Course) BeforeCreate(tx *gorm.DB) (err error) { + course.ID = uuid.New().String() + return +} diff --git a/rsrc.syso b/rsrc.syso new file mode 100644 index 0000000000000000000000000000000000000000..4f51dbdfdd978c863f0bc93a06992f0905b1925e GIT binary patch literal 459640 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

<#J^a?9KHwVjU9Z6S8OA&U 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 z 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 literal 0 HcmV?d00001 diff --git a/utils/auth.go b/utils/auth.go new file mode 100644 index 0000000..c4fd840 --- /dev/null +++ b/utils/auth.go @@ -0,0 +1,74 @@ +package utils + +import ( + "fmt" + "github.com/fatih/color" + "github.com/gin-gonic/gin" + "net/http" + "time" +) + +func CORSMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // 设置允许访问的域 + c.Writer.Header().Set("Access-Control-Allow-Origin", "*") + // 是否允许携带用户凭证 + c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") + // 允许的自定义请求头 + c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With, X-CSRF-Token, Accept, Origin") + // 允许的HTTP方法 + c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH") + + // 处理预检请求 + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(http.StatusOK) + return + } + + // 继续处理非预检请求 + c.Next() + } +} + +func CustomLogger() gin.HandlerFunc { + return func(c *gin.Context) { + // 获取开始时间 + start := time.Now() + + // 处理请求 + c.Next() + + // 获取状态码、耗时、客户端IP、方法、路径 + statusCode := c.Writer.Status() + latency := time.Since(start) + clientIP := c.ClientIP() + method := c.Request.Method + path := c.Request.URL.Path + + // 颜色设置 + statusColor := color.New(color.FgGreen).SprintFunc() + if statusCode >= 400 && statusCode < 500 { + statusColor = color.New(color.FgYellow).SprintFunc() + } else if statusCode >= 500 { + statusColor = color.New(color.FgRed).SprintFunc() + } + + methodColor := color.New(color.FgCyan).SprintFunc() + + // 打印彩色日志 + fmt.Printf("[GIN] %v | %s | %s | %13v | %s | %s\n", + time.Now().Format("2006/01/02 - 15:04:05"), + statusColor(statusCode), + clientIP, + latency, + methodColor(method), + path, + ) + } +} +func NotFoundHandler(c *gin.Context) { + c.JSON(http.StatusNotFound, gin.H{"code": 404, "msg": "无效路径!"}) +} +func NotMethodHandler(c *gin.Context) { + c.JSON(http.StatusMethodNotAllowed, gin.H{"code": 404, "msg": "请求方法错误!"}) +} diff --git a/utils/config.go b/utils/config.go new file mode 100644 index 0000000..a1a41a1 --- /dev/null +++ b/utils/config.go @@ -0,0 +1,123 @@ +package utils + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" + + "zhixiangtong/models" +) + +type TimeSet struct { + Index int `json:"index"` + Start string `json:"start"` + End string `json:"end"` + Time int `json:"time"` +} + +// Config 结构体定义服务配置 +type Config struct { + Host string `json:"host"` + Port string `json:"port"` + StartDate string `json:"startDate"` + TotalWeeks int `json:"totalWeeks"` + EndDate string `json:"endDate"` + TimeSetting []TimeSet `json:"timeSetting"` +} + +// InitConfig 初始化配置文件 +func InitConfig(dataDir string) (*Config, error) { + configPath := filepath.Join(dataDir, "config.json") + + // 检查配置文件是否存在 + if _, err := os.Stat(configPath); os.IsNotExist(err) { + log.Printf("配置文件不存在,正在创建默认配置文件:%s", configPath) + // JSON 数据 + timeSettingJSON := `[{"index": 1, "start": "08:00", "end": "08:45", "time": 0}, + {"index": 2, "start": "08:50", "end": "09:35", "time": 0}, + {"index": 3, "start": "09:55", "end": "10:40", "time": 0}, + {"index": 4, "start": "10:45", "end": "11:30", "time": 0}, + {"index": 5, "start": "11:35", "end": "12:20", "time": 0}, + {"index": 6, "start": "14:00", "end": "14:45", "time": 1}, + {"index": 7, "start": "14:50", "end": "15:35", "time": 1}, + {"index": 8, "start": "15:55", "end": "16:40", "time": 1}, + {"index": 9, "start": "16:45", "end": "17:30", "time": 1}, + {"index": 10, "start": "19:00", "end": "19:45", "time": 2}, + {"index": 11, "start": "19:50", "end": "20:35", "time": 2}, + {"index": 12, "start": "20:40", "end": "21:25", "time": 2}]` + + // 将 JSON 数据解析为 TimeSet 切片 + var timeSettings []TimeSet + err := json.Unmarshal([]byte(timeSettingJSON), &timeSettings) + if err != nil { + log.Fatalf("Failed to parse JSON: %v", err) + } + // 默认配置 + defaultConfig := Config{ + Host: "localhost", + Port: "8080", + StartDate: "2024-09-02", + EndDate: "2025-01-19", + TotalWeeks: 20, + TimeSetting: timeSettings, + } + + // 将默认配置写入配置文件 + configData, err := json.MarshalIndent(defaultConfig, "", " ") + if err != nil { + return nil, fmt.Errorf("无法创建默认配置文件:%v", err) + } + + err = ioutil.WriteFile(configPath, configData, 0644) + if err != nil { + return nil, fmt.Errorf("无法写入默认配置文件:%v", err) + } + } + + // 读取配置文件 + configData, err := ioutil.ReadFile(configPath) + if err != nil { + return nil, fmt.Errorf("无法读取配置文件:%v", err) + } + + // 解析配置文件 + var config Config + err = json.Unmarshal(configData, &config) + if err != nil { + return nil, fmt.Errorf("配置文件格式错误:%v", err) + } + + log.Printf("配置文件已加载:Host=%s, Port=%s", config.Host, config.Port) + return &config, nil +} + +// InitDatabase 初始化数据库,如果不存在则创建并迁移 +func InitDatabase(dataDir string) (*gorm.DB, error) { + dbPath := filepath.Join(dataDir, "database.db") + + // 检查数据库文件是否存在 + if _, err := os.Stat(dbPath); os.IsNotExist(err) { + log.Printf("数据库文件不存在,正在创建数据库:%s", dbPath) + } + + // 连接到SQLite数据库 + db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) + if err != nil { + return nil, fmt.Errorf("无法连接到数据库:%v", err) + } + + // 自动迁移数据库 + err = db.AutoMigrate(&models.Campus{}, &models.Building{}, &models.Classroom{}, &models.Course{}, &models.Classname{}) + if err != nil { + return nil, fmt.Errorf("数据库迁移失败:%v", err) + } + + log.Printf("数据库已初始化并连接成功:%s", dbPath) + return db, nil +} diff --git a/utils/router.go b/utils/router.go new file mode 100644 index 0000000..45c1e6e --- /dev/null +++ b/utils/router.go @@ -0,0 +1,402 @@ +package utils + +import ( + "fmt" + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "gorm.io/gorm" + "net/http" + "sort" + "strconv" + "sync" + "time" + "zhixiangtong/models" +) + +type Setting struct { + StartDate string `json:"startDate"` + TotalWeeks int `json:"totalWeeks"` + EndDate string `json:"endDate"` + TimeSetting []TimeSet `json:"timeSetting"` +} + +// RemoveIntDuplicates 去重函数,去除整数切片中的重复元素 +func RemoveIntDuplicates(elements []int) []int { + seen := make(map[int]bool) + var result []int + + for _, v := range elements { + if _, ok := seen[v]; !ok { + result = append(result, v) + seen[v] = true + } + } + + return result +} +func RemoveStringDuplicates(stringSlice []string) []string { + keys := make(map[string]bool) + var list []string + for _, entry := range stringSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +// SetupRouter 配置Gin路由 +func SetupRouter(db *gorm.DB, config *Config) *gin.Engine { + r := gin.New() + r.Use(CustomLogger()) + // 处理跨域 + r.Use(CORSMiddleware()) + // 获取所有校区 + r.GET("/getCampusList", func(c *gin.Context) { + // 使用 WaitGroup 等待 Goroutine 完成 + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() // 在 Goroutine 完成时调用 Done() + var campuses []models.Campus + if err := db.Find(&campuses).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "无法获取校区列表", "data": nil}) + return + } + c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "获取成功!", "data": campuses}) + return + }() + wg.Wait() + }) + // 获取校区内所有教学楼 + r.GET("/getBuilds/:id", func(c *gin.Context) { + campusId := c.Param("id") + if campusId == "" { + c.JSON(http.StatusBadRequest, gin.H{"code": 422, "msg": "参数错误,校区ID为必传项!", "data": nil}) + return + } + // 使用 WaitGroup 等待 Goroutine 完成 + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() // 在 Goroutine 完成时调用 Done() + var buildings []models.Building + if err := db.Find(&buildings, "campus_id = ?", campusId).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"code": 500, "msg": "获取失败!", "data": nil}) + return + } + c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "获取成功!", "data": buildings}) + return + }() + wg.Wait() + }) + // 获取说所有教学楼 + r.GET("/getBuildList", func(c *gin.Context) { + // 使用 WaitGroup 等待 Goroutine 完成 + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() // 在 Goroutine 完成时调用 Done() + var buildings []models.Building + if err := db.Find(&buildings).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "无法获取教学楼列表", "data": nil}) + return + } + c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "获取成功!", "data": buildings}) + return + }() + wg.Wait() + }) + // 获取教室列表 + r.GET("/getClassroomList/:id", func(c *gin.Context) { + buildId := c.Param("id") + var classrooms []models.Classroom + if buildId == "" { + c.JSON(http.StatusBadRequest, gin.H{"code": 422, "msg": "参数错误,教学楼ID为必传项!", "data": nil}) + return + } + // 使用 WaitGroup 等待 Goroutine 完成 + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() // 在 Goroutine 完成时调用 Done() + if result := db.Find(&classrooms, "build_id = ? ", buildId); result.Error != nil { + c.JSON(http.StatusNotFound, gin.H{"code": 500, "msg": "获取失败!", "data": nil}) + return + } + c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "获取成功!", "data": classrooms}) + return + }() + wg.Wait() + }) + // 获取校区班级列表 + r.GET("/getCampusClass/:id", func(c *gin.Context) { + var classNames []models.Classname + campusId := c.Param("id") + if campusId == "" { + c.JSON(http.StatusBadRequest, gin.H{"code": 422, "msg": "参数错误,校区ID为必传项!", "data": nil}) + return + } + // 使用 WaitGroup 等待 Goroutine 完成 + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() // 在 Goroutine 完成时调用 Done() + if result := db.Find(&classNames, "campus_id = ?", campusId); result.Error != nil { + c.JSON(http.StatusNotFound, gin.H{"code": 500, "msg": "获取失败!", "data": nil}) + return + } + c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "获取成功!", "data": classNames}) + return + }() + wg.Wait() + }) + // 获取班级课表 + r.GET("/getCourse/:id", func(c *gin.Context) { + var courses []models.Course + classnameId := c.Param("id") + if classnameId == "" { + c.JSON(http.StatusBadRequest, gin.H{"code": 422, "msg": "参数错误,班级ID为必传项!", "data": nil}) + return + } + // 使用 WaitGroup 等待 Goroutine 完成 + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() // 在 Goroutine 完成时调用 Done() + if result := db.Find(&courses, "classname_id = ? AND status = 1", classnameId); result.Error != nil { + c.JSON(http.StatusNotFound, gin.H{"code": 422, "msg": "课程查询失败!", "data": nil}) + return + } + // 按照课程名称、教室、星期几和上课时间去重 + uniqueCourses := make(map[string]map[string]map[int][]models.Course) + for _, course := range courses { + if uniqueCourses[course.Course] == nil { + uniqueCourses[course.Course] = make(map[string]map[int][]models.Course) + } + if uniqueCourses[course.Course][course.Classroom] == nil { + uniqueCourses[course.Course][course.Classroom] = make(map[int][]models.Course) + } + uniqueCourses[course.Course][course.Classroom][course.Day] = append(uniqueCourses[course.Course][course.Classroom][course.Day], course) + } + var resultList []map[string]interface{} + for courseName, classrooms := range uniqueCourses { + for classroom, days := range classrooms { + for day, courses := range days { + var weeks []int + for _, course := range courses { + weeks = append(weeks, course.Week) + } + weeks = RemoveIntDuplicates(weeks) + sort.Ints(weeks) + var timeRange []int + for _, t := range courses { + timeRange = append(timeRange, t.ClassTime) + } + timeRange = RemoveIntDuplicates(timeRange) + sort.Ints(timeRange) + resultList = append(resultList, map[string]interface{}{ + "id": uuid.New().String(), + "course": courseName, + "classroom": classroom, + "teacher": courses[0].Teacher, + "weeks": weeks, + "day": day + 1, + "time": timeRange, + }) + } + } + } + // 返回结果 + c.JSON(http.StatusOK, gin.H{ + "code": 200, + "msg": "获取成功!", + "data": resultList, + }) + return + }() + wg.Wait() + }) + // 获取空闲教室 + r.GET("/getEmptyClassroom", func(c *gin.Context) { + // 获取请求参数 + campusID := c.Query("campus_id") + buildID := c.Query("build_id") // 可选参数 + startStamp := c.Query("startStamp") + endStamp := c.Query("endStamp") + // 参数校验 + if campusID == "" || startStamp == "" || endStamp == "" { + c.JSON(http.StatusBadRequest, gin.H{"code": 422, "msg": "参数错误,校区ID和时间戳为必传项!", "data": nil}) + return + } + // 解析时间戳参数 + startTimestamp, err := strconv.ParseInt(startStamp, 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "开始时间戳格式错误!", "data": nil}) + return + } + endTimestamp, err := strconv.ParseInt(endStamp, 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "结束时间戳格式错误!", "data": nil}) + return + } + // 使用 WaitGroup 等待 Goroutine 完成 + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() // 在 Goroutine 完成时调用 Done() + // 查询有课的教室 classroom1 + var classroom1 []string + query := db.Model(&models.Course{}). + Where("campus_id = ? AND startStamp >= ? AND endStamp <= ? AND status = 1", + campusID, startTimestamp, endTimestamp) + if buildID != "" { + query = query.Where("build_id = ?", buildID) + } + err = query.Pluck("classroom_id", &classroom1).Error + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "查询课程数据失败!", "data": nil}) + return + } + // 查询所有教室 classroom2,根据 campus_id 和可选的 build_id 过滤 + var classroom2 []models.Classroom + query = db.Where("campus_id = ?", campusID) + if buildID != "" { + query = query.Where("build_id = ?", buildID) + } + err = query.Find(&classroom2).Error + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "查询教室数据失败!", "data": nil}) + return + } + // 使用一个 map 来判断 classroom1 中的教室是否有课 + classroom1Set := make(map[string]bool) + for _, id := range classroom1 { + classroom1Set[id] = true + } + // 过滤掉有课的教室,得到无课教室列表 data + var data []models.Classroom + for _, classroom := range classroom2 { + if !classroom1Set[classroom.ID] { + data = append(data, classroom) + } + } + c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "查询成功!", "data": data}) + return + }() + wg.Wait() + }) + // 获取教室课表 + r.GET("/getClassroomCourses", func(c *gin.Context) { + var courses []models.Course + classroomID := c.Query("classroom_id") + weekStr := c.Query("week") + + if classroomID == "" || weekStr == "" { + c.JSON(http.StatusBadRequest, gin.H{"code": 422, "msg": "参数错误,教室ID、周数为必传项!", "data": nil}) + return + } + + week, err := strconv.Atoi(weekStr) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"code": 422, "msg": "参数错误,周数格式不正确!", "data": nil}) + return + } + // 使用 WaitGroup 等待 Goroutine 完成 + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() // 在 Goroutine 完成时调用 Done() + // 根据 classroom_id 和时间范围筛选数据 + if result := db.Where("classroom_id = ? AND week = ? AND status = 1", classroomID, week).Find(&courses); result.Error != nil { + c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "课程查询失败!", "data": nil}) + return + } + + // 按照 week, day 和 classTime 聚合 + type AggregatedCourse struct { + Week int `json:"week"` + Day int `json:"day"` + ClassTime int `json:"classTime"` + Classnames []string `json:"classnames"` + Campus string `json:"campus"` + Build string `json:"build"` + StartTime time.Time `json:"startTime"` + EndTime time.Time `json:"endTime"` + StartStamp float64 `json:"startStamp"` + EndStamp float64 `json:"endStamp"` + ClassRoom string `json:"classroom"` + Course string `json:"course"` + Teacher string `json:"teacher"` + ID string `json:"id"` + } + + aggregatedData := make(map[string]AggregatedCourse) + + for _, course := range courses { + key := fmt.Sprintf("%d-%d-%d", course.Week, course.Day, course.ClassTime) + if _, exists := aggregatedData[key]; !exists { + aggregatedData[key] = AggregatedCourse{ + Week: course.Week, + Day: course.Day, + ClassTime: course.ClassTime, + Classnames: []string{}, + Campus: course.Campus, + Build: course.Build, + StartStamp: course.StartStamp, + EndStamp: course.EndStamp, + StartTime: course.StartTime, + EndTime: course.EndTime, + ClassRoom: course.Classroom, + Course: course.Course, + Teacher: course.Teacher, + ID: course.ID, + } + } + + aggCourse := aggregatedData[key] + aggCourse.Classnames = append(aggCourse.Classnames, course.ClassName) + aggregatedData[key] = aggCourse + } + + var resultList []AggregatedCourse + for _, aggCourse := range aggregatedData { + // 去重课程名称 + aggCourse.Classnames = RemoveStringDuplicates(aggCourse.Classnames) + resultList = append(resultList, aggCourse) + } + + // 返回聚合结果 + c.JSON(http.StatusOK, gin.H{ + "code": 200, + "msg": "获取成功!", + "data": resultList, + }) + return + }() + wg.Wait() + }) + // 获取学校配置 + r.GET("/getTermSetting", func(c *gin.Context) { + data := Setting{ + TimeSetting: config.TimeSetting, + StartDate: config.StartDate, + EndDate: config.EndDate, + TotalWeeks: config.TotalWeeks, + } + c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "获取成功", "data": data}) + }) + // 处理请求方法错 + r.NoMethod(NotMethodHandler) + // 处理无效路径 + r.NoRoute(NotFoundHandler) + return r +} diff --git a/智享通.ico b/智享通.ico new file mode 100644 index 0000000000000000000000000000000000000000..5f250c3f0ccb344f1e56bc88bd92b7f1fc2377a0 GIT binary patch literal 458870 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