route.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. import dbConnect from "../../lib/dbConnect";
  2. import Match from "../../models/Match";
  3. import { NextResponse } from "next/server";
  4. import { setCORSHeaders, handleError } from "../../lib/apiUtils";
  5. import moment from "moment-timezone/moment-timezone-utils";
  6. import { withAuth } from "../../middleware/authMiddleware";
  7. // 假设你的服务器时区是 'Asia/Shanghai'
  8. const timezone = "Asia/Shanghai";
  9. export async function GET(request) {
  10. await dbConnect();
  11. try {
  12. const { searchParams } = new URL(request.url);
  13. const action = searchParams.get("action");
  14. const type = searchParams.get("type");
  15. console.log("type", type);
  16. const current = parseInt(searchParams.get("current")) || 1;
  17. const pageSize = parseInt(searchParams.get("pageSize")) || 10;
  18. let response;
  19. const now = moment.tz(timezone);
  20. const currentDate = now.toDate();
  21. const currentTimeString = now.format("HH:mm");
  22. // 创建2小时前的时间点
  23. const twoHoursAgo = moment(now).subtract(2, "hours");
  24. const twoHoursAgoDate = twoHoursAgo.format("YYYY-MM-DD"); // 使用日期字符串
  25. const twoHoursAgoTime = twoHoursAgo.format("HH:mm");
  26. switch (action) {
  27. case "getMatches":
  28. const searchQuery = {};
  29. // 添加类型过滤
  30. if (type) {
  31. searchQuery.type = type;
  32. }
  33. for (const [key, value] of searchParams.entries()) {
  34. if (["action", "current", "pageSize", "type"].includes(key)) continue;
  35. switch (key) {
  36. case "homeTeamScore":
  37. case "awayTeamScore":
  38. searchQuery[key] = parseInt(value);
  39. break;
  40. case "date":
  41. searchQuery[key] = new Date(value);
  42. break;
  43. case "homeTeam":
  44. case "awayTeam":
  45. try {
  46. const teamQuery = JSON.parse(value);
  47. Object.entries(teamQuery).forEach(
  48. ([nestedKey, nestedValue]) => {
  49. searchQuery[`${key}.${nestedKey}`] = {
  50. $regex: nestedValue,
  51. $options: "i",
  52. };
  53. }
  54. );
  55. } catch (e) {
  56. searchQuery[`${key}.name`] = { $regex: value, $options: "i" };
  57. }
  58. break;
  59. // 根据比赛类型处理特定字段
  60. case "spread":
  61. case "totalPoints":
  62. if (type === "basketball") {
  63. searchQuery[`basketball.${key}`] = value;
  64. }
  65. break;
  66. case "firstTeamToScore":
  67. if (type === "football") {
  68. searchQuery[`football.result.${key}`] = value;
  69. }
  70. break;
  71. default:
  72. searchQuery[key] = { $regex: value, $options: "i" };
  73. }
  74. }
  75. console.log("Search Query:", searchQuery);
  76. // await Promise.all([
  77. // // 更新所有开始超过2小时的比赛为已结束
  78. // Match.updateMany(
  79. // {
  80. // $or: [
  81. // // 早于今天的比赛
  82. // { date: { $lt: twoHoursAgoDate } },
  83. // // 今天的比赛且开始时间在2小时前
  84. // {
  85. // date: twoHoursAgoDate,
  86. // time: { $lte: twoHoursAgoTime },
  87. // },
  88. // ],
  89. // status: { $in: ["未开始", "进行中"] },
  90. // ...(type ? { type } : {}),
  91. // },
  92. // { $set: { status: "已结束" } }
  93. // ),
  94. // // 更新其他应该进行中的比赛
  95. // Match.updateMany(
  96. // {
  97. // date: { $lte: currentDate },
  98. // time: { $lte: currentTimeString },
  99. // status: "未开始",
  100. // // 排除已经过去2小时的比赛(因为上面的查询会处理)
  101. // $or: [
  102. // { date: { $gt: twoHoursAgoDate } },
  103. // {
  104. // date: twoHoursAgoDate,
  105. // time: { $gt: twoHoursAgoTime },
  106. // },
  107. // ],
  108. // ...(type ? { type } : {}),
  109. // },
  110. // { $set: { status: "进行中" } }
  111. // ),
  112. // ]);
  113. const skip = (current - 1) * pageSize;
  114. const totalMatches = await Match.countDocuments(searchQuery);
  115. const matchesData = await Match.find(searchQuery)
  116. .sort({ date: 1, time: 1 })
  117. .skip(skip)
  118. .limit(pageSize);
  119. response = NextResponse.json({
  120. success: true,
  121. total: totalMatches,
  122. data: matchesData,
  123. });
  124. break;
  125. case "getMatchDays":
  126. const typeFilter = type ? { type } : {};
  127. const currentDay = moment().tz(timezone).startOf("day");
  128. const futureMatches = await Match.find({
  129. date: { $gte: currentDay.toDate() },
  130. status: { $in: ["未开始", "进行中"] },
  131. ...typeFilter,
  132. }).sort("date");
  133. const formattedDays = formatMatches(futureMatches);
  134. response = NextResponse.json({ success: true, data: formattedDays });
  135. break;
  136. case "getMatchesByDate":
  137. const dateParam = searchParams.get("date");
  138. if (!dateParam) {
  139. return NextResponse.json(
  140. { success: false, error: "Date parameter is required" },
  141. { status: 400 }
  142. );
  143. }
  144. const startDate = moment
  145. .tz(dateParam, timezone)
  146. .startOf("day")
  147. .toDate();
  148. const endDate = moment.tz(dateParam, timezone).endOf("day").toDate();
  149. const dateTypeFilter = type ? { type } : {};
  150. const matches = await Match.find({
  151. date: { $gte: startDate, $lte: endDate },
  152. status: { $in: ["未开始", "进行中"] },
  153. ...dateTypeFilter,
  154. }).sort({ time: 1 });
  155. response = NextResponse.json({ success: true, data: matches });
  156. break;
  157. default:
  158. return NextResponse.json(
  159. { success: false, error: "Invalid action" },
  160. { status: 400 }
  161. );
  162. }
  163. return setCORSHeaders(response);
  164. } catch (error) {
  165. return handleError(error);
  166. }
  167. }
  168. export const POST = withAuth(async (request) => {
  169. await dbConnect();
  170. try {
  171. const matchData = await request.json();
  172. // console.log(11, matchData);
  173. // 验证比赛类型
  174. if (
  175. !matchData.type ||
  176. !["football", "basketball"].includes(matchData.type)
  177. ) {
  178. return NextResponse.json(
  179. { success: false, error: "Invalid match type" },
  180. { status: 400 }
  181. );
  182. }
  183. // 根据类型验证必要字段
  184. if (matchData.type === "basketball") {
  185. if (!matchData.basketball?.spread || !matchData.basketball?.totalPoints) {
  186. return NextResponse.json(
  187. { success: false, error: "Missing required basketball fields" },
  188. { status: 400 }
  189. );
  190. }
  191. }
  192. const match = new Match(matchData);
  193. await match.save();
  194. const response = NextResponse.json(
  195. { success: true, data: match },
  196. { status: 201 }
  197. );
  198. return setCORSHeaders(response);
  199. } catch (error) {
  200. console.error("插入比赛数据时出错:", error);
  201. return NextResponse.json(
  202. { success: false, error: "创建比赛失败" },
  203. { status: 500 }
  204. );
  205. }
  206. });
  207. export const PUT = withAuth(async (request) => {
  208. await dbConnect();
  209. try {
  210. const { id, type, ...updateData } = await request.json();
  211. if (!id) {
  212. return NextResponse.json(
  213. { success: false, error: "缺少比赛ID" },
  214. { status: 400 }
  215. );
  216. }
  217. // 根据类型验证更新数据
  218. if (type === "basketball") {
  219. const basketballFields = updateData.basketball;
  220. if (
  221. basketballFields &&
  222. (!basketballFields.spread || !basketballFields.totalPoints)
  223. ) {
  224. return NextResponse.json(
  225. { success: false, error: "Missing required basketball fields" },
  226. { status: 400 }
  227. );
  228. }
  229. }
  230. const updatedMatch = await Match.findByIdAndUpdate(id, updateData, {
  231. new: true,
  232. runValidators: true,
  233. });
  234. if (!updatedMatch) {
  235. return NextResponse.json(
  236. { success: false, error: "未找到指定比赛" },
  237. { status: 404 }
  238. );
  239. }
  240. const response = NextResponse.json(
  241. { success: true, data: updatedMatch },
  242. { status: 200 }
  243. );
  244. return setCORSHeaders(response);
  245. } catch (error) {
  246. console.error("更新比赛数据时出错:", error);
  247. return NextResponse.json(
  248. { success: false, error: "更新比赛失败" },
  249. { status: 500 }
  250. );
  251. }
  252. });
  253. export const DELETE = withAuth(async (request) => {
  254. console.log("DELETE request received");
  255. await dbConnect();
  256. try {
  257. const url = new URL(request.url);
  258. const id = url.searchParams.get("id");
  259. const ids = url.searchParams.get("ids");
  260. console.log("Received delete request for id:", id, "or ids:", ids);
  261. if (!id && !ids) {
  262. return NextResponse.json(
  263. { success: false, error: "缺少比赛ID" },
  264. { status: 400 }
  265. );
  266. }
  267. let deletedMatches;
  268. let message;
  269. if (id) {
  270. // 单个删除
  271. deletedMatches = await Match.findByIdAndDelete(id);
  272. message = deletedMatches ? "比赛已成功删除" : "未找到指定比赛";
  273. } else {
  274. // 批量删除
  275. const idArray = ids.split(",");
  276. deletedMatches = await Match.deleteMany({ _id: { $in: idArray } });
  277. message = `成功删除 ${deletedMatches.deletedCount} 场比赛`;
  278. }
  279. if (
  280. !deletedMatches ||
  281. (Array.isArray(deletedMatches) && deletedMatches.length === 0)
  282. ) {
  283. return NextResponse.json(
  284. { success: false, error: "未找到指定比赛" },
  285. { status: 404 }
  286. );
  287. }
  288. const response = NextResponse.json(
  289. { success: true, message: message },
  290. { status: 200 }
  291. );
  292. // Set CORS headers
  293. return setCORSHeaders(response);
  294. } catch (error) {
  295. console.error("删除比赛数据时出错:", error);
  296. return NextResponse.json(
  297. { success: false, error: "删除比赛失败" },
  298. { status: 500 }
  299. );
  300. }
  301. });
  302. export async function OPTIONS() {
  303. const response = new NextResponse(null, { status: 204 });
  304. return setCORSHeaders(response);
  305. }
  306. function formatMatches(matches) {
  307. // 使用 Set 去除重复的日期
  308. const uniqueDates = Array.from(
  309. new Set(
  310. matches.map((match) => new Date(match.date).toISOString().split("T")[0])
  311. )
  312. );
  313. // 格式化唯一日期
  314. return uniqueDates.map((dateString, index) => {
  315. const date = new Date(dateString);
  316. const year = date.getFullYear();
  317. const month = String(date.getMonth() + 1).padStart(2, "0");
  318. const day = String(date.getDate()).padStart(2, "0");
  319. const weekdays = [
  320. "星期日",
  321. "星期一",
  322. "星期二",
  323. "星期三",
  324. "星期四",
  325. "星期五",
  326. "星期六",
  327. ];
  328. // 获取星期几
  329. const weekday = weekdays[date.getDay()];
  330. return {
  331. id: index + 1,
  332. title: weekday,
  333. date: `${year}-${month}-${day}`,
  334. };
  335. });
  336. }