chore: 同步本地 v9 整改与运营能力
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"silk-server-go/internal/middleware"
|
||||
"silk-server-go/internal/model"
|
||||
"silk-server-go/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// RegisterFarmRoutes 注册产量、死亡、淘汰、损失和成本记录路由。
|
||||
func RegisterFarmRoutes(rg *gin.RouterGroup, db *gorm.DB) {
|
||||
read := middleware.RequirePermission(db, "farm:read")
|
||||
write := middleware.RequirePermission(db, "farm:write")
|
||||
rg.GET("/production-loss-records", read, listProductionLossRecords(db))
|
||||
rg.GET("/production-loss-records/stats", read, productionLossStats(db))
|
||||
rg.POST("/production-loss-records", write, createProductionLossRecord(db))
|
||||
rg.PATCH("/production-loss-records/:id", write, updateProductionLossRecord(db))
|
||||
rg.DELETE("/production-loss-records/:id", write, deleteProductionLossRecord(db))
|
||||
}
|
||||
|
||||
func listProductionLossRecords(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
q := db.Model(&model.ProductionLossRecord{}).
|
||||
Joins("LEFT JOIN batches b ON b.id = production_loss_records.batch_id")
|
||||
q = applyRoomScope(q, c, "COALESCE(production_loss_records.room_id, b.room_id)")
|
||||
if roomID := c.Query("roomId"); roomID != "" {
|
||||
q = q.Where("production_loss_records.room_id = ?", roomID)
|
||||
}
|
||||
if batchID := c.Query("batchId"); batchID != "" {
|
||||
q = q.Where("production_loss_records.batch_id = ?", batchID)
|
||||
}
|
||||
var list []model.ProductionLossRecord
|
||||
q.Order("production_loss_records.record_date DESC").Limit(200).Find(&list)
|
||||
c.JSON(http.StatusOK, list)
|
||||
}
|
||||
}
|
||||
|
||||
func createProductionLossRecord(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var body struct {
|
||||
RoomID *string `json:"roomId"`
|
||||
BatchID *string `json:"batchId"`
|
||||
RecordDate *time.Time `json:"recordDate"`
|
||||
DeathCount *int `json:"deathCount"`
|
||||
CulledCount *int `json:"culledCount"`
|
||||
YieldKg *float64 `json:"yieldKg"`
|
||||
LossKg *float64 `json:"lossKg"`
|
||||
CostType *string `json:"costType"`
|
||||
CostAmount *float64 `json:"costAmount"`
|
||||
Note *string `json:"note"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if body.BatchID != nil {
|
||||
if !requireObjectAccess(c, db, "batch", *body.BatchID) {
|
||||
return
|
||||
}
|
||||
if body.RoomID == nil {
|
||||
if roomID, ok := objectRoomID(db, "batch", *body.BatchID); ok {
|
||||
body.RoomID = roomID
|
||||
}
|
||||
}
|
||||
}
|
||||
if body.RoomID != nil && !canAccessRoom(db, c, body.RoomID) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "无权在该蚕房下记录产量损失"})
|
||||
return
|
||||
}
|
||||
record := model.ProductionLossRecord{
|
||||
RoomID: body.RoomID, BatchID: body.BatchID,
|
||||
DeathCount: body.DeathCount, CulledCount: body.CulledCount,
|
||||
YieldKg: body.YieldKg, LossKg: body.LossKg,
|
||||
CostType: body.CostType, CostAmount: body.CostAmount,
|
||||
Note: body.Note, CreatedBy: currentUserID(c),
|
||||
}
|
||||
if body.RecordDate != nil {
|
||||
record.RecordDate = *body.RecordDate
|
||||
} else {
|
||||
record.RecordDate = time.Now()
|
||||
}
|
||||
if err := model.ValidateProductionLossRecord(record); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if err := db.Create(&record).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "创建产量损失记录失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, record)
|
||||
}
|
||||
}
|
||||
|
||||
func updateProductionLossRecord(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if !requireObjectAccess(c, db, "production_loss_record", id) {
|
||||
return
|
||||
}
|
||||
var record model.ProductionLossRecord
|
||||
if db.Where("id = ?", id).First(&record).Error != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "production loss record not found"})
|
||||
return
|
||||
}
|
||||
updates, err := bindUpdates(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if roomID, ok := updates["room_id"].(string); ok && !canAccessRoom(db, c, &roomID) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "无权迁移该记录到指定蚕房"})
|
||||
return
|
||||
}
|
||||
if batchID, ok := updates["batch_id"].(string); ok && !requireObjectAccess(c, db, "batch", batchID) {
|
||||
return
|
||||
}
|
||||
if len(updates) > 0 {
|
||||
if err := db.Model(&model.ProductionLossRecord{}).Where("id = ?", id).Updates(updates).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "更新产量损失记录失败"})
|
||||
return
|
||||
}
|
||||
}
|
||||
db.Where("id = ?", id).First(&record)
|
||||
c.JSON(http.StatusOK, record)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteProductionLossRecord(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if !requireObjectAccess(c, db, "production_loss_record", id) {
|
||||
return
|
||||
}
|
||||
var record model.ProductionLossRecord
|
||||
if db.Where("id = ?", id).First(&record).Error != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "production loss record not found"})
|
||||
return
|
||||
}
|
||||
db.Where("id = ?", id).Delete(&model.ProductionLossRecord{})
|
||||
c.JSON(http.StatusOK, gin.H{"id": id})
|
||||
}
|
||||
}
|
||||
|
||||
func productionLossStats(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
q := db.Table("production_loss_records pl").
|
||||
Select(`
|
||||
COALESCE(pl.room_id::text, '') AS room_id,
|
||||
COALESCE(pl.batch_id::text, '') AS batch_id,
|
||||
COALESCE(SUM(pl.death_count), 0) AS death_count,
|
||||
COALESCE(SUM(pl.culled_count), 0) AS culled_count,
|
||||
COALESCE(SUM(pl.yield_kg), 0) AS yield_kg,
|
||||
COALESCE(SUM(pl.loss_kg), 0) AS loss_kg,
|
||||
COALESCE(SUM(pl.cost_amount), 0) AS cost_amount,
|
||||
COUNT(*) AS record_count
|
||||
`).
|
||||
Joins("LEFT JOIN batches b ON b.id = pl.batch_id")
|
||||
q = applyRoomScope(q, c, "COALESCE(pl.room_id, b.room_id)")
|
||||
if from := c.Query("from"); from != "" {
|
||||
q = q.Where("pl.record_date >= ?", from)
|
||||
}
|
||||
if to := c.Query("to"); to != "" {
|
||||
q = q.Where("pl.record_date <= ?", to)
|
||||
}
|
||||
var rows []service.ProductionLossRow
|
||||
q.Group("pl.room_id, pl.batch_id").Scan(&rows)
|
||||
stats := service.AggregateProductionLoss(rows)
|
||||
fillProductionStatNames(db, stats)
|
||||
c.JSON(http.StatusOK, stats)
|
||||
}
|
||||
}
|
||||
|
||||
func fillProductionStatNames(db *gorm.DB, stats []service.ProductionLossStat) {
|
||||
roomNames := make(map[string]string)
|
||||
batchNames := make(map[string]string)
|
||||
for _, s := range stats {
|
||||
if s.RoomID != "" {
|
||||
if _, ok := roomNames[s.RoomID]; !ok {
|
||||
var room model.Room
|
||||
if db.Select("id", "name").Where("id = ?", s.RoomID).First(&room).Error == nil {
|
||||
roomNames[s.RoomID] = room.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.BatchID != "" {
|
||||
if _, ok := batchNames[s.BatchID]; !ok {
|
||||
var batch model.Batch
|
||||
if db.Select("id", "name").Where("id = ?", s.BatchID).First(&batch).Error == nil {
|
||||
batchNames[s.BatchID] = batch.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range stats {
|
||||
stats[i].RoomName = roomNames[stats[i].RoomID]
|
||||
stats[i].BatchName = batchNames[stats[i].BatchID]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user