feat: gateway store.
This commit is contained in:
+9
-1
@@ -30,6 +30,7 @@ const (
|
|||||||
analysisTypeHeartRateWithSteps = "heart_rate_with_steps"
|
analysisTypeHeartRateWithSteps = "heart_rate_with_steps"
|
||||||
sourceUpload = "upload"
|
sourceUpload = "upload"
|
||||||
sourceCloud = "cloud"
|
sourceCloud = "cloud"
|
||||||
|
sourceWechat = "wechat"
|
||||||
)
|
)
|
||||||
|
|
||||||
// readDocxContent 读取 .docx 文件并将其转换为结构化文本
|
// readDocxContent 读取 .docx 文件并将其转换为结构化文本
|
||||||
@@ -449,6 +450,13 @@ func resolveTeachingPlanContent(c *gin.Context, form *multipart.Form, source str
|
|||||||
}
|
}
|
||||||
content, err := readDocxContent(docxFiles[0])
|
content, err := readDocxContent(docxFiles[0])
|
||||||
return content, docxFiles[0].Size, err
|
return content, docxFiles[0].Size, err
|
||||||
|
case sourceWechat:
|
||||||
|
docxFiles := form.File["teaching_plan"]
|
||||||
|
if len(docxFiles) == 0 {
|
||||||
|
return "", 0, fmt.Errorf("Missing required file: teaching_plan (.docx)")
|
||||||
|
}
|
||||||
|
content, err := readDocxContent(docxFiles[0])
|
||||||
|
return content, docxFiles[0].Size, err
|
||||||
case sourceCloud:
|
case sourceCloud:
|
||||||
lessonPlanID := c.PostForm("lesson_plan_id")
|
lessonPlanID := c.PostForm("lesson_plan_id")
|
||||||
if strings.TrimSpace(lessonPlanID) == "" {
|
if strings.TrimSpace(lessonPlanID) == "" {
|
||||||
@@ -461,6 +469,6 @@ func resolveTeachingPlanContent(c *gin.Context, form *multipart.Form, source str
|
|||||||
content, err := readDocxContentFromPath(fileRecord.FilePath)
|
content, err := readDocxContentFromPath(fileRecord.FilePath)
|
||||||
return content, fileRecord.FileSize, err
|
return content, fileRecord.FileSize, err
|
||||||
default:
|
default:
|
||||||
return "", 0, fmt.Errorf("invalid teaching_plan_source, expected %s or %s", sourceUpload, sourceCloud)
|
return "", 0, fmt.Errorf("invalid teaching_plan_source, expected %s, %s or %s", sourceUpload, sourceWechat, sourceCloud)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,6 +577,8 @@ func translateSourceType(sourceType string) string {
|
|||||||
switch strings.TrimSpace(strings.ToLower(sourceType)) {
|
switch strings.TrimSpace(strings.ToLower(sourceType)) {
|
||||||
case "upload":
|
case "upload":
|
||||||
return "上传文件"
|
return "上传文件"
|
||||||
|
case "wechat":
|
||||||
|
return "微信分享"
|
||||||
case "cloud":
|
case "cloud":
|
||||||
return "云端文件"
|
return "云端文件"
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -74,6 +74,47 @@ func (gc *GatewayAdminController) List(c *gin.Context) {
|
|||||||
writeSuccess(c, http.StatusOK, "查询成功", items)
|
writeSuccess(c, http.StatusOK, "查询成功", items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetByMACForUser 按 MAC 查询网关信息
|
||||||
|
// GET /api/v1/gateways/by-mac?mac=
|
||||||
|
func (gc *GatewayAdminController) GetByMACForUser(c *gin.Context) {
|
||||||
|
macInput := strings.ToUpper(strings.TrimSpace(c.Query("mac")))
|
||||||
|
if macInput == "" {
|
||||||
|
writeError(c, http.StatusBadRequest, "mac参数不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 规范化:去掉冒号和横线,兼容不同存储格式
|
||||||
|
macClean := strings.ReplaceAll(strings.ReplaceAll(macInput, ":", ""), "-", "")
|
||||||
|
|
||||||
|
query := gc.DB.Where("REPLACE(REPLACE(UPPER(mac), ':', ''), '-', '') = ?", macClean)
|
||||||
|
|
||||||
|
roleValue, _ := c.Get("role")
|
||||||
|
role, _ := roleValue.(models.UserRole)
|
||||||
|
if role != models.UserRoleSuperAdmin {
|
||||||
|
regionIDs, err := getUserRegionIDsFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
writeError(c, http.StatusForbidden, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(regionIDs) == 0 {
|
||||||
|
writeError(c, http.StatusForbidden, "当前用户未配置可访问区域")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
query = query.Where("region_id IN ?", regionIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
var item models.Gateway
|
||||||
|
if err := query.First(&item).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
writeError(c, http.StatusNotFound, "未找到该网关")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeError(c, http.StatusInternalServerError, "查询网关失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSuccess(c, http.StatusOK, "查询成功", item)
|
||||||
|
}
|
||||||
|
|
||||||
// Create 创建新网关
|
// Create 创建新网关
|
||||||
// POST /api/gateways
|
// POST /api/gateways
|
||||||
func (gc *GatewayAdminController) Create(c *gin.Context) {
|
func (gc *GatewayAdminController) Create(c *gin.Context) {
|
||||||
@@ -341,3 +382,22 @@ func respondGatewayLookupError(c *gin.Context, err error) {
|
|||||||
}
|
}
|
||||||
writeError(c, http.StatusInternalServerError, "查询网关时出错")
|
writeError(c, http.StatusInternalServerError, "查询网关时出错")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUserRegionIDsFromContext(c *gin.Context) ([]uint32, error) {
|
||||||
|
regionValue, exists := c.Get("regionIDs")
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.New("missing user regions")
|
||||||
|
}
|
||||||
|
regionIDs, ok := regionValue.([]uint32)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("invalid user regions")
|
||||||
|
}
|
||||||
|
filtered := make([]uint32, 0, len(regionIDs))
|
||||||
|
for _, regionID := range regionIDs {
|
||||||
|
if regionID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filtered = append(filtered, regionID)
|
||||||
|
}
|
||||||
|
return filtered, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ type regionStatisticsItem struct {
|
|||||||
TotalOutputSizeBytes int64 `json:"totalOutputSizeBytes"`
|
TotalOutputSizeBytes int64 `json:"totalOutputSizeBytes"`
|
||||||
TotalDurationMs int64 `json:"totalDurationMs"`
|
TotalDurationMs int64 `json:"totalDurationMs"`
|
||||||
AvgDurationMs float64 `json:"avgDurationMs"`
|
AvgDurationMs float64 `json:"avgDurationMs"`
|
||||||
|
AvgTotalCost float64 `json:"avgTotalCost"`
|
||||||
TotalOriginalFileSize int64 `json:"totalOriginalFileSize"`
|
TotalOriginalFileSize int64 `json:"totalOriginalFileSize"`
|
||||||
TotalCompressedSize int64 `json:"totalCompressedSize"`
|
TotalCompressedSize int64 `json:"totalCompressedSize"`
|
||||||
TotalCost float64 `json:"totalCost"`
|
TotalCost float64 `json:"totalCost"`
|
||||||
@@ -272,6 +273,10 @@ func (sc *StatisticsController) StatisticsByRegion(c *gin.Context) {
|
|||||||
if r.Count > 0 {
|
if r.Count > 0 {
|
||||||
avgDuration = float64(r.TotalDurationMs) / float64(r.Count)
|
avgDuration = float64(r.TotalDurationMs) / float64(r.Count)
|
||||||
}
|
}
|
||||||
|
avgTotalCost := float64(0)
|
||||||
|
if r.Count > 0 {
|
||||||
|
avgTotalCost = r.TotalCost / float64(r.Count)
|
||||||
|
}
|
||||||
kgName := ""
|
kgName := ""
|
||||||
if regionID > 0 {
|
if regionID > 0 {
|
||||||
kgName = kindergartenMap[regionID]
|
kgName = kindergartenMap[regionID]
|
||||||
@@ -297,6 +302,7 @@ func (sc *StatisticsController) StatisticsByRegion(c *gin.Context) {
|
|||||||
TotalOutputSizeBytes: r.TotalOutputSizeBytes,
|
TotalOutputSizeBytes: r.TotalOutputSizeBytes,
|
||||||
TotalDurationMs: r.TotalDurationMs,
|
TotalDurationMs: r.TotalDurationMs,
|
||||||
AvgDurationMs: avgDuration,
|
AvgDurationMs: avgDuration,
|
||||||
|
AvgTotalCost: avgTotalCost,
|
||||||
TotalOriginalFileSize: r.TotalOriginalFileSize,
|
TotalOriginalFileSize: r.TotalOriginalFileSize,
|
||||||
TotalCompressedSize: r.TotalCompressedSize,
|
TotalCompressedSize: r.TotalCompressedSize,
|
||||||
TotalCost: r.TotalCost,
|
TotalCost: r.TotalCost,
|
||||||
@@ -343,6 +349,7 @@ func (sc *StatisticsController) StatisticsByRegion(c *gin.Context) {
|
|||||||
|
|
||||||
if overall.Count > 0 {
|
if overall.Count > 0 {
|
||||||
overall.AvgDurationMs = float64(overall.TotalDurationMs) / float64(overall.Count)
|
overall.AvgDurationMs = float64(overall.TotalDurationMs) / float64(overall.Count)
|
||||||
|
overall.AvgTotalCost = overall.TotalCost / float64(overall.Count)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeSuccess(c, http.StatusOK, "query success", gin.H{
|
writeSuccess(c, http.StatusOK, "query success", gin.H{
|
||||||
|
|||||||
@@ -7,6 +7,31 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func RequireOperatorOrHigher() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
roleValue, exists := c.Get("role")
|
||||||
|
if !exists {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": "missing user role"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
role, ok := roleValue.(models.UserRole)
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": "invalid user role"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if role != models.UserRoleOperator &&
|
||||||
|
role != models.UserRoleRegionAdmin &&
|
||||||
|
role != models.UserRoleSuperAdmin {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": "insufficient role permissions"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func RequireStepTrainingAccess() gin.HandlerFunc {
|
func RequireStepTrainingAccess() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
roleValue, exists := c.Get("role")
|
roleValue, exists := c.Get("role")
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ func SetupRouter() *gin.Engine {
|
|||||||
public.POST("/register", controllers.Register)
|
public.POST("/register", controllers.Register)
|
||||||
public.POST("/login", controllers.Login)
|
public.POST("/login", controllers.Login)
|
||||||
}
|
}
|
||||||
|
v1.GET("/gateways/by-mac", middleware.JWTAuth(), middleware.RequireOperatorOrHigher(), gatewayController.GetByMACForUser)
|
||||||
|
|
||||||
auth := v1.Group("/auth")
|
auth := v1.Group("/auth")
|
||||||
{
|
{
|
||||||
auth.GET("/token", deviceTokenHandler)
|
auth.GET("/token", deviceTokenHandler)
|
||||||
|
|||||||
Reference in New Issue
Block a user