issue_20 #24

Merged
huang merged 13 commits from issue_20 into main 2025-09-28 01:47:48 +08:00
2 changed files with 88 additions and 34 deletions
Showing only changes of commit aaca5b1723 - Show all commits

View File

@@ -123,14 +123,14 @@ func (c *Controller) CreateDevice(ctx *gin.Context) {
const actionType = "创建设备" const actionType = "创建设备"
var req CreateDeviceRequest var req CreateDeviceRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("创建设备: 参数绑定失败: %v", err) c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return return
} }
propertiesJSON, err := json.Marshal(req.Properties) propertiesJSON, err := json.Marshal(req.Properties)
if err != nil { if err != nil {
c.logger.Errorf("创建设备: 序列化属性失败: %v", err) c.logger.Errorf("%s: 序列化属性失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "属性字段格式错误", actionType, "属性序列化失败", req.Properties) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "属性字段格式错误", actionType, "属性序列化失败", req.Properties)
return return
} }
@@ -146,24 +146,25 @@ func (c *Controller) CreateDevice(ctx *gin.Context) {
// 在创建设备前进行自检 // 在创建设备前进行自检
if !device.SelfCheck() { if !device.SelfCheck() {
c.logger.Errorf("创建设备: 设备属性自检失败: %v", device) c.logger.Errorf("%s: 设备属性自检失败: %v", actionType, device)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备属性不符合要求", actionType, "设备属性自检失败", device) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备属性不符合要求", actionType, "设备属性自检失败", device)
return return
} }
if err := c.repo.Create(device); err != nil { if err := c.repo.Create(device); err != nil {
c.logger.Errorf("创建设备: 数据库操作失败: %v", err) c.logger.Errorf("%s: 数据库操作失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建设备失败: "+err.Error(), actionType, "数据库创建失败", device) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建设备失败: "+err.Error(), actionType, "数据库创建失败", device)
return return
} }
resp, err := newDeviceResponse(device) resp, err := newDeviceResponse(device)
if err != nil { if err != nil {
c.logger.Errorf("创建设备: 序列化响应失败: %v", err) c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备创建成功,但响应生成失败", actionType, "响应序列化失败", device) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备创建成功,但响应生成失败", actionType, "响应序列化失败", device)
return return
} }
c.logger.Infof("%s: 设备创建成功, ID: %d", actionType, device.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "设备创建成功", resp, actionType, "设备创建成功", resp) controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "设备创建成功", resp, actionType, "设备创建成功", resp)
} }
@@ -179,28 +180,37 @@ func (c *Controller) GetDevice(ctx *gin.Context) {
const actionType = "获取设备" const actionType = "获取设备"
deviceID := ctx.Param("id") deviceID := ctx.Param("id")
if deviceID == "" {
c.logger.Errorf("%s: 设备ID为空", actionType)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备ID不能为空", actionType, "设备ID为空", nil)
return
}
device, err := c.repo.FindByIDString(deviceID) device, err := c.repo.FindByIDString(deviceID)
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备不存在, ID: %s", actionType, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备未找到", actionType, "设备不存在", deviceID) controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备未找到", actionType, "设备不存在", deviceID)
return return
} }
if strings.Contains(err.Error(), "无效的设备ID格式") { if strings.Contains(err.Error(), "无效的设备ID格式") {
c.logger.Errorf("%s: 设备ID格式错误: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "设备ID格式错误", deviceID) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "设备ID格式错误", deviceID)
return return
} }
c.logger.Errorf("获取设备: 数据库操作失败: %v", err) c.logger.Errorf("%s: 数据库查询失败: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备信息失败: "+err.Error(), actionType, "数据库查询失败", deviceID) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备信息失败: "+err.Error(), actionType, "数据库查询失败", deviceID)
return return
} }
resp, err := newDeviceResponse(device) resp, err := newDeviceResponse(device)
if err != nil { if err != nil {
c.logger.Errorf("获取设备: 序列化响应失败: %v", err) c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, device)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备信息失败: 内部数据格式错误", actionType, "响应序列化失败", device) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备信息失败: 内部数据格式错误", actionType, "响应序列化失败", device)
return return
} }
c.logger.Infof("%s: 获取设备信息成功, ID: %d", actionType, device.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备信息成功", resp, actionType, "获取设备信息成功", resp) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备信息成功", resp, actionType, "获取设备信息成功", resp)
} }
@@ -215,18 +225,19 @@ func (c *Controller) ListDevices(ctx *gin.Context) {
const actionType = "获取设备列表" const actionType = "获取设备列表"
devices, err := c.repo.ListAll() devices, err := c.repo.ListAll()
if err != nil { if err != nil {
c.logger.Errorf("获取设备列表: 数据库操作失败: %v", err) c.logger.Errorf("%s: 数据库查询失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备列表失败: "+err.Error(), actionType, "数据库查询失败", nil) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备列表失败: "+err.Error(), actionType, "数据库查询失败", nil)
return return
} }
resp, err := newListDeviceResponse(devices) resp, err := newListDeviceResponse(devices)
if err != nil { if err != nil {
c.logger.Errorf("获取设备列表: 序列化响应失败: %v", err) c.logger.Errorf("%s: 序列化响应失败: %v, Devices: %+v", actionType, err, devices)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备列表失败: 内部数据格式错误", actionType, "响应序列化失败", devices) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备列表失败: 内部数据格式错误", actionType, "响应序列化失败", devices)
return return
} }
c.logger.Infof("%s: 获取设备列表成功, 数量: %d", actionType, len(devices))
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备列表成功", resp, actionType, "获取设备列表成功", resp) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备列表成功", resp, actionType, "获取设备列表成功", resp)
} }
@@ -248,14 +259,16 @@ func (c *Controller) UpdateDevice(ctx *gin.Context) {
existingDevice, err := c.repo.FindByIDString(deviceID) existingDevice, err := c.repo.FindByIDString(deviceID)
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备不存在, ID: %s", actionType, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备未找到", actionType, "设备不存在", deviceID) controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备未找到", actionType, "设备不存在", deviceID)
return return
} }
if strings.Contains(err.Error(), "无效的设备ID格式") { if strings.Contains(err.Error(), "无效的设备ID格式") {
c.logger.Errorf("%s: 设备ID格式错误: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "设备ID格式错误", deviceID) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "设备ID格式错误", deviceID)
return return
} }
c.logger.Errorf("更新设备: 查找设备失败: %v", err) c.logger.Errorf("%s: 数据库查询失败: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备失败: "+err.Error(), actionType, "数据库查询失败", deviceID) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备失败: "+err.Error(), actionType, "数据库查询失败", deviceID)
return return
} }
@@ -263,14 +276,14 @@ func (c *Controller) UpdateDevice(ctx *gin.Context) {
// 2. 绑定请求参数 // 2. 绑定请求参数
var req UpdateDeviceRequest var req UpdateDeviceRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("更新设备: 参数绑定失败: %v", err) c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return return
} }
propertiesJSON, err := json.Marshal(req.Properties) propertiesJSON, err := json.Marshal(req.Properties)
if err != nil { if err != nil {
c.logger.Errorf("更新设备: 序列化属性失败: %v", err) c.logger.Errorf("%s: 序列化属性失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "属性字段格式错误", actionType, "属性序列化失败", req.Properties) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "属性字段格式错误", actionType, "属性序列化失败", req.Properties)
return return
} }
@@ -285,25 +298,26 @@ func (c *Controller) UpdateDevice(ctx *gin.Context) {
// 在更新设备前进行自检 // 在更新设备前进行自检
if !existingDevice.SelfCheck() { if !existingDevice.SelfCheck() {
c.logger.Errorf("更新设备: 设备属性自检失败: %v", existingDevice) c.logger.Errorf("%s: 设备属性自检失败: %v", actionType, existingDevice)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备属性不符合要求", actionType, "设备属性自检失败", existingDevice) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备属性不符合要求", actionType, "设备属性自检失败", existingDevice)
return return
} }
// 4. 将修改后的 existingDevice 对象保存回数据库 // 4. 将修改后的 existingDevice 对象保存回数据库
if err := c.repo.Update(existingDevice); err != nil { if err := c.repo.Update(existingDevice); err != nil {
c.logger.Errorf("更新设备: 数据库操作失败: %v", err) c.logger.Errorf("%s: 数据库更新失败: %v, Device: %+v", actionType, err, existingDevice)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备失败: "+err.Error(), actionType, "数据库更新失败", existingDevice) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备失败: "+err.Error(), actionType, "数据库更新失败", existingDevice)
return return
} }
resp, err := newDeviceResponse(existingDevice) resp, err := newDeviceResponse(existingDevice)
if err != nil { if err != nil {
c.logger.Errorf("更新设备: 序列化响应失败: %v", err) c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, existingDevice)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备更新成功,但响应生成失败", actionType, "响应序列化失败", existingDevice) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备更新成功,但响应生成失败", actionType, "响应序列化失败", existingDevice)
return return
} }
c.logger.Infof("%s: 设备更新成功, ID: %d", actionType, existingDevice.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备更新成功", resp, actionType, "设备更新成功", resp) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备更新成功", resp, actionType, "设备更新成功", resp)
} }
@@ -322,15 +336,30 @@ func (c *Controller) DeleteDevice(ctx *gin.Context) {
// 我们需要先将字符串ID转换为uint因为Delete方法需要uint类型 // 我们需要先将字符串ID转换为uint因为Delete方法需要uint类型
idUint, err := strconv.ParseUint(deviceID, 10, 64) idUint, err := strconv.ParseUint(deviceID, 10, 64)
if err != nil { if err != nil {
c.logger.Errorf("%s: 设备ID格式错误: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的设备ID格式", actionType, "设备ID格式错误", deviceID) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的设备ID格式", actionType, "设备ID格式错误", deviceID)
return return
} }
// 检查设备是否存在(可选,但通常在删除前会检查)
_, err = c.repo.FindByIDString(deviceID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备不存在, ID: %s", actionType, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备不存在", actionType, "设备不存在", deviceID)
return
}
c.logger.Errorf("%s: 查找设备失败: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除设备失败: 查找设备时发生内部错误", actionType, "数据库查询失败", deviceID)
return
}
if err := c.repo.Delete(uint(idUint)); err != nil { if err := c.repo.Delete(uint(idUint)); err != nil {
c.logger.Errorf("删除设备: 数据库操作失败: %v", err) c.logger.Errorf("%s: 数据库删除失败: %v, ID: %d", actionType, err, idUint)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除设备失败: "+err.Error(), actionType, "数据库删除失败", deviceID) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除设备失败: "+err.Error(), actionType, "数据库删除失败", deviceID)
return return
} }
c.logger.Infof("%s: 设备删除成功, ID: %d", actionType, idUint)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备删除成功", nil, actionType, "设备删除成功", deviceID) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备删除成功", nil, actionType, "设备删除成功", deviceID)
} }

