This commit is contained in:
suguo 2026-03-18 17:18:06 +08:00
parent 495e98d424
commit 3f5d062ee8
11 changed files with 287 additions and 87 deletions

View File

@ -19,7 +19,7 @@ func routerSetup(router *gin.Engine) {
protected.GET("/orgs", handler.OrgList)
protected.POST("/orgs", handler.OrgCreate)
protected.GET("/orgs/:id", handler.OrgGet)
protected.GET("/orgs/:id", handler.OrgDetail)
protected.PUT("/orgs/:id", handler.OrgUpdate)
protected.DELETE("/orgs/:id", handler.OrgDelete)
@ -54,4 +54,5 @@ func routerSetup(router *gin.Engine) {
protected.DELETE("/devices/:id", handler.DeviceDelete)
protected.GET("/menus", handler.MenuList)
protected.GET("/options", handler.OptionMap)
}

19
handler/option-handler.go Normal file
View File

@ -0,0 +1,19 @@
package handler
import (
"net/http"
"github.com/gin-gonic/gin"
"myschools.me/heritage/heritage-api/service"
)
// 系统选项map
func OptionMap(c *gin.Context) {
name := c.Query("name")
optionMap, err := service.OptionMap(&name)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"})
return
}
c.JSON(http.StatusOK, gin.H{"data": optionMap})
}

View File

