修复时间显示和图像拓展功能
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

- 添加formatBeijingTime函数,将所有时间显示转换为北京时间(UTC+8)
- 更新相册、管理后台、精选页面的时间显示
- 图像拓展功能改为直接发送图片URL而不是base64
- 优化API性能,减少数据传输量
This commit is contained in:
Your Name 2025-12-30 20:16:47 +08:00
parent da1579d852
commit 60082aca6f
6 changed files with 136 additions and 27 deletions

View File

@ -3,6 +3,7 @@
import { useState, useEffect } from "react";
import useSWR from "swr";
import { toast } from "sonner";
import { formatBeijingTime } from "@/lib/utils";
import {
Card,
CardContent,
@ -1446,7 +1447,7 @@ export function AdminSettingsClient() {
{img.userName || img.userEmail?.split("@")[0]}
</div>
<div className="text-[9px] text-center text-muted-foreground/60">
{new Date(img.createdAt).toLocaleDateString("zh-CN", { month: "numeric", day: "numeric" })}
{formatBeijingTime(img.createdAt, { month: "numeric", day: "numeric" })}
</div>
</div>
))}

View File

@ -7,6 +7,7 @@ import { Dialog, DialogContent } from "@/components/ui/dialog";
import { ArrowLeft, Download, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Sparkles, User, Building2, Clock, Bot } from "lucide-react";
import { Input } from "@/components/ui/input";
import Image from "next/image";
import { formatBeijingTime } from "@/lib/utils";
type FeaturedImage = {
id: string;
@ -199,7 +200,7 @@ export default function FeaturedPage() {
</span>
<span className="flex items-center gap-1">
<Clock className="h-3 w-3" />
{new Date(img.createdAt).toLocaleDateString("zh-CN", {
{formatBeijingTime(img.createdAt, {
month: "short",
day: "numeric",
})}

View File

@ -8,6 +8,7 @@ import { ArrowLeft, Download, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsR
import { Input } from "@/components/ui/input";
import Image from "next/image";
import { toast } from "sonner";
import { formatBeijingTime } from "@/lib/utils";
type GalleryImage = {
id: string;
@ -341,7 +342,7 @@ export default function GalleryPage() {
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-2 translate-y-full group-hover:translate-y-0 transition-transform">
<p className="text-xs text-white truncate">{img.alt}</p>
<p className="text-xs text-white/70 mt-1">
{new Date(img.createdAt).toLocaleString("zh-CN", {
{formatBeijingTime(img.createdAt, {
month: "short",
day: "numeric",
hour: "2-digit",
@ -494,7 +495,7 @@ export default function GalleryPage() {
<div className="flex-1">
<p className="font-medium">{selectedImage.alt}</p>
<p className="text-sm text-muted-foreground mt-1">
{new Date(selectedImage.createdAt).toLocaleString("zh-CN")}
{formatBeijingTime(selectedImage.createdAt)}
</p>
</div>
<Button

View File

@ -281,16 +281,80 @@ export async function POST(request: NextRequest) {
let messageId: string | null = null;
if (contentType.includes("application/json")) {
// JSON格式:Base64图片
// JSON格式:支持Base64图片或图片URL
const body = await request.json();
imageBase64 = body.image_base64;
const imageUrl = body.image_url; // 优先使用URL
imageBase64 = body.image_base64; // 如果没有URL,使用Base64
prompt = body.prompt || "图像拓展处理";
chatId = body.chatId || null;
messageId = body.messageId || null;
// 如果提供了URL,直接使用;否则需要Base64
if (imageUrl) {
console.log("[Extend API] 使用图片URL:", imageUrl);
// 直接使用URL,跳过Base64转换和上传步骤
const startTime = Date.now();
const xScale = config.xScale || 1.5;
const yScale = config.yScale || 1.5;
console.log(`[Extend API] 拓展参数: X=${xScale}, Y=${yScale}`);
const extendedImageUrl = await extendImage(imageUrl, config.apiKey, xScale, yScale);
console.log("[Extend API] 拓展后图片URL:", extendedImageUrl);
// 下载拓展后的图片
console.log("[Extend API] 下载拓展后的图片...");
const imageResponse = await fetch(extendedImageUrl);
if (!imageResponse.ok) {
throw new Error(`下载拓展图片失败: ${imageResponse.status} ${imageResponse.statusText}`);
}
const arrayBuffer = await imageResponse.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const extendedImageBase64 = buffer.toString("base64");
// 上传到S3
console.log("[Extend API] 上传到S3...");
const { thumbnail, original } = await uploadBase64ToS3(extendedImageBase64, "image/jpeg");
// 保存到数据库(相册和生图记录)
console.log("[Extend API] 保存到数据库...");
let savedImage;
try {
[savedImage] = await db
.insert(generatedImage)
.values({
userId,
chatId: chatId || null,
messageId: messageId || null,
thumbnailUrl: thumbnail,
originalUrl: original,
prompt: prompt || "图像拓展处理",
modelId: "extend-1.0",
imageSize: "2K",
})
.returning();
console.log(`[Extend API] ✅ 图片已保存到相册和生图记录,ID: ${savedImage.id}`);
} catch (dbError) {
console.error("[Extend API] ❌ 保存到数据库失败:", dbError);
}
const processingTime = ((Date.now() - startTime) / 1000).toFixed(2);
console.log(`[Extend API] ✅ 图像拓展完成,处理时间: ${processingTime}秒`);
// 返回结果
return NextResponse.json({
success: true,
original_url: original,
thumbnail_url: thumbnail,
base64: extendedImageBase64,
processing_time: parseFloat(processingTime),
});
}
if (!imageBase64) {
return NextResponse.json(
{ error: "缺少image_base64参数" },
{ error: "缺少image_base64或image_url参数" },
{ status: 400 }
);
}
@ -352,21 +416,28 @@ export async function POST(request: NextRequest) {
console.log("[Extend API] 上传到S3...");
const { thumbnail, original } = await uploadBase64ToS3(extendedImageBase64, "image/jpeg");
// 6. 保存到数据库
// 6. 保存到数据库(相册和生图记录)
console.log("[Extend API] 保存到数据库...");
const [savedImage] = await db
.insert(generatedImage)
.values({
userId,
chatId,
messageId,
thumbnailUrl: thumbnail,
originalUrl: original,
prompt,
modelId: "extend-1.0",
imageSize: "2K",
})
.returning();
let savedImage;
try {
[savedImage] = await db
.insert(generatedImage)
.values({
userId,
chatId: chatId || null,
messageId: messageId || null,
thumbnailUrl: thumbnail,
originalUrl: original,
prompt: prompt || "图像拓展处理",
modelId: "extend-1.0",
imageSize: "2K",
})
.returning();
console.log(`[Extend API] ✅ 图片已保存到相册和生图记录,ID: ${savedImage.id}`);
} catch (dbError) {
console.error("[Extend API] ❌ 保存到数据库失败:", dbError);
// 数据库保存失败时仍然返回成功,但记录错误(图片已上传到S3,用户可以访问)
}
const processingTime = ((Date.now() - startTime) / 1000).toFixed(2);
console.log(`[Extend API] ✅ 图像拓展完成,处理时间: ${processingTime}秒`);

View File

@ -831,19 +831,17 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod
setIsExtending(true);
try {
// 使用原图URL获取原图数据,而不是canvas的预览图
console.log("[图像拓展] 使用原图URL获取数据:", originalImageUrl);
const base64Data = await imageUrlToBase64(originalImageUrl);
console.log("[图像拓展] 原图Base64长度:", base64Data.length, "字符");
// 直接使用原图URL,而不是转换为base64
console.log("[图像拓展] 使用原图URL:", originalImageUrl);
// 调用图像拓展API
// 调用图像拓展API(直接发送URL)
const response = await fetch("/api/extend", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
image_base64: base64Data,
image_url: originalImageUrl, // 直接发送图片URL
prompt: "画图标注-图像拓展处理", // 可选:添加提示词
}),
});

View File

@ -114,3 +114,40 @@ export function getTextFromMessage(message: ChatMessage | UIMessage): string {
.map((part) => (part as { type: 'text'; text: string}).text)
.join('');
}
/**
* 将UTC时间转换为北京时间(UTC+8)
* @param date - Date对象或时间字符串
* @returns 北京时间对应的Date对象
*/
export function toBeijingTime(date: Date | string): Date {
const d = typeof date === "string" ? new Date(date) : date;
// 获取UTC时间戳
const utcTime = d.getTime() + (d.getTimezoneOffset() * 60 * 1000);
// 转换为北京时间(UTC+8)
const beijingTime = new Date(utcTime + (8 * 60 * 60 * 1000));
return beijingTime;
}
/**
* 格式化时间为北京时间字符串
* @param date - Date对象或时间字符串
* @param options - Intl.DateTimeFormatOptions 格式化选项
* @returns 格式化后的北京时间字符串
*/
export function formatBeijingTime(
date: Date | string,
options?: Intl.DateTimeFormatOptions
): string {
const beijingTime = toBeijingTime(date);
const defaultOptions: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "numeric",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
timeZone: "Asia/Shanghai",
...options,
};
return beijingTime.toLocaleString("zh-CN", defaultOptions);
}