From cec6c292394437b0f90d06cbc53f84a9961d0d6e Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 20 Aug 2022 20:32:12 +0800 Subject: [PATCH] Some small Change --- .gitignore | 19 +- LICENSE | 201 ++++++++++++++++ Middleware/jwt_middleware.go | 48 ++++ README.md | 117 ++++++++++ admin/admin.go | 142 ------------ admin/pic.go | 58 ----- auth/auth.go | 211 ----------------- auth/login.go | 82 ------- auth/qrcode.go | 155 ------------- client/client.py | 22 ++ client/permission.xlsx | Bin 0 -> 9955 bytes client/user_manager.py | 146 ++++++++++++ common/common.go | 95 -------- config/config.go | 31 --- database/database.go | 112 ++++++++- go.mod | 45 +++- go.sum | 241 ++++++++++++++----- handlers/ddl_class_handlers.go | 168 ++++++++++++++ handlers/ddl_class_id_handlers.go | 191 +++++++++++++++ handlers/ddl_handlers.go | 136 +++++++++++ handlers/jw_handlers.go | 128 +++++++++++ handlers/picture_handlers.go | 69 ++++++ handlers/user_handlers.go | 371 ++++++++++++++++++++++++++++++ main.go | 163 +++++++------ models/ddl_notice.go | 21 ++ models/ddl_type.go | 22 ++ models/ics_infomation.go | 23 ++ models/jwt_claims.go | 11 + models/mysql_model.go | 18 ++ models/user_information.go | 29 +++ models/user_permission.go | 10 + protos/jwgl.pb.go | 369 +++++++++++++++++++++++++++++ protos/jwgl.proto | 36 +++ protos/jwgl_grpc.pb.go | 105 +++++++++ push/push.go | 199 ---------------- query/query.go | 136 ----------- static/login.html | 24 -- tool/config.go | 71 ++++++ tool/jwt.go | 80 +++++++ tool/log.go | 12 + tool/password.go | 18 ++ wecom/wecom.go | 45 ---- 42 files changed, 2841 insertions(+), 1339 deletions(-) create mode 100644 LICENSE create mode 100644 Middleware/jwt_middleware.go create mode 100644 README.md delete mode 100644 admin/admin.go delete mode 100644 admin/pic.go delete mode 100644 auth/auth.go delete mode 100644 auth/login.go delete mode 100644 auth/qrcode.go create mode 100644 client/client.py create mode 100644 client/permission.xlsx create mode 100644 client/user_manager.py delete mode 100644 common/common.go delete mode 100644 config/config.go create mode 100644 handlers/ddl_class_handlers.go create mode 100644 handlers/ddl_class_id_handlers.go create mode 100644 handlers/ddl_handlers.go create mode 100644 handlers/jw_handlers.go create mode 100644 handlers/picture_handlers.go create mode 100644 handlers/user_handlers.go create mode 100644 models/ddl_notice.go create mode 100644 models/ddl_type.go create mode 100644 models/ics_infomation.go create mode 100644 models/jwt_claims.go create mode 100644 models/mysql_model.go create mode 100644 models/user_information.go create mode 100644 models/user_permission.go create mode 100644 protos/jwgl.pb.go create mode 100644 protos/jwgl.proto create mode 100644 protos/jwgl_grpc.pb.go delete mode 100644 push/push.go delete mode 100644 query/query.go delete mode 100644 static/login.html create mode 100644 tool/config.go create mode 100644 tool/jwt.go create mode 100644 tool/log.go create mode 100644 tool/password.go delete mode 100644 wecom/wecom.go diff --git a/.gitignore b/.gitignore index 5e2877a..c833018 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,16 @@ -config/config-t.go -ddl -权限表.xls +# Goland Setting +.idea/ + +# Binary file +go_build_* +ddlBackend +__pycache__/ + +# Database File +*.db + +# Setting File +config.json + +# Pictures Directory +picture/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Middleware/jwt_middleware.go b/Middleware/jwt_middleware.go new file mode 100644 index 0000000..61c34ef --- /dev/null +++ b/Middleware/jwt_middleware.go @@ -0,0 +1,48 @@ +package Middleware + +import ( + "ddlBackend/tool" + "github.com/gin-gonic/gin" + "net/http" + "strings" + "time" +) + +// JWTAuthMiddleware JWT验证中间件 +func JWTAuthMiddleware() gin.HandlerFunc { + return func(context *gin.Context) { + // Authorization 的header由"Bearer " + token组成 + bearerLength := len("Bearer ") + + authHeader := context.GetHeader("Authorization") + + if len(authHeader) < bearerLength { + // 如果令牌的长度不够 + context.JSON(http.StatusUnauthorized, gin.H{ + "error": "The authorization header is incorrect", + }) + context.Abort() + return + } + + token := strings.TrimSpace(authHeader[bearerLength:]) + claims, err := tool.ParseJWTToken(token) + if err != nil { + // 解析令牌中遇到错误 + context.JSON(http.StatusUnauthorized, gin.H{ + "error": err.Error(), + }) + context.Abort() + } else if time.Now().Unix() > claims.ExpiresAt.Unix() { + // 令牌过期 + context.JSON(http.StatusUnauthorized, gin.H{ + "error": "The token has been expired", + }) + context.Abort() + } else { + // 没有问题就把信息记录在context中 + context.Set("Claims", *claims) + } + return + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..b30a933 --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ +# DDLBackend + +## 特点 + +- RESTful风格的DDL管理API + +- JWT令牌身份认证 + +## 安装 + +> 采用go 1.18 构建 + +将仓库克隆到本地 + +```bash +git clone https://github.com/jackfiled/DDLBackend.git +``` + +切换进入软件目录,使用`go build`编译可执行程序,运行程序。 + +```bash +cd DDLBackend +go build +./DDLBackend + +... +[GIN-debug] Listening and serving HTTP on :8080 +``` + +看见最后一行输出服务器在8080端口上监听说明运行成功。 + +### 配置文件 + +程序采用`config.json`文件作为配置文件,该文件的模板如下 + +```json +{ + "app_port": ":4000", + "jwt_secret": "MakeBUPTGreatAgain", + "jwgl_out_time": 0, + "root_config": { + "username": "root", + "password": "123456", + "class_name": "dddd", + "student_id": "0000000000", + "permission": 2 + }, + "use_mysql": false, + "mysql_config": { + "username": "admin", + "password": "123456", + "address": "localhost:3306", + "database_name": "name" + } +} +``` + +配置文件中各个字段的意义如下: + +- `app_port` 服务器运行的端口号 +- `jwt_secret` 签发JWT令牌时采用的密钥 +- `jwgl_out_time` 请求教务系统时,为避免过于频繁的请求而设置的超时时间 +- `root_config` 为方便管理 在程序开始运行时会自动在数据库中创建的管理用户账号 +- `use_mysql` 程序是否使用`mysql`作为数据库 + +`root_config`下述字段的意义如下: + +- `username`: 用户名/姓名 +- `password`: 密码 +- `class_name`: 所属班级 +- `student_id`: 学号 +- `permission`: 权限 + +`mysql_config`下述字段的意义如下: + +- `username`: 数据库用户名 +- `password`: 对应用户的密码 +- `address`: 数据库所在的地址 +- `database_name`: 数据库名称 + +> 在程序中,班级可被设置为"dddd"以表示大班或者"304"~"309"以表示各小班 +> +> 在程序中,人员的权限可被设置为如下三个层级: +> +> - 0 普通用户 只能查看而无法修改任何内容 +> - 1 班级管理员 可以修改本班的内容 +> - 2 根管理员 可以修改所有的内容 +> +> 在`use_mysql`字段被设置为`false`的状态下,`mysql_config`字段可以不填写 + +在根目录下不存在`config.json`配置文件的时候,程序会采用下述默认配置运行 + +```json +{ + "app_port": ":8080", + "jwt_secret": "MakeBUPTGreatAgain", + "jwgl_out_time": 24, + "root_config": { + "username": "root", + "password": "123456", + "classname": "dddd", + "student_id": "0000000000", + "permission": 2 + }, + "use_mysql": false +} +``` + +程序会在当前文件夹中自动创建一个`test.db`的数据库文件,采用`sqlite`作为默认的数据库。 + +## API文档 + +使用[Apifox](https://www.apifox.cn/) +产生的文档[链接](https://www.apifox.cn/apidoc/shared-5d0ad1be-c569-466d-9c59-3e4686b7e482/api-33104131) + + + diff --git a/admin/admin.go b/admin/admin.go deleted file mode 100644 index 70746bd..0000000 --- a/admin/admin.go +++ /dev/null @@ -1,142 +0,0 @@ -package admin - -import ( - "ddl/auth" - "ddl/common" - "ddl/database" - "ddl/push" - "errors" - "fmt" - "net/http" - "strconv" - - "github.com/gin-gonic/gin" - "github.com/jinzhu/gorm" -) - -func SaveHandler(c *gin.Context) { - var err error - - data := common.AdminNotice{} - err = c.BindJSON(&data) - if err != nil { - c.String(http.StatusBadRequest, err.Error()) - return - } - fmt.Println(data) - - userInfo, err := auth.GetCookieUserInfo(c) - if err != nil { - if errors.Is(err, http.ErrNoCookie) { - c.String(http.StatusBadRequest, err.Error()) - } else { - c.String(http.StatusInternalServerError, err.Error()) - } - } - - switch data.Class { - case "": - c.String(http.StatusBadRequest, err.Error()) - return - case "dddd": - if userInfo.Permission&(1<window.location.href='/login.html'") - return - } - - //已登陆 - //更新一下 - UpdateCookieUserInfo(c, userInfo.StudentID) - userInfo, _ = GetCookieUserInfo(c) - c.Header("Content-Type", "text/html") - c.String(200, ` - - - - - - -已登录 %s %d %d %d
- -点击退出登录 - - - - `, userInfo.UserID, userInfo.StudentID, userInfo.Class, userInfo.Permission, "http%3A%2F%2Fsquidward.top%3A8000%2Fapi%2Fauth_demo") - -} - -func CheckAuthHandler(c *gin.Context) { - userInfo, err := GetCookieUserInfo(c) - if err != nil { - if !errors.Is(err, http.ErrNoCookie) { - c.String(http.StatusInternalServerError, err.Error()) - return - } - c.JSON(200, false) - return - } - - //已登陆 - //更新一下 - if userInfo.StudentID == 0 { - userInfo.ExpTime = time.Now().AddDate(0, 0, 14) - data, _ := json.Marshal(userInfo) - fmt.Println(string(data)) - value, _ := Encrypt(string(data), []byte(config.ENCRYPT_KEY)) - // c.SetCookie("UserInfo", value, 14*24*3600, "/", "", false, true) - http.SetCookie(c.Writer, &http.Cookie{ - Name: "UserInfo", - Value: value, - Path: "/", - Domain: "", - MaxAge: 14 * 24 * 3600, - Secure: true, - HttpOnly: false, - SameSite: http.SameSiteNoneMode, - }) - } else { - UpdateCookieUserInfo(c, userInfo.StudentID) - } - - c.JSON(200, userInfo) -} diff --git a/auth/login.go b/auth/login.go deleted file mode 100644 index a03035f..0000000 --- a/auth/login.go +++ /dev/null @@ -1,82 +0,0 @@ -package auth - -import ( - "ddl/common" - "ddl/config" - "ddl/database" - "ddl/wecom" - "net/http" - - "github.com/gin-gonic/gin" -) - -func WechatLoginHandler(c *gin.Context) { - // fmt.Println(values) - code := c.Request.FormValue("code") - ref := c.Request.FormValue("ref") - if ref == "" { - ref = "http://squidward.top/" - } - if code == "" { - c.String(http.StatusBadRequest, "参数错误") - } else { - userID := wecom.GetWecomID(code) - // fmt.Println(userID) - if userID != "" { - userInfo := GetUserInfo(userID) - SetCookieUserInfo(c, userInfo) - - c.Header("Content-Type", "text/html") - c.String(200, "") - - // fmt.Println(userInfo) - // fmt.Fprintf(w, "%s %d %d %d", userInfo.UserID, userInfo.StudentID, userInfo.Class, userInfo.Permission) - } - } -} - -func LoginHandler(c *gin.Context) { - // fmt.Println(values) - key := c.Request.FormValue("key") - pass := LoginPassword(c, key) - if pass { - c.JSON(200, config.AdminKey[key]) - } else { - c.JSON(200, false) - } - -} - -func LogoutHandler(c *gin.Context) { - // fmt.Println(values) - ref := c.Request.FormValue("ref") - if ref == "" { - ref = "http://squidward.top/" - } - - DelCookieUserInfo(c) - - c.Header("Content-Type", "text/html") - c.String(200, "") - - // fmt.Println(userInfo) - // fmt.Fprintf(w, "%s %d %d %d", userInfo.UserID, userInfo.StudentID, userInfo.Class, userInfo.Permission) - -} - -func GetUserInfo(userID string) (userInfo common.UserInfo) { - database.DB.Where("userID=?", userID).First(&userInfo) - // fmt.Println(userInfo) - return -} - -//密码登录部分 - -func LoginPassword(c *gin.Context, key string) bool { - - userInfo, exist := config.AdminKey[key] - if exist { - SetCookieUserInfo(c, userInfo) - } - return exist -} diff --git a/auth/qrcode.go b/auth/qrcode.go deleted file mode 100644 index a7bbde8..0000000 --- a/auth/qrcode.go +++ /dev/null @@ -1,155 +0,0 @@ -package auth - -import ( - "ddl/common" - "ddl/config" - "ddl/wecom" - "encoding/json" - "fmt" - "net/http" - "sync" - "time" - - "github.com/gin-gonic/gin" - "github.com/go-basic/uuid" - "github.com/gorilla/websocket" -) - -type Session struct { - Time time.Time - UserInfo common.UserInfo - Wsconn *websocket.Conn -} - -var lock sync.RWMutex -var sessions = make(map[string]Session) - -var upgrader = websocket.Upgrader{ - // 解决跨域问题 - CheckOrigin: func(r *http.Request) bool { - return true - }, -} // use default options - -func QRCodeWechatLoginHandler(c *gin.Context) { - // fmt.Println(values) - code := c.Request.FormValue("code") - id := c.Request.FormValue("id") - ref := c.Request.FormValue("ref") - if ref == "" { - ref = "http://squidward.top/" - } - if code == "" { - c.String(http.StatusBadRequest, "参数错误") - } else { - userID := wecom.GetWecomID(code) - // fmt.Println(userID) - if userID != "" { - lock.Lock() - session, exist := sessions[id] - if exist { - session.UserInfo = GetUserInfo(userID) - sessions[id] = session - } else { - ref = "http://squidward.top/" - } - - lock.Unlock() - - c.Header("Content-Type", "text/html") - c.String(200, "") - - // fmt.Println(userInfo) - // fmt.Fprintf(w, "%s %d %d %d", userInfo.UserID, userInfo.StudentID, userInfo.Class, userInfo.Permission) - } - } -} - -func QRCodeWSHandler(c *gin.Context) { - ws, err := upgrader.Upgrade(c.Writer, c.Request, nil) - if err != nil { - fmt.Println("upgrade:", err) - return - } - defer ws.Close() - - lock.Lock() - id := uuid.New() - sessions[id] = Session{Time: time.Now(), Wsconn: ws} - lock.Unlock() - - for { - // _, exist := sessions[id] - // if !exist { - // break - // } - mt, message, err := ws.ReadMessage() - if err != nil { - fmt.Println("read:", err) - break - } - // fmt.Printf("recv: %s\n", message) - if string(message) == "?" { - lock.Lock() - session := sessions[id] - msgSend := "wait|" + id + "|" + time.Now().String() - finished := false - if session.UserInfo.StudentID != 0 { - session.UserInfo.ExpTime = time.Now().AddDate(0, 0, 14) - data, _ := json.Marshal(session.UserInfo) - fmt.Println(string(data)) - value, _ := Encrypt(string(data), []byte(config.ENCRYPT_KEY)) - msgSend = "ok|" + value - finished = true - } - sessions[id] = session - - lock.Unlock() - err = ws.WriteMessage(mt, []byte(msgSend)) - if err != nil { - fmt.Println("write:", err) - break - } - - if finished { - break - } - - } - } -} - -func QRCodeWSHeatBeat() { - for { - lock.Lock() - now := time.Now() - for id, s := range sessions { - // fmt.Println(sessions) - if now.After(s.Time.Add(time.Minute)) { - s.Wsconn.Close() - delete(sessions, id) - // fmt.Println("del") - } - } - lock.Unlock() - time.Sleep(time.Second * 30) - // fmt.Println("-") - } - -} - -func QRCodeLoginHandler(c *gin.Context) { - // fmt.Println(values) - cookie := c.Request.FormValue("cookie") - http.SetCookie(c.Writer, &http.Cookie{ - Name: "UserInfo", - Value: cookie, - Path: "/", - Domain: "", - MaxAge: 14 * 24 * 3600, - Secure: true, - HttpOnly: false, - SameSite: http.SameSiteNoneMode, - }) - c.JSON(200, true) -} diff --git a/client/client.py b/client/client.py new file mode 100644 index 0000000..224e379 --- /dev/null +++ b/client/client.py @@ -0,0 +1,22 @@ +import user_manager + +manager = user_manager.Manager() + +manager.header = { + "Authorization": "Bearer " + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdHVkZW50X2lkIjoiMDAwMDAwMDAwMCIsImNsYXNzbmFtZSI6ImRkZGQiLCJwZXJtaXNzaW9uIjoyLCJleHAiOjE2NjA4MTgxMzIsImlzcyI6IlNxdWlkV2FyZCJ9.dQ81IOrD-nmnNNQB6ZsAjLQAaWzou1hYoUjH3oHRbhE" +} + +def upload_excel(): + users = user_manager.parse_excel("permission.xlsx") + + for user in users: + manager.createUser(user) + +def get_users(): + users = manager.readUsers() + + for user in users: + print(user.toString()) + +upload_excel() +get_users() diff --git a/client/permission.xlsx b/client/permission.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2866938e1a01062af6aeb676f4bbe1c1ddb01986 GIT binary patch literal 9955 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 None: + if root == None: + print("Use the default url {}".format(self.rootUrl)) + else: + self.rootUrl = root + + def getRootUserToken(self, student_id: str, password: str) -> bool: + url = self.rootUrl + "/login" + response = requests.post(url, json={ + "student_id": student_id, + "password": password + }) + if response.status_code == 200: + jsonMap = json.loads(response.text) + self.header = { + "Authorization": "Bearer " + jsonMap["token"] + } + print(jsonMap["token"]) + return True + else: + print(response.text) + return False + + def readUsers(self): + url = self.rootUrl + "/users" + + response = requests.get(url, headers=self.header) + + if response.status_code == 200: + lst = json.loads(response.text) + users = [] + for item in lst: + user = UserModel() + user.fromJson(item) + users.append(user) + return users + else: + print(response.text) + return + + def readUser(self, id): + url = self.rootUrl + "/users/" + str(id) + response = requests.get(url, headers=self.header) + + if response.status_code == 200: + jsonMap = json.loads(response.text) + user = UserModel() + user.fromJson(jsonMap) + return user + else: + print(response.text) + + def createUser(self, user): + url = self.rootUrl + "/users" + + response = requests.post(url, json=user.toJson(), headers=self.header) + + if response.status_code == 201: + jsonMap = json.loads(response.text) + user = UserModel() + user.fromJson(jsonMap) + print(user.toString()) + else: + print(response.text) + + def updateUser(self, user): + url = self.rootUrl + "/users/" + str(user.id) + + jsonMap = user.toJson() + jsonMap["id"] = user.id + + response = requests.put(url, json=jsonMap, headers=self.header) + + if response.status_code != 204: + print(response.text) + + def deleteUser(self, id: int): + url = self.rootUrl + "/users/" + str(id) + + response = requests.delete(url, headers=self.header) + + if response.status_code != 204: + print(response.text) + + +class UserModel: + id: int + username: str + password: str + student_id: str + permission: int + class_name: str + + def fromJson(self, json: map): + self.id = json["id"] + self.username = json["username"] + self.password = json["password"] + self.student_id = json["student_id"] + self.permission = json["permission"] + self.class_name = json["classname"] + + def toJson(self): + result = {} + result["username"] = self.username + result["student_id"] = self.student_id + result["permission"] = self.permission + result["classname"] = self.class_name + result["password"] = self.password + return result + + def toString(self): + return """username: {} +student_id: {} +classname: {} +permission: {}""".format(self.username, self.student_id, self.class_name, self.permission) + + def checkSame(self, other: "UserModel"): + return self.username == other.username and self.student_id == other.student_id and self.class_name == other.class_name and self.permission == other.permission + + +def parse_excel(path: str)-> list: + work_book = openpyxl.load_workbook(path) + work_sheet = work_book.active + + users = [] + + for row in work_sheet.values: + user = UserModel() + user.class_name = str(row[0]) + user.student_id = str(row[1]) + user.username = row[2] + user.permission = row[3] + user.password = "123456" + + users.append(user) + + return users \ No newline at end of file diff --git a/common/common.go b/common/common.go deleted file mode 100644 index 7bff787..0000000 --- a/common/common.go +++ /dev/null @@ -1,95 +0,0 @@ -package common - -import ( - "io/ioutil" - "net/http" - "strings" - "time" - - "github.com/gin-gonic/gin" -) - -type DDLNotice struct { - Index int `gorm:"column:index_;primary_key" json:"index_"` - Time time.Time `gorm:"column:time" json:"time"` - DDL time.Time `gorm:"column:ddl" json:"ddl"` - StartTime *time.Time `gorm:"column:startTime" json:"startTime"` - Title string `gorm:"column:title" json:"title"` - Detail string `gorm:"column:detail" json:"detail"` - NoticeType int `gorm:"column:noticeType" json:"noticeType"` - Img string `gorm:"column:img" json:"img"` -} - -//用于接收管理端数据的结构 -type AdminNotice struct { - Class string `gorm:"-" json:"class"` //不写入数据库 - Index int `gorm:"column:index_;primary_key" json:"index_"` - DDL time.Time `gorm:"column:ddl" json:"ddl"` - StartTime *time.Time `gorm:"column:startTime" json:"startTime"` - Title string `gorm:"column:title" json:"title"` - Detail string `gorm:"column:detail" json:"detail"` - NoticeType int `gorm:"column:noticeType" json:"noticeType"` - Img string `gorm:"column:img" json:"img"` -} - -func (an *AdminNotice) TableName() string { - return an.Class -} - -var PartyMap = map[string]string{ - "dddd": "1", - "304": "2", - "305": "7", - "306": "8", - "307": "9", - "308": "10", - "309": "11", - "test": "6", -} - -type UserInfo struct { - UserID string `gorm:"column:userID"` - StudentID int32 `gorm:"column:studentID;primary_key"` - Permission int64 `gorm:"column:permission"` - Class int32 `gorm:"column:class"` - ExpTime time.Time `gorm:"-"` -} - -func (UserInfo) TableName() string { - return "user" -} - -func HttpGet(url string) string { - res, err := http.Get(url) - if err != nil { - return "" - } - robots, err := ioutil.ReadAll(res.Body) - res.Body.Close() - if err != nil { - return "" - } - return string(robots) -} - -func Cors() gin.HandlerFunc { - // const originList = `http://localhost:3000 - // http://127.0.0.1:3000` - return func(c *gin.Context) { - method := c.Request.Method - origin := c.GetHeader("Origin") - if c.GetHeader("Origin") != "" && (strings.Contains(origin, "http://localhost:") || strings.Contains(origin, "http://127.0.0.1:")) { - c.Header("Access-Control-Allow-Origin", c.GetHeader("Origin")) - // c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名 - } - c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") - c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization") - c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type") - c.Header("Access-Control-Allow-Credentials", "true") - - if method == "OPTIONS" { - c.AbortWithStatus(http.StatusNoContent) - } - c.Next() - } -} diff --git a/config/config.go b/config/config.go deleted file mode 100644 index 90fe13d..0000000 --- a/config/config.go +++ /dev/null @@ -1,31 +0,0 @@ -package config - -// //企业微信 -// const CORPID string = "" - -// //企业微信应用 -// const CORPSECRET string = "" - -// //数据库用户名密码 -// const DB_USER_PW string = ":" - -// //数据库主机 -// // const DB_HOST string = "lllccc.top:20570" - -// const DB_HOST string = "localhost" - -// //数据库名 -// const DB string = "" - -// //测试密钥,实际环境请修改 -// const ENCRYPT_KEY string = "" - -// //密码登录 -// var AdminKey = map[string]int64{ -// "admin": {Permission: 267387135, Class: -1, UserID: "超级管理员"}, -// "12345678": {Permission: 0, Class: 0, UserID: "什么权限也没有"},, -// } - -// const WEB_ADDR string = ":" - -// const UPLOAD_PATH string = "" diff --git a/database/database.go b/database/database.go index 56ab570..dcc9ed2 100644 --- a/database/database.go +++ b/database/database.go @@ -1,15 +1,113 @@ package database import ( - "ddl/config" + "ddlBackend/models" + "ddlBackend/tool" + "fmt" - "github.com/jinzhu/gorm" + "gorm.io/driver/mysql" + "gorm.io/driver/sqlite" + "gorm.io/gorm" ) -var DB *gorm.DB +// DDLTables DDL表列表 +var DDLTables = [7]string{"dddd", "304", "305", "306", "307", "308", "309"} -func Open() (err error) { - DB, err = gorm.Open("mysql", config.DB_USER_PW+"@("+config.DB_HOST+")/"+config.DB+"?charset=utf8&loc=Local&parseTime=true") - DB.LogMode(true) //开启sql debug 模式 - return err +// Database 数据库指针 +var Database *gorm.DB + +// OpenDatabase 打开数据库函数 +func OpenDatabase() (err error) { + if tool.Setting.UseMysql { + Database, err = gorm.Open(mysql.Open(tool.Setting.MysqlConfig.GenerateConnectionString()), &gorm.Config{}) + } else { + Database, err = gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) + } + if err != nil { + return err + } + + // 创建ddl相关的表 + for _, value := range DDLTables { + if !Database.Migrator().HasTable(value) { + err = Database.Table(value).AutoMigrate(&models.DDLNotice{}) + if err != nil { + return err + } + } + } + + // 创建用户表 + if !Database.Migrator().HasTable(&models.UserInformation{}) { + err = Database.AutoMigrate(&models.UserInformation{}) + if err != nil { + return err + } + } + + // 创建记录ICS信息表 + if !Database.Migrator().HasTable(&models.ICSInformation{}) { + err = Database.AutoMigrate(&models.ICSInformation{}) + if err != nil { + return err + } + } + + // 将配置文件中设置的根管理员存入数据库 + _, err = AdminLogin(tool.Setting.RootConfig.StudentID, tool.Setting.RootConfig.Password) + if err != nil { + // 遇到没有找到的错误 + // 在数据库中创建该用户 + info := tool.Setting.RootConfig + info.Password = tool.Sha256PasswordWithSalt(info.Password) + Database.Table("user_informations").Create(&info) + } + + return nil +} + +// GetDDLTable 获取指定班级的DDL事件表 +func GetDDLTable(className string) (*gorm.DB, error) { + for _, value := range DDLTables { + if className == value { + return Database.Table(className).Order("ddl_time DESC"), nil + } + } + return nil, fmt.Errorf("the table named %s not exists", className) +} + +// AdminLogin 管理员登录验证函数 +func AdminLogin(studentID string, password string) (*models.UserInformation, error) { + password = tool.Sha256PasswordWithSalt(password) + + var user models.UserInformation + result := Database.Table("user_informations").Where("student_id = ? AND password = ?", studentID, password).First(&user) + if result.Error != nil { + return nil, result.Error + } + + return &user, nil +} + +// UserLogin 用户登录函数 +func UserLogin(username string, studentID string) (*models.UserInformation, error) { + var user models.UserInformation + result := Database.Table("user_informations").Where("username = ? AND student_id = ?", username, studentID).First(&user) + if result.Error != nil { + return nil, result.Error + } + + return &user, nil +} + +// GetICSInformation 获得ICSInformation +func GetICSInformation(studentID string, semester string) (*models.ICSInformation, error) { + var info models.ICSInformation + result := Database.Table("ics_informations").Where("student_id = ? AND semester = ?", studentID, semester).First(&info) + + if result.Error != nil { + return nil, result.Error + } + + return &info, nil } diff --git a/go.mod b/go.mod index 44bb496..03cae07 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,41 @@ -module ddl +module ddlBackend -go 1.17 +go 1.18 require ( - github.com/go-sql-driver/mysql v1.6.0 // indirect - github.com/jinzhu/gorm v1.9.16 - github.com/json-iterator/go v1.1.12 + github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 + github.com/gin-gonic/gin v1.8.1 + github.com/golang-jwt/jwt/v4 v4.4.2 + github.com/satori/go.uuid v1.2.0 + google.golang.org/grpc v1.48.0 + google.golang.org/protobuf v1.28.0 + gorm.io/driver/mysql v1.3.6 + gorm.io/driver/sqlite v1.3.6 + gorm.io/gorm v1.23.8 ) require ( - github.com/gin-gonic/gin v1.7.7 - github.com/go-basic/uuid v1.0.0 - github.com/gorilla/websocket v1.5.0 - github.com/lib/pq v1.2.0 // indirect - github.com/mattn/go-sqlite3 v1.14.6 // indirect - golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.10.0 // indirect + github.com/go-sql-driver/mysql v1.6.0 // indirect + github.com/goccy/go-json v0.9.7 // indirect + github.com/golang/protobuf v1.5.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/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-sqlite3 v1.14.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect + golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 7e9111f..dca64c5 100644 --- a/go.sum +++ b/go.sum @@ -1,90 +1,217 @@ -github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= -github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc= +github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= -github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= -github.com/go-basic/uuid v1.0.0 h1:Faqtetcr8uwOzR2qp8RSpkahQiv4+BnJhrpuXPOo63M= -github.com/go-basic/uuid v1.0.0/go.mod h1:yVtVnsXcmaLc9F4Zw7hTV7R0+vtuQw00mdXi+F6tqco= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= -github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 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.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= -github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +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/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0= +github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.3.6 h1:BhX1Y/RyALb+T9bZ3t07wLnPZBukt+IRkMn8UZSNbGM= +gorm.io/driver/mysql v1.3.6/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= +gorm.io/driver/sqlite v1.3.6 h1:Fi8xNYCUplOqWiPa3/GuCeowRNBRGTf62DEmhMDHeQQ= +gorm.io/driver/sqlite v1.3.6/go.mod h1:Sg1/pvnKtbQ7jLXxfZa+jSHvoX8hoZA8cn4xllOMTgE= +gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE= +gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/handlers/ddl_class_handlers.go b/handlers/ddl_class_handlers.go new file mode 100644 index 0000000..27e05e0 --- /dev/null +++ b/handlers/ddl_class_handlers.go @@ -0,0 +1,168 @@ +package handlers + +import ( + "ddlBackend/database" + "ddlBackend/models" + "ddlBackend/tool" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +// CreateClassDDLHandler 班级url下创建DDL事件处理函数 +func CreateClassDDLHandler(context *gin.Context) { + className := context.Param("class") + + ok, err := checkClassAdminPermission(context, className) + if err != nil { + // 解析令牌和验证权限中遇到问题 + // 返回 500 服务器错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + var ddlNotice models.DDLNotice + err = context.ShouldBindJSON(&ddlNotice) + if err != nil { + // 请求体绑定失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + if ddlNotice.NoticeType == models.ALL { + // 类别设置为 1 全部 + // 不允许创建 + context.JSON(http.StatusBadRequest, gin.H{ + "error": "all notice type of ddl is prohibited", + }) + return + } + + if className != ddlNotice.ClassName { + // 请求url和请求体中班级不符 + // 返回 400 错误请求 + context.JSON(http.StatusBadRequest, gin.H{ + "error": "the url and the body are not the same", + }) + return + } + + var db *gorm.DB + db, err = database.GetDDLTable(className) + if err != nil { + // 无法获取对应班级的数据库 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + result := db.Create(&ddlNotice) + if result.Error != nil { + // 在数据库中创建失败 + // 返回 500 服务器错误 + tool.DDLLogError(result.Error.Error()) + context.JSON(http.StatusInternalServerError, gin.H{ + "error": result.Error.Error(), + }) + return + } + + context.JSON(http.StatusCreated, ddlNotice) +} + +// ReadClassDDLHandler 班级url下读取DDL事件处理函数 +func ReadClassDDLHandler(context *gin.Context) { + className := context.Param("class") + + start := context.DefaultQuery("start", "0") + step := context.DefaultQuery("step", "20") + noticeType := context.DefaultQuery("noticeType", "0") + + // 将过滤器参数从字符串转换为数字 + var err error + var startNum, stepNum, noticeTypeNum int + startNum, err = strconv.Atoi(start) + if err != nil { + // 请求参数转换失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + stepNum, err = strconv.Atoi(step) + if err != nil { + // 请求参数转换失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + noticeTypeNum, err = strconv.Atoi(noticeType) + if err != nil { + // 请求参数转换失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var ddlNotices []models.DDLNotice + var db *gorm.DB + db, err = database.GetDDLTable(className) + if err != nil { + // 无法获取对应班级的数据库 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + if noticeTypeNum == models.ALL { + db.Where("notice_type != ?", models.ALL).Offset(startNum).Limit(stepNum).Find(&ddlNotices) + } else { + db.Where("notice_type = ?", noticeTypeNum).Offset(startNum).Limit(stepNum).Find(&ddlNotices) + } + context.JSON(http.StatusOK, ddlNotices) +} + +// checkClassAdminPermission 检查当前请求令牌的持有者是否有权限修改当前班级的内容 +func checkClassAdminPermission(context *gin.Context, classname string) (bool, error) { + claims, err := tool.GetClaimsInContext(context) + + if err != nil { + return false, err + } + + // 权限为根管理员 + // 或者为本班的管理员 + // 即可修改该班的内容 + if claims.Permission > models.Administrator && (claims.ClassName == classname || claims.Permission > models.User) { + return true, nil + } else { + return false, nil + } +} diff --git a/handlers/ddl_class_id_handlers.go b/handlers/ddl_class_id_handlers.go new file mode 100644 index 0000000..fd6d1ad --- /dev/null +++ b/handlers/ddl_class_id_handlers.go @@ -0,0 +1,191 @@ +package handlers + +import ( + "ddlBackend/database" + "ddlBackend/models" + "ddlBackend/tool" + "fmt" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +func ReadClassIDDDLHandler(context *gin.Context) { + className := context.Param("class") + id := context.Param("id") + + var db *gorm.DB + var err error + db, err = database.GetDDLTable(className) + if err != nil { + // 获取指定班级的数据库失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var idNum int + idNum, err = strconv.Atoi(id) + if err != nil { + // 转换id字符串失败 + // 返回 400 请求错误 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var ddlNotice models.DDLNotice + result := db.First(&ddlNotice, idNum) + if result.Error != nil { + context.JSON(http.StatusNotFound, gin.H{ + "error": result.Error.Error(), + }) + } else { + context.JSON(http.StatusOK, ddlNotice) + } +} + +func UpdateClassIDDDLHandler(context *gin.Context) { + className := context.Param("class") + id := context.Param("id") + + ok, err := checkClassAdminPermission(context, className) + if err != nil { + // 解析令牌和验证权限中遇到问题 + // 返回 500 服务器错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + var db *gorm.DB + db, err = database.GetDDLTable(className) + if err != nil { + // 获取指定班级的数据库失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var idNum int + idNum, err = strconv.Atoi(id) + if err != nil { + // 转换id字符串失败 + // 返回 400 请求错误 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var ddlNotice models.DDLNotice + result := db.First(&ddlNotice, idNum) + + if result.Error != nil { + // 读取指定的事件失败 + // 返回 404 未找到错误 + context.JSON(http.StatusNotFound, gin.H{ + "err": result.Error.Error(), + }) + return + } + + err = context.ShouldBindJSON(&ddlNotice) + if err != nil { + // 绑定请求体失败 + // 返回 400 请求错误 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + if ddlNotice.ID != uint(idNum) { + // 请求体中的ID和url中的ID不符 + // 返回 400 请求错误 + context.JSON(http.StatusBadRequest, gin.H{ + "error": fmt.Sprintf("the id %d in the url and %d in the body are not the same", ddlNotice.ID, idNum), + }) + return + } + + db.Save(&ddlNotice) + context.JSON(http.StatusNoContent, gin.H{}) +} + +func DeleteClassIDDDLHandler(context *gin.Context) { + className := context.Param("class") + id := context.Param("id") + + ok, err := checkClassAdminPermission(context, className) + if err != nil { + // 解析令牌和验证权限中遇到问题 + // 返回 500 服务器错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + var db *gorm.DB + db, err = database.GetDDLTable(className) + if err != nil { + // 获取指定班级的数据库失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var idNum int + idNum, err = strconv.Atoi(id) + if err != nil { + // 转换id字符串失败 + // 返回 400 请求错误 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var ddlNotice models.DDLNotice + result := db.First(&ddlNotice, idNum) + + if result.Error != nil { + // 读取指定的事件失败 + // 返回 404 未找到错误 + context.JSON(http.StatusNotFound, gin.H{ + "err": result.Error.Error(), + }) + return + } + + db.Delete(&ddlNotice) + context.JSON(http.StatusNoContent, gin.H{}) +} diff --git a/handlers/ddl_handlers.go b/handlers/ddl_handlers.go new file mode 100644 index 0000000..58fc94a --- /dev/null +++ b/handlers/ddl_handlers.go @@ -0,0 +1,136 @@ +package handlers + +import ( + "ddlBackend/database" + "ddlBackend/models" + "ddlBackend/tool" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +// CreateDDLHandler 创建DDL事件处理函数 +func CreateDDLHandler(context *gin.Context) { + ok, err := tool.CheckPermission(context, models.Root) + if err != nil { + // 验证身份中出错 + // 返回 500 服务器错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + // 验证未通过 + // 返回 401 未授权错误 + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + var ddlNotice models.DDLNotice + + err = context.ShouldBindJSON(&ddlNotice) + if err != nil { + // 绑定json数据失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + if ddlNotice.NoticeType == models.ALL { + // 类别设置为 1 全部 + // 不允许创建 + context.JSON(http.StatusBadRequest, gin.H{ + "error": "all notice type of ddl is prohibited", + }) + return + } + + var db *gorm.DB + db, err = database.GetDDLTable(ddlNotice.ClassName) + if err != nil { + // 获取对应班级数据库失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + result := db.Create(&ddlNotice) + if result.Error != nil { + // 在数据库中创建失败 + // 返回 500 服务器错误 + tool.DDLLogError(result.Error.Error()) + context.JSONP(http.StatusInternalServerError, gin.H{ + "error": result.Error.Error(), + }) + return + } + + context.JSON(http.StatusCreated, ddlNotice) +} + +// ReadDDLHandler 读取DDL事件处理函数 +func ReadDDLHandler(context *gin.Context) { + start := context.DefaultQuery("start", "0") + step := context.DefaultQuery("step", "20") + noticeType := context.DefaultQuery("noticeType", "0") + + // 将过滤器参数从字符串转换为数字 + var err error + var startNum, stepNum, noticeTypeNum int + startNum, err = strconv.Atoi(start) + if err != nil { + // 请求参数转换失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + stepNum, err = strconv.Atoi(step) + if err != nil { + // 请求参数转换失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + noticeTypeNum, err = strconv.Atoi(noticeType) + if err != nil { + // 请求参数转换失败 + // 返回 400 错误请求 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var ddlNotices []models.DDLNotice + for _, value := range database.DDLTables { + db, _ := database.GetDDLTable(value) + var list []models.DDLNotice + if noticeTypeNum == models.ALL { + // 筛选所有的活动事件 + db.Where("notice_type != ?", models.DDL).Offset(startNum).Limit(stepNum).Find(&list) + } else { + // 筛选指定类型的事件 + db.Where("notice_type = ?", noticeTypeNum).Offset(startNum).Limit(stepNum).Find(&list) + } + ddlNotices = append(ddlNotices, list...) + } + + context.JSON(http.StatusOK, ddlNotices) +} diff --git a/handlers/jw_handlers.go b/handlers/jw_handlers.go new file mode 100644 index 0000000..9d39ef5 --- /dev/null +++ b/handlers/jw_handlers.go @@ -0,0 +1,128 @@ +package handlers + +import ( + "context" + "ddlBackend/database" + "ddlBackend/models" + "ddlBackend/protos" + "ddlBackend/tool" + "fmt" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func GetSemesterCalendarHandler(context *gin.Context) { + var model models.GetSemesterCalendarModel + err := context.ShouldBindJSON(&model) + if err != nil { + // 绑定请求结构体出错 + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + info, err := database.GetICSInformation(model.StudentID, model.Semester) + if err != nil { + // 说明没有请求过 + courses, icsStream, err := grpcGetSemester(model) + if err != nil { + tool.DDLLogError(err.Error()) + // RPC中出错 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + // 在数据库创建这个记录 + newInformation := models.ICSInformation{ + StudentID: model.StudentID, + Semester: model.Semester, + ICSStream: icsStream, + } + database.Database.Table("ics_informations").Create(&newInformation) + + context.JSON(http.StatusOK, courses) + return + } else { + duration := time.Since(info.UpdatedAt) + // 如果上次请求到现在的时间小于超时时间 + if int64(duration) <= tool.Setting.JWGLOutTime*int64(time.Hour) { + targetTime := info.UpdatedAt.Add(time.Duration(tool.Setting.JWGLOutTime * int64(time.Hour))) + context.JSON(http.StatusBadRequest, gin.H{ + "error": fmt.Sprintf("Too frequent requests, please try after %s", targetTime.Format("06-01-02 15:04")), + }) + } else { + // 已经超过超时时间 + courses, icsStream, err := grpcGetSemester(model) + if err != nil { + tool.DDLLogError(err.Error()) + // RPC中出错 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + info.ICSStream = icsStream + database.Database.Table("ics_informations").Save(info) + + context.JSON(http.StatusOK, courses) + return + } + } +} + +// GetICSFileHandler 返回ICS文件处理函数 +func GetICSFileHandler(context *gin.Context) { + studentID := context.Param("id") + semester := context.Param("semester") + // 去掉最后的.ics文件后缀 + semester = semester[:len(semester)-4] + + info, err := database.GetICSInformation(studentID, semester) + if err != nil { + context.JSON(http.StatusNotFound, gin.H{ + "error": "请先获得课表再尝试下载ICS日历文件", + }) + return + } + + context.Data(http.StatusOK, "text/calendar", info.ICSStream) +} + +// grpcGetSemester 远程过程调用获得课表的函数 +func grpcGetSemester(model models.GetSemesterCalendarModel) ([]*protos.Course, []byte, error) { + connection, err := grpc.Dial(tool.Setting.JWGrpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, nil, err + } + + defer func(connection *grpc.ClientConn) { + err := connection.Close() + if err != nil { + // 如果关闭连接失败 + tool.DDLLogError(err.Error()) + } + }(connection) + + client := protos.NewJwglerClient(connection) + + response, err := client.GetSemester(context.Background(), &protos.GetSemesterRequest{ + StudentID: model.StudentID, + Password: model.Password, + Semester: model.Semester, + }) + + if err != nil { + // 调用远程函数出错 + return nil, nil, err + } + + return response.Courses, response.IcsStream, nil +} diff --git a/handlers/picture_handlers.go b/handlers/picture_handlers.go new file mode 100644 index 0000000..f01ae80 --- /dev/null +++ b/handlers/picture_handlers.go @@ -0,0 +1,69 @@ +package handlers + +import ( + "ddlBackend/models" + "ddlBackend/tool" + "net/http" + "regexp" + "strings" + + "github.com/gin-gonic/gin" + uuid "github.com/satori/go.uuid" +) + +// UploadPictureHandler 上传文件处理函数 +func UploadPictureHandler(context *gin.Context) { + ok, err := tool.CheckPermission(context, models.Administrator) + + if err != nil { + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + file, err := context.FormFile("picture") + + if err != nil { + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + // 利用正则表达式判断文件名是否符合规则 + pattern, _ := regexp.Compile(`^.*\\.((png)|(jpg))$`) + if !pattern.MatchString(file.Filename) { + context.JSON(http.StatusBadRequest, gin.H{ + "error": "Only png and jpg picture allowed", + }) + return + } + + // 获得图片的后缀名 + fileNames := strings.Split(file.Filename, ".") + fileEndName := fileNames[len(fileNames)-1] + + // 利用uuid生成图片文件的新名称 + fileName := "./picture/" + uuid.NewV4().String() + "." + fileEndName + err = context.SaveUploadedFile(file, fileName) + if err != nil { + tool.DDLLogError(err.Error()) + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + context.JSON(http.StatusCreated, gin.H{ + // 这里只取文件名称第一位之后的值 + // 第一位是. 是没有必要的 + "address": fileName[1:], + }) +} diff --git a/handlers/user_handlers.go b/handlers/user_handlers.go new file mode 100644 index 0000000..5c2eb84 --- /dev/null +++ b/handlers/user_handlers.go @@ -0,0 +1,371 @@ +package handlers + +import ( + "ddlBackend/database" + "ddlBackend/models" + "ddlBackend/tool" + "fmt" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" +) + +// ReadUsersHandler 读取所有用户信息的处理函数 +func ReadUsersHandler(context *gin.Context) { + ok, err := tool.CheckPermission(context, models.Root) + if err != nil { + // 验证权限过程中出错 + // 返回 500 服务器错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + // 权限不够 + // 返回 401 未授权 + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + var users []models.UserInformation + + result := database.Database.Table("user_informations").Find(&users) + if result.Error != nil { + // 如果读取中出错 + tool.DDLLogError(result.Error.Error()) + context.JSON(http.StatusInternalServerError, gin.H{ + "error": result.Error.Error(), + }) + return + } + + context.JSON(http.StatusOK, users) +} + +// ReadSingleUserHandler 读取单个用户信息处理函数 +func ReadSingleUserHandler(context *gin.Context) { + ok, err := tool.CheckPermission(context, models.Root) + if err != nil { + // 验证权限过程中出错 + // 返回 500 服务器错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + // 权限不够 + // 返回 401 未授权 + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + var user models.UserInformation + id := context.Param("id") + idNum, err := strconv.Atoi(id) + if err != nil { + // url中参数读取出错 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + } + + result := database.Database.Table("user_informations").First(&user, idNum) + if result.Error != nil { + // 没找到 + tool.DDLLogError(result.Error.Error()) + context.JSON(http.StatusNotFound, gin.H{ + "error": result.Error.Error(), + }) + return + } + + context.JSON(http.StatusOK, user) +} + +// CreateUserHandler 创建用户处理函数 +func CreateUserHandler(context *gin.Context) { + ok, err := tool.CheckPermission(context, models.Root) + if err != nil { + // 验证权限过程中出错 + // 返回 500 服务器错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + // 权限不够 + // 返回 401 未授权 + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + var user models.UserInformation + err = context.ShouldBindJSON(&user) + if err != nil { + // 绑定json对象出错 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + // 将密码加盐哈希之后储存在数据库中 + user.Password = tool.Sha256PasswordWithSalt(user.Password) + + result := database.Database.Table("user_informations").Create(&user) + if result.Error != nil { + tool.DDLLogError(result.Error.Error()) + context.JSON(http.StatusInternalServerError, gin.H{ + "error": result.Error.Error(), + }) + return + } + + context.JSON(http.StatusCreated, user) +} + +// UpdateUserHandler 更新用户信息 +func UpdateUserHandler(context *gin.Context) { + ok, err := tool.CheckPermission(context, models.Root) + if err != nil { + // 验证权限过程中出错 + // 返回 500 服务器错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + // 权限不够 + // 返回 401 未授权 + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + id := context.Param("id") + idNum, err := strconv.Atoi(id) + if err != nil { + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var user models.UserInformation + err = context.ShouldBindJSON(&user) + if err != nil { + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + if user.ID != uint(idNum) { + // 请求体和url参数不匹配 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": fmt.Sprintf("the id %d in the url and %d in the body are not the same", idNum, user.ID), + }) + return + } + + // 密码加盐哈希之后再存入数据库 + user.Password = tool.Sha256PasswordWithSalt(user.Password) + + database.Database.Table("user_informations").Save(&user) + context.JSON(http.StatusNoContent, gin.H{}) +} + +// DeleteUserHandler 删除用户信息处理函数 +func DeleteUserHandler(context *gin.Context) { + ok, err := tool.CheckPermission(context, models.Root) + if err != nil { + // 验证权限过程中出错 + // 返回 500 服务器错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + if !ok { + // 权限不够 + // 返回 401 未授权 + context.JSON(http.StatusUnauthorized, gin.H{}) + return + } + + id := context.Param("id") + idNum, err := strconv.Atoi(id) + if err != nil { + tool.DDLLogError(err.Error()) + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + var user models.UserInformation + result := database.Database.Table("user_informations").First(&user, idNum) + if result.Error != nil { + tool.DDLLogError(result.Error.Error()) + context.JSON(http.StatusNotFound, gin.H{ + "error": result.Error.Error(), + }) + return + } + + database.Database.Table("user_informations").Delete(&user) + context.JSON(http.StatusNoContent, gin.H{}) +} + +// AdminLoginHandler 管理员登录处理函数 +func AdminLoginHandler(context *gin.Context) { + var loginModel models.AdminLoginModel + + err := context.ShouldBindJSON(&loginModel) + if err != nil { + // 绑定JSON出错 + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + } + + user, err := database.AdminLogin(loginModel.StudentID, loginModel.Password) + if err != nil { + // 数据库中查无此用户 + context.JSON(http.StatusNotFound, gin.H{ + "error": err.Error(), + }) + return + } + + token, err := tool.GenerateJWTToken(*user) + if err != nil { + // 产生JWT令牌中错误 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + context.JSON(http.StatusOK, gin.H{ + "token": token, + "username": user.Username, + "class_name": user.ClassName, + "student_id": user.StudentID, + "permission": user.Permission, + }) +} + +// UserLoginHandler 用户登录处理函数 +func UserLoginHandler(context *gin.Context) { + var loginModel models.UserLoginModel + + err := context.ShouldBindJSON(&loginModel) + if err != nil { + // 绑定JSON出错 + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + return + } + + user, err := database.UserLogin(loginModel.Username, loginModel.StudentID) + if err != nil { + // 数据库中查无此人 + context.JSON(http.StatusNotFound, gin.H{ + "error": err.Error(), + }) + return + } + + if user.Permission > models.User { + // 如果该用户的权限大于普通用户 + // 则不能通过学号姓名验证的方式获得令牌 + // 返回 403 Forbidden + context.JSON(http.StatusForbidden, gin.H{ + "error": "该用户身份为管理员,请通过学号密码登录", + }) + return + } + + token, err := tool.GenerateJWTToken(*user) + if err != nil { + // 产生令牌出错 + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + context.JSON(http.StatusOK, gin.H{ + "token": token, + "username": user.Username, + "class_name": user.ClassName, + "student_id": user.StudentID, + "permission": user.Permission, + }) +} + +// AdminUpdatePasswordHandler 管理员修改密码处理函数 +func AdminUpdatePasswordHandler(context *gin.Context) { + claims, err := tool.GetClaimsInContext(context) + if err != nil { + // 解析JWT令牌信息错误 + // 返回 500 服务器错误 + tool.DDLLogError(err.Error()) + context.JSON(http.StatusInternalServerError, gin.H{ + "error": err.Error(), + }) + return + } + + // 修改管理员密码的请求体保持和登录一致 + // 需要学号和新的密码 + var loginModel models.AdminLoginModel + err = context.ShouldBindJSON(&loginModel) + if err != nil { + // 绑定JSON出错 + context.JSON(http.StatusBadRequest, gin.H{ + "error": err.Error(), + }) + } + + var user models.UserInformation + result := database.Database.Table("user_informations").Where("student_id =?", loginModel.StudentID).Find(&user) + if result.Error != nil { + // 数据库中未发现该用户 + context.JSON(http.StatusNotFound, gin.H{ + "error": result.Error.Error(), + }) + return + } + + // 修改密码的权限要求: + // 令牌持有账号和欲修改密码的账号是同一个账号 + // 令牌的权限在管理员及以上 + if claims.StudentID == loginModel.StudentID && claims.Permission >= models.Administrator { + user.Password = loginModel.Password + database.Database.Table("user_informations").Save(&user) + + context.JSON(http.StatusOK, gin.H{}) + return + } else { + context.JSON(http.StatusBadRequest, gin.H{ + "error": "permission denied", + }) + return + } +} diff --git a/main.go b/main.go index 7e603f9..34ccc76 100644 --- a/main.go +++ b/main.go @@ -1,83 +1,80 @@ -package main - -import ( - "fmt" - "net/http" - "os" - "strconv" - - "ddl/admin" - "ddl/auth" - "ddl/common" - "ddl/config" - "ddl/database" - "ddl/query" - - "github.com/gin-gonic/gin" -) - -func main() { - err := database.Open() - if err != nil { - fmt.Println(err.Error()) - os.Exit(1) - } - - router := gin.Default() - - router.Use(common.Cors()) - - router.GET("/", indexHandler) - - //测试用 - router.StaticFile("/login.html", "./static/login.html") - - router.GET("/api/wechatlogin", auth.WechatLoginHandler) - router.POST("/api/login", auth.LoginHandler) - router.GET("/api/logout", auth.LogoutHandler) - - router.GET("/api/qrcode_login", auth.QRCodeLoginHandler) - router.GET("/api/qrcode_wechat_login", auth.QRCodeWechatLoginHandler) - router.Any("/api/qrcodews", auth.QRCodeWSHandler) - - router.GET("/WW_verify_udfdZsIBL9yNi4SN.txt", WWVerify) - - router.GET("/api/check_auth", auth.CheckAuthHandler) - router.GET("/api/auth_demo", auth.AuthDemoHandler) - router.GET("/api/set_permission_demo", setPermission) - - router.GET("/api/get_list", query.GetListHandler) - router.GET("/api/query_single", query.QuerySingleHandler) - - router.POST("/api/save", admin.SaveHandler) - router.DELETE("/api/delete", admin.DeleteHandler) - router.POST("/api/upload_img", admin.UploadFileHandler) - - go auth.QRCodeWSHeatBeat() - - if os.Getenv("DEBUG") != "" { - router.Run(":3004") - } else { - router.Run(config.WEB_ADDR) - } -} - -func indexHandler(c *gin.Context) { - c.String(200, "DDL系统API") -} - -//微信扫码授权验证 -func WWVerify(c *gin.Context) { - c.String(200, "udfdZsIBL9yNi4SN") -} - -func setPermission(c *gin.Context) { - if c.Request.FormValue("studentID") == "" { - c.String(http.StatusBadRequest, "参数错误") - return - } - studentID, _ := strconv.ParseInt(c.Request.FormValue("studentID"), 10, 32) - permission, _ := strconv.ParseInt(c.Request.FormValue("permission"), 10, 64) - userInfo := auth.SetUserPermission(int32(studentID), permission) - c.String(200, strconv.Itoa(int(userInfo.StudentID))+"权限修改为"+strconv.Itoa(int(userInfo.Permission))) -} +package main + +import ( + "ddlBackend/Middleware" + "ddlBackend/database" + "ddlBackend/handlers" + "ddlBackend/tool" + + "github.com/fvbock/endless" + "github.com/gin-gonic/gin" +) + +func main() { + // 读取配置文件 + err := tool.ReadConfig() + if err != nil { + tool.DDLLogError(err.Error()) + tool.DDLLogError("Read config file failed, using default setting") + } + + // 打开数据库 + err = database.OpenDatabase() + if err != nil { + tool.DDLLogError(err.Error()) + return + } + + route := gin.Default() + + // 登录 + route.POST("/login", handlers.AdminLoginHandler) + route.POST("/auth", handlers.UserLoginHandler) + + // 获取DDL事件列表 + route.GET("/ddlNotices", handlers.ReadDDLHandler) + route.GET("/ddlNotices/:class", handlers.ReadClassDDLHandler) + route.GET("/ddlNotices/:class/:id", handlers.ReadClassIDDDLHandler) + + // 获得教务课表的相关API + route.POST("/GetSemester", handlers.GetSemesterCalendarHandler) + route.GET("/Calendar/:id/:semester", handlers.GetICSFileHandler) + + // 图片文件路径 + route.Static("/picture", "./picture") + + // 修改DDL事件列表需要身份验证 + ddlNoticesRoute := route.Group("") + ddlNoticesRoute.Use(Middleware.JWTAuthMiddleware()) + { + ddlNoticesRoute.POST("/ddlNotices", handlers.CreateDDLHandler) + ddlNoticesRoute.POST("/ddlNotices/:class", handlers.CreateClassDDLHandler) + ddlNoticesRoute.PUT("/ddlNotices/:class/:id", handlers.UpdateClassIDDDLHandler) + ddlNoticesRoute.DELETE("/ddlNotices/:class/:id", handlers.DeleteClassIDDDLHandler) + } + + // 其他需要身份验证的API + adminRoute := route.Group("") + adminRoute.Use(Middleware.JWTAuthMiddleware()) + { + adminRoute.POST("/upload", handlers.UploadPictureHandler) + } + + // 用户管理相关API需要验证 + userRoute := route.Group("/users") + userRoute.Use(Middleware.JWTAuthMiddleware()) + { + userRoute.GET("/", handlers.ReadUsersHandler) + userRoute.POST("/", handlers.CreateUserHandler) + userRoute.POST("/password", handlers.AdminUpdatePasswordHandler) + userRoute.GET("/:id", handlers.ReadSingleUserHandler) + userRoute.PUT("/:id", handlers.UpdateUserHandler) + userRoute.DELETE("/:id", handlers.DeleteUserHandler) + } + + err = endless.ListenAndServe(tool.Setting.AppPort, route) + if err != nil { + tool.DDLLogError(err.Error()) + return + } +} diff --git a/models/ddl_notice.go b/models/ddl_notice.go new file mode 100644 index 0000000..f827d0f --- /dev/null +++ b/models/ddl_notice.go @@ -0,0 +1,21 @@ +package models + +import ( + "time" +) + +// DDLNotice DDL事件模型 +type DDLNotice struct { + // 数据库事件编号 + ID uint `gorm:"primaryKey" json:"id"` + // Title DDL事件的标题 + Title string `json:"title"` + // Detail DDL事件的详情 + Detail string `json:"detail"` + // DDLTime DDL时间 + DDLTime time.Time `json:"ddl_time"` + // NoticeType DDL的分类 + NoticeType int `json:"notice_type"` + // ClassName 班级名称 + ClassName string `json:"class_name"` +} diff --git a/models/ddl_type.go b/models/ddl_type.go new file mode 100644 index 0000000..ca48f6e --- /dev/null +++ b/models/ddl_type.go @@ -0,0 +1,22 @@ +package models + +const ( + // DDL DDL事件 + DDL = iota + // ALL 全部活动类型 + ALL + // OTHER 其他类型活动 + OTHER + // POLITICS 思想政治类活动 + POLITICS + // LITERAL 文体类活动 + LITERAL + // VOLUNTEER 志愿类活动 + VOLUNTEER + // LECTURE 讲座类活动 + LECTURE + // CONTEST 竞赛类活动 + CONTEST + // SELECTION 评优类活动 + SELECTION +) diff --git a/models/ics_infomation.go b/models/ics_infomation.go new file mode 100644 index 0000000..3b1a0ff --- /dev/null +++ b/models/ics_infomation.go @@ -0,0 +1,23 @@ +package models + +import "time" + +// ICSInformation 存储获得ICS文件以及相关的信息 +type ICSInformation struct { + ID uint `gorm:"primaryKey"` + // StudentID 学号 + StudentID string + // Semester 该课表的所在学期 + Semester string + // ICSStream ICS文件字节流 + ICSStream []byte + // UpdatedAt 更新的时间 + UpdatedAt time.Time +} + +// GetSemesterCalendarModel 获得课表的请求体 +type GetSemesterCalendarModel struct { + StudentID string `json:"student_id"` + Password string `json:"password"` + Semester string `json:"semester"` +} diff --git a/models/jwt_claims.go b/models/jwt_claims.go new file mode 100644 index 0000000..fae9f91 --- /dev/null +++ b/models/jwt_claims.go @@ -0,0 +1,11 @@ +package models + +import "github.com/golang-jwt/jwt/v4" + +// JWTClaims JWT实体信息 +type JWTClaims struct { + StudentID string `json:"student_id"` + ClassName string `json:"class_name"` + Permission uint `json:"permission"` + jwt.RegisteredClaims +} diff --git a/models/mysql_model.go b/models/mysql_model.go new file mode 100644 index 0000000..442f522 --- /dev/null +++ b/models/mysql_model.go @@ -0,0 +1,18 @@ +package models + +import "fmt" + +// MysqlModel Mysql数据库连接信息类 +type MysqlModel struct { + Username string `json:"username"` + Password string `json:"password"` + Address string `json:"address"` + DatabaseName string `json:"database_name"` +} + +func (model *MysqlModel) GenerateConnectionString() string { + result := fmt.Sprintf("%s:%s@tcp(%s)/%s", model.Username, model.Password, model.Address, model.DatabaseName) + // 配置字符串 + result = result + "?charset=utf8mb4&parseTime=True&loc=Local" + return result +} diff --git a/models/user_information.go b/models/user_information.go new file mode 100644 index 0000000..512e5a9 --- /dev/null +++ b/models/user_information.go @@ -0,0 +1,29 @@ +package models + +// UserInformation 用户信息模型 +type UserInformation struct { + // ID 数据库用户编号 + ID uint `gorm:"primaryKey" json:"id"` + // Username 用户名 + Username string `json:"username"` + // Password 用户密码 + Password string `json:"password"` + // ClassName 所在班级 + ClassName string `json:"class_name"` + // StudentID 学号 + StudentID string `json:"student_id"` + // Permission 权限 + Permission uint `json:"permission"` +} + +// AdminLoginModel 管理员登录JSON模型 +type AdminLoginModel struct { + StudentID string `json:"student_id"` + Password string `json:"password"` +} + +// UserLoginModel 用户登录JSON模型 +type UserLoginModel struct { + Username string `json:"username"` + StudentID string `json:"student_id"` +} diff --git a/models/user_permission.go b/models/user_permission.go new file mode 100644 index 0000000..052c58c --- /dev/null +++ b/models/user_permission.go @@ -0,0 +1,10 @@ +package models + +const ( + // User 用户 只可读取信息 + User = iota + // Administrator 管理员 可控制小班的内容 + Administrator + // Root 根管理员 可控制所有的内容 + Root +) diff --git a/protos/jwgl.pb.go b/protos/jwgl.pb.go new file mode 100644 index 0000000..217bce9 --- /dev/null +++ b/protos/jwgl.pb.go @@ -0,0 +1,369 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.6.1 +// source: protos/jwgl.proto + +package protos + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// 请求学期课表的信息 +type GetSemesterRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StudentID string `protobuf:"bytes,1,opt,name=studentID,proto3" json:"studentID,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + Semester string `protobuf:"bytes,3,opt,name=semester,proto3" json:"semester,omitempty"` +} + +func (x *GetSemesterRequest) Reset() { + *x = GetSemesterRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_jwgl_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSemesterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSemesterRequest) ProtoMessage() {} + +func (x *GetSemesterRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_jwgl_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSemesterRequest.ProtoReflect.Descriptor instead. +func (*GetSemesterRequest) Descriptor() ([]byte, []int) { + return file_protos_jwgl_proto_rawDescGZIP(), []int{0} +} + +func (x *GetSemesterRequest) GetStudentID() string { + if x != nil { + return x.StudentID + } + return "" +} + +func (x *GetSemesterRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *GetSemesterRequest) GetSemester() string { + if x != nil { + return x.Semester + } + return "" +} + +// 返回学期课表的信息 +type GetSemesterResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Courses []*Course `protobuf:"bytes,1,rep,name=courses,proto3" json:"courses,omitempty"` + IcsStream []byte `protobuf:"bytes,2,opt,name=icsStream,proto3" json:"icsStream,omitempty"` +} + +func (x *GetSemesterResponse) Reset() { + *x = GetSemesterResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_jwgl_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSemesterResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSemesterResponse) ProtoMessage() {} + +func (x *GetSemesterResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_jwgl_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSemesterResponse.ProtoReflect.Descriptor instead. +func (*GetSemesterResponse) Descriptor() ([]byte, []int) { + return file_protos_jwgl_proto_rawDescGZIP(), []int{1} +} + +func (x *GetSemesterResponse) GetCourses() []*Course { + if x != nil { + return x.Courses + } + return nil +} + +func (x *GetSemesterResponse) GetIcsStream() []byte { + if x != nil { + return x.IcsStream + } + return nil +} + +// 课程信息 +type Course struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Teacher string `protobuf:"bytes,2,opt,name=teacher,proto3" json:"teacher,omitempty"` + Place string `protobuf:"bytes,3,opt,name=place,proto3" json:"place,omitempty"` + Weeks []int32 `protobuf:"varint,4,rep,packed,name=weeks,proto3" json:"weeks,omitempty"` + BeginTimeString string `protobuf:"bytes,5,opt,name=beginTimeString,proto3" json:"beginTimeString,omitempty"` + EndTimeString string `protobuf:"bytes,6,opt,name=endTimeString,proto3" json:"endTimeString,omitempty"` + DayOfWeek int32 `protobuf:"varint,7,opt,name=dayOfWeek,proto3" json:"dayOfWeek,omitempty"` +} + +func (x *Course) Reset() { + *x = Course{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_jwgl_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Course) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Course) ProtoMessage() {} + +func (x *Course) ProtoReflect() protoreflect.Message { + mi := &file_protos_jwgl_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Course.ProtoReflect.Descriptor instead. +func (*Course) Descriptor() ([]byte, []int) { + return file_protos_jwgl_proto_rawDescGZIP(), []int{2} +} + +func (x *Course) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Course) GetTeacher() string { + if x != nil { + return x.Teacher + } + return "" +} + +func (x *Course) GetPlace() string { + if x != nil { + return x.Place + } + return "" +} + +func (x *Course) GetWeeks() []int32 { + if x != nil { + return x.Weeks + } + return nil +} + +func (x *Course) GetBeginTimeString() string { + if x != nil { + return x.BeginTimeString + } + return "" +} + +func (x *Course) GetEndTimeString() string { + if x != nil { + return x.EndTimeString + } + return "" +} + +func (x *Course) GetDayOfWeek() int32 { + if x != nil { + return x.DayOfWeek + } + return 0 +} + +var File_protos_jwgl_proto protoreflect.FileDescriptor + +var file_protos_jwgl_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6a, 0x77, 0x67, 0x6c, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6a, 0x77, 0x67, 0x6c, 0x22, 0x6a, 0x0a, 0x12, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x6d, 0x65, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x1a, 0x0a, + 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, + 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x6d, + 0x65, 0x73, 0x74, 0x65, 0x72, 0x22, 0x5b, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6d, 0x65, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x07, + 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x6a, 0x77, 0x67, 0x6c, 0x2e, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x52, 0x07, 0x63, 0x6f, 0x75, + 0x72, 0x73, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x63, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x69, 0x63, 0x73, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x22, 0xd0, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x63, 0x68, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x74, 0x65, 0x61, 0x63, 0x68, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x70, + 0x6c, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x65, 0x65, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x05, + 0x52, 0x05, 0x77, 0x65, 0x65, 0x6b, 0x73, 0x12, 0x28, 0x0a, 0x0f, 0x62, 0x65, 0x67, 0x69, 0x6e, + 0x54, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x61, 0x79, 0x4f, 0x66, + 0x57, 0x65, 0x65, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x79, 0x4f, + 0x66, 0x57, 0x65, 0x65, 0x6b, 0x32, 0x4c, 0x0a, 0x06, 0x4a, 0x77, 0x67, 0x6c, 0x65, 0x72, 0x12, + 0x42, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6d, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x18, + 0x2e, 0x6a, 0x77, 0x67, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6d, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6a, 0x77, 0x67, 0x6c, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x65, 0x6d, 0x65, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x19, 0x5a, 0x08, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0xaa, + 0x02, 0x0c, 0x4a, 0x77, 0x67, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_jwgl_proto_rawDescOnce sync.Once + file_protos_jwgl_proto_rawDescData = file_protos_jwgl_proto_rawDesc +) + +func file_protos_jwgl_proto_rawDescGZIP() []byte { + file_protos_jwgl_proto_rawDescOnce.Do(func() { + file_protos_jwgl_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_jwgl_proto_rawDescData) + }) + return file_protos_jwgl_proto_rawDescData +} + +var file_protos_jwgl_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_protos_jwgl_proto_goTypes = []interface{}{ + (*GetSemesterRequest)(nil), // 0: jwgl.GetSemesterRequest + (*GetSemesterResponse)(nil), // 1: jwgl.GetSemesterResponse + (*Course)(nil), // 2: jwgl.Course +} +var file_protos_jwgl_proto_depIdxs = []int32{ + 2, // 0: jwgl.GetSemesterResponse.courses:type_name -> jwgl.Course + 0, // 1: jwgl.Jwgler.GetSemester:input_type -> jwgl.GetSemesterRequest + 1, // 2: jwgl.Jwgler.GetSemester:output_type -> jwgl.GetSemesterResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_jwgl_proto_init() } +func file_protos_jwgl_proto_init() { + if File_protos_jwgl_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_jwgl_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSemesterRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_jwgl_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSemesterResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_jwgl_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Course); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_jwgl_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_jwgl_proto_goTypes, + DependencyIndexes: file_protos_jwgl_proto_depIdxs, + MessageInfos: file_protos_jwgl_proto_msgTypes, + }.Build() + File_protos_jwgl_proto = out.File + file_protos_jwgl_proto_rawDesc = nil + file_protos_jwgl_proto_goTypes = nil + file_protos_jwgl_proto_depIdxs = nil +} diff --git a/protos/jwgl.proto b/protos/jwgl.proto new file mode 100644 index 0000000..816400f --- /dev/null +++ b/protos/jwgl.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; + +option csharp_namespace = "JwglServices"; +option go_package = ".;protos"; + +package jwgl; + +service Jwgler { + rpc GetSemester (GetSemesterRequest) returns (GetSemesterResponse); +} + +// 请求学期课表的信息 +message GetSemesterRequest { + string studentID = 1; + string password = 2; + string semester = 3; +} + +// 返回学期课表的信息 +message GetSemesterResponse { + repeated Course courses = 1; + bytes icsStream = 2; +} + +// 课程信息 +message Course { + string name = 1; + string teacher = 2; + string place = 3; + repeated int32 weeks = 4; + string beginTimeString = 5; + string endTimeString =6; + int32 dayOfWeek = 7; +} + + diff --git a/protos/jwgl_grpc.pb.go b/protos/jwgl_grpc.pb.go new file mode 100644 index 0000000..c3bdd26 --- /dev/null +++ b/protos/jwgl_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.6.1 +// source: protos/jwgl.proto + +package protos + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// JwglerClient is the client API for Jwgler service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type JwglerClient interface { + GetSemester(ctx context.Context, in *GetSemesterRequest, opts ...grpc.CallOption) (*GetSemesterResponse, error) +} + +type jwglerClient struct { + cc grpc.ClientConnInterface +} + +func NewJwglerClient(cc grpc.ClientConnInterface) JwglerClient { + return &jwglerClient{cc} +} + +func (c *jwglerClient) GetSemester(ctx context.Context, in *GetSemesterRequest, opts ...grpc.CallOption) (*GetSemesterResponse, error) { + out := new(GetSemesterResponse) + err := c.cc.Invoke(ctx, "/jwgl.Jwgler/GetSemester", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// JwglerServer is the server API for Jwgler service. +// All implementations must embed UnimplementedJwglerServer +// for forward compatibility +type JwglerServer interface { + GetSemester(context.Context, *GetSemesterRequest) (*GetSemesterResponse, error) + mustEmbedUnimplementedJwglerServer() +} + +// UnimplementedJwglerServer must be embedded to have forward compatible implementations. +type UnimplementedJwglerServer struct { +} + +func (UnimplementedJwglerServer) GetSemester(context.Context, *GetSemesterRequest) (*GetSemesterResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSemester not implemented") +} +func (UnimplementedJwglerServer) mustEmbedUnimplementedJwglerServer() {} + +// UnsafeJwglerServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to JwglerServer will +// result in compilation errors. +type UnsafeJwglerServer interface { + mustEmbedUnimplementedJwglerServer() +} + +func RegisterJwglerServer(s grpc.ServiceRegistrar, srv JwglerServer) { + s.RegisterService(&Jwgler_ServiceDesc, srv) +} + +func _Jwgler_GetSemester_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSemesterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(JwglerServer).GetSemester(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/jwgl.Jwgler/GetSemester", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(JwglerServer).GetSemester(ctx, req.(*GetSemesterRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Jwgler_ServiceDesc is the grpc.ServiceDesc for Jwgler service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Jwgler_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "jwgl.Jwgler", + HandlerType: (*JwglerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetSemester", + Handler: _Jwgler_GetSemester_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/jwgl.proto", +} diff --git a/push/push.go b/push/push.go deleted file mode 100644 index 535daf8..0000000 --- a/push/push.go +++ /dev/null @@ -1,199 +0,0 @@ -package push - -import ( - "bytes" - "ddl/wecom" - "fmt" - "io/ioutil" - "net/http" - "strings" - - jsoniter "github.com/json-iterator/go" -) - -var json = jsoniter.ConfigCompatibleWithStandardLibrary - -//touser := "" //企业号中的用户帐号,在zabbix用户Media中配置,如果配置不正常,将按部门发送。 -//toparty := "1" //企业号中的部门id。 -// var corpid = "ww8a5308483ff283cc" //企业号的标识 - -// var agentid = 1000002 //企业号中的应用id。 -// var corpsecret = "EPQstC4qi51TcvtVQRzQ1HowUdJ4jrOG_cFgcIA160E" //企业号中的应用的Secret -var corpsecretMap = map[int]string{ - 1000002: "EPQstC4qi51TcvtVQRzQ1HowUdJ4jrOG_cFgcIA160E", //通知大全 - 1000005: "tFqgBhMGuktPfuEuXmvkXktU-W6Oq7cLFAg_n-WbwYQ", //活动大全 -} - -// var partyMap = map[string]string{ -// "dddd": "1", //大班 -// "304": "2", -// "305": "7", -// "306": "8", -// "307": "9", -// "308": "10", -// "309": "11", -// "test": "6", //测试 -// } - -var noticeTypeMap = map[int]string{ - 0: "DDL", - 1: "活动", - 2: "思政活动", - 3: "文体活动", - 4: "志愿活动", - 5: "讲座", - 6: "竞赛", - 7: "评优", -} - -type JSON struct { - Access_token string `json:"access_token"` -} - -type MESSAGES struct { - Touser string `json:"touser"` - Toparty string `json:"toparty"` - Msgtype string `json:"msgtype"` - Agentid int `json:"agentid"` - Text struct { - //Subject string `json:"subject"` - Content string `json:"content"` - } `json:"text"` - Safe int `json:"safe"` -} - -type MESSAGESCRAD struct { - Touser string `json:"touser"` - Toparty string `json:"toparty"` - Msgtype string `json:"msgtype"` - Agentid int `json:"agentid"` - Textcard struct { - //Subject string `json:"subject"` - Title string `json:"title"` - Description string `json:"description"` - URL string `json:"url"` - Btntxt string `json:"btntxt"` - } `json:"textcard"` - Safe int `json:"safe"` -} - -func sendData(access_token, msg string) { - send_url := "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + access_token - //print(send_url) - client := &http.Client{} - req, _ := http.NewRequest("POST", send_url, bytes.NewBuffer([]byte(msg))) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("charset", "UTF-8") - resp, err := client.Do(req) - if err != nil { - panic(err) - } - defer resp.Body.Close() - fmt.Println("response Status:", resp.Status) - body, _ := ioutil.ReadAll(resp.Body) - fmt.Println("response Body:", string(body)) -} - -//构造信息结构 -// func messages(touser string, toparty string, agentid int, content string) string { -// msg := MESSAGES{ -// Touser: touser, -// Toparty: toparty, -// Msgtype: "text", -// Agentid: agentid, -// Safe: 0, -// Text: struct { -// //Subject string `json:"subject"` -// Content string `json:"content"` -// }{Content: content}, -// } -// sed_msg, _ := json.Marshal(msg) -// //fmt.Printf("%s",string(sed_msg)) -// return string(sed_msg) -// } - -// func messagesGroup(toparty string, agentid int, content string) string { -// msg := MESSAGES{ -// Toparty: toparty, -// Msgtype: "text", -// Agentid: agentid, -// Safe: 0, -// Text: struct { -// //Subject string `json:"subject"` -// Content string `json:"content"` -// }{Content: content}, -// } -// sed_msg, _ := json.Marshal(msg) -// //fmt.Printf("%s",string(sed_msg)) -// return string(sed_msg) -// } - -func messagesGroupCard(toparty string, agentid int, title string, description string, url string) string { - msg := MESSAGESCRAD{ - Toparty: toparty, - Msgtype: "textcard", - Agentid: agentid, - Safe: 0, - Textcard: struct { - Title string `json:"title"` - Description string `json:"description"` - URL string `json:"url"` - Btntxt string `json:"btntxt"` - }{Title: title, Description: description, URL: url}, - } - sed_msg, _ := json.Marshal(msg) - //fmt.Printf("%s",string(sed_msg)) - return string(sed_msg) -} - -//发送消息 party:部门名 noticeType:通知类型 title:标题 dateTime:时间 detail:详情 url:卡片跳转链接 -// func sendNotice(parties []string, noticeType int, title string, dateTime string, detail string, url string) { -// accessToken := Get_AccessToken(corpid, corpsecret) -// var partyIDs string -// for _, party := range parties { -// partyIDs += partyMap[party] + "|" -// } -// partyIDs = partyIDs[:len(partyIDs)-1] -// noticeTypeString := typeMap[noticeType] - -// fmt.Println(partyIDs) -// // 序列化成json之后,\n会被转义,也就是变成了\\n,使用str替换,替换掉转义 -// msg := messagesGroupCard(partyIDs, agentid, title, "
"+dateTime+" | "+noticeTypeString+"

