Files
silk/server-go/internal/handler/lamp.go
T

239 lines
7.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"bytes"
"io"
"net/http"
"strconv"
"time"
"silk-server-go/internal/middleware"
"silk-server-go/internal/model"
"silk-server-go/internal/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// RegisterLampRoutes 注册 LAMP 检测路由
func RegisterLampRoutes(rg *gin.RouterGroup, db *gorm.DB, s3 *service.S3Service, imageBucket string) {
read := middleware.RequirePermission(db, "lamp:read")
write := middleware.RequirePermission(db, "lamp:write")
rg.GET("/lamp-tests", read, listLampTests(db))
rg.POST("/lamp-tests", write, createLampTest(db))
rg.PATCH("/lamp-tests/:id", write, updateLampTest(db))
rg.DELETE("/lamp-tests/:id", write, deleteLampTest(db))
rg.GET("/lamp-tests/:id/steps", read, listLampTestSteps(db))
rg.PATCH("/lamp-tests/:id/steps/:stepNo", write, updateLampTestStep(db))
rg.POST("/lamp-tests/:id/result-image", write, uploadLampResultImage(db, s3, imageBucket))
}
// listLampTests 检测任务单列表(roomId/batchId/status 过滤)
func listLampTests(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
q := db.Model(&model.LampTest{})
if room := c.Query("roomId"); room != "" {
q = q.Where("room_id = ?", room)
}
if batch := c.Query("batchId"); batch != "" {
q = q.Where("batch_id = ?", batch)
}
if status := c.Query("status"); status != "" {
q = q.Where("status = ?", status)
}
var list []model.LampTest
q.Order("created_at DESC").Find(&list)
c.JSON(http.StatusOK, list)
}
}
// createLampTest 新建任务单并自动生成标准 5 步
func createLampTest(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
var t model.LampTest
if err := c.ShouldBindJSON(&t); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
t.ID = ""
if t.RoomID != nil && !isUUID(*t.RoomID) {
c.JSON(http.StatusBadRequest, gin.H{"error": "roomId 不是合法的 UUID"})
return
}
if t.BatchID != nil && !isUUID(*t.BatchID) {
c.JSON(http.StatusBadRequest, gin.H{"error": "batchId 不是合法的 UUID"})
return
}
if t.Status == "" {
t.Status = "pending"
}
t.OperatorID = currentUserID(c)
if err := db.Create(&t).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "创建失败"})
return
}
for i, name := range model.DefaultLampSteps() {
step := model.LampTestStep{LampTestID: t.ID, StepNo: i + 1, Name: name}
if err := db.Create(&step).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建步骤失败"})
return
}
}
c.JSON(http.StatusCreated, t)
}
}
// updateLampTest 更新任务单;result 合法时自动置状态 resulted
func updateLampTest(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
var t model.LampTest
if db.Where("id = ?", id).First(&t).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "lamp test not found"})
return
}
updates, err := bindUpdates(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if r, ok := updates["result"]; ok {
result, _ := r.(string)
if result != "" && !model.ValidLampResult(result) {
c.JSON(http.StatusBadRequest, gin.H{"error": "result 仅支持 positive/negative/invalid"})
return
}
if result != "" {
updates["status"] = "resulted"
updates["resulted_at"] = time.Now()
} else {
updates["status"] = "testing"
}
}
if len(updates) > 0 {
db.Model(&model.LampTest{}).Where("id = ?", id).Updates(updates)
}
db.Where("id = ?", id).First(&t)
c.JSON(http.StatusOK, t)
}
}
// deleteLampTest 删除任务单(级联删除步骤)
func deleteLampTest(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
var t model.LampTest
if db.Where("id = ?", id).First(&t).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "lamp test not found"})
return
}
db.Where("lamp_test_id = ?", id).Delete(&model.LampTestStep{})
db.Where("id = ?", id).Delete(&model.LampTest{})
c.JSON(http.StatusOK, gin.H{"id": id})
}
}
// listLampTestSteps 任务单步骤
func listLampTestSteps(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
var t model.LampTest
if db.Where("id = ?", id).First(&t).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "lamp test not found"})
return
}
var steps []model.LampTestStep
db.Where("lamp_test_id = ?", id).Order("step_no ASC").Find(&steps)
c.JSON(http.StatusOK, steps)
}
}
// updateLampTestStep 更新步骤完成状态
func updateLampTestStep(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
stepNo, err := strconv.Atoi(c.Param("stepNo"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "stepNo 不合法"})
return
}
var step model.LampTestStep
if db.Where("lamp_test_id = ? AND step_no = ?", id, stepNo).First(&step).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "step not found"})
return
}
var body struct {
Done *bool `json:"done"`
Note *string `json:"note"`
}
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
updates := map[string]interface{}{}
if body.Done != nil {
updates["done"] = *body.Done
if *body.Done {
updates["done_at"] = time.Now()
} else {
updates["done_at"] = nil
}
if stepNo == 5 && *body.Done {
db.Model(&model.LampTest{}).Where("id = ?", id).Update("status", "testing")
}
}
if body.Note != nil {
updates["note"] = *body.Note
}
if len(updates) > 0 {
db.Model(&model.LampTestStep{}).Where("id = ?", step.ID).Updates(updates)
}
db.Where("id = ?", step.ID).First(&step)
c.JSON(http.StatusOK, step)
}
}
// uploadLampResultImage 结果照片上传(S3 lamp/ 前缀)
func uploadLampResultImage(db *gorm.DB, s3 *service.S3Service, bucket string) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
var t model.LampTest
if db.Where("id = ?", id).First(&t).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "lamp test not found"})
return
}
file, header, err := c.Request.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "请选择图片文件(字段名 file"})
return
}
defer file.Close()
if err := validateImageFile(header.Filename, header.Size); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
imageBytes, err := io.ReadAll(file)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "读取图片失败"})
return
}
key, ext, err := buildObjectKey("lamp", header.Filename)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
contentType, _ := imageContentType(ext)
if err := s3.EnsureBucket(bucket); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "图片存储不可用: " + err.Error()})
return
}
if err := s3.UploadImage(bucket, key, bytes.NewReader(imageBytes), contentType); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "图片上传失败: " + err.Error()})
return
}
url := s3.Endpoint() + "/" + bucket + "/" + key
db.Model(&model.LampTest{}).Where("id = ?", id).Update("result_image_url", url)
c.JSON(http.StatusOK, gin.H{"url": url})
}
}