239 lines
7.3 KiB
Go
239 lines
7.3 KiB
Go
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})
|
||
}
|
||
}
|