View File

@@ -120,6 +120,7 @@ func (c *Controller) CreatePlan(ctx *gin.Context) {
var req CreatePlanRequest var req CreatePlanRequest
const actionType = "创建计划" const actionType = "创建计划"
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return return
} }
@@ -127,6 +128,7 @@ func (c *Controller) CreatePlan(ctx *gin.Context) {
// 使用已有的转换函数,它已经包含了验证和重排逻辑 // 使用已有的转换函数,它已经包含了验证和重排逻辑
planToCreate, err := PlanFromCreateRequest(&req) planToCreate, err := PlanFromCreateRequest(&req)
if err != nil { if err != nil {
c.logger.Errorf("%s: 计划数据校验失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划数据校验失败: "+err.Error(), actionType, "计划数据校验失败", req) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划数据校验失败: "+err.Error(), actionType, "计划数据校验失败", req)
return return
} }
@@ -141,6 +143,7 @@ func (c *Controller) CreatePlan(ctx *gin.Context) {
// 调用仓库方法创建计划 // 调用仓库方法创建计划
if err := c.planRepo.CreatePlan(planToCreate); err != nil { if err := c.planRepo.CreatePlan(planToCreate); err != nil {
c.logger.Errorf("%s: 数据库创建计划失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建计划失败: "+err.Error(), actionType, "数据库创建计划失败", planToCreate) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建计划失败: "+err.Error(), actionType, "数据库创建计划失败", planToCreate)
return return
} }
@@ -154,12 +157,13 @@ func (c *Controller) CreatePlan(ctx *gin.Context) {
// 使用已有的转换函数将创建后的模型转换为响应对象 // 使用已有的转换函数将创建后的模型转换为响应对象
resp, err := PlanToResponse(planToCreate) resp, err := PlanToResponse(planToCreate)
if err != nil { if err != nil {
c.logger.Errorf("创建计划: 序列化响应失败: %v", err) c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划创建成功,但响应生成失败", actionType, "响应序列化失败", planToCreate) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划创建成功,但响应生成失败", actionType, "响应序列化失败", planToCreate)
return return
} }
// 使用统一的成功响应函数 // 使用统一的成功响应函数
c.logger.Infof("%s: 计划创建成功, ID: %d", actionType, planToCreate.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "计划创建成功", resp, actionType, "计划创建成功", resp) controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "计划创建成功", resp, actionType, "计划创建成功", resp)
} }
@@ -177,6 +181,7 @@ func (c *Controller) GetPlan(ctx *gin.Context) {
idStr := ctx.Param("id") idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32) id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil { if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
return return
} }
@@ -186,11 +191,12 @@ func (c *Controller) GetPlan(ctx *gin.Context) {
if err != nil { if err != nil {
// 判断是否为“未找到”错误 // 判断是否为“未找到”错误
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 计划不存在, ID: %d", actionType, id)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id) controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id)
return return
} }
// 其他数据库错误视为内部错误 // 其他数据库错误视为内部错误
c.logger.Errorf("获取计划详情失败: %v", err) c.logger.Errorf("%s: 数据库查询失败: %v, ID: %d", actionType, err, id)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情时发生内部错误", actionType, "数据库查询失败", id) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情时发生内部错误", actionType, "数据库查询失败", id)
return return
} }
@@ -198,12 +204,13 @@ func (c *Controller) GetPlan(ctx *gin.Context) {
// 3. 将模型转换为响应 DTO // 3. 将模型转换为响应 DTO
resp, err := PlanToResponse(plan) resp, err := PlanToResponse(plan)
if err != nil { if err != nil {
c.logger.Errorf("获取计划详情: 序列化响应失败: %v", err) c.logger.Errorf("%s: 序列化响应失败: %v, Plan: %+v", actionType, err, plan)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情失败: 内部数据格式错误", actionType, "响应序列化失败", plan) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情失败: 内部数据格式错误", actionType, "响应序列化失败", plan)
return return
} }
// 4. 发送成功响应 // 4. 发送成功响应
c.logger.Infof("%s: 获取计划详情成功, ID: %d", actionType, id)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取计划详情成功", resp, actionType, "获取计划详情成功", resp) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取计划详情成功", resp, actionType, "获取计划详情成功", resp)
} }
@@ -219,7 +226,7 @@ func (c *Controller) ListPlans(ctx *gin.Context) {
// 1. 调用仓库层获取所有计划 // 1. 调用仓库层获取所有计划
plans, err := c.planRepo.ListBasicPlans() plans, err := c.planRepo.ListBasicPlans()
if err != nil { if err != nil {
c.logger.Errorf("获取计划列表失败: %v", err) c.logger.Errorf("%s: 数据库查询失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划列表时发生内部错误", actionType, "数据库查询失败", nil) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划列表时发生内部错误", actionType, "数据库查询失败", nil)
return return
} }
@@ -229,7 +236,7 @@ func (c *Controller) ListPlans(ctx *gin.Context) {
for _, p := range plans { for _, p := range plans {
resp, err := PlanToResponse(&p) resp, err := PlanToResponse(&p)
if err != nil { if err != nil {
c.logger.Errorf("获取计划列表: 序列化响应失败: %v", err) c.logger.Errorf("%s: 序列化响应失败: %v, Plan: %+v", actionType, err, p)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划列表失败: 内部数据格式错误", actionType, "响应序列化失败", p) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划列表失败: 内部数据格式错误", actionType, "响应序列化失败", p)
return return
} }
@@ -241,6 +248,7 @@ func (c *Controller) ListPlans(ctx *gin.Context) {
Plans: planResponses, Plans: planResponses,
Total: len(planResponses), Total: len(planResponses),
} }
c.logger.Infof("%s: 获取计划列表成功, 数量: %d", actionType, len(planResponses))
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取计划列表成功", resp, actionType, "获取计划列表成功", resp) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取计划列表成功", resp, actionType, "获取计划列表成功", resp)
} }
@@ -260,6 +268,7 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
idStr := ctx.Param("id") idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32) id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil { if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
return return
} }
@@ -267,6 +276,7 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
// 2. 绑定请求体 // 2. 绑定请求体
var req UpdatePlanRequest var req UpdatePlanRequest
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return return
} }
@@ -274,6 +284,7 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
// 3. 将请求转换为模型(转换函数带校验) // 3. 将请求转换为模型(转换函数带校验)
planToUpdate, err := PlanFromUpdateRequest(&req) planToUpdate, err := PlanFromUpdateRequest(&req)
if err != nil { if err != nil {
c.logger.Errorf("%s: 计划数据校验失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划数据校验失败: "+err.Error(), actionType, "计划数据校验失败", req) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划数据校验失败: "+err.Error(), actionType, "计划数据校验失败", req)
return return
} }
@@ -291,10 +302,11 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
_, err = c.planRepo.GetBasicPlanByID(uint(id)) _, err = c.planRepo.GetBasicPlanByID(uint(id))
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 计划不存在, ID: %d", actionType, id)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id) controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id)
return return
} }
c.logger.Errorf("获取计划详情失败: %v", err) c.logger.Errorf("%s: 获取计划详情失败: %v, ID: %d", actionType, err, id)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情时发生内部错误", actionType, "数据库查询失败", id) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划详情时发生内部错误", actionType, "数据库查询失败", id)
return return
} }
@@ -305,6 +317,7 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
c.logger.Infof("计划 #%d 被更新,执行计数器已重置为 0。", planToUpdate.ID) c.logger.Infof("计划 #%d 被更新,执行计数器已重置为 0。", planToUpdate.ID)
if err := c.planRepo.UpdatePlan(planToUpdate); err != nil { if err := c.planRepo.UpdatePlan(planToUpdate); err != nil {
c.logger.Errorf("%s: 数据库更新计划失败: %v, Plan: %+v", actionType, err, planToUpdate)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新计划失败: "+err.Error(), actionType, "数据库更新计划失败", planToUpdate) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新计划失败: "+err.Error(), actionType, "数据库更新计划失败", planToUpdate)
return return
} }
@@ -318,7 +331,7 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
// 6. 获取更新后的完整计划用于响应 // 6. 获取更新后的完整计划用于响应
updatedPlan, err := c.planRepo.GetPlanByID(uint(id)) updatedPlan, err := c.planRepo.GetPlanByID(uint(id))
if err != nil { if err != nil {
c.logger.Errorf("获取更新后计划详情失败: %v", err) c.logger.Errorf("%s: 获取更新后计划详情失败: %v, ID: %d", actionType, err, id)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取更新后计划详情时发生内部错误", actionType, "获取更新后计划详情失败", id) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取更新后计划详情时发生内部错误", actionType, "获取更新后计划详情失败", id)
return return
} }
@@ -326,18 +339,19 @@ func (c *Controller) UpdatePlan(ctx *gin.Context) {
// 7. 将模型转换为响应 DTO // 7. 将模型转换为响应 DTO
resp, err := PlanToResponse(updatedPlan) resp, err := PlanToResponse(updatedPlan)
if err != nil { if err != nil {
c.logger.Errorf("更新计划: 序列化响应失败: %v", err) c.logger.Errorf("%s: 序列化响应失败: %v, Updated Plan: %+v", actionType, err, updatedPlan)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划更新成功,但响应生成失败", actionType, "响应序列化失败", updatedPlan) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划更新成功,但响应生成失败", actionType, "响应序列化失败", updatedPlan)
return return
} }
// 8. 发送成功响应 // 8. 发送成功响应
c.logger.Infof("%s: 计划更新成功, ID: %d", actionType, updatedPlan.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划更新成功", resp, actionType, "计划更新成功", resp) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划更新成功", resp, actionType, "计划更新成功", resp)
} }
// DeletePlan godoc // DeletePlan godoc
// @Summary 删除计划 // @Summary 删除计划
// @Description 根据计划ID删除计划。 // @Description 根据计划ID删除计划。(软删除)
// @Tags 计划管理 // @Tags 计划管理
// @Produce json // @Produce json
// @Param id path int true "计划ID" // @Param id path int true "计划ID"
@@ -349,6 +363,7 @@ func (c *Controller) DeletePlan(ctx *gin.Context) {
idStr := ctx.Param("id") idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32) id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil { if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
return return
} }
@@ -357,10 +372,11 @@ func (c *Controller) DeletePlan(ctx *gin.Context) {
plan, err := c.planRepo.GetBasicPlanByID(uint(id)) plan, err := c.planRepo.GetBasicPlanByID(uint(id))
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 计划不存在, ID: %d", actionType, id)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id) controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id)
return return
} }
c.logger.Errorf("启动计划时获取计划信息失败: %v", err) c.logger.Errorf("%s: 获取计划信息失败: %v, ID: %d", actionType, err, id)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划信息时发生内部错误", actionType, "数据库查询失败", id) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划信息时发生内部错误", actionType, "数据库查询失败", id)
return return
} }
@@ -368,7 +384,7 @@ func (c *Controller) DeletePlan(ctx *gin.Context) {
// 3. 停止这个计划 // 3. 停止这个计划
if plan.Status == models.PlanStatusEnabled { if plan.Status == models.PlanStatusEnabled {
if err := c.planRepo.StopPlanTransactionally(uint(id)); err != nil { if err := c.planRepo.StopPlanTransactionally(uint(id)); err != nil {
c.logger.Errorf("停止计划 #%d 失败: %v", id, err) c.logger.Errorf("%s: 停止计划失败: %v, ID: %d", actionType, err, id)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "停止计划时发生内部错误: "+err.Error(), actionType, "停止计划失败", id) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "停止计划时发生内部错误: "+err.Error(), actionType, "停止计划失败", id)
return return
} }
@@ -376,12 +392,13 @@ func (c *Controller) DeletePlan(ctx *gin.Context) {
// 4. 调用仓库层删除计划 // 4. 调用仓库层删除计划
if err := c.planRepo.DeletePlan(uint(id)); err != nil { if err := c.planRepo.DeletePlan(uint(id)); err != nil {
c.logger.Errorf("删除计划: 数据库操作失败: %v", err) c.logger.Errorf("%s: 数据库删除失败: %v, ID: %d", actionType, err, id)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除计划时发生内部错误", actionType, "数据库删除失败", id) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除计划时发生内部错误", actionType, "数据库删除失败", id)
return return
} }
// 5. 发送成功响应 // 5. 发送成功响应
c.logger.Infof("%s: 计划删除成功, ID: %d", actionType, id)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划删除成功", nil, actionType, "计划删除成功", id) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划删除成功", nil, actionType, "计划删除成功", id)
} }
@@ -399,6 +416,7 @@ func (c *Controller) StartPlan(ctx *gin.Context) {
idStr := ctx.Param("id") idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32) id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil { if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
return return
} }
@@ -407,16 +425,18 @@ func (c *Controller) StartPlan(ctx *gin.Context) {
plan, err := c.planRepo.GetBasicPlanByID(uint(id)) plan, err := c.planRepo.GetBasicPlanByID(uint(id))
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 计划不存在, ID: %d", actionType, id)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id) controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id)
return return
} }
c.logger.Errorf("启动计划时获取计划信息失败: %v", err) c.logger.Errorf("%s: 获取计划信息失败: %v, ID: %d", actionType, err, id)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划信息时发生内部错误", actionType, "数据库查询失败", id) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划信息时发生内部错误", actionType, "数据库查询失败", id)
return return
} }
// 3. 检查计划当前状态 // 3. 检查计划当前状态
if plan.Status == models.PlanStatusEnabled { if plan.Status == models.PlanStatusEnabled {
c.logger.Warnf("%s: 计划已处于启动状态,无需重复操作, ID: %d", actionType, id)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划已处于启动状态,无需重复操作", actionType, "计划已处于启动状态", id) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划已处于启动状态,无需重复操作", actionType, "计划已处于启动状态", id)
return return
} }
@@ -427,7 +447,7 @@ func (c *Controller) StartPlan(ctx *gin.Context) {
// 如果计划是从停止或失败状态重新启动且计数器不为0则重置执行计数 // 如果计划是从停止或失败状态重新启动且计数器不为0则重置执行计数
if plan.ExecuteCount > 0 { if plan.ExecuteCount > 0 {
if err := c.planRepo.UpdateExecuteCount(plan.ID, 0); err != nil { if err := c.planRepo.UpdateExecuteCount(plan.ID, 0); err != nil {
c.logger.Errorf("重置计划 #%d 执行计数失败: %v", plan.ID, err) c.logger.Errorf("%s: 重置计划执行计数失败: %v, ID: %d", actionType, err, plan.ID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "重置计划执行计数失败", actionType, "重置执行计数失败", plan.ID) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "重置计划执行计数失败", actionType, "重置执行计数失败", plan.ID)
return return
} }
@@ -436,7 +456,7 @@ func (c *Controller) StartPlan(ctx *gin.Context) {
// 更新计划状态为“已启动” // 更新计划状态为“已启动”
if err := c.planRepo.UpdatePlanStatus(plan.ID, models.PlanStatusEnabled); err != nil { if err := c.planRepo.UpdatePlanStatus(plan.ID, models.PlanStatusEnabled); err != nil {
c.logger.Errorf("更新计划 #%d 状态失败: %v", plan.ID, err) c.logger.Errorf("%s: 更新计划状态失败: %v, ID: %d", actionType, err, plan.ID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新计划状态失败", actionType, "更新计划状态失败", plan.ID) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新计划状态失败", actionType, "更新计划状态失败", plan.ID)
return return
} }
@@ -451,12 +471,13 @@ func (c *Controller) StartPlan(ctx *gin.Context) {
// 5. 为计划创建或更新触发器 // 5. 为计划创建或更新触发器
if err := c.analysisPlanTaskManager.CreateOrUpdateTrigger(plan.ID); err != nil { if err := c.analysisPlanTaskManager.CreateOrUpdateTrigger(plan.ID); err != nil {
// 此处错误不回滚状态,因为状态更新已成功,但需要明确告知用户触发器创建失败 // 此处错误不回滚状态,因为状态更新已成功,但需要明确告知用户触发器创建失败
c.logger.Errorf("为计划 #%d 创建或更新触发器失败: %v", plan.ID, err) c.logger.Errorf("%s: 创建或更新触发器失败: %v, ID: %d", actionType, err, plan.ID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划状态已更新,但创建执行触发器失败,请检查计划配置或稍后重试", actionType, "创建执行触发器失败", plan.ID) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "计划状态已更新,但创建执行触发器失败,请检查计划配置或稍后重试", actionType, "创建执行触发器失败", plan.ID)
return return
} }
// 6. 发送成功响应 // 6. 发送成功响应
c.logger.Infof("%s: 计划已成功启动, ID: %d", actionType, id)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划已成功启动", nil, actionType, "计划已成功启动", id) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划已成功启动", nil, actionType, "计划已成功启动", id)
} }
@@ -474,6 +495,7 @@ func (c *Controller) StopPlan(ctx *gin.Context) {
idStr := ctx.Param("id") idStr := ctx.Param("id")
id, err := strconv.ParseUint(idStr, 10, 32) id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil { if err != nil {
c.logger.Errorf("%s: 计划ID格式错误: %v, ID: %s", actionType, err, idStr)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的计划ID格式", actionType, "计划ID格式错误", idStr)
return return
} }
@@ -482,27 +504,30 @@ func (c *Controller) StopPlan(ctx *gin.Context) {
plan, err := c.planRepo.GetBasicPlanByID(uint(id)) plan, err := c.planRepo.GetBasicPlanByID(uint(id))
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 计划不存在, ID: %d", actionType, id)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id) controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "计划不存在", actionType, "计划不存在", id)
return return
} }
c.logger.Errorf("启动计划时获取计划信息失败: %v", err) c.logger.Errorf("%s: 获取计划信息失败: %v, ID: %d", actionType, err, id)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划信息时发生内部错误", actionType, "数据库查询失败", id) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划信息时发生内部错误", actionType, "数据库查询失败", id)
return return
} }
// 3. 检查计划当前状态 // 3. 检查计划当前状态
if plan.Status != models.PlanStatusEnabled { if plan.Status != models.PlanStatusEnabled {
c.logger.Warnf("%s: 计划当前不是启用状态, ID: %d, Status: %s", actionType, id, plan.Status)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划当前不是启用状态", actionType, "计划未启用", id) controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "计划当前不是启用状态", actionType, "计划未启用", id)
return return
} }
// 4. 调用仓库层方法,该方法内部处理事务 // 4. 调用仓库层方法,该方法内部处理事务
if err := c.planRepo.StopPlanTransactionally(uint(id)); err != nil { if err := c.planRepo.StopPlanTransactionally(uint(id)); err != nil {
c.logger.Errorf("停止计划 #%d 失败: %v", id, err) c.logger.Errorf("%s: 停止计划失败: %v, ID: %d", actionType, err, id)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "停止计划时发生内部错误: "+err.Error(), actionType, "停止计划失败", id) controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "停止计划时发生内部错误: "+err.Error(), actionType, "停止计划失败", id)
return return
} }
// 5. 发送成功响应 // 5. 发送成功响应
c.logger.Infof("%s: 计划已成功停止, ID: %d", actionType, id)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划已成功停止", nil, actionType, "计划已成功停止", id) controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "计划已成功停止", nil, actionType, "计划已成功停止", id)
} }