@ -7,52 +7,28 @@ import (
"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
var req service.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)
o, err := service.OrgCreate(req)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"org": o})
c.JSON(http.StatusOK, service.OrgResponse{Org: o})
}
func OrgUpdate(c *gin.Context) {
id := c.Param("id")
var req OrgUpdateRequest
var req service.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)
o, err := service.OrgUpdate(id, req)
if err != nil {
if err == service.ErrNotFound {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"})
@ -61,12 +37,12 @@ func OrgUpdate(c *gin.Context) {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"org": o})
c.JSON(http.StatusOK, service.OrgResponse{Org: o})
}
func OrgDelete(c *gin.Context) {
id := c.Param("id")
if err := service.OrgDeleteOrg(id); err != nil {
if err := service.OrgDelete(id); err != nil {
if err == service.ErrNotFound {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"})
return
@ -77,9 +53,9 @@ func OrgDelete(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"data": "ok"})
}
func OrgGet(c *gin.Context) {
func OrgDetail(c *gin.Context) {
id := c.Param("id")
o, err := service.OrgGetOrg(id)
o, err := service.OrgDetail(id)
if err != nil {
if err == service.ErrNotFound {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"data": "不存在"})
@ -88,7 +64,7 @@ func OrgGet(c *gin.Context) {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"data": "查询失败"})
return
}
c.JSON(http.StatusOK, gin.H{"org": o})
c.JSON(http.StatusOK, service.OrgResponse{Org: o})
}
func OrgList(c *gin.Context) {
@ -98,15 +74,15 @@ func OrgList(c *gin.Context) {
if parentID != "" {
pid = &parentID
}
items, total, err := service.OrgListOrgs(pid, page, size)
items, total, err := service.OrgPage(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,
c.JSON(http.StatusOK, service.OrgListResponse{
Items: items,
Total: total,
Page: page,
Size: size,
})
}

31
model/artifact-model.go Normal file
View File

@ -0,0 +1,31 @@
package model
import "time"
// 文物表
type Artifact struct {
ID string `json:"id" gorm:"type:varchar(32);primaryKey"`
Code string `json:"code" gorm:"type:varchar(64);not null;uniqueIndex;comment:'文物编码'"`
Name string `json:"name" gorm:"type:varchar(128);not null;index"`
OrgID string `json:"orgId" gorm:"type:varchar(32);not null;index"`
Category string `json:"category" gorm:"type:varchar(50);default:'';comment:'文物类别'"`
Material string `json:"material" gorm:"type:varchar(50);default:'';comment:'材质'"`
Period string `json:"period" gorm:"type:varchar(50);default:'';comment:'年代'"`
Description string `json:"description" gorm:"type:text;comment:'文物描述'"`
CurrentUser string `json:"currentUser" gorm:"type:varchar(32);default:'';comment:'当前所属用户'"`
Remark string `json:"remark" gorm:"type:varchar(255);comment:'文物备注'"`
Province string `json:"province" gorm:"type:varchar(50);default:'';comment:'省'"`
City string `json:"city" gorm:"type:varchar(50);default:'';comment:'市'"`
District string `json:"district" gorm:"type:varchar(50);default:'';comment:'区/县'"`
Address string `json:"address" gorm:"type:varchar(255);default:'';comment:'详细地址'"`
StatisticsPeriod string `json:"statisticsPeriod" gorm:"type:varchar(50);default:'';comment:'统计年代'"`
LevelType string `json:"levelType" gorm:"type:varchar(50);default:'';comment:'层级类型'"`
BuildingType string `json:"buildingType" gorm:"type:varchar(50);default:'';comment:'建筑类型'"`
CurrentOrg string `json:"currentOrg" gorm:"type:varchar(32);default:'';comment:'当前所属机构'"`
Introduction string `json:"introduction" gorm:"type:text;comment:'简介'"`
Images string `json:"images" gorm:"type:varchar(255);default:'';comment:'图片'"`
Icon string `json:"icon" gorm:"type:varchar(255);default:'';comment:'图标'"`
Sort int `json:"sort" gorm:"type:int;default:0;comment:'排序'"`
CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"`
UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"`
}

8
model/option-model.go Normal file
View File

@ -0,0 +1,8 @@
package model
// 系统选项表
type Option struct {
ID uint `json:"id" gorm:"primaryKey"`
OptionName string `json:"optionName" gorm:"type:varchar(10);not null;index;comment:单位选项,level-保护单位级别,type-保护单位类别,batch-公布批次"`
OptionValue string `json:"optionValue" gorm:"type:varchar(50);not null;"`
}

View File

@ -2,14 +2,19 @@ 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"`
ID string `json:"id" gorm:"type:varchar(32);primaryKey"`
Code string `json:"code" gorm:"type:varchar(64);not null;uniqueIndex;comment:'组织编码'"`
ParentID string `json:"parentId" gorm:"type:varchar(32);index"`
Name string `json:"name" gorm:"type:varchar(128);not null;index"`
OrgType string `json:"orgType" gorm:"type:varchar(10);not null;comment:'组织类型'"`
ProtectionLevel string `json:"protectionLevel" gorm:"type:varchar(10);default:'';comment:'保护单位级别'"`
ProtectionType string `json:"protectionType" gorm:"type:varchar(10);default:'';comment:'保护单位类别'"`
AnnouncementBatch string `json:"announcementBatch" gorm:"type:varchar(50);default:'';comment:'公布批次'"`
AdminRegion string `json:"adminRegion" gorm:"type:varchar(100);default:'';comment:'行政区域'"`
Introduction string `json:"introduction" gorm:"type:text;comment:'简介'"`
Remark string `json:"remark" gorm:"type:varchar(255);default:'';comment:'组织备注'"`
CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"`
UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"`
}

47
mysql/option-mysql.go Normal file
View File

@ -0,0 +1,47 @@
package mysql
import (
"errors"
"gorm.io/gorm"
"myschools.me/heritage/heritage-api/model"
)
// 获取选项
func OptionPluck(name *string) ([]string, error) {
db, err := newDB()
if err != nil {
return nil, err
}
var options []string
if err := db.Where("option_name=?", *name).Pluck("option_value", &options).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
}
return options, nil
}
// 获取选项数量
func OptionCount() (*int64, error) {
db, err := newDB()
if err != nil {
return nil, err
}
var count int64
if err := db.Model(&model.Option{}).Count(&count).Error; err != nil {
return nil, err
}
return &count, nil
}
// OptionCreateBatch 批量创建选项
func OptionCreateBatch(options []model.Option) error {
db, err := newDB()
if err != nil {
return err
}
return db.Create(&options).Error
}

View File

@ -9,18 +9,27 @@ func init() {
if err != nil {
panic(err)
}
//系统相关表
if err := db.AutoMigrate(
&model.Role{},
&model.Permission{},
&model.User{},
&model.Org{},
&model.Menu{},
&model.Option{},
); err != nil {
panic(err)
}
//业务相关表
if err := db.AutoMigrate(
&model.Project{},
&model.Task{},
&model.Point{},
&model.DataRecord{},
&model.Device{},
&model.DeviceIngestData{},
&model.Menu{},
&model.Artifact{},
); err != nil {
panic(err)
}

View File

@ -19,12 +19,12 @@ func Bootstrap() {
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"},
{ID: 3, Name: "组织机构", Icon: "team", Path: "/org", Sort: 1, PermissionCode: "org:list"},
{ID: 4, Name: "项目管理", Icon: "project", Path: "/project", Sort: 2, PermissionCode: "project:list"},
{ID: 5, Name: "任务管理", Icon: "profile", Path: "/task", Sort: 3, PermissionCode: "task:list"},
{ID: 6, Name: "监测点位", Icon: "environment", Path: "/point", Sort: 4, PermissionCode: "point:list"},
{ID: 7, Name: "数据记录", Icon: "database", Path: "/data", Sort: 5, PermissionCode: "data:list"},
{ID: 8, Name: "设备管理", Icon: "cluster", Path: "/device", Sort: 6, PermissionCode: "device:list"},
}
if err := mysql.MenuCreateBatch(menus); err != nil {
logrus.WithFields(logrus.Fields{
@ -116,4 +116,31 @@ func Bootstrap() {
panic(err)
}
}
// 初始化组织选项
optionCount, err := mysql.OptionCount()
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "Bootstrap",
}).Warnf("mysql.OptionCount: %v", err)
panic(err)
}
if *optionCount == 0 {
orgOptions := []model.Option{
{OptionName: "level", OptionValue: "国家级"},
{OptionName: "level", OptionValue: "省级"},
{OptionName: "level", OptionValue: "市级"},
{OptionName: "type", OptionValue: "古建筑"},
{OptionName: "type", OptionValue: "古遗址"},
{OptionName: "type", OptionValue: "古墓葬"},
{OptionName: "batch", OptionValue: "第一批"},
{OptionName: "batch", OptionValue: "第二批"},
}
if err := mysql.OptionCreateBatch(orgOptions); err != nil {
logrus.WithFields(logrus.Fields{
"func": "Bootstrap",
}).Warnf("mysql.OptionCreateBatch: %v", err)
panic(err)
}
}
}

