业务数据进入

This commit is contained in:
suguo 2026-03-13 16:35:54 +08:00
parent 0e9533feac
commit 495e98d424
46 changed files with 3417 additions and 143 deletions

View File

@ -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": "权限校验失败",

View File

@ -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)
}

2
go.mod
View File

@ -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

View File

@ -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()
}

View File

@ -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
}

188
handler/data-handler.go Normal file
View File

@ -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)
}

189
handler/device-handler.go Normal file
View File

@ -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,
})
}

27
handler/menu-handler.go Normal file
View File

@ -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,
})
}

112
handler/org-handler.go Normal file
View File

@ -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,
})
}

187
handler/point-handler.go Normal file
View File

@ -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,
})
}

256
handler/project-handler.go Normal file
View File

@ -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,
})
}

2
handler/readme.md Normal file
View File

@ -0,0 +1,2 @@
1. 当前层出现错误需要返回给前端显示,注意错误信息不能包含敏感信息
2. 当前层主要控制请求参数的校验和响应格式

246
handler/task-handler.go Normal file
View File

@ -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,
})
}

View File

@ -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()
// 等待服务关闭信号,使用通道

20
model/data-model.go Normal file
View File

@ -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"`
}

45
model/device-model.go Normal file
View File

@ -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"`
}

15
model/menu-model.go Normal file
View File

@ -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:"-"`
}

15
model/org-model.go Normal file
View File

@ -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"`
}

View File

@ -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"`

29
model/point-model.go Normal file
View File

@ -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"`
}

32
model/project-model.go Normal file
View File

@ -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"`
}

32
model/task-model.go Normal file
View File

@ -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"`
}

88
mysql/data-mysql.go Normal file
View File

@ -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
}

78
mysql/device-mysql.go Normal file
View File

@ -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
}

50
mysql/menu-mysql.go Normal file
View File

@ -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
}

74
mysql/org-mysql.go Normal file
View File

@ -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
}

View File

@ -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
}

84
mysql/point-mysql.go Normal file
View File

@ -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
}

82
mysql/project-mysql.go Normal file
View File

@ -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
}

68
mysql/role-mysql.go Normal file
View File

@ -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
}

View File

@ -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, "-", "")
}

81
mysql/task-mysql.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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",

View File

@ -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, "-", "")
}
// 专用给创建新用户的ID32位
func newUserID() string {
i := uuid.Must(uuid.NewV7()).String()
return strings.ReplaceAll(i, "-", "")
}
// 专给机构创建用的ID20位
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, "-", "")

View File

@ -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)
}
}
}

151
service/data-service.go Normal file
View File

@ -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)
}

171
service/device-service.go Normal file
View File

@ -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)
}

71
service/menu-service.go Normal file
View File

@ -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
}

136
service/org-service.go Normal file
View File

@ -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)
}

View File

@ -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
}

172
service/point-service.go Normal file
View File

@ -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)
}

205
service/project-service.go Normal file
View File

@ -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)
}

View File

@ -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)
}

1
service/readme.md Normal file
View File

@ -0,0 +1 @@
1. 当前层出现错误需要进行logrus记录

188
service/task-service.go Normal file
View File

@ -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)
}