Files
NextEdu/src/modules/elective/data-access-settings.ts
SpecialX 138b6f1b00 feat(dashboard,diagnostic,elective): add widgets, layout, parent dashboard, role-config, services, elective components
dashboard:

- Add comparison-badge, dashboard-notification-widget, dashboard-responsive-layout, dashboard-time-range-filter

- Add parent-dashboard components directory

- Add config, hooks, and services directories

diagnostic:

- Add role-config and services directory

elective:

- Add elective-course-detail, elective-stats-cards, parent-selection-view components

- Add data-access-settings and data-access-stats
2026-07-03 10:25:46 +08:00

98 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import "server-only"
import { cache } from "react"
import { eq, and } from "drizzle-orm"
import { db } from "@/shared/db"
import { systemSettings } from "@/shared/db/schema"
/**
* 选课模块配置化设置P2-4 新增)。
*
* 设计原则:
* - 复用全局 `system_settings` 表category="elective"),避免新增独立表
* - 支持按年级覆盖key=`creditLimit:grade:<gradeId>`fallback 到全局key=`creditLimit:default`
* - 用 React `cache()` 包装,单次请求内去重
* - 配置缺失时使用默认值(向后兼容)
*
* 配置项:
* - `creditLimit:default` / `creditLimit:grade:<gradeId>`:学期学分上限(默认 10
* - `capacityNotifyThreshold`:容量阈值通知比例 0-1默认 0.9
*/
const SETTINGS_CATEGORY = "elective"
/** 默认学期学分上限K12 选修课) */
const DEFAULT_MAX_CREDIT_PER_TERM = 10
/** 默认容量阈值通知比例90% */
const DEFAULT_CAPACITY_NOTIFY_THRESHOLD = 0.9
/**
* 读取 system_settings 中指定 key 的值。
* 失败或未配置时返回 null不抛错保证向后兼容
*/
async function readSettingValue(key: string): Promise<string | null> {
const [row] = await db
.select({ value: systemSettings.value, valueType: systemSettings.valueType })
.from(systemSettings)
.where(
and(
eq(systemSettings.category, SETTINGS_CATEGORY),
eq(systemSettings.key, key)
)
)
.limit(1)
return row?.value ?? null
}
/**
* 获取学期学分上限P2-4 新增)。
*
* 查询顺序:
* 1. 若传入 gradeId先查 `creditLimit:grade:<gradeId>`
* 2. 若未配置或未传入 gradeIdfallback 到 `creditLimit:default`
* 3. 都未配置则返回默认值 10
*
* @param gradeId 学生所在年级 ID可选
*/
export const getElectiveCreditLimit = cache(
async (gradeId?: string | null): Promise<number> => {
if (gradeId) {
const gradeValue = await readSettingValue(`creditLimit:grade:${gradeId}`)
if (gradeValue !== null) {
const parsed = Number(gradeValue)
if (!Number.isNaN(parsed) && parsed > 0) return parsed
}
}
const defaultValue = await readSettingValue("creditLimit:default")
if (defaultValue !== null) {
const parsed = Number(defaultValue)
if (!Number.isNaN(parsed) && parsed > 0) return parsed
}
return DEFAULT_MAX_CREDIT_PER_TERM
}
)
/**
* 获取容量阈值通知比例P2-4 新增)。
*
* 当课程 `enrolledCount >= capacity * threshold` 时触发管理员通知。
* 默认 0.990%)。
*/
export const getCapacityNotifyThreshold = cache(
async (): Promise<number> => {
const value = await readSettingValue("capacityNotifyThreshold")
if (value !== null) {
const parsed = Number(value)
if (!Number.isNaN(parsed) && parsed > 0 && parsed <= 1) return parsed
}
return DEFAULT_CAPACITY_NOTIFY_THRESHOLD
}
)
/** 导出默认值常量(供测试与文档引用) */
export const ELECTIVE_DEFAULTS = {
MAX_CREDIT_PER_TERM: DEFAULT_MAX_CREDIT_PER_TERM,
CAPACITY_NOTIFY_THRESHOLD: DEFAULT_CAPACITY_NOTIFY_THRESHOLD,
} as const