19
service/option-service.go Normal file
View File

@ -0,0 +1,19 @@
package service
import (
"github.com/sirupsen/logrus"
"myschools.me/heritage/heritage-api/mysql"
)
// 获取组织选项映射,用于前端下拉列表
func OptionMap(name *string) ([]string, error) {
options, err := mysql.OptionPluck(name)
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "OptionMap",
}).Warnf("mysql.OptionPluck: %v", err)
return nil, err
}
return options, nil
}

View File

@ -11,29 +11,78 @@ import (
var ErrNotFound = errors.New("not found")
func OrgCreateOrg(parentID, name string, enabled bool, sort int, remark string) (*model.Org, error) {
name = strings.TrimSpace(name)
// OrgCreateRequest 创建组织请求结构
type OrgCreateRequest struct {
ParentID string `json:"parentId"`
Name string `json:"name"`
Enabled *bool `json:"enabled"`
Sort *int `json:"sort"`
Remark string `json:"remark"`
ProtectionLevel string `json:"protectionLevel"`
ProtectionType string `json:"protectionType"`
AnnouncementBatch string `json:"announcementBatch"`
AdminRegion string `json:"adminRegion"`
CurrentOrg string `json:"currentOrg"`
CurrentUser string `json:"currentUser"`
Introduction string `json:"introduction"`
}
// OrgUpdateRequest 更新组织请求结构
type OrgUpdateRequest struct {
ParentID *string `json:"parentId"`
Name *string `json:"name"`
Enabled *bool `json:"enabled"`
Sort *int `json:"sort"`
Remark *string `json:"remark"`
ProtectionLevel *string `json:"protectionLevel"`
ProtectionType *string `json:"protectionType"`
AnnouncementBatch *string `json:"announcementBatch"`
AdminRegion *string `json:"adminRegion"`
CurrentOrg *string `json:"currentOrg"`
CurrentUser *string `json:"currentUser"`
Introduction *string `json:"introduction"`
}
// OrgResponse 组织响应结构
type OrgResponse struct {
Org *model.Org `json:"org"`
}
// OrgListResponse 组织列表响应结构
type OrgListResponse struct {
Items []model.Org `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
func OrgCreate(req OrgCreateRequest) (*model.Org, error) {
name := strings.TrimSpace(req.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),
ID: newID(),
ParentID: strings.TrimSpace(req.ParentID),
Name: name,
Remark: strings.TrimSpace(req.Remark),
ProtectionLevel: strings.TrimSpace(req.ProtectionLevel),
ProtectionType: strings.TrimSpace(req.ProtectionType),
AnnouncementBatch: strings.TrimSpace(req.AnnouncementBatch),
AdminRegion: strings.TrimSpace(req.AdminRegion),
Introduction: strings.TrimSpace(req.Introduction),
}
if err := mysql.OrgCreate(o); err != nil {
logrus.WithFields(logrus.Fields{
"func": "OrgCreateOrg",
"func": "OrgCreate",
}).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) {
func OrgUpdate(idv string, req OrgUpdateRequest) (*model.Org, error) {
oid := strings.TrimSpace(idv)
if oid == "" {
return nil, ErrNotFound
@ -41,45 +90,54 @@ func OrgUpdateOrg(idv string, parentID, name *string, enabled *bool, sort *int,
existing, found, err := mysql.OrgByID(&oid)
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "OrgUpdateOrg",
"func": "OrgUpdate",
}).Warnf("mysql.OrgByID: %v", err)
return nil, err
}
if !found || existing == nil {
logrus.WithFields(logrus.Fields{
"func": "OrgUpdateOrg",
"func": "OrgUpdate",
}).Warnf("mysql.OrgByID: not found")
return nil, ErrNotFound
}
if parentID != nil {
existing.ParentID = strings.TrimSpace(*parentID)
if req.ParentID != nil {
existing.ParentID = strings.TrimSpace(*req.ParentID)
}
if name != nil {
n := strings.TrimSpace(*name)
if req.Name != nil {
n := strings.TrimSpace(*req.Name)
if n == "" {
return nil, errors.New("name required")
}
existing.Name = n
}
if enabled != nil {
existing.Enabled = *enabled
if req.Remark != nil {
existing.Remark = strings.TrimSpace(*req.Remark)
}
if sort != nil {
existing.Sort = *sort
if req.ProtectionLevel != nil {
existing.ProtectionLevel = strings.TrimSpace(*req.ProtectionLevel)
}
if remark != nil {
existing.Remark = strings.TrimSpace(*remark)
if req.ProtectionType != nil {
existing.ProtectionType = strings.TrimSpace(*req.ProtectionType)
}
if req.AnnouncementBatch != nil {
existing.AnnouncementBatch = strings.TrimSpace(*req.AnnouncementBatch)
}
if req.AdminRegion != nil {
existing.AdminRegion = strings.TrimSpace(*req.AdminRegion)
}
if req.Introduction != nil {
existing.Introduction = strings.TrimSpace(*req.Introduction)
}
if err := mysql.OrgUpdate(existing); err != nil {
logrus.WithFields(logrus.Fields{
"func": "OrgUpdateOrg",
"func": "OrgUpdate",
}).Warnf("mysql.OrgUpdate: %v", err)
return nil, err
}
return existing, nil
}
func OrgDeleteOrg(idv string) error {
func OrgDelete(idv string) error {
oid := strings.TrimSpace(idv)
if oid == "" {
return ErrNotFound
@ -87,20 +145,20 @@ func OrgDeleteOrg(idv string) error {
ok, err := mysql.OrgDelete(&oid)
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "OrgDeleteOrg",
"func": "OrgDelete",
}).Warnf("mysql.OrgDelete: %v", err)
return err
}
if !ok {
logrus.WithFields(logrus.Fields{
"func": "OrgDeleteOrg",
"func": "OrgDelete",
}).Warnf("mysql.OrgDelete: not found")
return ErrNotFound
}
return nil
}
func OrgGetOrg(idv string) (*model.Org, error) {
func OrgDetail(idv string) (*model.Org, error) {
oid := strings.TrimSpace(idv)
if oid == "" {
return nil, ErrNotFound
@ -108,20 +166,20 @@ func OrgGetOrg(idv string) (*model.Org, error) {
o, found, err := mysql.OrgByID(&oid)
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "OrgGetOrg",
"func": "OrgDetail",
}).Warnf("mysql.OrgByID: %v", err)
return nil, err
}
if !found || o == nil {
logrus.WithFields(logrus.Fields{
"func": "OrgGetOrg",
"func": "OrgDetail",
}).Warnf("mysql.OrgByID: not found")
return nil, ErrNotFound
}
return o, nil
}
func OrgListOrgs(parentID *string, page, size int) ([]model.Org, int64, error) {
func OrgPage(parentID *string, page, size int) ([]model.Org, int64, error) {
if page < 1 {
page = 1
}