package handlers import ( "bytes" "database/sql" "encoding/json" "errors" "fmt" "io" "licensing-cotton/internal/config" "licensing-cotton/internal/database" "licensing-cotton/internal/models" "net/http" "time" ) // CreateRequest 记录新的 renew 申请 (如果设备未在申请列表中) func CreateRequest(deviceID string) error { var existingStatus string // 查询该设备是否已经有 `pending_requests` 记录 err := database.DB.QueryRow(`SELECT status FROM pending_requests WHERE device_id = ? ORDER BY request_time DESC LIMIT 1`, deviceID). Scan(&existingStatus) if err == nil { if existingStatus == "pending" { // 如果已有 `pending` 申请,则不允许重复提交 return errors.New("该设备已有未处理的续期申请,等待管理员审批") } // 如果 `status` 是 `approved` 或 `rejected`,则允许提交新申请 } else if err != sql.ErrNoRows { // 查询时发生错误 return fmt.Errorf("查询续期申请失败: %w", err) } // 插入新的申请(设为 `pending`) _, err = database.DB.Exec( `INSERT INTO pending_requests (device_id, request_time, status) VALUES (?, ?, ?)`, deviceID, time.Now(), "pending", ) if err != nil { return fmt.Errorf("创建续期申请失败: %w", err) } return nil } func HandleListPendingRequests(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed) return } // 管理员权限检查 if !isAdminRequest(w, r) { return } // **查询所有未审批的请求** rows, err := database.DB.Query(` SELECT id, device_id, request_time, status, approved_by, approved_at, expiration FROM pending_requests WHERE status='pending' `) if err != nil { http.Error(w, "查询待审批请求失败: "+err.Error(), http.StatusInternalServerError) return } defer rows.Close() pendingRequests := []models.PendingRequest{} for rows.Next() { var req models.PendingRequest var approvedBy sql.NullString var approvedAt sql.NullTime var expiration sql.NullTime // **扫描所有字段** if err := rows.Scan(&req.ID, &req.DeviceID, &req.RequestTime, &req.Status, &approvedBy, &approvedAt, &expiration); err != nil { http.Error(w, "解析查询结果失败: "+err.Error(), http.StatusInternalServerError) return } // **处理 `NULL` 值** if approvedBy.Valid { req.ApprovedBy = &approvedBy.String } if approvedAt.Valid { req.ApprovedAt = &approvedAt.Time } if expiration.Valid { req.Expiration = &expiration.Time } pendingRequests = append(pendingRequests, req) } // **返回 JSON** w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(pendingRequests) } func HandleDeviceRequest(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed) return } if !isAdminRequest(w, r) { return } var req struct { DeviceID string `json:"device_id"` Expiration string `json:"expiration"` ApprovedBy string `json:"approved_by"` Action string `json:"action"` // "approve" 或 "reject" } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "解析请求失败: "+err.Error(), http.StatusBadRequest) return } switch req.Action { case "approve": handleApprove(w, struct{ DeviceID, Expiration, ApprovedBy, Action string }(req)) case "reject": handleReject(w, struct{ DeviceID, Expiration, ApprovedBy, Action string }(req)) default: http.Error(w, "无效的操作", http.StatusBadRequest) } } func handleApprove(w http.ResponseWriter, req struct{ DeviceID, Expiration, ApprovedBy, Action string }) { expirationTime, err := time.Parse(time.RFC3339, req.Expiration) if err != nil { http.Error(w, "无效的 expiration 格式", http.StatusBadRequest) return } var exists bool err = database.DB.QueryRow(`SELECT EXISTS(SELECT 1 FROM devices WHERE device_id = ?)`, req.DeviceID).Scan(&exists) if err != nil { http.Error(w, "数据库查询失败: "+err.Error(), http.StatusInternalServerError) return } if !exists { _, err = database.DB.Exec(`INSERT INTO devices (device_id, expiration) VALUES (?, ?)`, req.DeviceID, expirationTime) } else { _, err = database.DB.Exec(`UPDATE devices SET expiration = ? WHERE device_id = ?`, expirationTime, req.DeviceID) } if err != nil { http.Error(w, "处理设备记录失败: "+err.Error(), http.StatusInternalServerError) return } _, err = database.DB.Exec(`UPDATE pending_requests SET status='approved', approved_by=?, approved_at=? WHERE device_id=?`, req.ApprovedBy, time.Now(), req.DeviceID) if err != nil { http.Error(w, "更新请求状态失败: "+err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(map[string]string{ "message": fmt.Sprintf("设备 %s 已审批,授权至 %s", req.DeviceID, req.Expiration), }) } func handleReject(w http.ResponseWriter, req struct{ DeviceID, Expiration, ApprovedBy, Action string }) { currentTime := time.Now() _, err := database.DB.Exec(`UPDATE pending_requests SET status='rejected', approved_by=?, approved_at=? WHERE device_id=?`, req.ApprovedBy, currentTime, req.DeviceID) if err != nil { http.Error(w, "拒绝请求失败: "+err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(map[string]string{ "message": fmt.Sprintf("设备 %s 的请求已被拒绝", req.DeviceID), }) } func SetAutoRenewAllDevices(w http.ResponseWriter, r *http.Request) { bodyBytes, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "Failed to read request body: "+err.Error(), http.StatusInternalServerError) return } r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 重置 r.Body 供后续使用 if !isAdminRequest(w, r) { return } // 解析请求体中的 "enabled" 字段 var req struct { Enabled string `json:"enabled"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Failed to read request body: "+err.Error(), http.StatusInternalServerError) //http.Error(w, `{"error":"解析请求失败","code":400}`, http.StatusBadRequest) return } // 设置全局变量 config.AutoRenewAllDevices = req.Enabled == "true" // 返回成功响应 msg := fmt.Sprintf(`{"message":"AutoRenewAllDevices set to %v","code":200}`, req.Enabled) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(msg)) }