diff --git a/Dockerfile b/Dockerfile
index 0208fd6..48d801b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -86,7 +86,7 @@ RUN test -s "/app/public/brand/header-logo.png" && \
test -f "/app/prisma/migrations/20260824_expand_ding_workflow_insights/migration.sql" && \
test -f "/app/prisma/migrations/20260826_add_ding_workflow_demand_type/migration.sql" && \
test -f "/app/prisma/migrations/20260827_add_ding_workflow_detail_fields/migration.sql" && \
- test -f "/app/prisma/migrations/20260826_add_user_video_early_access/migration.sql"
+ test -f "/app/prisma/migrations/20260826_add_pack_video_early_access/migration.sql"
# 这两个动态路由负责兜底访问本地上传文件;如果它们在构建上下文里丢失,
# 线上音频/视频就会直接 404。这里提前失败,避免再把缺路由的镜像发出去。
@@ -205,7 +205,7 @@ COPY --from=builder --chown=root:root /app/prisma/migrations/20260820_add_ding_w
COPY --from=builder --chown=root:root /app/prisma/migrations/20260824_expand_ding_workflow_insights ./prisma/migrations/20260824_expand_ding_workflow_insights
COPY --from=builder --chown=root:root /app/prisma/migrations/20260826_add_ding_workflow_demand_type ./prisma/migrations/20260826_add_ding_workflow_demand_type
COPY --from=builder --chown=root:root /app/prisma/migrations/20260827_add_ding_workflow_detail_fields ./prisma/migrations/20260827_add_ding_workflow_detail_fields
-COPY --from=builder --chown=root:root /app/prisma/migrations/20260826_add_user_video_early_access ./prisma/migrations/20260826_add_user_video_early_access
+COPY --from=builder --chown=root:root /app/prisma/migrations/20260826_add_pack_video_early_access ./prisma/migrations/20260826_add_pack_video_early_access
COPY --from=builder --chown=nextjs:nodejs /app/lib ./lib
COPY --from=builder --chown=nextjs:nodejs /app/scripts/public-traffic-rollup-from-nginx.py ./scripts/public-traffic-rollup-from-nginx.py
COPY --from=builder --chown=nextjs:nodejs /app/scripts/collapse-performance-survey-template-versions.mjs ./scripts/collapse-performance-survey-template-versions.mjs
diff --git a/app/api/admin/users/route.ts b/app/api/admin/users/route.ts
index a9b13ae..6cb969c 100644
--- a/app/api/admin/users/route.ts
+++ b/app/api/admin/users/route.ts
@@ -22,7 +22,6 @@ export async function GET() {
isActive: true, // 保留,向后兼容
status: true, // 新增:用户状态(阶段一)
canDownload: true,
- videoEarlyAccess: true, // 视频提前访问豁免(仅超管可开关)
department: true, // 保留,向后兼容
departmentId: true, // 新增:部门ID(阶段二)
managedDepartment: true, // 管理员负责的部门
diff --git a/app/api/admin/users/toggle-video-early-access/route.ts b/app/api/admin/users/toggle-video-early-access/route.ts
deleted file mode 100644
index a397f97..0000000
--- a/app/api/admin/users/toggle-video-early-access/route.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-// 切换用户「视频提前访问」豁免API(跳过视频类素材 15 天保护期)
-// 仅超级管理员可操作,与"设为管理员"同级的高敏感授权。
-import { NextRequest, NextResponse } from 'next/server';
-import { getServerSession } from 'next-auth';
-import { authOptions } from '@/lib/auth';
-import { prisma } from '@/lib/prisma';
-
-// POST - 切换视频提前访问豁免(仅超级管理员)
-export async function POST(request: NextRequest) {
- try {
- const session = await getServerSession(authOptions);
- if (!session?.user || !(session.user as any).id) {
- return NextResponse.json(
- { error: '未登录' },
- { status: 401 }
- );
- }
-
- if (!(session.user as any).isSuperAdmin) {
- return NextResponse.json(
- { error: '仅超级管理员可操作' },
- { status: 403 }
- );
- }
-
- const { userId } = await request.json();
-
- if (!userId) {
- return NextResponse.json(
- { error: '缺少用户ID' },
- { status: 400 }
- );
- }
-
- const user = await prisma.user.findUnique({
- where: { id: userId },
- });
-
- if (!user) {
- return NextResponse.json(
- { error: '用户不存在' },
- { status: 404 }
- );
- }
-
- const updatedUser = await prisma.user.update({
- where: { id: userId },
- data: { videoEarlyAccess: !user.videoEarlyAccess },
- });
-
- return NextResponse.json({
- message: updatedUser.videoEarlyAccess
- ? '已开启视频提前访问(跳过 15 天保护期)'
- : '已关闭视频提前访问(恢复 15 天保护期)',
- videoEarlyAccess: updatedUser.videoEarlyAccess,
- });
- } catch (error) {
- console.error('Error toggling video early access:', error);
- return NextResponse.json(
- { error: '操作失败' },
- { status: 500 }
- );
- }
-}
diff --git a/app/api/organization/permission-groups/route.ts b/app/api/organization/permission-groups/route.ts
index 7276951..19b73c0 100644
--- a/app/api/organization/permission-groups/route.ts
+++ b/app/api/organization/permission-groups/route.ts
@@ -69,6 +69,7 @@ export async function GET() {
id: true,
slug: true,
name: true,
+ videoEarlyAccess: true,
items: {
select: {
materialType: true,
@@ -170,6 +171,7 @@ export async function GET() {
groups: packs.map((pack) => ({
slug: pack.slug,
name: pack.name,
+ videoEarlyAccess: pack.videoEarlyAccess,
permissions: packItemsToMap(pack.items),
departments: (departmentsByPack.get(pack.id) || []).map((path) => ({
path,
@@ -232,6 +234,15 @@ export async function PUT(request: NextRequest) {
return NextResponse.json({ ok: true })
}
+ if (action === 'set-video-early-access') {
+ const enabled = body?.enabled === true
+ await prisma.materialPermissionPack.update({
+ where: { id: pack.id },
+ data: { videoEarlyAccess: enabled },
+ })
+ return NextResponse.json({ ok: true, videoEarlyAccess: enabled })
+ }
+
if (action === 'delete') {
await prisma.$transaction([
prisma.userPermissionInherit.deleteMany({ where: { packId: pack.id } }),
diff --git a/components/department-dashboard/PermissionGroupsDashboard.tsx b/components/department-dashboard/PermissionGroupsDashboard.tsx
index fb2415e..8b4c0ce 100644
--- a/components/department-dashboard/PermissionGroupsDashboard.tsx
+++ b/components/department-dashboard/PermissionGroupsDashboard.tsx
@@ -30,6 +30,7 @@ type Member = {
type Group = {
slug: string
name: string
+ videoEarlyAccess?: boolean
permissions: Record
视频提前访问
++ 开启后,本组成员不受视频类素材 15 天保护期限制 +
+