From 495e98d424d20af82f3169e3076f853bce8d05f6 Mon Sep 17 00:00:00 2001 From: suguo <25950955@qq.com> Date: Fri, 13 Mar 2026 16:35:54 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E6=95=B0=E6=8D=AE=E8=BF=9B?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gin/filter-gin.go | 6 +- gin/router-gin.go | 46 ++++++- go.mod | 2 +- handler/auth-handler.go | 30 ++-- handler/base-handler.go | 32 +++++ handler/data-handler.go | 188 +++++++++++++++++++++++++ handler/device-handler.go | 189 ++++++++++++++++++++++++++ handler/menu-handler.go | 27 ++++ handler/org-handler.go | 112 +++++++++++++++ handler/point-handler.go | 187 +++++++++++++++++++++++++ handler/project-handler.go | 256 +++++++++++++++++++++++++++++++++++ handler/readme.md | 2 + handler/task-handler.go | 246 +++++++++++++++++++++++++++++++++ main.go | 2 + model/data-model.go | 20 +++ model/device-model.go | 45 ++++++ model/menu-model.go | 15 ++ model/org-model.go | 15 ++ model/permission-model.go | 2 +- model/point-model.go | 29 ++++ model/project-model.go | 32 +++++ model/task-model.go | 32 +++++ mysql/data-mysql.go | 88 ++++++++++++ mysql/device-mysql.go | 78 +++++++++++ mysql/menu-mysql.go | 50 +++++++ mysql/org-mysql.go | 74 ++++++++++ mysql/permission-mysql.go | 29 ++-- mysql/point-mysql.go | 84 ++++++++++++ mysql/project-mysql.go | 82 +++++++++++ mysql/role-mysql.go | 68 ++++++++++ mysql/tables-mysql.go | 90 ++---------- mysql/task-mysql.go | 81 +++++++++++ mysql/user-mysql.go | 40 +++--- service/auth-service.go | 28 +++- service/base-service.go | 20 ++- service/bootstrap-service.go | 119 ++++++++++++++++ service/data-service.go | 151 +++++++++++++++++++++ service/device-service.go | 171 +++++++++++++++++++++++ service/menu-service.go | 71 ++++++++++ service/org-service.go | 136 +++++++++++++++++++ service/password-service.go | 13 +- service/point-service.go | 172 +++++++++++++++++++++++ service/project-service.go | 205 ++++++++++++++++++++++++++++ service/rbac-service.go | 6 +- service/readme.md | 1 + service/task-service.go | 188 +++++++++++++++++++++++++ 46 files changed, 3417 insertions(+), 143 deletions(-) create mode 100644 handler/data-handler.go create mode 100644 handler/device-handler.go create mode 100644 handler/menu-handler.go create mode 100644 handler/org-handler.go create mode 100644 handler/point-handler.go create mode 100644 handler/project-handler.go create mode 100644 handler/readme.md create mode 100644 handler/task-handler.go create mode 100644 model/data-model.go create mode 100644 model/device-model.go create mode 100644 model/menu-model.go create mode 100644 model/org-model.go create mode 100644 model/point-model.go create mode 100644 model/project-model.go create mode 100644 model/task-model.go create mode 100644 mysql/data-mysql.go create mode 100644 mysql/device-mysql.go create mode 100644 mysql/menu-mysql.go create mode 100644 mysql/org-mysql.go create mode 100644 mysql/point-mysql.go create mode 100644 mysql/project-mysql.go create mode 100644 mysql/role-mysql.go create mode 100644 mysql/task-mysql.go create mode 100644 service/bootstrap-service.go create mode 100644 service/data-service.go create mode 100644 service/device-service.go create mode 100644 service/menu-service.go create mode 100644 service/org-service.go create mode 100644 service/point-service.go create mode 100644 service/project-service.go create mode 100644 service/readme.md create mode 100644 service/task-service.go diff --git a/gin/filter-gin.go b/gin/filter-gin.go index 2ccdd52..6bbde20 100644 --- a/gin/filter-gin.go +++ b/gin/filter-gin.go @@ -65,7 +65,7 @@ func authorize() gin.HandlerFunc { } permissionCode := c.Request.Method + ":" + fullPath - defined, err := service.PermissionDefined(permissionCode) + defined, err := service.RbacPermissionDefined(permissionCode) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ "data": "权限校验失败", @@ -77,7 +77,7 @@ func authorize() gin.HandlerFunc { return } - roleID, found, err := service.UserRoleIDByUserID(u.ID) + roleID, found, err := service.RbacUserRoleIDByUserID(u.ID) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ "data": "权限校验失败", @@ -97,7 +97,7 @@ func authorize() gin.HandlerFunc { return } - allowed, err := service.RoleHasPermission(roleID, permissionCode) + allowed, err := service.RbacRoleHasPermission(roleID, permissionCode) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ "data": "权限校验失败", diff --git a/gin/router-gin.go b/gin/router-gin.go index aa71599..7069ee1 100644 --- a/gin/router-gin.go +++ b/gin/router-gin.go @@ -9,11 +9,49 @@ import ( func routerSetup(router *gin.Engine) { router.Use(gin.Recovery()) api := router.Group("/api") - api.POST("/login", handler.Login) + api.POST("/login", handler.AuthLogin) protected := router.Group("/api") protected.Use(auth(), authorize()) - protected.POST("/logout", handler.Logout) - protected.GET("/me", handler.Me) - protected.POST("/password", handler.ChangePassword) + protected.POST("/logout", handler.AuthLogout) + protected.GET("/me", handler.AuthMe) + protected.POST("/password", handler.AuthChangePassword) + + protected.GET("/orgs", handler.OrgList) + protected.POST("/orgs", handler.OrgCreate) + protected.GET("/orgs/:id", handler.OrgGet) + protected.PUT("/orgs/:id", handler.OrgUpdate) + protected.DELETE("/orgs/:id", handler.OrgDelete) + + protected.GET("/projects", handler.ProjectList) + protected.POST("/projects", handler.ProjectCreate) + protected.GET("/projects/:id", handler.ProjectGet) + protected.PUT("/projects/:id", handler.ProjectUpdate) + protected.DELETE("/projects/:id", handler.ProjectDelete) + + protected.GET("/tasks", handler.TaskList) + protected.POST("/tasks", handler.TaskCreate) + protected.GET("/tasks/:id", handler.TaskGet) + protected.PUT("/tasks/:id", handler.TaskUpdate) + protected.DELETE("/tasks/:id", handler.TaskDelete) + + protected.GET("/points", handler.PointList) + protected.POST("/points", handler.PointCreate) + protected.GET("/points/:id", handler.PointGet) + protected.PUT("/points/:id", handler.PointUpdate) + protected.DELETE("/points/:id", handler.PointDelete) + + protected.GET("/data-records", handler.DataList) + protected.POST("/data-records", handler.DataCreate) + protected.GET("/data-records/:id", handler.DataGet) + protected.PUT("/data-records/:id", handler.DataUpdate) + protected.DELETE("/data-records/:id", handler.DataDelete) + + protected.GET("/devices", handler.DeviceList) + protected.POST("/devices", handler.DeviceCreate) + protected.GET("/devices/:id", handler.DeviceGet) + protected.PUT("/devices/:id", handler.DeviceUpdate) + protected.DELETE("/devices/:id", handler.DeviceDelete) + + protected.GET("/menus", handler.MenuList) } diff --git a/go.mod b/go.mod index 144f78e..2fbe25c 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 github.com/sirupsen/logrus v1.9.4 + golang.org/x/crypto v0.48.0 gorm.io/driver/mysql v1.6.0 gorm.io/gorm v1.31.1 gorm.io/plugin/dbresolver v1.6.2 @@ -46,7 +47,6 @@ require ( github.com/ugorji/go/codec v1.3.1 // indirect go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect golang.org/x/arch v0.22.0 // indirect - golang.org/x/crypto v0.48.0 // indirect golang.org/x/net v0.51.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect diff --git a/handler/auth-handler.go b/handler/auth-handler.go index 2b184b2..f4410b6 100644 --- a/handler/auth-handler.go +++ b/handler/auth-handler.go @@ -10,24 +10,24 @@ import ( "myschools.me/heritage/heritage-api/service" ) -type loginRequest struct { +type AuthLoginRequest struct { UserName string `json:"userName"` Username string `json:"username"` Password string `json:"password"` } -type loginResponse struct { +type AuthLoginResponse struct { Token string `json:"token"` User *model.User `json:"user"` } -type changePasswordRequest struct { +type AuthChangePasswordRequest struct { OldPassword string `json:"oldPassword"` NewPassword string `json:"newPassword"` } -func Login(c *gin.Context) { - var req loginRequest +func AuthLogin(c *gin.Context) { + var req AuthLoginRequest if err := c.ShouldBindJSON(&req); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ "data": "参数错误", @@ -45,7 +45,7 @@ func Login(c *gin.Context) { return } - token, safeUser, err := service.Login(req.UserName, req.Password) + token, safeUser, err := service.AuthLogin(req.UserName, req.Password) if err != nil { if err == service.ErrInvalidCredentials { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ @@ -59,24 +59,24 @@ func Login(c *gin.Context) { return } - c.JSON(http.StatusOK, loginResponse{ + c.JSON(http.StatusOK, AuthLoginResponse{ Token: token, User: safeUser, }) } -func Logout(c *gin.Context) { +func AuthLogout(c *gin.Context) { token := c.GetHeader("Authorization") if token == "" { token = c.Query("Authorization") } - _ = service.Logout(token) + _ = service.AuthLogout(token) c.JSON(http.StatusOK, gin.H{ "data": "ok", }) } -func Me(c *gin.Context) { +func AuthMe(c *gin.Context) { usr := currentUser(c) if usr == nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ @@ -89,7 +89,7 @@ func Me(c *gin.Context) { }) } -func ChangePassword(c *gin.Context) { +func AuthChangePassword(c *gin.Context) { usr := currentUser(c) if usr == nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ @@ -98,7 +98,7 @@ func ChangePassword(c *gin.Context) { return } - var req changePasswordRequest + var req AuthChangePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ "data": "参数错误", @@ -107,7 +107,7 @@ func ChangePassword(c *gin.Context) { } req.OldPassword = strings.TrimSpace(req.OldPassword) req.NewPassword = strings.TrimSpace(req.NewPassword) - err := service.ChangePassword(usr.ID, req.OldPassword, req.NewPassword) + err := service.AuthChangePassword(usr.ID, req.OldPassword, req.NewPassword) if err != nil { switch err { case service.ErrInvalidArgument: @@ -128,7 +128,7 @@ func ChangePassword(c *gin.Context) { }) default: c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ - "data": changePasswordInternalError(err), + "data": authChangePasswordInternalError(err), }) } return @@ -139,7 +139,7 @@ func ChangePassword(c *gin.Context) { }) } -func changePasswordInternalError(err error) string { +func authChangePasswordInternalError(err error) string { if os.Getenv("DEBUG") == "true" && err != nil { return "修改失败: " + err.Error() } diff --git a/handler/base-handler.go b/handler/base-handler.go index b698ef7..8e6b8ce 100644 --- a/handler/base-handler.go +++ b/handler/base-handler.go @@ -1,6 +1,11 @@ +// 这是handler层的基础函数,不必要求规范的方法名格式 package handler import ( + "strconv" + "strings" + "time" + "github.com/gin-gonic/gin" "myschools.me/heritage/heritage-api/model" ) @@ -17,3 +22,30 @@ func currentUser(c *gin.Context) *model.User { } return u } + +func pageAndSize(c *gin.Context) (int, int) { + page, _ := strconv.Atoi(c.Query("page")) + size, _ := strconv.Atoi(c.Query("size")) + if page <= 0 { + page = 1 + } + if size <= 0 { + size = 20 + } + if size > 200 { + size = 200 + } + return page, size +} + +func parseDateOnly(s *string) (*time.Time, error) { + *s = strings.TrimSpace(*s) + if *s == "" { + return nil, nil + } + t, err := time.ParseInLocation("2006-01-02", *s, time.Local) + if err != nil { + return nil, err + } + return &t, nil +} diff --git a/handler/data-handler.go b/handler/data-handler.go new file mode 100644 index 0000000..d1dcba6 --- /dev/null +++ b/handler/data-handler.go @@ -0,0 +1,188 @@ +package handler + +import ( + "net/http" + "strings" + "time" + + "github.com/gin-gonic/gin" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/service" +) + +type DataCreateRequest struct { + Value string `json:"value"` + IndicatorParamID string `json:"indicatorParamId"` + PointID string `json:"pointId"` + TaskID string `json:"taskId"` + CollectedAt string `json:"collectedAt"` + CreatorUserID string `json:"creatorUserId"` + Remark string `json:"remark"` +} + +type DataUpdateRequest struct { + Value *string `json:"value"` + IndicatorParamID *string `json:"indicatorParamId"` + PointID *string `json:"pointId"` + TaskID *string `json:"taskId"` + CollectedAt *string `json:"collectedAt"` + Remark *string `json:"remark"` +} + +func DataCreate(c *gin.Context) { + var req DataCreateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + t, err := dataParseTime(req.CollectedAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "采集时间格式错误"}) + return + } + usr := currentUser(c) + creatorID := req.CreatorUserID + if creatorID == "" && usr != nil { + creatorID = usr.ID + } + r := &model.DataRecord{ + Value: req.Value, + IndicatorParamID: req.IndicatorParamID, + PointID: req.PointID, + TaskID: req.TaskID, + CreatorUserID: creatorID, + Remark: req.Remark, + } + if t != nil { + r.CollectedAt = *t + } + out, err := service.DataRecordCreate(r) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"dataRecord": out}) +} + +func DataUpdate(c *gin.Context) { + id := c.Param("id") + var req DataUpdateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + patch := &model.DataRecord{} + if req.Value != nil { + patch.Value = *req.Value + } + if req.IndicatorParamID != nil { + patch.IndicatorParamID = *req.IndicatorParamID + } + if req.PointID != nil { + patch.PointID = *req.PointID + } + if req.TaskID != nil { + patch.TaskID = *req.TaskID + } + if req.CollectedAt != nil { + t, err := dataParseTime(*req.CollectedAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "采集时间格式错误"}) + return + } + if t != nil { + patch.CollectedAt = *t + } + } + if req.Remark != nil { + patch.Remark = *req.Remark + } + out, err := service.DataRecordUpdate(id, patch) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"dataRecord": out}) +} + +func DataDelete(c *gin.Context) { + id := c.Param("id") + if err := service.DataRecordDelete(id); err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "删除失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"data": "ok"}) +} + +func DataGet(c *gin.Context) { + id := c.Param("id") + r, err := service.DataGetDataRecord(id) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"dataRecord": r}) +} + +func DataList(c *gin.Context) { + page, size := pageAndSize(c) + pointID := c.Query("pointId") + taskID := c.Query("taskId") + startAtStr := c.Query("startAt") + endAtStr := c.Query("endAt") + + var pid *string + if pointID != "" { + pid = &pointID + } + var tid *string + if taskID != "" { + tid = &taskID + } + + startAt, err := dataParseTime(startAtStr) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "startAt格式错误"}) + return + } + endAt, err := dataParseTime(endAtStr) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "endAt格式错误"}) + return + } + + items, total, err := service.DataListDataRecords(pid, tid, startAt, endAt, page, size) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{ + "items": items, + "total": total, + "page": page, + "size": size, + }) +} + +func dataParseTime(s string) (*time.Time, error) { + s = strings.TrimSpace(s) + if s == "" { + return nil, nil + } + if t, err := time.ParseInLocation(time.RFC3339, s, time.Local); err == nil { + return &t, nil + } + return parseDateOnly(&s) +} diff --git a/handler/device-handler.go b/handler/device-handler.go new file mode 100644 index 0000000..0fa2cf2 --- /dev/null +++ b/handler/device-handler.go @@ -0,0 +1,189 @@ +package handler + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/service" +) + +type DeviceCreateRequest struct { + Name string `json:"name"` + ModelID string `json:"modelId"` + SerialNo string `json:"serialNo"` + StatusCode string `json:"statusCode"` + PointID string `json:"pointId"` + InstalledAt string `json:"installedAt"` + InstalledImage string `json:"installedImage"` + Channel1ParamID string `json:"channel1ParamId"` + Channel2ParamID string `json:"channel2ParamId"` + Channel3ParamID string `json:"channel3ParamId"` + CreatorUserID string `json:"creatorUserId"` + Remark string `json:"remark"` +} + +type DeviceUpdateRequest struct { + Name *string `json:"name"` + ModelID *string `json:"modelId"` + SerialNo *string `json:"serialNo"` + StatusCode *string `json:"statusCode"` + PointID *string `json:"pointId"` + InstalledAt *string `json:"installedAt"` + InstalledImage *string `json:"installedImage"` + Channel1ParamID *string `json:"channel1ParamId"` + Channel2ParamID *string `json:"channel2ParamId"` + Channel3ParamID *string `json:"channel3ParamId"` + CreatorUserID *string `json:"creatorUserId"` + Remark *string `json:"remark"` +} + +func DeviceCreate(c *gin.Context) { + var req DeviceCreateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + t, err := parseDateOnly(&req.InstalledAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "安装日期格式错误"}) + return + } + usr := currentUser(c) + creatorID := strings.TrimSpace(req.CreatorUserID) + if creatorID == "" && usr != nil { + creatorID = usr.ID + } + d := &model.Device{ + Name: req.Name, + ModelID: req.ModelID, + SerialNo: req.SerialNo, + StatusCode: req.StatusCode, + PointID: req.PointID, + InstalledImage: req.InstalledImage, + Channel1ParamID: req.Channel1ParamID, + Channel2ParamID: req.Channel2ParamID, + Channel3ParamID: req.Channel3ParamID, + CreatorUserID: creatorID, + Remark: req.Remark, + } + if t != nil { + d.InstalledAt = t + } + out, err := service.DeviceCreateDevice(d) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"device": out}) +} + +func DeviceUpdate(c *gin.Context) { + id := c.Param("id") + var req DeviceUpdateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + patch := &model.Device{} + if req.Name != nil { + patch.Name = *req.Name + } + if req.ModelID != nil { + patch.ModelID = *req.ModelID + } + if req.SerialNo != nil { + patch.SerialNo = *req.SerialNo + } + if req.StatusCode != nil { + patch.StatusCode = *req.StatusCode + } + if req.PointID != nil { + patch.PointID = *req.PointID + } + if req.InstalledAt != nil { + t, err := parseDateOnly(req.InstalledAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "安装日期格式错误"}) + return + } + patch.InstalledAt = t + } + if req.InstalledImage != nil { + patch.InstalledImage = *req.InstalledImage + } + if req.Channel1ParamID != nil { + patch.Channel1ParamID = *req.Channel1ParamID + } + if req.Channel2ParamID != nil { + patch.Channel2ParamID = *req.Channel2ParamID + } + if req.Channel3ParamID != nil { + patch.Channel3ParamID = *req.Channel3ParamID + } + if req.CreatorUserID != nil { + patch.CreatorUserID = *req.CreatorUserID + } + if req.Remark != nil { + patch.Remark = *req.Remark + } + + out, err := service.DeviceUpdateDevice(id, patch) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"device": out}) +} + +func DeviceDelete(c *gin.Context) { + id := c.Param("id") + if err := service.DeviceDeleteDevice(id); err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "删除失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"data": "ok"}) +} + +func DeviceGet(c *gin.Context) { + id := c.Param("id") + d, err := service.DeviceGetDevice(id) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"device": d}) +} + +func DeviceList(c *gin.Context) { + page, size := pageAndSize(c) + keyword := c.Query("keyword") + var kw *string + if keyword != "" { + kw = &keyword + } + items, total, err := service.DeviceListDevices(kw, page, size) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{ + "items": items, + "total": total, + "page": page, + "size": size, + }) +} diff --git a/handler/menu-handler.go b/handler/menu-handler.go new file mode 100644 index 0000000..d42b7bc --- /dev/null +++ b/handler/menu-handler.go @@ -0,0 +1,27 @@ +package handler + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "myschools.me/heritage/heritage-api/service" +) + +func MenuList(c *gin.Context) { + usr := currentUser(c) + if usr == nil { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ + "data": "无效TOKEN, 请重新登录!", + }) + return + } + + menus, err := service.MenuList(usr) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{ + "items": menus, + }) +} diff --git a/handler/org-handler.go b/handler/org-handler.go new file mode 100644 index 0000000..9952c96 --- /dev/null +++ b/handler/org-handler.go @@ -0,0 +1,112 @@ +package handler + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "myschools.me/heritage/heritage-api/service" +) + +type OrgCreateRequest struct { + ParentID string `json:"parentId"` + Name string `json:"name"` + Enabled *bool `json:"enabled"` + Sort *int `json:"sort"` + Remark string `json:"remark"` +} + +type OrgUpdateRequest struct { + ParentID *string `json:"parentId"` + Name *string `json:"name"` + Enabled *bool `json:"enabled"` + Sort *int `json:"sort"` + Remark *string `json:"remark"` +} + +func OrgCreate(c *gin.Context) { + var req OrgCreateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + enabled := true + if req.Enabled != nil { + enabled = *req.Enabled + } + sort := 0 + if req.Sort != nil { + sort = *req.Sort + } + o, err := service.OrgCreateOrg(req.ParentID, req.Name, enabled, sort, req.Remark) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"org": o}) +} + +func OrgUpdate(c *gin.Context) { + id := c.Param("id") + var req OrgUpdateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + o, err := service.OrgUpdateOrg(id, req.ParentID, req.Name, req.Enabled, req.Sort, req.Remark) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"org": o}) +} + +func OrgDelete(c *gin.Context) { + id := c.Param("id") + if err := service.OrgDeleteOrg(id); err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "删除失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"data": "ok"}) +} + +func OrgGet(c *gin.Context) { + id := c.Param("id") + o, err := service.OrgGetOrg(id) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"org": o}) +} + +func OrgList(c *gin.Context) { + page, size := pageAndSize(c) + parentID := c.Query("parentId") + var pid *string + if parentID != "" { + pid = &parentID + } + items, total, err := service.OrgListOrgs(pid, page, size) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{ + "items": items, + "total": total, + "page": page, + "size": size, + }) +} diff --git a/handler/point-handler.go b/handler/point-handler.go new file mode 100644 index 0000000..b836cad --- /dev/null +++ b/handler/point-handler.go @@ -0,0 +1,187 @@ +package handler + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/service" +) + +type PointCreateRequest struct { + Name string `json:"name"` + X float64 `json:"x"` + Y float64 `json:"y"` + Z float64 `json:"z"` + LocationObjectID string `json:"locationObjectId"` + StatusCode string `json:"statusCode"` + Image string `json:"image"` + ObjectID string `json:"objectId"` + IndicatorID string `json:"indicatorId"` + CreatorPersonID string `json:"creatorPersonId"` + SensorID string `json:"sensorId"` + CreatedTaskID string `json:"createdTaskId"` + Remark string `json:"remark"` +} + +type PointUpdateRequest struct { + Name *string `json:"name"` + X *float64 `json:"x"` + Y *float64 `json:"y"` + Z *float64 `json:"z"` + LocationObjectID *string `json:"locationObjectId"` + StatusCode *string `json:"statusCode"` + Image *string `json:"image"` + ObjectID *string `json:"objectId"` + IndicatorID *string `json:"indicatorId"` + CreatorPersonID *string `json:"creatorPersonId"` + SensorID *string `json:"sensorId"` + CreatedTaskID *string `json:"createdTaskId"` + Remark *string `json:"remark"` +} + +func PointCreate(c *gin.Context) { + var req PointCreateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + p := &model.Point{ + Name: req.Name, + X: req.X, + Y: req.Y, + Z: req.Z, + LocationObjectID: req.LocationObjectID, + StatusCode: req.StatusCode, + Image: req.Image, + ObjectID: req.ObjectID, + IndicatorID: req.IndicatorID, + CreatorPersonID: req.CreatorPersonID, + SensorID: req.SensorID, + CreatedTaskID: req.CreatedTaskID, + Remark: req.Remark, + } + out, err := service.PointCreatePoint(p) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"point": out}) +} + +func PointUpdate(c *gin.Context) { + id := c.Param("id") + var req PointUpdateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + patch := &model.Point{} + if req.Name != nil { + patch.Name = *req.Name + } + if req.X != nil { + patch.X = *req.X + } + if req.Y != nil { + patch.Y = *req.Y + } + if req.Z != nil { + patch.Z = *req.Z + } + if req.LocationObjectID != nil { + patch.LocationObjectID = *req.LocationObjectID + } + if req.StatusCode != nil { + patch.StatusCode = *req.StatusCode + } + if req.Image != nil { + patch.Image = *req.Image + } + if req.ObjectID != nil { + patch.ObjectID = *req.ObjectID + } + if req.IndicatorID != nil { + patch.IndicatorID = *req.IndicatorID + } + if req.CreatorPersonID != nil { + patch.CreatorPersonID = *req.CreatorPersonID + } + if req.SensorID != nil { + patch.SensorID = *req.SensorID + } + if req.CreatedTaskID != nil { + patch.CreatedTaskID = *req.CreatedTaskID + } + if req.Remark != nil { + patch.Remark = *req.Remark + } + + out, err := service.PointUpdatePoint(id, patch) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"point": out}) +} + +func PointDelete(c *gin.Context) { + id := c.Param("id") + if err := service.PointDeletePoint(id); err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "删除失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"data": "ok"}) +} + +func PointGet(c *gin.Context) { + id := c.Param("id") + p, err := service.PointGetPoint(id) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"point": p}) +} + +func PointList(c *gin.Context) { + page, size := pageAndSize(c) + objectID := c.Query("objectId") + indicatorID := c.Query("indicatorId") + keyword := c.Query("keyword") + var oid *string + if objectID != "" { + oid = &objectID + } + var iid *string + if indicatorID != "" { + iid = &indicatorID + } + var kw *string + if keyword != "" { + kw = &keyword + } + items, total, err := service.PointListPoints(oid, iid, kw, page, size) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{ + "items": items, + "total": total, + "page": page, + "size": size, + }) +} diff --git a/handler/project-handler.go b/handler/project-handler.go new file mode 100644 index 0000000..b72e5f3 --- /dev/null +++ b/handler/project-handler.go @@ -0,0 +1,256 @@ +package handler + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/service" +) + +type ProjectCreateRequest struct { + ProjectNo string `json:"projectNo"` + Name string `json:"name"` + TypeCode string `json:"typeCode"` + BuildingIDs string `json:"buildingIds"` + StatusCode string `json:"statusCode"` + + ResponsibleOrgID string `json:"responsibleOrgId"` + ImplementOrgIDs string `json:"implementOrgIds"` + LeaderPersonID string `json:"leaderPersonId"` + ParticipantPersonIDs string `json:"participantPersonIds"` + + StartAt string `json:"startAt"` + EndAt string `json:"endAt"` + CompletedAt string `json:"completedAt"` + + Description string `json:"description"` + Attachments string `json:"attachments"` + OrgID string `json:"orgId"` + CreatorUserID string `json:"creatorUserId"` + Remark string `json:"remark"` +} + +type ProjectUpdateRequest struct { + ProjectNo *string `json:"projectNo"` + Name *string `json:"name"` + TypeCode *string `json:"typeCode"` + BuildingIDs *string `json:"buildingIds"` + StatusCode *string `json:"statusCode"` + + ResponsibleOrgID *string `json:"responsibleOrgId"` + ImplementOrgIDs *string `json:"implementOrgIds"` + LeaderPersonID *string `json:"leaderPersonId"` + ParticipantPersonIDs *string `json:"participantPersonIds"` + + StartAt *string `json:"startAt"` + EndAt *string `json:"endAt"` + CompletedAt *string `json:"completedAt"` + + Description *string `json:"description"` + Attachments *string `json:"attachments"` + OrgID *string `json:"orgId"` + CreatorUserID *string `json:"creatorUserId"` + Remark *string `json:"remark"` +} + +func ProjectCreate(c *gin.Context) { + var req ProjectCreateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + + startAt, err := parseDateOnly(&req.StartAt) + if err != nil || startAt == nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "开始时间格式错误"}) + return + } + endAt, err := parseDateOnly(&req.EndAt) + if err != nil || endAt == nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "结束时间格式错误"}) + return + } + completedAt, err := parseDateOnly(&req.CompletedAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "实际完成时间格式错误"}) + return + } + + usr := currentUser(c) + creatorID := req.CreatorUserID + if creatorID == "" && usr != nil { + creatorID = usr.ID + } + + p := &model.Project{ + ProjectNo: req.ProjectNo, + Name: req.Name, + TypeCode: req.TypeCode, + BuildingIDs: req.BuildingIDs, + StatusCode: req.StatusCode, + ResponsibleOrgID: req.ResponsibleOrgID, + ImplementOrgIDs: req.ImplementOrgIDs, + LeaderPersonID: req.LeaderPersonID, + ParticipantPersonIDs: req.ParticipantPersonIDs, + StartAt: *startAt, + EndAt: *endAt, + Description: req.Description, + Attachments: req.Attachments, + OrgID: req.OrgID, + CreatorUserID: creatorID, + Remark: req.Remark, + } + if completedAt != nil { + p.CompletedAt = completedAt + } + + out, err := service.ProjectCreateProject(p) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"project": out}) +} + +func ProjectUpdate(c *gin.Context) { + id := c.Param("id") + var req ProjectUpdateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + + patch := &model.Project{} + if req.ProjectNo != nil { + patch.ProjectNo = *req.ProjectNo + } + if req.Name != nil { + patch.Name = *req.Name + } + if req.TypeCode != nil { + patch.TypeCode = *req.TypeCode + } + if req.BuildingIDs != nil { + patch.BuildingIDs = *req.BuildingIDs + } + if req.StatusCode != nil { + patch.StatusCode = *req.StatusCode + } + if req.ResponsibleOrgID != nil { + patch.ResponsibleOrgID = *req.ResponsibleOrgID + } + if req.ImplementOrgIDs != nil { + patch.ImplementOrgIDs = *req.ImplementOrgIDs + } + if req.LeaderPersonID != nil { + patch.LeaderPersonID = *req.LeaderPersonID + } + if req.ParticipantPersonIDs != nil { + patch.ParticipantPersonIDs = *req.ParticipantPersonIDs + } + if req.StartAt != nil { + t, err := parseDateOnly(req.StartAt) + if err != nil || t == nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "开始时间格式错误"}) + return + } + patch.StartAt = *t + } + if req.EndAt != nil { + t, err := parseDateOnly(req.EndAt) + if err != nil || t == nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "结束时间格式错误"}) + return + } + patch.EndAt = *t + } + if req.CompletedAt != nil { + t, err := parseDateOnly(req.CompletedAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "实际完成时间格式错误"}) + return + } + patch.CompletedAt = t + } + if req.Description != nil { + patch.Description = *req.Description + } + if req.Attachments != nil { + patch.Attachments = *req.Attachments + } + if req.OrgID != nil { + patch.OrgID = *req.OrgID + } + if req.CreatorUserID != nil { + patch.CreatorUserID = *req.CreatorUserID + } + if req.Remark != nil { + patch.Remark = *req.Remark + } + + out, err := service.ProjectUpdateProject(id, patch) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"project": out}) +} + +func ProjectDelete(c *gin.Context) { + id := c.Param("id") + if err := service.ProjectDelete(id); err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "删除失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"data": "ok"}) +} + +func ProjectGet(c *gin.Context) { + id := c.Param("id") + p, err := service.ProjectGet(id) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"project": p}) +} + +func ProjectList(c *gin.Context) { + page, size := pageAndSize(c) + orgID := c.Query("orgId") + keyword := c.Query("keyword") + + var oid *string + if orgID != "" { + oid = &orgID + } + var kw *string + if keyword != "" { + kw = &keyword + } + + items, total, err := service.ProjectList(oid, kw, page, size) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{ + "items": items, + "total": total, + "page": page, + "size": size, + }) +} diff --git a/handler/readme.md b/handler/readme.md new file mode 100644 index 0000000..927b2f8 --- /dev/null +++ b/handler/readme.md @@ -0,0 +1,2 @@ +1. 当前层出现错误需要返回给前端显示,注意错误信息不能包含敏感信息 +2. 当前层主要控制请求参数的校验和响应格式 \ No newline at end of file diff --git a/handler/task-handler.go b/handler/task-handler.go new file mode 100644 index 0000000..b4d38e9 --- /dev/null +++ b/handler/task-handler.go @@ -0,0 +1,246 @@ +package handler + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/service" +) + +type TaskCreateRequest struct { + Name string `json:"name"` + OrgID string `json:"orgId"` + ProjectID string `json:"projectId"` + ObjectIDs string `json:"objectIds"` + TypeCode string `json:"typeCode"` + IndicatorIDs string `json:"indicatorIds"` + PackageIDs string `json:"packageIds"` + ExecutorPersonID string `json:"executorPersonId"` + ConfirmerPersonID string `json:"confirmerPersonId"` + StartAt string `json:"startAt"` + EndAt string `json:"endAt"` + ExecutedAt string `json:"executedAt"` + SinglePoint bool `json:"singlePoint"` + PeriodicType int `json:"periodicType"` + PeriodDays int `json:"periodDays"` + StatusCode string `json:"statusCode"` + Remark string `json:"remark"` +} + +type TaskUpdateRequest struct { + Name *string `json:"name"` + OrgID *string `json:"orgId"` + ProjectID *string `json:"projectId"` + ObjectIDs *string `json:"objectIds"` + TypeCode *string `json:"typeCode"` + IndicatorIDs *string `json:"indicatorIds"` + PackageIDs *string `json:"packageIds"` + ExecutorPersonID *string `json:"executorPersonId"` + ConfirmerPersonID *string `json:"confirmerPersonId"` + StartAt *string `json:"startAt"` + EndAt *string `json:"endAt"` + ExecutedAt *string `json:"executedAt"` + SinglePoint *bool `json:"singlePoint"` + PeriodicType *int `json:"periodicType"` + PeriodDays *int `json:"periodDays"` + StatusCode *string `json:"statusCode"` + Remark *string `json:"remark"` +} + +func TaskCreate(c *gin.Context) { + var req TaskCreateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + + startAt, err := parseDateOnly(&req.StartAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "开始时间格式错误"}) + return + } + endAt, err := parseDateOnly(&req.EndAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "结束时间格式错误"}) + return + } + executedAt, err := parseDateOnly(&req.ExecutedAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "实际实施时间格式错误"}) + return + } + + t := &model.Task{ + Name: req.Name, + OrgID: req.OrgID, + ProjectID: req.ProjectID, + ObjectIDs: req.ObjectIDs, + TypeCode: req.TypeCode, + IndicatorIDs: req.IndicatorIDs, + PackageIDs: req.PackageIDs, + ExecutorPersonID: req.ExecutorPersonID, + ConfirmerPersonID: req.ConfirmerPersonID, + SinglePoint: req.SinglePoint, + PeriodicType: req.PeriodicType, + PeriodDays: req.PeriodDays, + StatusCode: req.StatusCode, + Remark: req.Remark, + } + if startAt != nil { + t.StartAt = *startAt + } + if endAt != nil { + t.EndAt = *endAt + } + if executedAt != nil { + t.ExecutedAt = executedAt + } + + out, err := service.TaskCreateTask(t) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"task": out}) +} + +func TaskUpdate(c *gin.Context) { + id := c.Param("id") + var req TaskUpdateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + + patch := &model.Task{} + if req.Name != nil { + patch.Name = *req.Name + } + if req.OrgID != nil { + patch.OrgID = *req.OrgID + } + if req.ProjectID != nil { + patch.ProjectID = *req.ProjectID + } + if req.ObjectIDs != nil { + patch.ObjectIDs = *req.ObjectIDs + } + if req.TypeCode != nil { + patch.TypeCode = *req.TypeCode + } + if req.IndicatorIDs != nil { + patch.IndicatorIDs = *req.IndicatorIDs + } + if req.PackageIDs != nil { + patch.PackageIDs = *req.PackageIDs + } + if req.ExecutorPersonID != nil { + patch.ExecutorPersonID = *req.ExecutorPersonID + } + if req.ConfirmerPersonID != nil { + patch.ConfirmerPersonID = *req.ConfirmerPersonID + } + if req.StartAt != nil { + t, err := parseDateOnly(req.StartAt) + if err != nil || t == nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "开始时间格式错误"}) + return + } + patch.StartAt = *t + } + if req.EndAt != nil { + t, err := parseDateOnly(req.EndAt) + if err != nil || t == nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "结束时间格式错误"}) + return + } + patch.EndAt = *t + } + if req.ExecutedAt != nil { + t, err := parseDateOnly(req.ExecutedAt) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "实际实施时间格式错误"}) + return + } + patch.ExecutedAt = t + } + if req.SinglePoint != nil { + patch.SinglePoint = *req.SinglePoint + } + if req.PeriodicType != nil { + patch.PeriodicType = *req.PeriodicType + } + if req.PeriodDays != nil { + patch.PeriodDays = *req.PeriodDays + } + if req.StatusCode != nil { + patch.StatusCode = *req.StatusCode + } + if req.Remark != nil { + patch.Remark = *req.Remark + } + + out, err := service.TaskUpdateTask(id, patch) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"task": out}) +} + +func TaskDelete(c *gin.Context) { + id := c.Param("id") + if err := service.TaskDeleteTask(id); err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "删除失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"data": "ok"}) +} + +func TaskGet(c *gin.Context) { + id := c.Param("id") + t, err := service.TaskGetTask(id) + if err != nil { + if err == service.ErrNotFound { + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"}) + return + } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{"task": t}) +} + +func TaskList(c *gin.Context) { + page, size := pageAndSize(c) + projectID := c.Query("projectId") + keyword := c.Query("keyword") + var pid *string + if projectID != "" { + pid = &projectID + } + var kw *string + if keyword != "" { + kw = &keyword + } + items, total, err := service.TaskListTasks(pid, kw, page, size) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"}) + return + } + c.JSON(http.StatusOK, gin.H{ + "items": items, + "total": total, + "page": page, + "size": size, + }) +} diff --git a/main.go b/main.go index e55b0d8..2ab3168 100644 --- a/main.go +++ b/main.go @@ -6,9 +6,11 @@ import ( "github.com/sirupsen/logrus" "myschools.me/heritage/heritage-api/gin" + "myschools.me/heritage/heritage-api/service" ) func main() { + service.Bootstrap() gin.Service() // 等待服务关闭信号,使用通道 diff --git a/model/data-model.go b/model/data-model.go new file mode 100644 index 0000000..bb28beb --- /dev/null +++ b/model/data-model.go @@ -0,0 +1,20 @@ +package model + +import "time" + +type DataRecord struct { + ID string `json:"id" gorm:"type:varchar(32);primaryKey"` + + Value string `json:"value" gorm:"type:text;not null"` + IndicatorParamID string `json:"indicatorParamId" gorm:"type:varchar(32);not null;index"` + PointID string `json:"pointId" gorm:"type:varchar(32);not null;index"` + TaskID string `json:"taskId" gorm:"type:varchar(32);not null;index"` + CollectedAt time.Time `json:"collectedAt" gorm:"not null;index"` + + CreatorUserID string `json:"creatorUserId" gorm:"type:varchar(32);not null;index"` + + Remark string `json:"remark" gorm:"type:varchar(255);default:''"` + Deleted bool `json:"deleted" gorm:"not null;default:false;index"` + CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"` + UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"` +} diff --git a/model/device-model.go b/model/device-model.go new file mode 100644 index 0000000..8515d09 --- /dev/null +++ b/model/device-model.go @@ -0,0 +1,45 @@ +package model + +import "time" + +type Device struct { + ID string `json:"id" gorm:"type:varchar(32);primaryKey"` + + Name string `json:"name" gorm:"type:varchar(128);not null"` + ModelID string `json:"modelId" gorm:"type:varchar(32);index"` + SerialNo string `json:"serialNo" gorm:"type:varchar(64);index"` + StatusCode string `json:"statusCode" gorm:"type:varchar(64);not null;index"` + PointID string `json:"pointId" gorm:"type:varchar(32);index"` + InstalledAt *time.Time `json:"installedAt"` + InstalledImage string `json:"installedImage" gorm:"type:text;not null"` + + Channel1ParamID string `json:"channel1ParamId" gorm:"type:varchar(32);index"` + Channel2ParamID string `json:"channel2ParamId" gorm:"type:varchar(32);index"` + Channel3ParamID string `json:"channel3ParamId" gorm:"type:varchar(32);index"` + + CreatorUserID string `json:"creatorUserId" gorm:"type:varchar(32);not null;index"` + + Remark string `json:"remark" gorm:"type:varchar(255);default:''"` + Deleted bool `json:"deleted" gorm:"not null;default:false;index"` + CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"` + UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"` +} + +type DeviceIngestData struct { + ID string `json:"id" gorm:"type:varchar(32);primaryKey"` + + DeviceID string `json:"deviceId" gorm:"type:varchar(32);not null;index"` + PointID string `json:"pointId" gorm:"type:varchar(32);not null;index"` + + Raw1 string `json:"raw1" gorm:"type:varchar(64);default:''"` + Raw2 string `json:"raw2" gorm:"type:varchar(64);default:''"` + Raw3 string `json:"raw3" gorm:"type:varchar(64);default:''"` + + Value1 string `json:"value1" gorm:"type:varchar(64);default:''"` + Value2 string `json:"value2" gorm:"type:varchar(64);default:''"` + Value3 string `json:"value3" gorm:"type:varchar(64);default:''"` + + CollectedAt time.Time `json:"collectedAt" gorm:"not null;index"` + + Deleted bool `json:"deleted" gorm:"not null;default:false;index"` +} diff --git a/model/menu-model.go b/model/menu-model.go new file mode 100644 index 0000000..66d3514 --- /dev/null +++ b/model/menu-model.go @@ -0,0 +1,15 @@ +package model + +// Menu represents a menu item + +// swagger:model Menu +type Menu struct { + ID uint `gorm:"primarykey" json:"id"` + ParentID uint `json:"parentId"` + Name string `json:"name"` + Icon string `json:"icon"` + Path string `json:"path"` + Sort int `json:"sort"` + PermissionCode string `json:"permissionCode"` + Children []*Menu `json:"children" gorm:"-"` +} diff --git a/model/org-model.go b/model/org-model.go new file mode 100644 index 0000000..b83add4 --- /dev/null +++ b/model/org-model.go @@ -0,0 +1,15 @@ +package model + +import "time" + +type Org struct { + ID string `json:"id" gorm:"type:varchar(32);primaryKey"` + ParentID string `json:"parentId" gorm:"type:varchar(32);index"` + Name string `json:"name" gorm:"type:varchar(128);not null;index"` + Enabled bool `json:"enabled" gorm:"not null;default:true;index"` + Sort int `json:"sort" gorm:"not null;default:0"` + Remark string `json:"remark" gorm:"type:varchar(255);default:''"` + Deleted bool `json:"deleted" gorm:"not null;default:false;index"` + CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"` + UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"` +} diff --git a/model/permission-model.go b/model/permission-model.go index a88d617..b66e66a 100644 --- a/model/permission-model.go +++ b/model/permission-model.go @@ -1,7 +1,7 @@ package model type Permission struct { - ID string `gorm:"type:varchar(32);primaryKey"` + ID uint `gorm:"primaryKey"` RoleID string `gorm:"type:varchar(32);not null;index"` Code string `gorm:"type:varchar(128);not null;index"` Name string `gorm:"type:varchar(128);not null"` diff --git a/model/point-model.go b/model/point-model.go new file mode 100644 index 0000000..e6805be --- /dev/null +++ b/model/point-model.go @@ -0,0 +1,29 @@ +package model + +import "time" + +type Point struct { + ID string `json:"id" gorm:"type:varchar(32);primaryKey"` + + Name string `json:"name" gorm:"type:varchar(128);not null"` + X float64 `json:"x" gorm:"not null;default:0"` + Y float64 `json:"y" gorm:"not null;default:0"` + Z float64 `json:"z" gorm:"not null;default:0"` + + LocationObjectID string `json:"locationObjectId" gorm:"type:varchar(32);index"` + StatusCode string `json:"statusCode" gorm:"type:varchar(64);not null;index"` + Image string `json:"image" gorm:"type:text;not null"` + + ObjectID string `json:"objectId" gorm:"type:varchar(32);not null;index"` + + IndicatorID string `json:"indicatorId" gorm:"type:varchar(32);not null;index"` + + CreatorPersonID string `json:"creatorPersonId" gorm:"type:varchar(32);not null;index"` + SensorID string `json:"sensorId" gorm:"type:varchar(32);index"` + CreatedTaskID string `json:"createdTaskId" gorm:"type:varchar(32);index"` + + Remark string `json:"remark" gorm:"type:varchar(255);default:''"` + Deleted bool `json:"deleted" gorm:"not null;default:false;index"` + CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"` + UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"` +} diff --git a/model/project-model.go b/model/project-model.go new file mode 100644 index 0000000..80535f1 --- /dev/null +++ b/model/project-model.go @@ -0,0 +1,32 @@ +package model + +import "time" + +type Project struct { + ID string `json:"id" gorm:"type:varchar(32);primaryKey"` + + ProjectNo string `json:"projectNo" gorm:"type:varchar(32);not null;uniqueIndex"` + Name string `json:"name" gorm:"type:varchar(100);not null"` + TypeCode string `json:"typeCode" gorm:"type:varchar(64);not null;index"` + BuildingIDs string `json:"buildingIds" gorm:"type:text;not null"` + StatusCode string `json:"statusCode" gorm:"type:varchar(64);not null;index"` + + ResponsibleOrgID string `json:"responsibleOrgId" gorm:"type:varchar(32);not null;index"` + ImplementOrgIDs string `json:"implementOrgIds" gorm:"type:text;not null"` + + LeaderPersonID string `json:"leaderPersonId" gorm:"type:varchar(32);not null;index"` + ParticipantPersonIDs string `json:"participantPersonIds" gorm:"type:text;not null"` + + StartAt time.Time `json:"startAt" gorm:"not null;index"` + EndAt time.Time `json:"endAt" gorm:"not null;index"` + CompletedAt *time.Time `json:"completedAt"` + Description string `json:"description" gorm:"type:text;not null"` + Attachments string `json:"attachments" gorm:"type:text;not null"` + OrgID string `json:"orgId" gorm:"type:varchar(32);not null;index"` + CreatorUserID string `json:"creatorUserId" gorm:"type:varchar(32);not null;index"` + + Remark string `json:"remark" gorm:"type:varchar(255);default:''"` + Deleted bool `json:"deleted" gorm:"not null;default:false;index"` + CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"` + UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"` +} diff --git a/model/task-model.go b/model/task-model.go new file mode 100644 index 0000000..b6fbfc1 --- /dev/null +++ b/model/task-model.go @@ -0,0 +1,32 @@ +package model + +import "time" + +type Task struct { + ID string `json:"id" gorm:"type:varchar(32);primaryKey"` + + Name string `json:"name" gorm:"type:varchar(128);default:''"` + OrgID string `json:"orgId" gorm:"type:varchar(32);index"` + ProjectID string `json:"projectId" gorm:"type:varchar(32);index"` + ObjectIDs string `json:"objectIds" gorm:"type:text;not null"` + TypeCode string `json:"typeCode" gorm:"type:varchar(64);not null;index"` + IndicatorIDs string `json:"indicatorIds" gorm:"type:text;not null"` + PackageIDs string `json:"packageIds" gorm:"type:text;not null"` + + ExecutorPersonID string `json:"executorPersonId" gorm:"type:varchar(32);not null;index"` + ConfirmerPersonID string `json:"confirmerPersonId" gorm:"type:varchar(32);not null;index"` + + StartAt time.Time `json:"startAt" gorm:"index"` + EndAt time.Time `json:"endAt" gorm:"index"` + ExecutedAt *time.Time `json:"executedAt"` + + SinglePoint bool `json:"singlePoint" gorm:"not null;default:false"` + PeriodicType int `json:"periodicType" gorm:"not null;default:0;index"` + PeriodDays int `json:"periodDays" gorm:"not null;default:0"` + StatusCode string `json:"statusCode" gorm:"type:varchar(64);not null;index"` + + Remark string `json:"remark" gorm:"type:varchar(255);default:''"` + Deleted bool `json:"deleted" gorm:"not null;default:false;index"` + CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"` + UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"` +} diff --git a/mysql/data-mysql.go b/mysql/data-mysql.go new file mode 100644 index 0000000..45bb8da --- /dev/null +++ b/mysql/data-mysql.go @@ -0,0 +1,88 @@ +package mysql + +import ( + "errors" + "time" + + "gorm.io/gorm" + "myschools.me/heritage/heritage-api/model" +) + +func DataRecordCreate(r *model.DataRecord) error { + db, err := newDB() + if err != nil { + return err + } + return db.Create(r).Error +} + +func DataRecordUpdate(r *model.DataRecord) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.DataRecord{}).Where("id = ? AND deleted = false", r.ID).Updates(r) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func DataRecordDelete(id *string) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.DataRecord{}).Where("id = ? AND deleted = false", *id).Update("deleted", true) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func DataRecordByID(id *string) (*model.DataRecord, bool, error) { + db, err := newDB() + if err != nil { + return nil, false, err + } + var r model.DataRecord + if err := db.Where("id = ? AND deleted = false", *id).First(&r).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, false, nil + } + return nil, false, err + } + return &r, true, nil +} + +func DataRecordList(pointID, taskID *string, startAt, endAt *time.Time, offset, limit *int) ([]model.DataRecord, int64, error) { + db, err := newDB() + if err != nil { + return nil, 0, err + } + + q := db.Model(&model.DataRecord{}).Where("deleted = false") + if pointID != nil && *pointID != "" { + q = q.Where("point_id = ?", *pointID) + } + if taskID != nil && *taskID != "" { + q = q.Where("task_id = ?", *taskID) + } + if startAt != nil { + q = q.Where("collected_at >= ?", *startAt) + } + if endAt != nil { + q = q.Where("collected_at <= ?", *endAt) + } + + var total int64 + if err := q.Count(&total).Error; err != nil { + return nil, 0, err + } + + var items []model.DataRecord + if err := q.Order("collected_at desc").Offset(*offset).Limit(*limit).Find(&items).Error; err != nil { + return nil, 0, err + } + return items, total, nil +} diff --git a/mysql/device-mysql.go b/mysql/device-mysql.go new file mode 100644 index 0000000..c981778 --- /dev/null +++ b/mysql/device-mysql.go @@ -0,0 +1,78 @@ +package mysql + +import ( + "errors" + + "gorm.io/gorm" + "myschools.me/heritage/heritage-api/model" +) + +func DeviceCreate(d *model.Device) error { + db, err := newDB() + if err != nil { + return err + } + return db.Create(d).Error +} + +func DeviceUpdate(d *model.Device) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.Device{}).Where("id = ? AND deleted = false", d.ID).Updates(d) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func DeviceDelete(id *string) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.Device{}).Where("id = ? AND deleted = false", *id).Update("deleted", true) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func DeviceByID(id *string) (*model.Device, bool, error) { + db, err := newDB() + if err != nil { + return nil, false, err + } + var d model.Device + if err := db.Where("id = ? AND deleted = false", *id).First(&d).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, false, nil + } + return nil, false, err + } + return &d, true, nil +} + +func DeviceList(keyword *string, offset, limit *int) ([]model.Device, int64, error) { + db, err := newDB() + if err != nil { + return nil, 0, err + } + q := db.Model(&model.Device{}).Where("deleted = false") + if keyword != nil && *keyword != "" { + kw := "%" + *keyword + "%" + q = q.Where("name LIKE ? OR serial_no LIKE ?", kw, kw) + } + + var total int64 + if err := q.Count(&total).Error; err != nil { + return nil, 0, err + } + + var items []model.Device + if err := q.Order("created_at desc").Offset(*offset).Limit(*limit).Find(&items).Error; err != nil { + return nil, 0, err + } + return items, total, nil +} diff --git a/mysql/menu-mysql.go b/mysql/menu-mysql.go new file mode 100644 index 0000000..2723083 --- /dev/null +++ b/mysql/menu-mysql.go @@ -0,0 +1,50 @@ +package mysql + +import ( + "myschools.me/heritage/heritage-api/model" +) + +func MenuList() ([]model.Menu, error) { + db, err := newDB() + if err != nil { + return nil, err + } + + var menus []model.Menu + if err := db.Order("sort ASC").Find(&menus).Error; err != nil { + return nil, err + } + return menus, nil +} + +func MenuCreate(obj *model.Menu) (*model.Menu, error) { + db, err := newDB() + if err != nil { + return nil, err + } + if err := db.Create(obj).Error; err != nil { + return nil, err + } + return obj, nil +} + +func MenuCount() (*int64, error) { + db, err := newDB() + if err != nil { + return nil, err + } + + var count int64 + if err := db.Model(&model.Menu{}).Count(&count).Error; err != nil { + return nil, err + } + return &count, nil +} + +func MenuCreateBatch(menus []model.Menu) error { + db, err := newDB() + if err != nil { + return err + } + return db.Create(&menus).Error +} diff --git a/mysql/org-mysql.go b/mysql/org-mysql.go new file mode 100644 index 0000000..ab7746c --- /dev/null +++ b/mysql/org-mysql.go @@ -0,0 +1,74 @@ +package mysql + +import ( + "errors" + + "gorm.io/gorm" + "myschools.me/heritage/heritage-api/model" +) + +func OrgCreate(org *model.Org) error { + db, err := newDB() + if err != nil { + return err + } + return db.Create(org).Error +} + +func OrgUpdate(org *model.Org) error { + db, err := newDB() + if err != nil { + return err + } + return db.Model(&model.Org{}).Where("id = ? AND deleted = false", org.ID).Updates(org).Error +} + +func OrgDelete(id *string) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.Org{}).Where("id = ? AND deleted = false", *id).Update("deleted", true) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func OrgByID(id *string) (*model.Org, bool, error) { + db, err := newDB() + if err != nil { + return nil, false, err + } + var o model.Org + if err := db.Where("id = ? AND deleted = false", *id).First(&o).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, false, nil + } + return nil, false, err + } + return &o, true, nil +} + +func OrgList(parentID *string, offset, limit *int) ([]model.Org, int64, error) { + db, err := newDB() + if err != nil { + return nil, 0, err + } + + q := db.Model(&model.Org{}).Where("deleted = false") + if parentID != nil { + q = q.Where("parent_id = ?", *parentID) + } + + var total int64 + if err := q.Count(&total).Error; err != nil { + return nil, 0, err + } + + var items []model.Org + if err := q.Order("sort asc, created_at desc").Offset(*offset).Limit(*limit).Find(&items).Error; err != nil { + return nil, 0, err + } + return items, total, nil +} diff --git a/mysql/permission-mysql.go b/mysql/permission-mysql.go index 38c107f..0d082b6 100644 --- a/mysql/permission-mysql.go +++ b/mysql/permission-mysql.go @@ -2,7 +2,7 @@ package mysql import "myschools.me/heritage/heritage-api/model" -func permissionDefined(permissionCode *string) (bool, error) { +func PermissionDefined(permissionCode *string) (bool, error) { db, err := newDB() if err != nil { return false, err @@ -15,25 +15,26 @@ func permissionDefined(permissionCode *string) (bool, error) { return count > 0, nil } -func roleHasPermission(roleID, permissionCode *string) (bool, error) { +func PermissionCount() (*int64, error) { db, err := newDB() if err != nil { - return false, err + return nil, err } var count int64 - if err := db.Model(&model.Permission{}). - Where("role_id = ? AND (code = ? OR code = ?)", *roleID, *permissionCode, "*"). - Count(&count).Error; err != nil { - return false, err + if err := db.Model(&model.Permission{}).Count(&count).Error; err != nil { + return nil, err } - return count > 0, nil + return &count, nil } -func PermissionDefined(permissionCode *string) (bool, error) { - return permissionDefined(permissionCode) -} - -func RoleHasPermission(roleID, permissionCode *string) (bool, error) { - return roleHasPermission(roleID, permissionCode) +func PermissionCreate(obj *model.Permission) (*model.Permission, error) { + db, err := newDB() + if err != nil { + return nil, err + } + if err := db.Create(obj).Error; err != nil { + return nil, err + } + return obj, nil } diff --git a/mysql/point-mysql.go b/mysql/point-mysql.go new file mode 100644 index 0000000..28d0dad --- /dev/null +++ b/mysql/point-mysql.go @@ -0,0 +1,84 @@ +package mysql + +import ( + "errors" + + "gorm.io/gorm" + "myschools.me/heritage/heritage-api/model" +) + +func PointCreate(p *model.Point) error { + db, err := newDB() + if err != nil { + return err + } + return db.Create(p).Error +} + +func PointUpdate(p *model.Point) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.Point{}).Where("id = ? AND deleted = false", p.ID).Updates(p) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func PointDelete(id *string) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.Point{}).Where("id = ? AND deleted = false", *id).Update("deleted", true) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func PointByID(id *string) (*model.Point, bool, error) { + db, err := newDB() + if err != nil { + return nil, false, err + } + var p model.Point + if err := db.Where("id = ? AND deleted = false", *id).First(&p).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, false, nil + } + return nil, false, err + } + return &p, true, nil +} + +func PointList(objectID, indicatorID, keyword *string, offset, limit *int) ([]model.Point, int64, error) { + db, err := newDB() + if err != nil { + return nil, 0, err + } + q := db.Model(&model.Point{}).Where("deleted = false") + if objectID != nil && *objectID != "" { + q = q.Where("object_id = ?", *objectID) + } + if indicatorID != nil && *indicatorID != "" { + q = q.Where("indicator_id = ?", *indicatorID) + } + if keyword != nil && *keyword != "" { + kw := "%" + *keyword + "%" + q = q.Where("name LIKE ?", kw) + } + + var total int64 + if err := q.Count(&total).Error; err != nil { + return nil, 0, err + } + + var items []model.Point + if err := q.Order("created_at desc").Offset(*offset).Limit(*limit).Find(&items).Error; err != nil { + return nil, 0, err + } + return items, total, nil +} diff --git a/mysql/project-mysql.go b/mysql/project-mysql.go new file mode 100644 index 0000000..5c90ca8 --- /dev/null +++ b/mysql/project-mysql.go @@ -0,0 +1,82 @@ +package mysql + +import ( + "errors" + + "gorm.io/gorm" + "myschools.me/heritage/heritage-api/model" +) + +func ProjectCreate(p *model.Project) error { + db, err := newDB() + if err != nil { + return err + } + return db.Create(p).Error +} + +func ProjectUpdate(p *model.Project) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.Project{}).Where("id = ? AND deleted = false", p.ID).Updates(p) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func ProjectDelete(id *string) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.Project{}).Where("id = ? AND deleted = false", *id).Update("deleted", true) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func ProjectByID(id *string) (*model.Project, bool, error) { + db, err := newDB() + if err != nil { + return nil, false, err + } + var p model.Project + if err := db.Where("id = ? AND deleted = false", *id).First(&p).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, false, nil + } + return nil, false, err + } + return &p, true, nil +} + +func ProjectList(orgID, keyword *string, offset, limit *int) ([]model.Project, int64, error) { + db, err := newDB() + if err != nil { + return nil, 0, err + } + + q := db.Model(&model.Project{}).Where("deleted = false") + if orgID != nil && *orgID != "" { + q = q.Where("org_id = ?", *orgID) + } + if keyword != nil && *keyword != "" { + kw := "%" + *keyword + "%" + q = q.Where("project_no LIKE ? OR name LIKE ?", kw, kw) + } + + var total int64 + if err := q.Count(&total).Error; err != nil { + return nil, 0, err + } + + var items []model.Project + if err := q.Order("created_at desc").Offset(*offset).Limit(*limit).Find(&items).Error; err != nil { + return nil, 0, err + } + return items, total, nil +} diff --git a/mysql/role-mysql.go b/mysql/role-mysql.go new file mode 100644 index 0000000..94d5262 --- /dev/null +++ b/mysql/role-mysql.go @@ -0,0 +1,68 @@ +package mysql + +import "myschools.me/heritage/heritage-api/model" + +func RolePermissionsList(roleID *string) ([]string, error) { + db, err := newDB() + if err != nil { + return nil, err + } + + var codes []string + if err := db.Model(&model.Permission{}).Where("role_id = ?", *roleID).Pluck("code", &codes).Error; err != nil { + return nil, err + } + return codes, nil +} + +func RoleHasPermission(roleID, permissionCode *string) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + + var count int64 + if err := db.Model(&model.Permission{}). + Where("role_id = ? AND (code = ? OR code = ?)", *roleID, *permissionCode, "*"). + Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +func RoleCount() (*int64, error) { + db, err := newDB() + if err != nil { + return nil, err + } + + var count int64 + if err := db.Model(&model.Role{}).Count(&count).Error; err != nil { + return nil, err + } + return &count, nil +} + +func RoleCreate(obj *model.Role) (*model.Role, error) { + db, err := newDB() + if err != nil { + return nil, err + } + if err := db.Create(obj).Error; err != nil { + return nil, err + } + return obj, nil +} + +func RoleByCode(code string) (*model.Role, bool, error) { + db, err := newDB() + if err != nil { + return nil, false, err + } + + var r model.Role + if err := db.Where("code = ?", code).First(&r).Error; err != nil { + return nil, false, err + } + return &r, true, nil +} diff --git a/mysql/tables-mysql.go b/mysql/tables-mysql.go index d002f5f..8222d84 100644 --- a/mysql/tables-mysql.go +++ b/mysql/tables-mysql.go @@ -1,89 +1,27 @@ package mysql import ( - "os" - "strings" - - "github.com/google/uuid" - "golang.org/x/crypto/bcrypt" "myschools.me/heritage/heritage-api/model" ) -func Bootstrap() { - if os.Getenv("MYSQL_INIT") != "true" { - return - } - +func init() { db, err := newDB() if err != nil { panic(err) } - if err := db.AutoMigrate(&model.Role{}, &model.Permission{}, &model.User{}); err != nil { + if err := db.AutoMigrate( + &model.Role{}, + &model.Permission{}, + &model.User{}, + &model.Org{}, + &model.Project{}, + &model.Task{}, + &model.Point{}, + &model.DataRecord{}, + &model.Device{}, + &model.DeviceIngestData{}, + &model.Menu{}, + ); err != nil { panic(err) } - - var roleCount int64 - if err := db.Model(&model.Role{}).Count(&roleCount).Error; err != nil { - panic(err) - } - var permissionCount int64 - if err := db.Model(&model.Permission{}).Count(&permissionCount).Error; err != nil { - panic(err) - } - var userCount int64 - if err := db.Model(&model.User{}).Count(&userCount).Error; err != nil { - panic(err) - } - - var defaultRole model.Role - if roleCount == 0 { - defaultRole = model.Role{ - ID: newID(), - Code: "admin", - Name: "管理员", - } - if err := db.Create(&defaultRole).Error; err != nil { - panic(err) - } - } else { - if err := db.Where("code = ?", "admin").First(&defaultRole).Error; err != nil { - if err := db.First(&defaultRole).Error; err != nil { - panic(err) - } - } - } - - if permissionCount == 0 { - p := model.Permission{ - ID: newID(), - RoleID: defaultRole.ID, - Code: "*", - Name: "全部权限", - } - if err := db.Create(&p).Error; err != nil { - panic(err) - } - } - - if userCount == 0 { - defaultPwd := "admin" - h, err := bcrypt.GenerateFromPassword([]byte(defaultPwd), bcrypt.DefaultCost) - if err != nil { - panic(err) - } - u := model.User{ - ID: newID(), - UserName: "admin", - PasswordHash: string(h), - RoleID: defaultRole.ID, - } - if err := db.Create(&u).Error; err != nil { - panic(err) - } - } -} - -func newID() string { - id := uuid.Must(uuid.NewV7()).String() - return strings.ReplaceAll(id, "-", "") } diff --git a/mysql/task-mysql.go b/mysql/task-mysql.go new file mode 100644 index 0000000..5a67912 --- /dev/null +++ b/mysql/task-mysql.go @@ -0,0 +1,81 @@ +package mysql + +import ( + "errors" + + "gorm.io/gorm" + "myschools.me/heritage/heritage-api/model" +) + +func TaskCreate(t *model.Task) error { + db, err := newDB() + if err != nil { + return err + } + return db.Create(t).Error +} + +func TaskUpdate(t *model.Task) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.Task{}).Where("id = ? AND deleted = false", t.ID).Updates(t) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func TaskDelete(id *string) (bool, error) { + db, err := newDB() + if err != nil { + return false, err + } + tx := db.Model(&model.Task{}).Where("id = ? AND deleted = false", *id).Update("deleted", true) + if tx.Error != nil { + return false, tx.Error + } + return tx.RowsAffected > 0, nil +} + +func TaskByID(id *string) (*model.Task, bool, error) { + db, err := newDB() + if err != nil { + return nil, false, err + } + var t model.Task + if err := db.Where("id = ? AND deleted = false", *id).First(&t).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, false, nil + } + return nil, false, err + } + return &t, true, nil +} + +func TaskList(projectID, keyword *string, offset, limit *int) ([]model.Task, int64, error) { + db, err := newDB() + if err != nil { + return nil, 0, err + } + q := db.Model(&model.Task{}).Where("deleted = false") + if projectID != nil && *projectID != "" { + q = q.Where("project_id = ?", *projectID) + } + if keyword != nil && *keyword != "" { + kw := "%" + *keyword + "%" + q = q.Where("name LIKE ?", kw) + } + + var total int64 + if err := q.Count(&total).Error; err != nil { + return nil, 0, err + } + + var items []model.Task + if err := q.Order("created_at desc").Offset(*offset).Limit(*limit).Find(&items).Error; err != nil { + return nil, 0, err + } + return items, total, nil +} diff --git a/mysql/user-mysql.go b/mysql/user-mysql.go index b86a20f..158e41d 100644 --- a/mysql/user-mysql.go +++ b/mysql/user-mysql.go @@ -7,7 +7,7 @@ import ( "myschools.me/heritage/heritage-api/model" ) -func userByUserName(userName *string) (*model.User, bool, error) { +func UserByUserName(userName *string) (*model.User, bool, error) { db, err := newDB() if err != nil { return nil, false, err @@ -23,7 +23,7 @@ func userByUserName(userName *string) (*model.User, bool, error) { return &u, true, nil } -func userByID(userID *string) (*model.User, bool, error) { +func UserByID(userID *string) (*model.User, bool, error) { db, err := newDB() if err != nil { return nil, false, err @@ -39,7 +39,7 @@ func userByID(userID *string) (*model.User, bool, error) { return &u, true, nil } -func updateUserPasswordHash(userID, passwordHash *string) (bool, error) { +func UpdateUserPasswordHash(userID, passwordHash *string) (bool, error) { db, err := newDB() if err != nil { return false, err @@ -51,7 +51,7 @@ func updateUserPasswordHash(userID, passwordHash *string) (bool, error) { return tx.RowsAffected > 0, nil } -func userRoleIDByUserID(userID *string) (*string, bool, error) { +func UserRoleIDByUserID(userID *string) (*string, bool, error) { db, err := newDB() if err != nil { return nil, false, err @@ -67,18 +67,26 @@ func userRoleIDByUserID(userID *string) (*string, bool, error) { return &u.RoleID, true, nil } -func UserRoleIDByUserID(userID *string) (*string, bool, error) { - return userRoleIDByUserID(userID) +func UserCount() (*int64, error) { + db, err := newDB() + if err != nil { + return nil, err + } + + var count int64 + if err := db.Model(&model.User{}).Count(&count).Error; err != nil { + return nil, err + } + return &count, nil } -func UserByUserName(userName *string) (*model.User, bool, error) { - return userByUserName(userName) -} - -func UserByID(userID *string) (*model.User, bool, error) { - return userByID(userID) -} - -func UpdateUserPasswordHash(userID, passwordHash *string) (bool, error) { - return updateUserPasswordHash(userID, passwordHash) +func UserCreate(obj *model.User) (*model.User, error) { + db, err := newDB() + if err != nil { + return nil, err + } + if err := db.Create(obj).Error; err != nil { + return nil, err + } + return obj, nil } diff --git a/service/auth-service.go b/service/auth-service.go index 9dbc169..1e1d37d 100644 --- a/service/auth-service.go +++ b/service/auth-service.go @@ -18,7 +18,7 @@ var ( ErrUserNotFound = errors.New("user not found") ) -func Login(userName, plainPassword string) (string, *model.User, error) { +func AuthLogin(userName, plainPassword string) (string, *model.User, error) { userName = strings.TrimSpace(userName) if userName == "" || plainPassword == "" { return "", nil, ErrInvalidCredentials @@ -33,9 +33,17 @@ func Login(userName, plainPassword string) (string, *model.User, error) { return "", nil, err } if !found || u == nil || u.PasswordHash == "" { + logrus.WithFields(logrus.Fields{ + "func": "service.AuthLogin", + "userName": userName, + }).Warnf("user not found or password not set") return "", nil, ErrInvalidCredentials } - if !VerifyPassword(u.PasswordHash, plainPassword) { + if !PasswordVerify(u.PasswordHash, plainPassword) { + logrus.WithFields(logrus.Fields{ + "func": "service.AuthLogin", + "userName": userName, + }).Warnf("password verification failed") return "", nil, ErrInvalidCredentials } @@ -55,7 +63,7 @@ func Login(userName, plainPassword string) (string, *model.User, error) { return token, safeUser, nil } -func Logout(token string) error { +func AuthLogout(token string) error { token = strings.TrimSpace(token) token = strings.TrimPrefix(token, "Bearer ") token = strings.TrimPrefix(token, "bearer ") @@ -65,7 +73,7 @@ func Logout(token string) error { return redis.UserTokenDel(&token) } -func ChangePassword(userID, oldPassword, newPassword string) error { +func AuthChangePassword(userID, oldPassword, newPassword string) error { oldPassword = strings.TrimSpace(oldPassword) newPassword = strings.TrimSpace(newPassword) if oldPassword == "" || newPassword == "" { @@ -84,14 +92,22 @@ func ChangePassword(userID, oldPassword, newPassword string) error { return err } if !found || dbUser == nil || dbUser.PasswordHash == "" { + logrus.WithFields(logrus.Fields{ + "func": "service.AuthChangePassword", + "userID": userID, + }).Warnf("user not found") return ErrUserNotFound } - if !VerifyPassword(dbUser.PasswordHash, oldPassword) { + if !PasswordVerify(dbUser.PasswordHash, oldPassword) { + logrus.WithFields(logrus.Fields{ + "func": "service.AuthChangePassword", + "userID": userID, + }).Warnf("old password verification failed") return ErrOldPasswordWrong } - hash, err := HashPassword(newPassword) + hash, err := PasswordHash(newPassword) if err != nil { logrus.WithFields(logrus.Fields{ "func": "service.ChangePassword", diff --git a/service/base-service.go b/service/base-service.go index 467d1e3..426c10a 100644 --- a/service/base-service.go +++ b/service/base-service.go @@ -6,11 +6,29 @@ import ( "github.com/google/uuid" ) -func NewID() string { +func newID() string { i := uuid.Must(uuid.NewV7()).String() return strings.ReplaceAll(i, "-", "") } +// 专用给创建新用户的ID,32位 +func newUserID() string { + i := uuid.Must(uuid.NewV7()).String() + return strings.ReplaceAll(i, "-", "") +} + +// 专给机构创建用的ID,20位 +func newOrgID() string { + i := uuid.Must(uuid.NewV7()).String() + return strings.ReplaceAll(i, "-", "")[:20] +} + +// 项目ID22位 +func newProjectID() string { + i := uuid.Must(uuid.NewV7()).String() + return strings.ReplaceAll(i, "-", "")[:20] +} + func newToken() string { i := uuid.Must(uuid.NewV7()).String() return strings.ReplaceAll(i, "-", "") diff --git a/service/bootstrap-service.go b/service/bootstrap-service.go new file mode 100644 index 0000000..bb62e00 --- /dev/null +++ b/service/bootstrap-service.go @@ -0,0 +1,119 @@ +package service + +import ( + "github.com/sirupsen/logrus" + "golang.org/x/crypto/bcrypt" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/mysql" +) + +func Bootstrap() { + menuCount, err := mysql.MenuCount() + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.MenuCount: %v", err) + panic(err) + } + if *menuCount == 0 { + menus := []model.Menu{ + {ID: 1, Name: "首页", Icon: "home", Path: "/dashboard", Sort: 1, PermissionCode: "dashboard"}, + {ID: 2, Name: "系统管理", Icon: "setting", Path: "/system", Sort: 2, PermissionCode: "system"}, + {ID: 3, ParentID: 2, Name: "组织机构", Icon: "team", Path: "/system/org", Sort: 1, PermissionCode: "org:list"}, + {ID: 4, ParentID: 2, Name: "项目管理", Icon: "project", Path: "/system/project", Sort: 2, PermissionCode: "project:list"}, + {ID: 5, ParentID: 2, Name: "任务管理", Icon: "profile", Path: "/system/task", Sort: 3, PermissionCode: "task:list"}, + {ID: 6, ParentID: 2, Name: "监测点位", Icon: "environment", Path: "/system/point", Sort: 4, PermissionCode: "point:list"}, + {ID: 7, ParentID: 2, Name: "数据记录", Icon: "database", Path: "/system/data", Sort: 5, PermissionCode: "data:list"}, + {ID: 8, ParentID: 2, Name: "设备管理", Icon: "cluster", Path: "/system/device", Sort: 6, PermissionCode: "device:list"}, + } + if err := mysql.MenuCreateBatch(menus); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.MenuCreateBatch: %v", err) + panic(err) + } + } + + roleCount, err := mysql.RoleCount() + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.RoleCount: %v", err) + panic(err) + } + + var defaultRole *model.Role + if *roleCount == 0 { + defaultRole = &model.Role{ + ID: newUserID(), + Code: "admin", + Name: "管理员", + } + if _, err := mysql.RoleCreate(defaultRole); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.RoleCreate: %v", err) + panic(err) + } + } else { + var found bool + defaultRole, found, err = mysql.RoleByCode("admin") + if err != nil || !found { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.RoleByCode: %v", err) + panic("admin role not found") + } + } + + permissionCount, err := mysql.PermissionCount() + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.PermissionCount: %v", err) + panic(err) + } + if *permissionCount == 0 { + p := &model.Permission{ + RoleID: defaultRole.ID, + Code: "*", + Name: "全部权限", + } + if _, err := mysql.PermissionCreate(p); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.PermissionCreate: %v", err) + panic(err) + } + } + + userCount, err := mysql.UserCount() + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.UserCount: %v", err) + panic(err) + } + if *userCount == 0 { + defaultPwd := "admin" + h, err := bcrypt.GenerateFromPassword([]byte(defaultPwd), bcrypt.DefaultCost) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("bcrypt.GenerateFromPassword: %v", err) + panic(err) + } + u := &model.User{ + ID: newUserID(), + UserName: "admin", + PasswordHash: string(h), + RoleID: defaultRole.ID, + } + if _, err := mysql.UserCreate(u); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.UserCreate: %v", err) + panic(err) + } + } +} diff --git a/service/data-service.go b/service/data-service.go new file mode 100644 index 0000000..5dd8b9a --- /dev/null +++ b/service/data-service.go @@ -0,0 +1,151 @@ +package service + +import ( + "errors" + "strings" + "time" + + "github.com/sirupsen/logrus" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/mysql" +) + +func DataRecordCreate(r *model.DataRecord) (*model.DataRecord, error) { + if r == nil { + return nil, errors.New("invalid data") + } + r.ID = strings.TrimSpace(r.ID) + if r.ID == "" { + r.ID = newID() + } + r.Value = strings.TrimSpace(r.Value) + r.IndicatorParamID = strings.TrimSpace(r.IndicatorParamID) + r.PointID = strings.TrimSpace(r.PointID) + r.TaskID = strings.TrimSpace(r.TaskID) + r.CreatorUserID = strings.TrimSpace(r.CreatorUserID) + r.Remark = strings.TrimSpace(r.Remark) + if r.CollectedAt.IsZero() { + r.CollectedAt = time.Now() + } + if r.Value == "" || r.IndicatorParamID == "" || r.PointID == "" || r.TaskID == "" || r.CreatorUserID == "" { + return nil, errors.New("missing required fields") + } + if err := mysql.DataRecordCreate(r); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DataRecordCreate", + }).Warnf("mysql.DataRecordCreate: %v", err) + return nil, err + } + return r, nil +} + +func DataRecordUpdate(idv string, patch *model.DataRecord) (*model.DataRecord, error) { + rid := strings.TrimSpace(idv) + if rid == "" { + return nil, ErrNotFound + } + existing, found, err := mysql.DataRecordByID(&rid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DataRecordUpdate", + }).Warnf("mysql.DataRecordByID: %v", err) + return nil, err + } + if !found || existing == nil { + logrus.WithFields(logrus.Fields{ + "func": "DataRecordUpdate", + }).Warnf("mysql.DataRecordByID: not found") + return nil, ErrNotFound + } + if patch == nil { + return existing, nil + } + if s := strings.TrimSpace(patch.Value); s != "" { + existing.Value = s + } + if s := strings.TrimSpace(patch.IndicatorParamID); s != "" { + existing.IndicatorParamID = s + } + if s := strings.TrimSpace(patch.PointID); s != "" { + existing.PointID = s + } + if s := strings.TrimSpace(patch.TaskID); s != "" { + existing.TaskID = s + } + if !patch.CollectedAt.IsZero() { + existing.CollectedAt = patch.CollectedAt + } + if s := strings.TrimSpace(patch.Remark); s != "" { + existing.Remark = s + } + ok, err := mysql.DataRecordUpdate(existing) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DataRecordUpdate", + }).Warnf("mysql.DataRecordUpdate: %v", err) + return nil, err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "DataRecordUpdate", + }).Warnf("mysql.DataRecordUpdate: not found") + return nil, ErrNotFound + } + return existing, nil +} + +func DataRecordDelete(idv string) error { + rid := strings.TrimSpace(idv) + if rid == "" { + return ErrNotFound + } + ok, err := mysql.DataRecordDelete(&rid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DataRecordDelete", + }).Warnf("mysql.DataRecordDelete: %v", err) + return err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "DataRecordDelete", + }).Warnf("mysql.DataRecordDelete: not found") + return ErrNotFound + } + return nil +} + +func DataGetDataRecord(idv string) (*model.DataRecord, error) { + rid := strings.TrimSpace(idv) + if rid == "" { + return nil, ErrNotFound + } + r, found, err := mysql.DataRecordByID(&rid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DataGetDataRecord", + }).Warnf("mysql.DataRecordByID: %v", err) + return nil, err + } + if !found || r == nil { + logrus.WithFields(logrus.Fields{ + "func": "DataGetDataRecord", + }).Warnf("mysql.DataRecordByID: not found") + return nil, ErrNotFound + } + return r, nil +} + +func DataListDataRecords(pointID, taskID *string, startAt, endAt *time.Time, page, size int) ([]model.DataRecord, int64, error) { + if page < 1 { + page = 1 + } + if size < 1 { + size = 20 + } + if size > 200 { + size = 200 + } + offset := (page - 1) * size + return mysql.DataRecordList(pointID, taskID, startAt, endAt, &offset, &size) +} diff --git a/service/device-service.go b/service/device-service.go new file mode 100644 index 0000000..ef969ba --- /dev/null +++ b/service/device-service.go @@ -0,0 +1,171 @@ +package service + +import ( + "errors" + "strings" + + "github.com/sirupsen/logrus" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/mysql" +) + +func DeviceCreateDevice(d *model.Device) (*model.Device, error) { + if d == nil { + return nil, errors.New("invalid device") + } + d.ID = strings.TrimSpace(d.ID) + if d.ID == "" { + d.ID = newID() + } + d.Name = strings.TrimSpace(d.Name) + d.ModelID = strings.TrimSpace(d.ModelID) + d.SerialNo = strings.TrimSpace(d.SerialNo) + d.StatusCode = strings.TrimSpace(d.StatusCode) + d.PointID = strings.TrimSpace(d.PointID) + d.InstalledImage = strings.TrimSpace(d.InstalledImage) + d.Channel1ParamID = strings.TrimSpace(d.Channel1ParamID) + d.Channel2ParamID = strings.TrimSpace(d.Channel2ParamID) + d.Channel3ParamID = strings.TrimSpace(d.Channel3ParamID) + d.CreatorUserID = strings.TrimSpace(d.CreatorUserID) + d.Remark = strings.TrimSpace(d.Remark) + if d.Name == "" || d.StatusCode == "" || d.CreatorUserID == "" { + return nil, errors.New("missing required fields") + } + if err := mysql.DeviceCreate(d); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DeviceCreateDevice", + }).Warnf("mysql.DeviceCreate: %v", err) + return nil, err + } + return d, nil +} + +func DeviceUpdateDevice(idv string, patch *model.Device) (*model.Device, error) { + did := strings.TrimSpace(idv) + if did == "" { + return nil, ErrNotFound + } + existing, found, err := mysql.DeviceByID(&did) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DeviceUpdateDevice", + }).Warnf("mysql.DeviceByID: %v", err) + return nil, err + } + if !found || existing == nil { + logrus.WithFields(logrus.Fields{ + "func": "DeviceUpdateDevice", + }).Warnf("mysql.DeviceByID: not found") + return nil, ErrNotFound + } + if patch == nil { + return existing, nil + } + if s := strings.TrimSpace(patch.Name); s != "" { + existing.Name = s + } + if s := strings.TrimSpace(patch.ModelID); s != "" { + existing.ModelID = s + } + if s := strings.TrimSpace(patch.SerialNo); s != "" { + existing.SerialNo = s + } + if s := strings.TrimSpace(patch.StatusCode); s != "" { + existing.StatusCode = s + } + if s := strings.TrimSpace(patch.PointID); s != "" { + existing.PointID = s + } + if patch.InstalledAt != nil { + t := *patch.InstalledAt + existing.InstalledAt = &t + } + if s := strings.TrimSpace(patch.InstalledImage); s != "" { + existing.InstalledImage = s + } + if s := strings.TrimSpace(patch.Channel1ParamID); s != "" { + existing.Channel1ParamID = s + } + if s := strings.TrimSpace(patch.Channel2ParamID); s != "" { + existing.Channel2ParamID = s + } + if s := strings.TrimSpace(patch.Channel3ParamID); s != "" { + existing.Channel3ParamID = s + } + if s := strings.TrimSpace(patch.CreatorUserID); s != "" { + existing.CreatorUserID = s + } + if s := strings.TrimSpace(patch.Remark); s != "" { + existing.Remark = s + } + ok, err := mysql.DeviceUpdate(existing) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DeviceUpdateDevice", + }).Warnf("mysql.DeviceUpdate: %v", err) + return nil, err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "DeviceUpdateDevice", + }).Warnf("mysql.DeviceUpdate: not found") + return nil, ErrNotFound + } + return existing, nil +} + +func DeviceDeleteDevice(idv string) error { + did := strings.TrimSpace(idv) + if did == "" { + return ErrNotFound + } + ok, err := mysql.DeviceDelete(&did) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DeviceDeleteDevice", + }).Warnf("mysql.DeviceDelete: %v", err) + return err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "DeviceDeleteDevice", + }).Warnf("mysql.DeviceDelete: not found") + return ErrNotFound + } + return nil +} + +func DeviceGetDevice(idv string) (*model.Device, error) { + did := strings.TrimSpace(idv) + if did == "" { + return nil, ErrNotFound + } + d, found, err := mysql.DeviceByID(&did) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "DeviceGetDevice", + }).Warnf("mysql.DeviceByID: %v", err) + return nil, err + } + if !found || d == nil { + logrus.WithFields(logrus.Fields{ + "func": "DeviceGetDevice", + }).Warnf("mysql.DeviceByID: not found") + return nil, ErrNotFound + } + return d, nil +} + +func DeviceListDevices(keyword *string, page, size int) ([]model.Device, int64, error) { + if page < 1 { + page = 1 + } + if size < 1 { + size = 20 + } + if size > 200 { + size = 200 + } + offset := (page - 1) * size + return mysql.DeviceList(keyword, &offset, &size) +} diff --git a/service/menu-service.go b/service/menu-service.go new file mode 100644 index 0000000..53296af --- /dev/null +++ b/service/menu-service.go @@ -0,0 +1,71 @@ +package service + +import ( + "github.com/sirupsen/logrus" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/mysql" +) + +func MenuList(usr *model.User) ([]*model.Menu, error) { + // 获取用户角色 ID + roleID, found, err := mysql.UserRoleIDByUserID(&usr.ID) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "MenuList", + }).Warnf("mysql.UserRoleIDByUserID: %v", err) + return nil, err + } + if !found { + return nil, nil + } + + // 获取角色拥有的所有权限 + permissionCodes, err := mysql.RolePermissionsList(roleID) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "MenuList", + }).Warnf("mysql.RolePermissionsList: %v", err) + return nil, err + } + + // 获取所有菜单 + allMenus, err := mysql.MenuList() + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "MenuList", + }).Warnf("mysql.MenuList: %v", err) + return nil, err + } + + // 权限检查辅助函数 + hasPermission := func(code string) bool { + for _, p := range permissionCodes { + if p == "*" || p == code { + return true + } + } + return false + } + + // 过滤有权限的菜单 + var filteredMenus []*model.Menu + for i := range allMenus { + if hasPermission(allMenus[i].PermissionCode) { + filteredMenus = append(filteredMenus, &allMenus[i]) + } + } + + return menuTreeBuild(filteredMenus, 0), nil +} + +func menuTreeBuild(menus []*model.Menu, parentID uint) []*model.Menu { + var tree []*model.Menu + for _, menu := range menus { + if menu.ParentID == parentID { + children := menuTreeBuild(menus, menu.ID) + menu.Children = children + tree = append(tree, menu) + } + } + return tree +} diff --git a/service/org-service.go b/service/org-service.go new file mode 100644 index 0000000..f932ca9 --- /dev/null +++ b/service/org-service.go @@ -0,0 +1,136 @@ +package service + +import ( + "errors" + "strings" + + "github.com/sirupsen/logrus" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/mysql" +) + +var ErrNotFound = errors.New("not found") + +func OrgCreateOrg(parentID, name string, enabled bool, sort int, remark string) (*model.Org, error) { + name = strings.TrimSpace(name) + if name == "" { + return nil, errors.New("name required") + } + o := &model.Org{ + ID: newID(), + ParentID: strings.TrimSpace(parentID), + Name: name, + Enabled: enabled, + Sort: sort, + Remark: strings.TrimSpace(remark), + } + if err := mysql.OrgCreate(o); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "OrgCreateOrg", + }).Warnf("mysql.OrgCreate: %v", err) + return nil, err + } + return o, nil +} + +func OrgUpdateOrg(idv string, parentID, name *string, enabled *bool, sort *int, remark *string) (*model.Org, error) { + oid := strings.TrimSpace(idv) + if oid == "" { + return nil, ErrNotFound + } + existing, found, err := mysql.OrgByID(&oid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "OrgUpdateOrg", + }).Warnf("mysql.OrgByID: %v", err) + return nil, err + } + if !found || existing == nil { + logrus.WithFields(logrus.Fields{ + "func": "OrgUpdateOrg", + }).Warnf("mysql.OrgByID: not found") + return nil, ErrNotFound + } + if parentID != nil { + existing.ParentID = strings.TrimSpace(*parentID) + } + if name != nil { + n := strings.TrimSpace(*name) + if n == "" { + return nil, errors.New("name required") + } + existing.Name = n + } + if enabled != nil { + existing.Enabled = *enabled + } + if sort != nil { + existing.Sort = *sort + } + if remark != nil { + existing.Remark = strings.TrimSpace(*remark) + } + if err := mysql.OrgUpdate(existing); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "OrgUpdateOrg", + }).Warnf("mysql.OrgUpdate: %v", err) + return nil, err + } + return existing, nil +} + +func OrgDeleteOrg(idv string) error { + oid := strings.TrimSpace(idv) + if oid == "" { + return ErrNotFound + } + ok, err := mysql.OrgDelete(&oid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "OrgDeleteOrg", + }).Warnf("mysql.OrgDelete: %v", err) + return err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "OrgDeleteOrg", + }).Warnf("mysql.OrgDelete: not found") + return ErrNotFound + } + return nil +} + +func OrgGetOrg(idv string) (*model.Org, error) { + oid := strings.TrimSpace(idv) + if oid == "" { + return nil, ErrNotFound + } + o, found, err := mysql.OrgByID(&oid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "OrgGetOrg", + }).Warnf("mysql.OrgByID: %v", err) + return nil, err + } + if !found || o == nil { + logrus.WithFields(logrus.Fields{ + "func": "OrgGetOrg", + }).Warnf("mysql.OrgByID: not found") + return nil, ErrNotFound + } + return o, nil +} + +func OrgListOrgs(parentID *string, page, size int) ([]model.Org, int64, error) { + if page < 1 { + page = 1 + } + if size < 1 { + size = 20 + } + if size > 200 { + size = 200 + } + offset := (page - 1) * size + return mysql.OrgList(parentID, &offset, &size) +} diff --git a/service/password-service.go b/service/password-service.go index 2d44e57..de5e274 100644 --- a/service/password-service.go +++ b/service/password-service.go @@ -1,15 +1,22 @@ package service -import "golang.org/x/crypto/bcrypt" +import ( + "golang.org/x/crypto/bcrypt" -func HashPassword(password string) (string, error) { + "github.com/sirupsen/logrus" +) + +func PasswordHash(password string) (string, error) { h, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "PasswordHash", + }).Warnf("bcrypt.GenerateFromPassword: %v", err) return "", err } return string(h), nil } -func VerifyPassword(hash, password string) bool { +func PasswordVerify(hash, password string) bool { return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil } diff --git a/service/point-service.go b/service/point-service.go new file mode 100644 index 0000000..82b1816 --- /dev/null +++ b/service/point-service.go @@ -0,0 +1,172 @@ +package service + +import ( + "errors" + "strings" + + "github.com/sirupsen/logrus" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/mysql" +) + +func PointCreatePoint(p *model.Point) (*model.Point, error) { + if p == nil { + return nil, errors.New("invalid point") + } + p.ID = strings.TrimSpace(p.ID) + if p.ID == "" { + p.ID = newID() + } + p.Name = strings.TrimSpace(p.Name) + p.LocationObjectID = strings.TrimSpace(p.LocationObjectID) + p.StatusCode = strings.TrimSpace(p.StatusCode) + p.Image = strings.TrimSpace(p.Image) + p.ObjectID = strings.TrimSpace(p.ObjectID) + p.IndicatorID = strings.TrimSpace(p.IndicatorID) + p.CreatorPersonID = strings.TrimSpace(p.CreatorPersonID) + p.SensorID = strings.TrimSpace(p.SensorID) + p.CreatedTaskID = strings.TrimSpace(p.CreatedTaskID) + p.Remark = strings.TrimSpace(p.Remark) + + if p.Name == "" || p.StatusCode == "" || p.ObjectID == "" || p.IndicatorID == "" || p.CreatorPersonID == "" { + return nil, errors.New("missing required fields") + } + if p.Image == "" { + p.Image = "" + } + + if err := mysql.PointCreate(p); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "PointCreatePoint", + }).Warnf("mysql.PointCreate: %v", err) + return nil, err + } + return p, nil +} + +func PointUpdatePoint(idv string, patch *model.Point) (*model.Point, error) { + pid := strings.TrimSpace(idv) + if pid == "" { + return nil, ErrNotFound + } + existing, found, err := mysql.PointByID(&pid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "PointUpdatePoint", + }).Warnf("mysql.PointByID: %v", err) + return nil, err + } + if !found || existing == nil { + logrus.WithFields(logrus.Fields{ + "func": "PointUpdatePoint", + }).Warnf("mysql.PointByID: not found") + return nil, ErrNotFound + } + if patch == nil { + return existing, nil + } + if s := strings.TrimSpace(patch.Name); s != "" { + existing.Name = s + } + existing.X = patch.X + existing.Y = patch.Y + existing.Z = patch.Z + if s := strings.TrimSpace(patch.LocationObjectID); s != "" { + existing.LocationObjectID = s + } + if s := strings.TrimSpace(patch.StatusCode); s != "" { + existing.StatusCode = s + } + if s := strings.TrimSpace(patch.Image); s != "" { + existing.Image = s + } + if s := strings.TrimSpace(patch.ObjectID); s != "" { + existing.ObjectID = s + } + if s := strings.TrimSpace(patch.IndicatorID); s != "" { + existing.IndicatorID = s + } + if s := strings.TrimSpace(patch.CreatorPersonID); s != "" { + existing.CreatorPersonID = s + } + if s := strings.TrimSpace(patch.SensorID); s != "" { + existing.SensorID = s + } + if s := strings.TrimSpace(patch.CreatedTaskID); s != "" { + existing.CreatedTaskID = s + } + if s := strings.TrimSpace(patch.Remark); s != "" { + existing.Remark = s + } + + ok, err := mysql.PointUpdate(existing) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "PointUpdatePoint", + }).Warnf("mysql.PointUpdate: %v", err) + return nil, err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "PointUpdatePoint", + }).Warnf("mysql.PointUpdate: not found") + return nil, ErrNotFound + } + return existing, nil +} + +func PointDeletePoint(idv string) error { + pid := strings.TrimSpace(idv) + if pid == "" { + return ErrNotFound + } + ok, err := mysql.PointDelete(&pid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "PointDeletePoint", + }).Warnf("mysql.PointDelete: %v", err) + return err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "PointDeletePoint", + }).Warnf("mysql.PointDelete: not found") + return ErrNotFound + } + return nil +} + +func PointGetPoint(idv string) (*model.Point, error) { + pid := strings.TrimSpace(idv) + if pid == "" { + return nil, ErrNotFound + } + p, found, err := mysql.PointByID(&pid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "PointGetPoint", + }).Warnf("mysql.PointByID: %v", err) + return nil, err + } + if !found || p == nil { + logrus.WithFields(logrus.Fields{ + "func": "PointGetPoint", + }).Warnf("mysql.PointByID: not found") + return nil, ErrNotFound + } + return p, nil +} + +func PointListPoints(objectID, indicatorID, keyword *string, page, size int) ([]model.Point, int64, error) { + if page < 1 { + page = 1 + } + if size < 1 { + size = 20 + } + if size > 200 { + size = 200 + } + offset := (page - 1) * size + return mysql.PointList(objectID, indicatorID, keyword, &offset, &size) +} diff --git a/service/project-service.go b/service/project-service.go new file mode 100644 index 0000000..510eae0 --- /dev/null +++ b/service/project-service.go @@ -0,0 +1,205 @@ +package service + +import ( + "errors" + "strings" + + "github.com/sirupsen/logrus" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/mysql" +) + +func ProjectCreateProject(p *model.Project) (*model.Project, error) { + if p == nil { + return nil, errors.New("invalid project") + } + p.ID = strings.TrimSpace(p.ID) + if p.ID == "" { + p.ID = newProjectID() + } + p.ProjectNo = strings.TrimSpace(p.ProjectNo) + if p.ProjectNo == "" { + p.ProjectNo = strings.ToUpper(newID()) + } + p.Name = strings.TrimSpace(p.Name) + p.TypeCode = strings.TrimSpace(p.TypeCode) + p.BuildingIDs = strings.TrimSpace(p.BuildingIDs) + p.StatusCode = strings.TrimSpace(p.StatusCode) + p.ResponsibleOrgID = strings.TrimSpace(p.ResponsibleOrgID) + p.ImplementOrgIDs = strings.TrimSpace(p.ImplementOrgIDs) + p.LeaderPersonID = strings.TrimSpace(p.LeaderPersonID) + p.ParticipantPersonIDs = strings.TrimSpace(p.ParticipantPersonIDs) + p.Description = strings.TrimSpace(p.Description) + p.Attachments = strings.TrimSpace(p.Attachments) + p.OrgID = strings.TrimSpace(p.OrgID) + p.CreatorUserID = strings.TrimSpace(p.CreatorUserID) + p.Remark = strings.TrimSpace(p.Remark) + + if p.Name == "" || p.TypeCode == "" || p.BuildingIDs == "" || p.StatusCode == "" || p.ResponsibleOrgID == "" || p.LeaderPersonID == "" || p.OrgID == "" || p.CreatorUserID == "" { + return nil, errors.New("missing required fields") + } + if p.StartAt.IsZero() || p.EndAt.IsZero() { + return nil, errors.New("startAt/endAt required") + } + if p.EndAt.Before(p.StartAt) { + return nil, errors.New("endAt before startAt") + } + + if err := mysql.ProjectCreate(p); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "ProjectCreateProject", + }).Warnf("mysql.ProjectCreate: %v", err) + return nil, err + } + return p, nil +} + +func ProjectUpdateProject(idv string, patch *model.Project) (*model.Project, error) { + pid := strings.TrimSpace(idv) + if pid == "" { + return nil, ErrNotFound + } + existing, found, err := mysql.ProjectByID(&pid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "ProjectUpdateProject", + }).Warnf("mysql.ProjectByID: %v", err) + return nil, err + } + if !found || existing == nil { + logrus.WithFields(logrus.Fields{ + "func": "ProjectUpdateProject", + }).Warnf("mysql.ProjectByID: not found") + return nil, ErrNotFound + } + if patch == nil { + return existing, nil + } + + if s := strings.TrimSpace(patch.Name); s != "" { + existing.Name = s + } + if s := strings.TrimSpace(patch.TypeCode); s != "" { + existing.TypeCode = s + } + if s := strings.TrimSpace(patch.BuildingIDs); s != "" { + existing.BuildingIDs = s + } + if s := strings.TrimSpace(patch.StatusCode); s != "" { + existing.StatusCode = s + } + if s := strings.TrimSpace(patch.ResponsibleOrgID); s != "" { + existing.ResponsibleOrgID = s + } + if s := strings.TrimSpace(patch.ImplementOrgIDs); s != "" { + existing.ImplementOrgIDs = s + } + if s := strings.TrimSpace(patch.LeaderPersonID); s != "" { + existing.LeaderPersonID = s + } + if s := strings.TrimSpace(patch.ParticipantPersonIDs); s != "" { + existing.ParticipantPersonIDs = s + } + if !patch.StartAt.IsZero() { + existing.StartAt = patch.StartAt + } + if !patch.EndAt.IsZero() { + existing.EndAt = patch.EndAt + } + if patch.CompletedAt != nil { + t := *patch.CompletedAt + existing.CompletedAt = &t + } + if patch.CompletedAt == nil && patch.Description == "" && patch.Attachments == "" { + } + if patch.Description != "" { + existing.Description = strings.TrimSpace(patch.Description) + } + if patch.Attachments != "" { + existing.Attachments = strings.TrimSpace(patch.Attachments) + } + if s := strings.TrimSpace(patch.OrgID); s != "" { + existing.OrgID = s + } + if s := strings.TrimSpace(patch.CreatorUserID); s != "" { + existing.CreatorUserID = s + } + if s := strings.TrimSpace(patch.Remark); s != "" { + existing.Remark = s + } + + if existing.EndAt.Before(existing.StartAt) { + return nil, errors.New("endAt before startAt") + } + + ok, err := mysql.ProjectUpdate(existing) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "ProjectUpdateProject", + }).Warnf("mysql.ProjectUpdate: %v", err) + return nil, err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "ProjectUpdateProject", + }).Warnf("mysql.ProjectUpdate: not found") + return nil, ErrNotFound + } + return existing, nil +} + +func ProjectDelete(idv string) error { + pid := strings.TrimSpace(idv) + if pid == "" { + return ErrNotFound + } + ok, err := mysql.ProjectDelete(&pid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "ProjectDelete", + }).Warnf("mysql.ProjectDelete: %v", err) + return err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "ProjectDelete", + }).Warnf("mysql.ProjectDelete: not found") + return ErrNotFound + } + return nil +} + +func ProjectGet(idv string) (*model.Project, error) { + pid := strings.TrimSpace(idv) + if pid == "" { + return nil, ErrNotFound + } + p, found, err := mysql.ProjectByID(&pid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "ProjectGet", + }).Warnf("mysql.ProjectByID: %v", err) + return nil, err + } + if !found || p == nil { + logrus.WithFields(logrus.Fields{ + "func": "ProjectGet", + }).Warnf("mysql.ProjectByID: not found") + return nil, ErrNotFound + } + return p, nil +} + +func ProjectList(orgID, keyword *string, page, size int) ([]model.Project, int64, error) { + if page < 1 { + page = 1 + } + if size < 1 { + size = 20 + } + if size > 200 { + size = 200 + } + offset := (page - 1) * size + return mysql.ProjectList(orgID, keyword, &offset, &size) +} diff --git a/service/rbac-service.go b/service/rbac-service.go index d33aa60..5f4e03a 100644 --- a/service/rbac-service.go +++ b/service/rbac-service.go @@ -2,14 +2,14 @@ package service import "myschools.me/heritage/heritage-api/mysql" -func PermissionDefined(permissionCode string) (bool, error) { +func RbacPermissionDefined(permissionCode string) (bool, error) { return mysql.PermissionDefined(&permissionCode) } -func UserRoleIDByUserID(userID string) (*string, bool, error) { +func RbacUserRoleIDByUserID(userID string) (*string, bool, error) { return mysql.UserRoleIDByUserID(&userID) } -func RoleHasPermission(roleID *string, permissionCode string) (bool, error) { +func RbacRoleHasPermission(roleID *string, permissionCode string) (bool, error) { return mysql.RoleHasPermission(roleID, &permissionCode) } diff --git a/service/readme.md b/service/readme.md new file mode 100644 index 0000000..7526819 --- /dev/null +++ b/service/readme.md @@ -0,0 +1 @@ +1. 当前层出现错误需要进行logrus记录 \ No newline at end of file diff --git a/service/task-service.go b/service/task-service.go new file mode 100644 index 0000000..fc04076 --- /dev/null +++ b/service/task-service.go @@ -0,0 +1,188 @@ +package service + +import ( + "errors" + "strings" + + "github.com/sirupsen/logrus" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/mysql" +) + +func TaskCreateTask(t *model.Task) (*model.Task, error) { + if t == nil { + return nil, errors.New("invalid task") + } + t.ID = strings.TrimSpace(t.ID) + if t.ID == "" { + t.ID = newID() + } + t.Name = strings.TrimSpace(t.Name) + t.OrgID = strings.TrimSpace(t.OrgID) + t.ProjectID = strings.TrimSpace(t.ProjectID) + t.ObjectIDs = strings.TrimSpace(t.ObjectIDs) + t.TypeCode = strings.TrimSpace(t.TypeCode) + t.IndicatorIDs = strings.TrimSpace(t.IndicatorIDs) + t.PackageIDs = strings.TrimSpace(t.PackageIDs) + t.ExecutorPersonID = strings.TrimSpace(t.ExecutorPersonID) + t.ConfirmerPersonID = strings.TrimSpace(t.ConfirmerPersonID) + t.StatusCode = strings.TrimSpace(t.StatusCode) + t.Remark = strings.TrimSpace(t.Remark) + + if t.ObjectIDs == "" || t.TypeCode == "" || t.ExecutorPersonID == "" || t.ConfirmerPersonID == "" || t.StatusCode == "" { + return nil, errors.New("missing required fields") + } + + if err := mysql.TaskCreate(t); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "TaskCreateTask", + }).Warnf("mysql.TaskCreate: %v", err) + return nil, err + } + return t, nil +} + +func TaskUpdateTask(idv string, patch *model.Task) (*model.Task, error) { + tid := strings.TrimSpace(idv) + if tid == "" { + return nil, ErrNotFound + } + existing, found, err := mysql.TaskByID(&tid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "TaskUpdateTask", + }).Warnf("mysql.TaskByID: %v", err) + return nil, err + } + if !found || existing == nil { + logrus.WithFields(logrus.Fields{ + "func": "TaskUpdateTask", + }).Warnf("mysql.TaskByID: not found") + return nil, ErrNotFound + } + if patch == nil { + return existing, nil + } + + if s := strings.TrimSpace(patch.Name); s != "" { + existing.Name = s + } + if s := strings.TrimSpace(patch.OrgID); s != "" { + existing.OrgID = s + } + if s := strings.TrimSpace(patch.ProjectID); s != "" { + existing.ProjectID = s + } + if s := strings.TrimSpace(patch.ObjectIDs); s != "" { + existing.ObjectIDs = s + } + if s := strings.TrimSpace(patch.TypeCode); s != "" { + existing.TypeCode = s + } + if s := strings.TrimSpace(patch.IndicatorIDs); s != "" { + existing.IndicatorIDs = s + } + if s := strings.TrimSpace(patch.PackageIDs); s != "" { + existing.PackageIDs = s + } + if s := strings.TrimSpace(patch.ExecutorPersonID); s != "" { + existing.ExecutorPersonID = s + } + if s := strings.TrimSpace(patch.ConfirmerPersonID); s != "" { + existing.ConfirmerPersonID = s + } + if !patch.StartAt.IsZero() { + existing.StartAt = patch.StartAt + } + if !patch.EndAt.IsZero() { + existing.EndAt = patch.EndAt + } + if patch.ExecutedAt != nil { + t := *patch.ExecutedAt + existing.ExecutedAt = &t + } + existing.SinglePoint = patch.SinglePoint + if patch.PeriodicType != 0 { + existing.PeriodicType = patch.PeriodicType + } + if patch.PeriodDays != 0 { + existing.PeriodDays = patch.PeriodDays + } + if s := strings.TrimSpace(patch.StatusCode); s != "" { + existing.StatusCode = s + } + if s := strings.TrimSpace(patch.Remark); s != "" { + existing.Remark = s + } + + ok, err := mysql.TaskUpdate(existing) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "TaskUpdateTask", + }).Warnf("mysql.TaskUpdate: %v", err) + return nil, err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "TaskUpdateTask", + }).Warnf("mysql.TaskUpdate: not found") + return nil, ErrNotFound + } + return existing, nil +} + +func TaskDeleteTask(idv string) error { + tid := strings.TrimSpace(idv) + if tid == "" { + return ErrNotFound + } + ok, err := mysql.TaskDelete(&tid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "TaskDeleteTask", + }).Warnf("mysql.TaskDelete: %v", err) + return err + } + if !ok { + logrus.WithFields(logrus.Fields{ + "func": "TaskDeleteTask", + }).Warnf("mysql.TaskDelete: not found") + return ErrNotFound + } + return nil +} + +func TaskGetTask(idv string) (*model.Task, error) { + tid := strings.TrimSpace(idv) + if tid == "" { + return nil, ErrNotFound + } + t, found, err := mysql.TaskByID(&tid) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "TaskGetTask", + }).Warnf("mysql.TaskByID: %v", err) + return nil, err + } + if !found || t == nil { + logrus.WithFields(logrus.Fields{ + "func": "TaskGetTask", + }).Warnf("mysql.TaskByID: not found") + return nil, ErrNotFound + } + return t, nil +} + +func TaskListTasks(projectID, keyword *string, page, size int) ([]model.Task, int64, error) { + if page < 1 { + page = 1 + } + if size < 1 { + size = 20 + } + if size > 200 { + size = 200 + } + offset := (page - 1) * size + return mysql.TaskList(projectID, keyword, &offset, &size) +}