You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.6 KiB
197 lines
5.6 KiB
package service
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"laic-backend/common"
|
|
"laic-backend/logger"
|
|
"laic-backend/model"
|
|
"laic-backend/tool"
|
|
"laic-backend/vo"
|
|
)
|
|
|
|
type RouteService struct{}
|
|
|
|
var DefaultRouteService = &RouteService{}
|
|
|
|
// GetPage 航线分页列表
|
|
func (s *RouteService) GetPage(userID int64, isAdmin bool, req *vo.RoutePageReq) (*common.PageResponse[model.Route], *common.BusiError) {
|
|
db := common.DB.Model(&model.Route{}).Scopes(withUserFilter(userID, isAdmin))
|
|
if req.Keyword != "" {
|
|
kw := "%" + req.Keyword + "%"
|
|
db = db.Where("name LIKE ? OR description LIKE ?", kw, kw)
|
|
}
|
|
|
|
var total int64
|
|
if err := db.Count(&total).Error; err != nil {
|
|
logger.ERROR("统计航线失败", err)
|
|
return nil, common.ErrInternal
|
|
}
|
|
var list []model.Route
|
|
// Count in the same list query so the endpoint does not issue one waypoint
|
|
// query per route. The read-only Route.WaypointCount field receives the
|
|
// correlated subquery result.
|
|
db = db.Select("route.*, (SELECT COUNT(*) FROM route_waypoint rw WHERE rw.route_id = route.id) AS waypoint_count")
|
|
if err := db.Scopes(req.Paginate).Order("created_at DESC, id DESC").Find(&list).Error; err != nil {
|
|
logger.ERROR("查询航线列表失败", err)
|
|
return nil, common.ErrInternal
|
|
}
|
|
return common.Page(req.Pagination, total, list), nil
|
|
}
|
|
|
|
// GetDetail 航线详情(含航点)
|
|
func (s *RouteService) GetDetail(userID int64, isAdmin bool, id int64) (*vo.RouteVO, *common.BusiError) {
|
|
var route model.Route
|
|
if err := common.DB.Scopes(withUserFilter(userID, isAdmin)).First(&route, id).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, common.ErrRouteNotFound
|
|
}
|
|
return nil, common.ErrInternal
|
|
}
|
|
waypoints, err := s.waypoints(id)
|
|
if err != nil {
|
|
logger.ERROR("查询航点失败", err)
|
|
return nil, common.ErrInternal
|
|
}
|
|
route.WaypointCount = int64(len(waypoints))
|
|
return &vo.RouteVO{Route: &route, Waypoints: waypoints}, nil
|
|
}
|
|
|
|
// Create 新增航线(含航点)
|
|
func (s *RouteService) Create(userID int64, req *vo.RouteCreateReq) (*vo.RouteVO, *common.BusiError) {
|
|
id, err := tool.NextID()
|
|
if err != nil {
|
|
return nil, common.ErrInternal
|
|
}
|
|
now := time.Now()
|
|
route := &model.Route{
|
|
ID: id,
|
|
UserID: userID,
|
|
Name: req.Name,
|
|
Description: req.Description,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
waypoints, err := buildWaypoints(id, req.Waypoints)
|
|
if err != nil {
|
|
return nil, common.ErrInternal
|
|
}
|
|
|
|
err = common.DB.Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.Create(route).Error; err != nil {
|
|
return err
|
|
}
|
|
if len(waypoints) > 0 {
|
|
return tx.Create(&waypoints).Error
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
logger.ERROR("新增航线失败", err)
|
|
return nil, common.ErrInternal
|
|
}
|
|
route.WaypointCount = int64(len(waypoints))
|
|
return &vo.RouteVO{Route: route, Waypoints: waypoints}, nil
|
|
}
|
|
|
|
// Update 编辑航线(提供 waypoints 则整体替换航点)
|
|
func (s *RouteService) Update(userID int64, isAdmin bool, id int64, req *vo.RouteUpdateReq) (*vo.RouteVO, *common.BusiError) {
|
|
var route model.Route
|
|
if err := common.DB.Scopes(withUserFilter(userID, isAdmin)).First(&route, id).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, common.ErrRouteNotFound
|
|
}
|
|
return nil, common.ErrInternal
|
|
}
|
|
|
|
err := common.DB.Transaction(func(tx *gorm.DB) error {
|
|
updates := map[string]any{"updated_at": time.Now()}
|
|
if req.Name != "" {
|
|
updates["name"] = req.Name
|
|
}
|
|
if req.Description != "" {
|
|
updates["description"] = req.Description
|
|
}
|
|
if err := tx.Model(&route).Updates(updates).Error; err != nil {
|
|
return err
|
|
}
|
|
if req.Waypoints != nil {
|
|
if err := tx.Where("route_id = ?", id).Delete(&model.RouteWaypoint{}).Error; err != nil {
|
|
return err
|
|
}
|
|
waypoints, err := buildWaypoints(id, *req.Waypoints)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(waypoints) > 0 {
|
|
return tx.Create(&waypoints).Error
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
logger.ERROR("更新航线失败", err)
|
|
return nil, common.ErrInternal
|
|
}
|
|
|
|
common.DB.First(&route, id)
|
|
waypoints, _ := s.waypoints(id)
|
|
route.WaypointCount = int64(len(waypoints))
|
|
return &vo.RouteVO{Route: &route, Waypoints: waypoints}, nil
|
|
}
|
|
|
|
// Delete 删除航线(含航点)
|
|
func (s *RouteService) Delete(userID int64, isAdmin bool, id int64) *common.BusiError {
|
|
err := common.DB.Transaction(func(tx *gorm.DB) error {
|
|
res := tx.Scopes(withUserFilter(userID, isAdmin)).Delete(&model.Route{}, id)
|
|
if res.Error != nil {
|
|
return res.Error
|
|
}
|
|
if res.RowsAffected == 0 {
|
|
return gorm.ErrRecordNotFound
|
|
}
|
|
return tx.Where("route_id = ?", id).Delete(&model.RouteWaypoint{}).Error
|
|
})
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return common.ErrRouteNotFound
|
|
}
|
|
logger.ERROR("删除航线失败", err)
|
|
return common.ErrInternal
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *RouteService) waypoints(routeID int64) ([]model.RouteWaypoint, error) {
|
|
var waypoints []model.RouteWaypoint
|
|
err := common.DB.Where("route_id = ?", routeID).Order("seq ASC").Find(&waypoints).Error
|
|
return waypoints, err
|
|
}
|
|
|
|
// buildWaypoints 将航点请求转换为模型,Seq 从 1 递增
|
|
func buildWaypoints(routeID int64, reqs []vo.RouteWaypointReq) ([]model.RouteWaypoint, error) {
|
|
out := make([]model.RouteWaypoint, 0, len(reqs))
|
|
now := time.Now()
|
|
for i, w := range reqs {
|
|
id, err := tool.NextID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, model.RouteWaypoint{
|
|
ID: id,
|
|
RouteID: routeID,
|
|
Seq: i + 1,
|
|
Longitude: w.Longitude,
|
|
Latitude: w.Latitude,
|
|
Altitude: w.Altitude,
|
|
Speed: w.Speed,
|
|
Yaw: w.Yaw,
|
|
HoldSec: w.HoldSec,
|
|
CreatedAt: now,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|