"+detail, url) -// msg = strings.Replace(msg, "\\\\", "\\", -1) - -// // fmt.Println(strings.Replace(msg,"\\\\","\\",-1)) -// SendData(accessToken, msg) -// } - -//partyID:部门ID noticeType:通知类型 title:标题 dateTime:时间 detail:详情 url:卡片跳转链接 -func SendNotice(partyIDs string, noticeType int, title string, dateTime string, detail string, url string) { - var agentid int - if noticeType == 0 { - agentid = 1000002 - } else { - agentid = 1000005 - } - accessToken := wecom.GetAccessToken(corpsecretMap[agentid]) - - fmt.Println(partyIDs) - // 序列化成json之后,\n会被转义,也就是变成了\\n,使用str替换,替换掉转义 - msg := messagesGroupCard(partyIDs, agentid, title, "
"+dateTime+" | "+noticeTypeMap[noticeType]+"

"+detail, url) - msg = strings.Replace(msg, "\\\\", "\\", -1) - - // fmt.Println(strings.Replace(msg,"\\\\","\\",-1)) - sendData(accessToken, msg) -} - -// //partyID:部门ID noticeType:通知类型 title:标题 dateTime:时间 detail:详情 url:卡片跳转链接 -// func SendNoticeActivity(partyIDs string, noticeType int, title string, dateTime string, detail string, url string) { -// var agentid = 1000005 -// accessToken := wecom.GetAccessToken(corpsecretMap[agentid]) - -// fmt.Println(partyIDs) -// // 序列化成json之后,\n会被转义,也就是变成了\\n,使用str替换,替换掉转义 -// msg := messagesGroupCard(partyIDs, agentid, title, "
"+dateTime+" | "+noticeTypeMap[noticeType]+"

