node-resource-utils 0.0.0-stage → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +35 -2
- package/lib/sync.js +121 -0
- package/package.json +14 -4
- package/resources/.cache/ref.b64 +1 -0
- package/resources/README.md +97 -0
- package/resources/_.env +6 -0
- package/resources/_.env.example +25 -0
- package/resources/_.gitignore +18 -0
- package/resources/_.lintstagedrc.json +9 -0
- package/resources/_.npmrc +5 -0
- package/resources/_.prettierrc +7 -0
- package/resources/commitlint.config.mjs +1 -0
- package/resources/content/docs/README.md +63 -0
- package/resources/content/docs/faq.md +107 -0
- package/resources/content/docs/features/_meta.json +5 -0
- package/resources/content/docs/features/ai.md +71 -0
- package/resources/content/docs/features/appearance.md +47 -0
- package/resources/content/docs/features/feedback.md +79 -0
- package/resources/content/docs/features/files.md +74 -0
- package/resources/content/docs/features/graph.md +66 -0
- package/resources/content/docs/features/inbox.md +61 -0
- package/resources/content/docs/guide/_meta.json +5 -0
- package/resources/content/docs/guide/account.md +76 -0
- package/resources/content/docs/guide/data.md +87 -0
- package/resources/content/docs/guide/navigation.md +81 -0
- package/resources/content/docs/guide/permissions.md +67 -0
- package/resources/content/docs/guide/system.md +92 -0
- package/resources/content/docs/quick-start.md +70 -0
- package/resources/content/docs/shortcuts.md +69 -0
- package/resources/eslint.config.mjs +17 -0
- package/resources/next-env.d.ts +6 -0
- package/resources/next.config.ts +13 -0
- package/resources/postcss.config.mjs +7 -0
- package/resources/prisma/org.config.ts +98 -0
- package/resources/prisma/schema.prisma +336 -0
- package/resources/prisma/seed.ts +721 -0
- package/resources/prisma/seedSafe.ts +347 -0
- package/resources/prisma.config.ts +14 -0
- package/resources/scripts/check-config.mjs +773 -0
- package/resources/scripts/lib/schema-graph.mjs +410 -0
- package/resources/scripts/patch-next-trace.mjs +86 -0
- package/resources/scripts/suggest-relations.mjs +105 -0
- package/resources/src/app/(admin)/ai/page.tsx +469 -0
- package/resources/src/app/(admin)/dashboard/page.tsx +260 -0
- package/resources/src/app/(admin)/demo/employees/page.tsx +508 -0
- package/resources/src/app/(admin)/demo/files/page.tsx +451 -0
- package/resources/src/app/(admin)/demo/loading/page.tsx +513 -0
- package/resources/src/app/(admin)/error.tsx +96 -0
- package/resources/src/app/(admin)/feedback/page.tsx +573 -0
- package/resources/src/app/(admin)/graph/page.tsx +1224 -0
- package/resources/src/app/(admin)/inbox/page.tsx +31 -0
- package/resources/src/app/(admin)/layout.tsx +58 -0
- package/resources/src/app/(admin)/loading.tsx +31 -0
- package/resources/src/app/(admin)/system/depts/page.tsx +415 -0
- package/resources/src/app/(admin)/system/logs/page.tsx +215 -0
- package/resources/src/app/(admin)/system/menus/page.tsx +403 -0
- package/resources/src/app/(admin)/system/roles/page.tsx +433 -0
- package/resources/src/app/(admin)/system/users/page.tsx +620 -0
- package/resources/src/app/api/account-recovery/route.ts +132 -0
- package/resources/src/app/api/ai/act/route.ts +70 -0
- package/resources/src/app/api/ai/ask/route.ts +82 -0
- package/resources/src/app/api/ai/chat/route.ts +404 -0
- package/resources/src/app/api/ai/report/route.ts +164 -0
- package/resources/src/app/api/auth/[...nextauth]/route.ts +3 -0
- package/resources/src/app/api/auth/change-password/route.ts +57 -0
- package/resources/src/app/api/auth/forgot/route.ts +98 -0
- package/resources/src/app/api/auth/register/route.ts +95 -0
- package/resources/src/app/api/auth/register-config/route.ts +51 -0
- package/resources/src/app/api/badges/route.ts +39 -0
- package/resources/src/app/api/depts/[id]/route.ts +145 -0
- package/resources/src/app/api/depts/route.ts +126 -0
- package/resources/src/app/api/docs/export/route.ts +87 -0
- package/resources/src/app/api/docs/parse/route.ts +57 -0
- package/resources/src/app/api/docs/search/route.ts +22 -0
- package/resources/src/app/api/employees/[id]/route.ts +147 -0
- package/resources/src/app/api/employees/export/route.ts +115 -0
- package/resources/src/app/api/employees/import/route.ts +205 -0
- package/resources/src/app/api/employees/route.ts +149 -0
- package/resources/src/app/api/export/route.ts +254 -0
- package/resources/src/app/api/favorites/route.ts +56 -0
- package/resources/src/app/api/feedback/ai/route.ts +31 -0
- package/resources/src/app/api/feedback/approve/route.ts +67 -0
- package/resources/src/app/api/feedback/report/route.ts +213 -0
- package/resources/src/app/api/feedback/route.ts +246 -0
- package/resources/src/app/api/files/[name]/route.ts +82 -0
- package/resources/src/app/api/graph/focus/route.ts +75 -0
- package/resources/src/app/api/graph/node/move/route.ts +48 -0
- package/resources/src/app/api/graph/node/route.ts +274 -0
- package/resources/src/app/api/graph/route.ts +272 -0
- package/resources/src/app/api/health/route.ts +11 -0
- package/resources/src/app/api/inbox/read/route.ts +36 -0
- package/resources/src/app/api/inbox/summary/route.ts +15 -0
- package/resources/src/app/api/logs/route.ts +82 -0
- package/resources/src/app/api/menus/[id]/route.ts +132 -0
- package/resources/src/app/api/menus/mine/route.ts +23 -0
- package/resources/src/app/api/menus/route.ts +89 -0
- package/resources/src/app/api/roles/[id]/menus/route.ts +73 -0
- package/resources/src/app/api/roles/[id]/route.ts +96 -0
- package/resources/src/app/api/roles/route.ts +114 -0
- package/resources/src/app/api/search/route.ts +22 -0
- package/resources/src/app/api/stats/route.ts +92 -0
- package/resources/src/app/api/upload/route.ts +38 -0
- package/resources/src/app/api/users/[id]/password/route.ts +55 -0
- package/resources/src/app/api/users/[id]/route.ts +145 -0
- package/resources/src/app/api/users/route.ts +153 -0
- package/resources/src/app/api/visits/route.ts +48 -0
- package/resources/src/app/change-password/ChangePasswordForm.tsx +174 -0
- package/resources/src/app/change-password/page.tsx +19 -0
- package/resources/src/app/docs/[[...slug]]/page.tsx +56 -0
- package/resources/src/app/docs/layout.tsx +34 -0
- package/resources/src/app/docs/loading.tsx +19 -0
- package/resources/src/app/docs/not-found.tsx +25 -0
- package/resources/src/app/forgot/page.tsx +392 -0
- package/resources/src/app/global-error.tsx +75 -0
- package/resources/src/app/globals.css +235 -0
- package/resources/src/app/layout.tsx +44 -0
- package/resources/src/app/login/page.tsx +267 -0
- package/resources/src/app/page.tsx +14 -0
- package/resources/src/app/register/page.tsx +237 -0
- package/resources/src/auth.ts +215 -0
- package/resources/src/components/AiPanel.tsx +970 -0
- package/resources/src/components/AuthContext.tsx +82 -0
- package/resources/src/components/DataTable.tsx +459 -0
- package/resources/src/components/EChart.tsx +140 -0
- package/resources/src/components/FeedbackAiModal.tsx +476 -0
- package/resources/src/components/FeedbackReportModal.tsx +479 -0
- package/resources/src/components/Markdown.tsx +532 -0
- package/resources/src/components/OntologyGraph.tsx +1196 -0
- package/resources/src/components/Providers.tsx +99 -0
- package/resources/src/components/VisitTracker.tsx +89 -0
- package/resources/src/components/admin/AdminShell.tsx +749 -0
- package/resources/src/components/admin/CountBadge.tsx +67 -0
- package/resources/src/components/admin/GlobalSearch.tsx +509 -0
- package/resources/src/components/admin/InboxBell.tsx +78 -0
- package/resources/src/components/admin/VisitStats.tsx +145 -0
- package/resources/src/components/admin/menu-icons.tsx +56 -0
- package/resources/src/components/ai/AiInputForm.tsx +351 -0
- package/resources/src/components/appearance/AppearanceDrawer.tsx +268 -0
- package/resources/src/components/appearance/AppearanceProvider.tsx +160 -0
- package/resources/src/components/docs/DocArticle.tsx +106 -0
- package/resources/src/components/docs/DocToc.tsx +100 -0
- package/resources/src/components/docs/DocsChrome.tsx +259 -0
- package/resources/src/components/docs/DocsHome.tsx +209 -0
- package/resources/src/components/docs/DocsSearch.tsx +365 -0
- package/resources/src/components/docs/docs.css +1458 -0
- package/resources/src/components/feedback/AsyncButton.tsx +108 -0
- package/resources/src/components/feedback/LoadingOverlay.tsx +125 -0
- package/resources/src/components/feedback/PressableCard.tsx +92 -0
- package/resources/src/components/feedback/README.md +97 -0
- package/resources/src/components/feedback/RouteProgress.tsx +108 -0
- package/resources/src/components/feedback/Skeleton.tsx +249 -0
- package/resources/src/components/feedback/StatCard.tsx +70 -0
- package/resources/src/components/feedback/TaskHint.tsx +153 -0
- package/resources/src/components/feedback/index.ts +93 -0
- package/resources/src/components/feedback/motion.css +204 -0
- package/resources/src/components/feedback/motion.ts +63 -0
- package/resources/src/components/feedback/useCountUp.ts +110 -0
- package/resources/src/components/feedback/usePending.ts +209 -0
- package/resources/src/components/feedback/useRowFlash.ts +40 -0
- package/resources/src/components/inbox/InboxView.tsx +409 -0
- package/resources/src/components/ui/button.tsx +35 -0
- package/resources/src/components/ui/card.tsx +37 -0
- package/resources/src/components/ui/input.tsx +19 -0
- package/resources/src/lib/ai.ts +310 -0
- package/resources/src/lib/aiContext.ts +357 -0
- package/resources/src/lib/aiForm.test.ts +191 -0
- package/resources/src/lib/aiForm.ts +532 -0
- package/resources/src/lib/aiFormTypes.ts +83 -0
- package/resources/src/lib/aiToken.ts +72 -0
- package/resources/src/lib/aiTools.ts +823 -0
- package/resources/src/lib/api.ts +160 -0
- package/resources/src/lib/appInfo.ts +128 -0
- package/resources/src/lib/appearance.ts +643 -0
- package/resources/src/lib/badge-count.ts +146 -0
- package/resources/src/lib/badges.test.ts +58 -0
- package/resources/src/lib/badges.ts +79 -0
- package/resources/src/lib/brand-mark.test.ts +87 -0
- package/resources/src/lib/brand-mark.ts +45 -0
- package/resources/src/lib/docs-shared.test.ts +148 -0
- package/resources/src/lib/docs-shared.ts +422 -0
- package/resources/src/lib/docs.ts +381 -0
- package/resources/src/lib/err-text.ts +21 -0
- package/resources/src/lib/excel.ts +152 -0
- package/resources/src/lib/favorite-path.ts +60 -0
- package/resources/src/lib/favorites.ts +59 -0
- package/resources/src/lib/features.ts +69 -0
- package/resources/src/lib/feedback-access.ts +17 -0
- package/resources/src/lib/feedbackAi.ts +1621 -0
- package/resources/src/lib/formRules.ts +76 -0
- package/resources/src/lib/format.ts +52 -0
- package/resources/src/lib/graph-cascade.ts +304 -0
- package/resources/src/lib/graph-focus.ts +301 -0
- package/resources/src/lib/graph-link.ts +17 -0
- package/resources/src/lib/graph-node-write.ts +150 -0
- package/resources/src/lib/graph-schema.ts +475 -0
- package/resources/src/lib/graph-scope.test.ts +46 -0
- package/resources/src/lib/graph-scope.ts +28 -0
- package/resources/src/lib/http.ts +157 -0
- package/resources/src/lib/inbox-types.ts +57 -0
- package/resources/src/lib/inbox.ts +55 -0
- package/resources/src/lib/menu-service.ts +204 -0
- package/resources/src/lib/notifications.ts +113 -0
- package/resources/src/lib/office.ts +531 -0
- package/resources/src/lib/ontology.ts +277 -0
- package/resources/src/lib/openaiCompat.ts +257 -0
- package/resources/src/lib/pending.ts +98 -0
- package/resources/src/lib/prisma.ts +30 -0
- package/resources/src/lib/rbac.test.ts +106 -0
- package/resources/src/lib/rbac.ts +185 -0
- package/resources/src/lib/search-data.ts +158 -0
- package/resources/src/lib/security-questions.ts +20 -0
- package/resources/src/lib/skin-theme.ts +155 -0
- package/resources/src/lib/system-config.ts +55 -0
- package/resources/src/lib/theme.ts +96 -0
- package/resources/src/lib/todos/feedback.ts +86 -0
- package/resources/src/lib/todos/index.ts +73 -0
- package/resources/src/lib/todos/types.ts +33 -0
- package/resources/src/lib/upload.ts +106 -0
- package/resources/src/lib/utils.test.ts +9 -0
- package/resources/src/lib/utils.ts +6 -0
- package/resources/src/lib/validation.test.ts +297 -0
- package/resources/src/lib/validation.ts +658 -0
- package/resources/src/lib/visit-stats.ts +109 -0
- package/resources/src/lib/visits.ts +111 -0
- package/resources/src/lib/zodField.ts +44 -0
- package/resources/tsconfig.json +41 -0
- package/resources/vitest.config.ts +16 -0
- package/resources/vitest.setup.ts +1 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// 业务系统数据模型(SQLite / Prisma 7)
|
|
3
|
+
//
|
|
4
|
+
// 约定:
|
|
5
|
+
// - SQLite 不支持 enum,一律用 String / Int + 注释说明取值
|
|
6
|
+
// - 主键统一 cuid()
|
|
7
|
+
// - 业务表带 createdAt / updatedAt
|
|
8
|
+
// - 逻辑删除用 deletedAt(null = 未删除),便于审计与恢复
|
|
9
|
+
// - 多对多用显式中间表(RoleMenu / UserRole),便于日后加扩展字段
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
generator client {
|
|
13
|
+
provider = "prisma-client-js"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Prisma 7 起 datasource 里不再允许写 url(写了报 P1012),连接串在 prisma.config.ts
|
|
17
|
+
datasource db {
|
|
18
|
+
provider = "sqlite"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------- 组织与账号
|
|
22
|
+
|
|
23
|
+
model Department {
|
|
24
|
+
id String @id @default(cuid())
|
|
25
|
+
name String
|
|
26
|
+
code String @unique
|
|
27
|
+
parentId String?
|
|
28
|
+
parent Department? @relation("DeptTree", fields: [parentId], references: [id])
|
|
29
|
+
children Department[] @relation("DeptTree")
|
|
30
|
+
sort Int @default(0)
|
|
31
|
+
status Int @default(1) // 1 启用 0 停用
|
|
32
|
+
remark String?
|
|
33
|
+
createdAt DateTime @default(now())
|
|
34
|
+
updatedAt DateTime @updatedAt
|
|
35
|
+
|
|
36
|
+
users User[]
|
|
37
|
+
employees Employee[]
|
|
38
|
+
|
|
39
|
+
@@index([parentId])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
model User {
|
|
43
|
+
id String @id @default(cuid())
|
|
44
|
+
username String @unique // 用户名
|
|
45
|
+
password String // bcrypt 哈希,永远不要存明文
|
|
46
|
+
name String // 姓名
|
|
47
|
+
email String?
|
|
48
|
+
phone String?
|
|
49
|
+
avatar String?
|
|
50
|
+
deptId String?
|
|
51
|
+
dept Department? @relation(fields: [deptId], references: [id])
|
|
52
|
+
status Int @default(1) // 1 启用 0 停用
|
|
53
|
+
isSuper Int @default(0) // 1 = 超级管理员,绕过权限校验
|
|
54
|
+
/// 密保问题(创建账号时设置;忘记密码可凭问题 + 答案自助找回)
|
|
55
|
+
securityQuestion String?
|
|
56
|
+
/// 密保答案的 bcrypt 哈希;空 = 未设置密保。
|
|
57
|
+
/// 列名带 Hash 是刻意的:graph-schema / AI 工具的敏感字段过滤按 /hash/ 匹配,
|
|
58
|
+
/// 避免图谱详情或 AI 查询把答案哈希带出去。
|
|
59
|
+
securityAnswerHash String?
|
|
60
|
+
/// 1 = 密码被管理员重置,下次登录必须先修改密码
|
|
61
|
+
mustChangePassword Int @default(0)
|
|
62
|
+
lastLogin DateTime?
|
|
63
|
+
remark String?
|
|
64
|
+
deletedAt DateTime?
|
|
65
|
+
createdAt DateTime @default(now())
|
|
66
|
+
updatedAt DateTime @updatedAt
|
|
67
|
+
|
|
68
|
+
roles UserRole[]
|
|
69
|
+
logs OperationLog[]
|
|
70
|
+
favorites UserFavorite[]
|
|
71
|
+
feedbacks Feedback[]
|
|
72
|
+
notifications Notification[]
|
|
73
|
+
|
|
74
|
+
@@index([deptId])
|
|
75
|
+
@@index([deletedAt])
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// 用户收藏(书签):把常用功能固定到侧边栏顶部,一键直达。
|
|
79
|
+
///
|
|
80
|
+
/// 为什么存 path 而不是 menuId:菜单主键是 cuid(),重跑 seed 会重新生成,
|
|
81
|
+
/// 收藏会集体失效;path(如 /system/users)稳定且天然可跳转。
|
|
82
|
+
/// 名称、图标、面包屑一律在渲染时用菜单树反查 —— 于是菜单改名、权限收回、
|
|
83
|
+
/// 功能开关关闭这三种情况都会自动跟随,收藏表里不需要存任何冗余文案。
|
|
84
|
+
model UserFavorite {
|
|
85
|
+
id String @id @default(cuid())
|
|
86
|
+
userId String
|
|
87
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
88
|
+
path String // 收藏的路由,必须来自当前用户可见的菜单树
|
|
89
|
+
sort Int @default(0) // 预留:日后支持拖拽排序
|
|
90
|
+
createdAt DateTime @default(now())
|
|
91
|
+
|
|
92
|
+
@@unique([userId, path]) // 同一个人同一页只能收藏一次
|
|
93
|
+
@@index([userId])
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
model Role {
|
|
97
|
+
id String @id @default(cuid())
|
|
98
|
+
code String @unique
|
|
99
|
+
name String
|
|
100
|
+
description String?
|
|
101
|
+
sort Int @default(0)
|
|
102
|
+
status Int @default(1)
|
|
103
|
+
/// 数据范围(行级可见性):1 全部 / 2 本部门及下级 / 3 本部门 / 4 仅本人
|
|
104
|
+
/// 一个用户挂多个角色时取最宽的一档(数值最小者生效);超级管理员恒为全部。
|
|
105
|
+
/// 与菜单权限(RoleMenu)正交:菜单决定「能不能进这个页面」,它决定「进去能看见哪些行」。
|
|
106
|
+
dataScope Int @default(2)
|
|
107
|
+
createdAt DateTime @default(now())
|
|
108
|
+
updatedAt DateTime @updatedAt
|
|
109
|
+
|
|
110
|
+
users UserRole[]
|
|
111
|
+
menus RoleMenu[]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
model UserRole {
|
|
115
|
+
userId String
|
|
116
|
+
roleId String
|
|
117
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
118
|
+
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
|
119
|
+
|
|
120
|
+
@@id([userId, roleId])
|
|
121
|
+
@@index([roleId])
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ------------------------------------------------------------------- 菜单权限
|
|
125
|
+
|
|
126
|
+
model Menu {
|
|
127
|
+
id String @id @default(cuid())
|
|
128
|
+
parentId String?
|
|
129
|
+
parent Menu? @relation("MenuTree", fields: [parentId], references: [id])
|
|
130
|
+
children Menu[] @relation("MenuTree")
|
|
131
|
+
name String // 显示名
|
|
132
|
+
path String? // 路由地址;目录与按钮可为空
|
|
133
|
+
icon String? // 图标名
|
|
134
|
+
keywords String? // 搜索别名:空格或逗号分隔的口语词,全局搜索用(如「请假 销假」)
|
|
135
|
+
/// 侧边栏红色数字徽标的计数源(如 feedback:attention)。
|
|
136
|
+
/// 为空 = 该菜单不显示徽标。取值必须是 src/lib/badges.ts 里 BADGE_SOURCES 的 key,
|
|
137
|
+
/// 且 badge-count.ts 里有同名计数器 —— npm run check:config 会拦不一致的情况。
|
|
138
|
+
badgeKey String?
|
|
139
|
+
type String @default("menu") // dir 目录 | menu 菜单 | button 按钮
|
|
140
|
+
permCode String? @unique // 权限标识,如 system:user:add
|
|
141
|
+
sort Int @default(0)
|
|
142
|
+
hidden Int @default(0) // 1 = 菜单不显示(但路由可达)
|
|
143
|
+
status Int @default(1)
|
|
144
|
+
createdAt DateTime @default(now())
|
|
145
|
+
updatedAt DateTime @updatedAt
|
|
146
|
+
|
|
147
|
+
roles RoleMenu[]
|
|
148
|
+
|
|
149
|
+
@@index([parentId])
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
model RoleMenu {
|
|
153
|
+
roleId String
|
|
154
|
+
menuId String
|
|
155
|
+
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
|
156
|
+
menu Menu @relation(fields: [menuId], references: [id], onDelete: Cascade)
|
|
157
|
+
|
|
158
|
+
@@id([roleId, menuId])
|
|
159
|
+
@@index([menuId])
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// --------------------------------------------------------------- 操作审计日志
|
|
163
|
+
|
|
164
|
+
model OperationLog {
|
|
165
|
+
id String @id @default(cuid())
|
|
166
|
+
userId String?
|
|
167
|
+
user User? @relation(fields: [userId], references: [id])
|
|
168
|
+
username String?
|
|
169
|
+
module String // 模块名,如 用户管理
|
|
170
|
+
action String // 动作:新增 / 修改 / 删除 / 导出 / 登录
|
|
171
|
+
method String?
|
|
172
|
+
path String?
|
|
173
|
+
targetId String?
|
|
174
|
+
detail String? // 变更摘要(JSON 字符串)
|
|
175
|
+
ip String?
|
|
176
|
+
userAgent String?
|
|
177
|
+
status Int @default(1) // 1 成功 0 失败
|
|
178
|
+
durationMs Int?
|
|
179
|
+
createdAt DateTime @default(now())
|
|
180
|
+
|
|
181
|
+
@@index([userId])
|
|
182
|
+
@@index([createdAt])
|
|
183
|
+
@@index([module])
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// --------------------------------------------------------------- 系统配置
|
|
187
|
+
|
|
188
|
+
/// 系统配置(键值对表)。
|
|
189
|
+
///
|
|
190
|
+
/// 放的是「运行时才需要、且要能在界面上改」的设置,目前用于注册开关
|
|
191
|
+
/// (key = register.enabled)。为什么不用 features.ts 里的编译期开关:
|
|
192
|
+
/// 那个的语义是「这个模块要不要」,改一次要重新构建;而注册开关是
|
|
193
|
+
/// 超级管理员在用户管理页随手可切的运营开关,必须当场生效、重启后仍保持。
|
|
194
|
+
///
|
|
195
|
+
/// 取值约定:布尔统一存 '1' / '0',缺行 = 默认值(见 lib/system-config.ts)。
|
|
196
|
+
model SystemConfig {
|
|
197
|
+
key String @id
|
|
198
|
+
value String
|
|
199
|
+
updatedAt DateTime @updatedAt
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ============================================================ 演示业务模型
|
|
203
|
+
// ↓↓↓ 一个完整的业务模块示例(员工管理)。
|
|
204
|
+
// 开发新模块时照这个模式复制:模型 + 路由 + 页面,三段式。
|
|
205
|
+
// 不需要演示数据时删掉这一段即可。
|
|
206
|
+
|
|
207
|
+
model Employee {
|
|
208
|
+
id String @id @default(cuid())
|
|
209
|
+
code String @unique // 工号
|
|
210
|
+
name String // 姓名
|
|
211
|
+
idCard String? // 身份证号(18 位,含校验位;列表展示时脱敏)
|
|
212
|
+
gender String @default("male") // male | female
|
|
213
|
+
deptId String?
|
|
214
|
+
dept Department? @relation(fields: [deptId], references: [id])
|
|
215
|
+
position String?
|
|
216
|
+
phone String?
|
|
217
|
+
email String?
|
|
218
|
+
hireDate DateTime?
|
|
219
|
+
salary Float? // 月薪(SQLite 无 decimal 类型)
|
|
220
|
+
status Int @default(1) // 1 在职 0 离职
|
|
221
|
+
remark String?
|
|
222
|
+
deletedAt DateTime?
|
|
223
|
+
createdAt DateTime @default(now())
|
|
224
|
+
updatedAt DateTime @updatedAt
|
|
225
|
+
|
|
226
|
+
@@index([deptId])
|
|
227
|
+
@@index([deletedAt])
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// 问题反馈(骨架内置,见 src/app/(admin)/feedback)
|
|
232
|
+
// ============================================================================
|
|
233
|
+
model Feedback {
|
|
234
|
+
id String @id @default(cuid())
|
|
235
|
+
// bug=功能异常 / suggest=体验建议 / data=数据问题 / other=其他 / account=账号申诉(忘记密码)
|
|
236
|
+
type String @default("bug")
|
|
237
|
+
title String
|
|
238
|
+
detail String
|
|
239
|
+
pagePath String?
|
|
240
|
+
contact String?
|
|
241
|
+
/// 证明材料 URL 列表(JSON 字符串:[{ name, url, size }]),仅账号申诉使用
|
|
242
|
+
attachments String?
|
|
243
|
+
// pending=待处理 / doing=处理中 / resolved=已解决 / closed=已关闭
|
|
244
|
+
status String @default("pending")
|
|
245
|
+
reply String?
|
|
246
|
+
/// 用户最后一次查看回复的时间。管理员写入新回复时重置为 null = 未读,
|
|
247
|
+
/// 用户打开反馈页时置为当前时间。侧边栏徽标靠它算「我的反馈有新回复」。
|
|
248
|
+
readAt DateTime?
|
|
249
|
+
userId String
|
|
250
|
+
user User @relation(fields: [userId], references: [id])
|
|
251
|
+
username String
|
|
252
|
+
|
|
253
|
+
createdAt DateTime @default(now())
|
|
254
|
+
updatedAt DateTime @updatedAt
|
|
255
|
+
deletedAt DateTime?
|
|
256
|
+
|
|
257
|
+
@@index([status])
|
|
258
|
+
@@index([userId])
|
|
259
|
+
@@index([createdAt])
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ============================================================================
|
|
263
|
+
// 站内消息(骨架内置,见 src/lib/notifications.ts、顶栏铃铛与 /inbox)
|
|
264
|
+
//
|
|
265
|
+
// 只建一张消息表,**不建待办表**:
|
|
266
|
+
// 待办是从各业务模块实时汇总出来的(src/lib/todos 的注册表),业务处理完就
|
|
267
|
+
// 自动消失;如果落到库里,就得额外维护"什么时候该删这一行",一旦与真实状态
|
|
268
|
+
// 脱节,用户会看到一堆点进去已经没有的假待办。
|
|
269
|
+
//
|
|
270
|
+
// 一条记录只属于一个用户:
|
|
271
|
+
// 广播消息由 notify() 拆成多条写入,换取最简单的「单用户已读」语义,
|
|
272
|
+
// 不必再维护一张 (messageId, userId) 的已读表。
|
|
273
|
+
// ============================================================================
|
|
274
|
+
model Notification {
|
|
275
|
+
id String @id @default(cuid())
|
|
276
|
+
userId String
|
|
277
|
+
/// 收件人删除账号时消息一起删;消息没有独立保留价值
|
|
278
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
279
|
+
title String
|
|
280
|
+
body String?
|
|
281
|
+
/// 业务来源(仅用于列表上打标):system / feedback / approval / business
|
|
282
|
+
type String @default("system")
|
|
283
|
+
/// 提示级别:info / success / warning / error(SQLite 无 enum,用 String)
|
|
284
|
+
level String @default("info")
|
|
285
|
+
/// 站内跳转路径,必须以 / 开头;纯通知留空
|
|
286
|
+
link String?
|
|
287
|
+
/// 已读时间;null = 未读,顶栏铃铛按它计数
|
|
288
|
+
readAt DateTime?
|
|
289
|
+
createdAt DateTime @default(now())
|
|
290
|
+
|
|
291
|
+
// 铃铛每次都要「我的未读」,列表要「我的最近消息」,两条走不同索引
|
|
292
|
+
@@index([userId, readAt])
|
|
293
|
+
@@index([userId, createdAt])
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ============================================================================
|
|
297
|
+
// 访问统计(骨架内置,见 src/lib/visit-stats.ts 与顶栏「在线 / 今日 / 总访问」)
|
|
298
|
+
//
|
|
299
|
+
// 底座而不是"给某个页面加个数字",所以拆成两张表:
|
|
300
|
+
// Visitor 一个浏览器一个访客(匿名 id 存 localStorage),心跳只刷新 lastSeenAt
|
|
301
|
+
// Visit 一次真实页面浏览;心跳不写,避免在线人数把 PV 刷爆
|
|
302
|
+
//
|
|
303
|
+
// 为什么匿名 id 由客户端生成而不是用 IP:
|
|
304
|
+
// 内网 NAT 下几十个人可能共用一个出口 IP,按 IP 算在线人数会永远显示 1;
|
|
305
|
+
// 多标签页共享同一个 localStorage id,同一个人也只算一次。
|
|
306
|
+
// ============================================================================
|
|
307
|
+
model Visitor {
|
|
308
|
+
id String @id @default(cuid())
|
|
309
|
+
/// 客户端生成的稳定匿名标识(8-64 位字母/数字/下划线/连字符)
|
|
310
|
+
visitorId String @unique
|
|
311
|
+
/// 已登录时顺带记下用户;未登录为空。留空不建外键,游客不会被用户表约束住
|
|
312
|
+
userId String?
|
|
313
|
+
ip String?
|
|
314
|
+
userAgent String?
|
|
315
|
+
firstSeenAt DateTime @default(now())
|
|
316
|
+
lastSeenAt DateTime @default(now())
|
|
317
|
+
|
|
318
|
+
@@index([lastSeenAt])
|
|
319
|
+
@@index([userId])
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
model Visit {
|
|
323
|
+
id String @id @default(cuid())
|
|
324
|
+
/// 对应 Visitor.visitorId;不建外键,统计表不参与业务级联删除
|
|
325
|
+
visitorId String
|
|
326
|
+
userId String?
|
|
327
|
+
/// 页面路由(不含 query/hash),保留原始 path 便于日后做页面排行
|
|
328
|
+
path String
|
|
329
|
+
ip String?
|
|
330
|
+
userAgent String?
|
|
331
|
+
createdAt DateTime @default(now())
|
|
332
|
+
|
|
333
|
+
@@index([createdAt])
|
|
334
|
+
@@index([visitorId, createdAt])
|
|
335
|
+
@@index([path])
|
|
336
|
+
}
|