feat: 添加房源审核功能及定时审核任务

This commit is contained in:
Cuishibing
2026-03-24 22:54:24 +08:00
parent 9998fb8649
commit 1b6e7fa886
6 changed files with 72 additions and 6 deletions

View File

@@ -0,0 +1,30 @@
import { NextRequest, NextResponse } from 'next/server';
import pool from '@/lib/db';
const CRON_SECRET = 'smalltown_review_secret_2024';
export async function POST(request: NextRequest) {
let connection;
try {
const authHeader = request.headers.get('authorization');
if (authHeader !== `Bearer ${CRON_SECRET}`) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
connection = await pool.getConnection();
const [result] = await connection.query(
"UPDATE houses SET status = 'approved', reviewed_at = NOW() WHERE status = 'pending'"
);
const affectedRows = (result as any).affectedRowCount || 0;
connection.release();
return NextResponse.json({ success: true, approved: affectedRows });
} catch (error) {
console.error('Review error:', error);
return NextResponse.json({ error: '审核失败' }, { status: 500 });
} finally {
if (connection) connection.release();
}
}

View File

@@ -10,10 +10,11 @@ export async function GET(
const { id } = await params;
connection = await pool.getConnection();
const [rows] = await connection.query<any[]>('SELECT * FROM houses WHERE id = ?', [id]);
const [rows] = await connection.query<any[]>("SELECT * FROM houses WHERE id = ? AND status = 'approved'", [id]);
if (rows.length === 0) {
return NextResponse.json({ error: '房屋不存在' }, { status: 404 });
connection.release();
return NextResponse.json({ error: '房屋不存在或待审核' }, { status: 404 });
}
const row = rows[0];
@@ -30,6 +31,7 @@ export async function GET(
createdAt: row.created_at
};
connection.release();
return NextResponse.json({ house });
} catch (error) {
console.error('Get house error:', error);
@@ -75,7 +77,7 @@ export async function PUT(
const { title, description, price, district, address, phone, images } = body;
await connection.query(
'UPDATE houses SET title = ?, description = ?, price = ?, district = ?, address = ?, phone = ?, images = ? WHERE id = ?',
'UPDATE houses SET title = ?, description = ?, price = ?, district = ?, address = ?, phone = ?, images = ?, status = ?, reject_reason = NULL WHERE id = ?',
[
title || houses[0].title,
description ?? houses[0].description,
@@ -84,6 +86,7 @@ export async function PUT(
address || houses[0].address,
phone || houses[0].phone,
images ? JSON.stringify(images) : houses[0].images,
'pending',
id
]
);
@@ -100,6 +103,8 @@ export async function PUT(
address: row.address,
phone: row.phone,
images: row.images ? JSON.parse(row.images) : [],
status: row.status,
reject_reason: row.reject_reason,
createdAt: row.created_at
};

View File

@@ -11,7 +11,7 @@ export async function GET(request: NextRequest) {
connection = await pool.getConnection();
let sql = 'SELECT * FROM houses WHERE 1=1';
let sql = "SELECT * FROM houses WHERE status = 'approved'";
const params: any[] = [];
if (district && district !== '全部') {
@@ -42,6 +42,7 @@ export async function GET(request: NextRequest) {
createdAt: row.created_at
}));
connection.release();
return NextResponse.json({ houses });
} catch (error) {
console.error('Get houses error:', error);
@@ -65,6 +66,7 @@ export async function POST(request: NextRequest) {
const [users] = await connection.query<any[]>('SELECT username FROM users WHERE token = ?', [token]);
if (users.length === 0) {
connection.release();
return NextResponse.json({ error: '用户不存在' }, { status: 401 });
}
@@ -72,13 +74,14 @@ export async function POST(request: NextRequest) {
const { title, description, price, district, address, phone, images } = body;
if (!title || !price || !district || !address || !phone) {
connection.release();
return NextResponse.json({ error: '请填写完整信息' }, { status: 400 });
}
const id = crypto.randomUUID();
await connection.query(
'INSERT INTO houses (id, owner, title, description, price, district, address, phone, images, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[id, users[0].username, title, description || '', Number(price), district, address, phone, JSON.stringify(images || []), new Date()]
'INSERT INTO houses (id, owner, title, description, price, district, address, phone, images, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[id, users[0].username, title, description || '', Number(price), district, address, phone, JSON.stringify(images || []), 'pending', new Date()]
);
const house = {
@@ -91,9 +94,11 @@ export async function POST(request: NextRequest) {
address,
phone,
images: images || [],
status: 'pending',
createdAt: new Date()
};
connection.release();
return NextResponse.json({ success: true, house });
} catch (error) {
console.error('Create house error:', error);

View File

@@ -34,6 +34,9 @@ export async function GET(request: NextRequest) {
address: row.address,
phone: row.phone,
images: row.images ? JSON.parse(row.images) : [],
status: row.status,
reject_reason: row.reject_reason,
reviewed_at: row.reviewed_at,
createdAt: row.created_at
}));

View File

@@ -13,6 +13,9 @@ interface House {
address: string;
phone: string;
images: string[];
status: string;
reject_reason?: string;
reviewed_at?: string;
createdAt: string;
}
@@ -271,6 +274,20 @@ export default function OwnerDashboard() {
</span>
<span className="text-gray-400 text-xs">{house.district}</span>
</div>
<div className="mt-2">
{house.status === 'pending' && (
<span className="inline-block bg-yellow-100 text-yellow-700 text-xs px-2 py-1 rounded"></span>
)}
{house.status === 'approved' && (
<span className="inline-block bg-green-100 text-green-700 text-xs px-2 py-1 rounded"></span>
)}
{house.status === 'rejected' && (
<span className="inline-block bg-red-100 text-red-700 text-xs px-2 py-1 rounded"></span>
)}
</div>
{house.status === 'rejected' && house.reject_reason && (
<p className="text-red-500 text-xs mt-1">{house.reject_reason}</p>
)}
</div>
</div>
</Link>