"+detail, url) -// msg = strings.Replace(msg, "\\\\", "\\", -1) - -// // fmt.Println(strings.Replace(msg,"\\\\","\\",-1)) -// sendData(accessToken, msg) -// } diff --git a/query/query.go b/query/query.go deleted file mode 100644 index 6dff487..0000000 --- a/query/query.go +++ /dev/null @@ -1,136 +0,0 @@ -package query - -import ( - "ddl/common" - "ddl/database" - "encoding/json" - "net/http" - "strconv" - "time" - - "github.com/gin-gonic/gin" - _ "github.com/jinzhu/gorm/dialects/mysql" //这个一定要引入哦!! -) - -// func Base64Decode(sEnc string) string { - -// // Base64 Standard Decoding -// sDec, err := base64.StdEncoding.DecodeString(sEnc) -// if err != nil { -// fmt.Printf("Error decoding string: %s ", err.Error()) -// return "" -// } - -// return string(sDec) -// } - -func QuerySingleHandler(c *gin.Context) { - class := c.Request.FormValue("class") - index_ := c.Request.FormValue("index_") - if class == "" || index_ == "" { - c.String(http.StatusBadRequest, "参数错误") - return - } - - noticeArr := []common.DDLNotice{} - - database.DB.Table(class).Where("index_=?", index_).Find(¬iceArr) - data, _ := json.Marshal(noticeArr) - - // w.Header().Set("Access-Control-Allow-Origin", "*") //允许访问所有域 - // w.Header().Add("Access-Control-Allow-Headers", "Content-Type") //header的类型 - - c.Data(200, "application/json", data) -} - -func GetListHandler(c *gin.Context) { - class := c.Request.FormValue("class") - - if class == "" { - class = "dddd" - } - - start := c.Request.FormValue("start") - if start == "" { - start = "0" - } - - step := c.Request.FormValue("step") - if step == "" { - step = "20" - } - - noticeType := c.Request.FormValue("noticeType") - if noticeType == "" { - noticeType = "-1" - } - - noticeArr := []common.DDLNotice{} - - // db.Table(class).Find(¬iceArr).Order("ddl DESC") - - // for _, notice := range noticeArr { - // nowStamp := time.Now().Unix() - // ddlStamp := notice.DDL.Unix() - // stateNew := notice.State - - // if nowStamp-ddlStamp > 259200 { - // stateNew = 3 // 不显示 i.e. 超时 3 天以上 - // } else if ddlStamp <= nowStamp { - // stateNew = 2 // 超时 - // } else if ddlStamp-nowStamp > 86400 { - // stateNew = 1 // 进行中 - // } else { - // stateNew = 0 // 紧急 i.e. 距 ddl 1 天以内 - // } - - // if stateNew != notice.State { - // var noticeOp DDLNotice - // db.Table(class).Where("index_ = ?", notice.Index).Take(¬iceOp) - // noticeOp.State = stateNew - // db.Table(class).Save(¬iceOp)//此处有BUG!!!!!! - // } else if stateNew == 3 { - // // ddl DESC 排序,如果已经 discarded 且不需更新则后面也不需更新 - // // 这个优化其实没啥用,把上面那个判完之后不交互数据库了,算几个时间戳减法也不浪费时间 - // // TODO) 真该优化了的话到时候在 SELECT * 的时候整个 LIMIT - // break - // } - // } - - if noticeType == "-1" { - database.DB.Table(class).Limit(step).Offset(start).Order("time DESC").Find(¬iceArr) - } else if noticeType == "-2" { - now := time.Now() - if start == "0" { - //未过期通知 - database.DB.Table(class).Where("noticeType != 0 and ddl >= ?", now).Order("ddl").Find(¬iceArr) - } - //已过期通知 - noticeArr1 := []common.DDLNotice{} - database.DB.Table(class).Limit(step).Offset(start).Where("noticeType != 0 and ddl < ?", now).Order("ddl DESC").Find(¬iceArr1) - - //合并 - noticeArr = append(noticeArr, noticeArr1...) - } else { - noticeTypeInt, _ := strconv.Atoi(noticeType) - - now := time.Now() - if start == "0" { - //未过期通知 - database.DB.Table(class).Where("noticeType = ? and ddl >= ?", noticeTypeInt, now).Order("ddl").Find(¬iceArr) - } - //已过期通知 - noticeArr1 := []common.DDLNotice{} - database.DB.Table(class).Limit(step).Offset(start).Where("noticeType = ? and ddl < ?", noticeTypeInt, now).Order("ddl DESC").Find(¬iceArr1) - - //合并 - noticeArr = append(noticeArr, noticeArr1...) - } - - data, _ := json.Marshal(noticeArr) - - // w.Header().Set("Access-Control-Allow-Origin", "*") //允许访问所有域 - // w.Header().Add("Access-Control-Allow-Headers", "Content-Type") //header的类型 - - c.Data(200, "application/json", data) -} diff --git a/static/login.html b/static/login.html deleted file mode 100644 index 1254660..0000000 --- a/static/login.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - 未登录 - - 微信/企业微信一键登录 - - - 企业微信扫码登录 - - \ No newline at end of file diff --git a/tool/config.go b/tool/config.go new file mode 100644 index 0000000..61b6815 --- /dev/null +++ b/tool/config.go @@ -0,0 +1,71 @@ +package tool + +import ( + "bufio" + "ddlBackend/models" + "encoding/json" + "os" +) + +type Config struct { + AppPort string `json:"app_port"` + JWTSecret string `json:"jwt_secret"` + PasswordSalt string `json:"password_salt"` + JWGLOutTime int64 `json:"jwgl_out_time"` + JWGrpcAddress string `json:"jw_grpc_address"` + RootConfig models.UserInformation `json:"root_config"` + UseMysql bool `json:"use_mysql"` + MysqlConfig models.MysqlModel `json:"mysql_config"` +} + +// DefaultSetting 默认配置文件 +var DefaultSetting = Config{ + AppPort: ":8080", + JWTSecret: "MakeBuptGreatAgain", + JWGLOutTime: 24, + PasswordSalt: "MakeBuptGreatAgain", + JWGrpcAddress: "rrricardo.top:7000", + RootConfig: models.UserInformation{ + Username: "root", + Password: "123456", + ClassName: "dddd", + StudentID: "0000000000", + Permission: models.Root, + }, + UseMysql: false, +} + +// Setting 配置文件对象 +var Setting Config + +// ReadConfig 读取配置文件 +func ReadConfig() error { + file, err := os.Open("config.json") + // 函数执行完成后关闭文件 + defer func(file *os.File) { + err := file.Close() + if err != nil { + // 关闭文件中错误 + DDLLogError(err.Error()) + } + }(file) + + if err != nil { + // 读取配置文件错误 + // 采用默认配置 + Setting = DefaultSetting + return err + } + + reader := bufio.NewReader(file) + decoder := json.NewDecoder(reader) + err = decoder.Decode(&Setting) + if err != nil { + // 解析json失败 + // 采用默认配置 + Setting = DefaultSetting + return err + } + + return nil +} diff --git a/tool/jwt.go b/tool/jwt.go new file mode 100644 index 0000000..9bf528d --- /dev/null +++ b/tool/jwt.go @@ -0,0 +1,80 @@ +package tool + +import ( + "ddlBackend/models" + "errors" + "time" + + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v4" +) + +// GenerateJWTToken 生成JWT令牌 +func GenerateJWTToken(info models.UserInformation) (string, error) { + // 设置token的有效时间为24小时 + expireTime := time.Now().Add(24 * time.Hour) + + // 设置token中的信息 + claims := models.JWTClaims{ + StudentID: info.StudentID, + ClassName: info.ClassName, + Permission: info.Permission, + RegisteredClaims: jwt.RegisteredClaims{ + // token失效时间 + ExpiresAt: jwt.NewNumericDate(expireTime), + // token签发人 + Issuer: "SquidWard", + }, + } + + tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return tokenClaims.SignedString([]byte(Setting.JWTSecret)) +} + +// ParseJWTToken 解析令牌并返回其中的信息 +func ParseJWTToken(token string) (*models.JWTClaims, error) { + tokenClaims, err := jwt.ParseWithClaims(token, &models.JWTClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(Setting.JWTSecret), nil + }) + + if tokenClaims != nil { + // 尝试判断令牌中的信息是否正确 + claims, ok := tokenClaims.Claims.(*models.JWTClaims) + // 如果信息正确且令牌有效 + if ok && tokenClaims.Valid { + return claims, nil + } + } + + return nil, err +} + +// GetClaimsInContext 获得HTTP上下文中的JWT令牌信息 +func GetClaimsInContext(context *gin.Context) (*models.JWTClaims, error) { + value, exist := context.Get("Claims") + if !exist { + // 没有找到令牌信息 + return nil, errors.New("no JWT claims") + } + + claims, ok := value.(models.JWTClaims) + if !ok { + return nil, errors.New("can not read claims") + } + + return &claims, nil +} + +// CheckPermission 验证请求者的权限 +func CheckPermission(context *gin.Context, permission uint) (bool, error) { + claims, err := GetClaimsInContext(context) + if err != nil { + return false, err + } + + if claims.Permission >= permission { + return true, nil + } else { + return false, nil + } +} diff --git a/tool/log.go b/tool/log.go new file mode 100644 index 0000000..c1930da --- /dev/null +++ b/tool/log.go @@ -0,0 +1,12 @@ +package tool + +import ( + "fmt" + "time" +) + +// DDLLogError 记录错误日志 +func DDLLogError(message string) { + timeString := time.Now().Format("01-02 15:04:05") + fmt.Printf("%s DDL-Error: %s", timeString, message) +} diff --git a/tool/password.go b/tool/password.go new file mode 100644 index 0000000..fa8d8e6 --- /dev/null +++ b/tool/password.go @@ -0,0 +1,18 @@ +package tool + +import ( + "crypto/sha256" + "encoding/hex" +) + +// Sha256PasswordWithSalt 密码加盐hash10000次函数 +func Sha256PasswordWithSalt(password string) string { + salt := []byte(Setting.PasswordSalt) + result := sha256.Sum256(append([]byte(password), salt...)) + + for i := 0; i < 9999; i++ { + result = sha256.Sum256(append(result[:], salt...)) + } + + return hex.EncodeToString(result[:]) +} diff --git a/wecom/wecom.go b/wecom/wecom.go deleted file mode 100644 index 663de9d..0000000 --- a/wecom/wecom.go +++ /dev/null @@ -1,45 +0,0 @@ -package wecom - -import ( - "ddl/common" - "ddl/config" - "encoding/json" - "fmt" -) - -type GetAccessTokenRes struct { - Access_token string -} - -type GetUserIDRes struct { - UserID string - Errmsg string -} - -//微信登录部分 - -func GetAccessToken(corpsecret string) string { - data := common.HttpGet("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + config.CORPID + "&corpsecret=" + corpsecret) - res := GetAccessTokenRes{} - err := json.Unmarshal([]byte(data), &res) - if err != nil { - fmt.Println("GetAccessToken failed") - return "" - } else { - return res.Access_token - } -} - -func GetWecomID(code string) string { - accessToken := GetAccessToken(config.CORPSECRET) - data := common.HttpGet("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=" + accessToken + "&code=" + code) - res := GetUserIDRes{} - err := json.Unmarshal([]byte(data), &res) - // fmt.Println(data) - if err != nil || res.Errmsg != "ok" { - fmt.Printf("GetWecomID failed\n%s\n", res.Errmsg) - return "" - } else { - return res.UserID - } -} -- Gitee