feat: 完成 P1 全部功能 + 修复 proxy 导出 + 切换 MySQL 端口至 14013
## P1 功能(20 项) - 站内消息系统、家长仪表盘、学生考勤管理 - Excel 导入导出、用户批量导入、成绩导出 - 排课规则+自动排课+课表调整 - 成绩趋势+对比分析、密码安全策略、速率限制 - 数据变更日志、文件预览+存储策略、全文检索 - 依赖审计集成 CI、数据库定时备份、E2E 测试完善 - 通知偏好管理 ## 基础设施修复 - src/proxy.ts: 将 middleware 导出重命名为 proxy(Next.js 16 要求) - .env: MySQL 端口从 13002 切换至 14013 - scripts/create-db.ts: 新增数据库初始化脚本 ## 架构文档同步 - 004_architecture_impact_map.md 和 005_architecture_data.json 完整记录所有新增表、模块、路由、权限、依赖关系
This commit is contained in:
120
src/modules/announcements/data-access.ts
Normal file
120
src/modules/announcements/data-access.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import "server-only"
|
||||
|
||||
import { cache } from "react"
|
||||
import { and, desc, eq } from "drizzle-orm"
|
||||
|
||||
import { db } from "@/shared/db"
|
||||
import { announcements, users } from "@/shared/db/schema"
|
||||
import type {
|
||||
Announcement,
|
||||
AnnouncementStatus,
|
||||
AnnouncementType,
|
||||
GetAnnouncementsParams,
|
||||
} from "./types"
|
||||
|
||||
const toIso = (d: Date | null | undefined): string | null =>
|
||||
d ? d.toISOString() : null
|
||||
|
||||
const mapRow = (
|
||||
row: {
|
||||
id: string
|
||||
title: string
|
||||
content: string
|
||||
type: "school" | "grade" | "class"
|
||||
status: "draft" | "published" | "archived"
|
||||
targetGradeId: string | null
|
||||
targetClassId: string | null
|
||||
authorId: string
|
||||
authorName: string | null
|
||||
publishedAt: Date | null
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
): Announcement => ({
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
content: row.content,
|
||||
type: row.type,
|
||||
status: row.status,
|
||||
targetGradeId: row.targetGradeId,
|
||||
targetClassId: row.targetClassId,
|
||||
authorId: row.authorId,
|
||||
authorName: row.authorName,
|
||||
publishedAt: toIso(row.publishedAt),
|
||||
createdAt: toIso(row.createdAt) as string,
|
||||
updatedAt: toIso(row.updatedAt) as string,
|
||||
})
|
||||
|
||||
export const getAnnouncements = cache(
|
||||
async (params?: GetAnnouncementsParams): Promise<Announcement[]> => {
|
||||
try {
|
||||
const page = Math.max(1, params?.page ?? 1)
|
||||
const pageSize = Math.max(1, params?.pageSize ?? 20)
|
||||
const offset = (page - 1) * pageSize
|
||||
|
||||
const conditions = []
|
||||
if (params?.status) {
|
||||
conditions.push(eq(announcements.status, params.status as AnnouncementStatus))
|
||||
}
|
||||
if (params?.type) {
|
||||
conditions.push(eq(announcements.type, params.type as AnnouncementType))
|
||||
}
|
||||
|
||||
const rows = await db
|
||||
.select({
|
||||
id: announcements.id,
|
||||
title: announcements.title,
|
||||
content: announcements.content,
|
||||
type: announcements.type,
|
||||
status: announcements.status,
|
||||
targetGradeId: announcements.targetGradeId,
|
||||
targetClassId: announcements.targetClassId,
|
||||
authorId: announcements.authorId,
|
||||
authorName: users.name,
|
||||
publishedAt: announcements.publishedAt,
|
||||
createdAt: announcements.createdAt,
|
||||
updatedAt: announcements.updatedAt,
|
||||
})
|
||||
.from(announcements)
|
||||
.leftJoin(users, eq(users.id, announcements.authorId))
|
||||
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
||||
.orderBy(desc(announcements.createdAt))
|
||||
.limit(pageSize)
|
||||
.offset(offset)
|
||||
|
||||
return rows.map(mapRow)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const getAnnouncementById = cache(
|
||||
async (id: string): Promise<Announcement | null> => {
|
||||
try {
|
||||
const [row] = await db
|
||||
.select({
|
||||
id: announcements.id,
|
||||
title: announcements.title,
|
||||
content: announcements.content,
|
||||
type: announcements.type,
|
||||
status: announcements.status,
|
||||
targetGradeId: announcements.targetGradeId,
|
||||
targetClassId: announcements.targetClassId,
|
||||
authorId: announcements.authorId,
|
||||
authorName: users.name,
|
||||
publishedAt: announcements.publishedAt,
|
||||
createdAt: announcements.createdAt,
|
||||
updatedAt: announcements.updatedAt,
|
||||
})
|
||||
.from(announcements)
|
||||
.leftJoin(users, eq(users.id, announcements.authorId))
|
||||
.where(eq(announcements.id, id))
|
||||
.limit(1)
|
||||
|
||||
return row ? mapRow(row) : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user