diff --git a/pc/src/components/CommonHeader.vue b/pc/src/components/CommonHeader.vue index af4257f..6a2abd2 100644 --- a/pc/src/components/CommonHeader.vue +++ b/pc/src/components/CommonHeader.vue @@ -108,7 +108,7 @@ async function refreshCache() { // 触发菜单刷新事件,通知CommonAside组件刷新菜单 window.dispatchEvent(new CustomEvent('menu-cache-refreshed')); - ElMessage.success('菜单缓存和路由更新成功'); + ElMessage.success('更新成功'); } catch (error) { console.error('Failed to refresh cache', error); ElMessage.error('更新缓存失败,请检查网络连接'); diff --git a/pc/src/views/apps/oa/OPTIMIZATION_SUMMARY.md b/pc/src/views/apps/oa/OPTIMIZATION_SUMMARY.md deleted file mode 100644 index f624747..0000000 --- a/pc/src/views/apps/oa/OPTIMIZATION_SUMMARY.md +++ /dev/null @@ -1,138 +0,0 @@ -# OA 模块优化总结 - -## 优化完成时间 -2024年 - -## 优化内容 - -### 1. 创建 OA 基础数据 Store (`pc/src/stores/oa.js`) - -**功能特性:** -- ✅ 智能缓存机制(5分钟缓存) -- ✅ 并发请求控制 -- ✅ 批量数据获取 -- ✅ 统一数据管理 - -### 2. 重构的页面 - -#### ✅ 员工管理 (`employees/index.vue`) -- 使用 OA Store 获取部门、职位、角色数据 -- 利用缓存机制,减少重复请求 -- 数据更新后自动刷新缓存 - -#### ✅ 部门管理 (`departments/index.vue`) -- 使用 OA Store 管理部门数据 -- 添加/编辑/删除后自动刷新缓存 -- 使用响应式数据绑定 - -#### ✅ 职位管理 (`positions/index.vue`) -- 使用 OA Store 获取部门和职位数据 -- 利用缓存机制优化性能 -- 数据更新后自动刷新缓存 - -#### ✅ 组织架构 (`organization/index.vue`) -- 使用 OA Store 获取部门和职位数据 -- 部门树和职位列表共享同一份数据 -- 数据更新后自动刷新缓存 - -## 性能提升 - -### 优化前 -- 每次进入页面都发起 3-4 个独立请求 -- 无缓存机制,频繁切换页面产生大量重复请求 -- 服务器压力大 - -### 优化后 -- **首次访问**:并行请求所有基础数据(更快) -- **缓存有效期内**:只请求业务数据(员工列表等) -- **请求次数减少**:75% 减少 -- **响应速度提升**:缓存命中时,几乎瞬时响应 -- **服务器压力降低**:减少 75% 的基础数据请求 - -## 缓存策略 - -### 缓存时间 -- 默认:5 分钟 -- 可配置:在 `oa.js` 中修改 `cacheTime` 常量 - -### 缓存管理 -- 自动失效:缓存过期后自动刷新 -- 手动刷新:调用 `refresh` 方法 -- 数据更新后:自动刷新相关缓存 - -## 使用方式 - -### 在页面中使用 - -```javascript -import { useOAStore } from '@/stores/oa'; - -const oaStore = useOAStore(); - -// 页面初始化 -onMounted(async () => { - // 使用批量获取,自动利用缓存 - await oaStore.fetchAllBaseData(); - // 然后获取业务数据 - await fetchEmployees(); -}); -``` - -### 数据更新后刷新缓存 - -```javascript -// 添加/编辑/删除后 -await addDepartment(data); -await oaStore.refreshDepartments(); // 刷新部门缓存 -``` - -## 优化效果对比 - -| 指标 | 优化前 | 优化后 | 提升 | -|------|--------|--------|------| -| 请求次数(缓存命中) | 4 次 | 1 次 | **减少 75%** | -| 响应时间(缓存命中) | ~500ms | ~50ms | **提升 90%** | -| 服务器压力 | 高 | 低 | **降低 75%** | -| 代码复用性 | 低 | 高 | **提升** | -| 数据一致性 | 一般 | 优秀 | **提升** | - -## 后续建议 - -1. **其他模块**:可以将类似的优化应用到其他模块(如用户管理、权限管理等) -2. **缓存时间**:根据业务需求调整缓存时间 -3. **后端优化**:考虑提供批量接口,一次返回所有基础数据 -4. **监控**:添加性能监控,跟踪缓存命中率 - -## 注意事项 - -1. **缓存时间**:默认 5 分钟,可根据业务需求调整 -2. **数据一致性**:更新数据后记得调用 `refresh` 方法 -3. **租户隔离**:缓存是基于当前租户的,不同租户数据不会混淆 -4. **内存占用**:缓存数据存储在内存中,页面刷新后会清空 - -## 文件清单 - -### 新增文件 -- `pc/src/stores/oa.js` - OA 基础数据 Store -- `pc/src/stores/README_OA.md` - Store 使用文档 -- `pc/src/views/apps/oa/OPTIMIZATION_SUMMARY.md` - 优化总结(本文件) - -### 重构文件 -- `pc/src/views/apps/oa/employees/index.vue` - 员工管理页面 -- `pc/src/views/apps/oa/departments/index.vue` - 部门管理页面 -- `pc/src/views/apps/oa/positions/index.vue` - 职位管理页面 -- `pc/src/views/apps/oa/organization/index.vue` - 组织架构页面 - -## 测试建议 - -1. **功能测试**:确保所有 CRUD 操作正常工作 -2. **缓存测试**:验证缓存机制是否正常工作 -3. **性能测试**:对比优化前后的性能指标 -4. **并发测试**:验证并发请求控制是否正常 - ---- - -**优化完成!** 🎉 - -所有 OA 模块页面已成功使用统一的 Store 进行数据管理,大幅提升了性能和代码质量。 - diff --git a/pc/src/views/apps/oa/components/employees/EmployeeEdit.vue b/pc/src/views/apps/oa/components/employees/EmployeeEdit.vue index 6adce9c..ce42eaa 100644 --- a/pc/src/views/apps/oa/components/employees/EmployeeEdit.vue +++ b/pc/src/views/apps/oa/components/employees/EmployeeEdit.vue @@ -69,6 +69,7 @@ style="width: 100%" :loading="loadingRoles" clearable + :key="`role-${filteredRoleList.length}-${form.tenant_id}-${form.role}`" >
- 暂无可用角色 + 暂无可用角色(当前租户ID: {{ form.tenant_id }})
警告:当前选择的角色ID ({{ form.role }}) 在角色列表中不存在 @@ -139,32 +140,79 @@ const props = defineProps<{ loadingRoles: boolean; }>(); -// 根据当前员工的 tenant_id 过滤角色列表 +// 根据当前员工的 tenant_id 和角色的 default 字段过滤角色列表 const filteredRoleList = computed(() => { if (!props.roleList || props.roleList.length === 0) { return []; } - // 获取当前员工的 tenant_id - const currentTenantId = props.formData?.tenant_id; + // 获取当前员工的 tenant_id(优先使用 form.value.tenant_id,因为表单数据可能已更新) + const currentTenantId = form.value.tenant_id !== null && form.value.tenant_id !== undefined + ? form.value.tenant_id + : (props.formData?.tenant_id !== null && props.formData?.tenant_id !== undefined + ? props.formData.tenant_id + : null); + + // 调试日志 + console.log('filteredRoleList - currentTenantId:', currentTenantId); + console.log('filteredRoleList - roleList length:', props.roleList.length); if (currentTenantId === null || currentTenantId === undefined) { // 如果没有 tenant_id,返回所有角色(不应该发生,但做个兼容) + console.log('filteredRoleList - 没有 tenant_id,返回所有角色'); return props.roleList; } - // 过滤角色:显示当前租户的角色(tenant_id 匹配)和公共角色(tenant_id 为 0) - return props.roleList.filter((role: any) => { + const currentTenantIdNum = Number(currentTenantId); + + // 过滤角色:根据 default 字段和 tenant_id 进行筛选 + // default = 1: 只匹配给 tenant_id = 1 的租户 + // default = 2: 分配给所有租户使用 + // default = 3: 租户单独设置的,给自己用(tenant_id 匹配当前租户) + const filtered = props.roleList.filter((role: any) => { // 兼容不同的字段名:tenantId 或 tenant_id const roleTenantId = role.tenantId !== undefined ? role.tenantId : (role.tenant_id !== undefined ? role.tenant_id : null); + const roleDefault = role.default !== undefined ? role.default : (role.Default !== undefined ? role.Default : 2); // 默认为2(所有租户可用) - // 转换为数字进行比较 - const currentTenantIdNum = Number(currentTenantId); const roleTenantIdNum = roleTenantId !== null && roleTenantId !== undefined ? Number(roleTenantId) : null; + const roleDefaultNum = Number(roleDefault); - // 显示当前租户的角色或公共角色(tenant_id 为 0) - return roleTenantIdNum === currentTenantIdNum || roleTenantIdNum === 0; + let match = false; + + // 情况1: default = 1,只给租户1使用 + if (roleDefaultNum === 1) { + match = currentTenantIdNum === 1; + } + // 情况2: default = 2,所有租户可用 + else if (roleDefaultNum === 2) { + match = true; + } + // 情况3: default = 3,租户专属(tenant_id 必须匹配) + else if (roleDefaultNum === 3) { + match = roleTenantIdNum === currentTenantIdNum; + } + // 兼容旧数据:如果没有 default 字段,使用旧的逻辑 + else { + // 如果 tenant_id = 0,认为是公共角色,所有租户可用 + // 如果 tenant_id 匹配,认为是租户专属角色 + match = roleTenantIdNum === 0 || roleTenantIdNum === currentTenantIdNum; + } + + if (match) { + console.log('filteredRoleList - 匹配角色:', { + id: role.roleId || role.id || role.role_id, + name: role.roleName || role.name || role.role_name, + roleTenantId: roleTenantIdNum, + roleDefault: roleDefaultNum, + currentTenantId: currentTenantIdNum + }); + } + + return match; }); + + console.log('filteredRoleList - 过滤后数量:', filtered.length); + return filtered; }); // 根据选择的部门过滤职位列表 @@ -267,11 +315,19 @@ watch(() => props.visible, async (val) => { department_id: props.formData.department_id ? Number(props.formData.department_id) : null, position_id: props.formData.position_id ? Number(props.formData.position_id) : null, role: props.formData.role ? Number(props.formData.role) : null, + tenant_id: props.formData.tenant_id ? Number(props.formData.tenant_id) : null, }; + console.log('EmployeeEdit - 编辑模式,设置表单数据:', { + tenant_id: form.value.tenant_id, + role: form.value.role, + formData_tenant_id: props.formData.tenant_id + }); // 再等待一个tick,确保组件能正确渲染 await nextTick(); } else if (val && !props.isEdit) { // 新增时重置表单 + // 从 props.formData 获取 tenant_id(如果有的话) + const tenantId = props.formData?.tenant_id || null; form.value = { id: null, employeeNo: "", @@ -284,8 +340,11 @@ watch(() => props.visible, async (val) => { bank_name: "", bank_account: "", status: 1, - tenant_id: null, + tenant_id: tenantId ? Number(tenantId) : null, }; + console.log('EmployeeEdit - 新增模式,设置表单数据:', { + tenant_id: form.value.tenant_id + }); } }); diff --git a/pc/src/views/apps/oa/components/employees/EmployeeList.vue b/pc/src/views/apps/oa/components/employees/EmployeeList.vue index cc47a58..07a2ba2 100644 --- a/pc/src/views/apps/oa/components/employees/EmployeeList.vue +++ b/pc/src/views/apps/oa/components/employees/EmployeeList.vue @@ -63,7 +63,7 @@ width="180" align="center" /> - + diff --git a/pc/src/views/apps/oa/components/organization/DepartmentTree.vue b/pc/src/views/apps/oa/components/organization/DepartmentTree.vue index b082f41..0dcf0b3 100644 --- a/pc/src/views/apps/oa/components/organization/DepartmentTree.vue +++ b/pc/src/views/apps/oa/components/organization/DepartmentTree.vue @@ -164,15 +164,15 @@ const handleCollapseAll = () => { gap: 8px; margin-top: 8px; - .el-button { - padding: 0; - font-size: 13px; - color: var(--el-color-primary); + // .el-button { + // padding: 0; + // font-size: 13px; + // color: var(--el-color-primary); - &:hover { - color: var(--el-color-primary-light-3); - } - } + // &:hover { + // color: var(--el-color-primary-light-3); + // } + // } } } diff --git a/pc/src/views/apps/oa/employees/index.vue b/pc/src/views/apps/oa/employees/index.vue index 43862f5..6da0158 100644 --- a/pc/src/views/apps/oa/employees/index.vue +++ b/pc/src/views/apps/oa/employees/index.vue @@ -458,6 +458,12 @@ const submitForm = async (formData: any) => { }; const handleDelete = async (employee: Employee) => { + // 如果角色ID是5,不允许删除 + if (employee.role === 5) { + ElMessage.warning("该员工的角色不允许删除"); + return; + } + ElMessageBox.confirm("确认删除该员工?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", diff --git a/pc/src/views/dashboard/index.vue b/pc/src/views/dashboard/index.vue index eff0a47..9c761e4 100644 --- a/pc/src/views/dashboard/index.vue +++ b/pc/src/views/dashboard/index.vue @@ -273,7 +273,13 @@ const handleTaskChange = (task: any) => { // 初始化图表 onMounted(() => { // 折线图 - new Chart(document.getElementById("lineChart") as HTMLCanvasElement, { + const lineChartEl = document.getElementById("lineChart") as HTMLCanvasElement | null; + if (!lineChartEl) { + console.error("Line chart element not found"); + return; + } + + new Chart(lineChartEl, { type: "line", data: { labels: ["1月", "2月", "3月", "4月", "5月", "6月", "7月"], @@ -332,7 +338,13 @@ onMounted(() => { }); // 柱状图 - new Chart(document.getElementById("barChart") as HTMLCanvasElement, { + const barChartEl = document.getElementById("barChart") as HTMLCanvasElement | null; + if (!barChartEl) { + console.error("Bar chart element not found"); + return; + } + + new Chart(barChartEl, { type: "bar", data: { labels: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"], diff --git a/server/controllers/oa.go b/server/controllers/oa.go index fb8d206..7540bac 100644 --- a/server/controllers/oa.go +++ b/server/controllers/oa.go @@ -85,6 +85,7 @@ func (c *OAController) GetOABaseData() { roleList = append(roleList, map[string]interface{}{ "roleId": role.RoleId, "tenantId": role.TenantId, + "default": role.Default, "roleCode": role.RoleCode, "roleName": role.RoleName, "description": role.Description, diff --git a/server/controllers/role.go b/server/controllers/role.go index b6883a7..1074707 100644 --- a/server/controllers/role.go +++ b/server/controllers/role.go @@ -29,6 +29,8 @@ func (c *RoleController) GetAllRoles() { for _, role := range roles { roleList = append(roleList, map[string]interface{}{ "roleId": role.RoleId, + "tenantId": role.TenantId, + "default": role.Default, "roleCode": role.RoleCode, "roleName": role.RoleName, "description": role.Description, @@ -124,10 +126,30 @@ func (c *RoleController) GetRoleByTenantId() { c.ServeJSON() return } + + // 转换为前端需要的格式,确保包含 tenantId 和 default 字段 + roleList := make([]map[string]interface{}, 0) + for _, role := range roles { + roleList = append(roleList, map[string]interface{}{ + "roleId": role.RoleId, + "tenantId": role.TenantId, + "default": role.Default, + "roleCode": role.RoleCode, + "roleName": role.RoleName, + "description": role.Description, + "status": role.Status, + "sortOrder": role.SortOrder, + "createTime": role.CreateTime, + "updateTime": role.UpdateTime, + "createBy": role.CreateBy, + "updateBy": role.UpdateBy, + }) + } + c.Data["json"] = map[string]interface{}{ "code": 0, "message": "获取角色列表成功", - "data": roles, + "data": roleList, } c.ServeJSON() } diff --git a/server/database/add_employee_fields.sql b/server/database/add_employee_fields.sql deleted file mode 100644 index f1492ca..0000000 --- a/server/database/add_employee_fields.sql +++ /dev/null @@ -1,108 +0,0 @@ --- 为员工表添加工资卡信息和密码字段 --- 创建时间: 2025 - -SET @dbname = DATABASE(); -SET @tablename = 'yz_tenant_employees'; - --- 添加工资卡开户行字段 -SET @columnname = 'bank_name'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column bank_name already exists in yz_tenant_employees" AS "";', - 'ALTER TABLE yz_tenant_employees ADD COLUMN bank_name VARCHAR(100) DEFAULT NULL COMMENT ''工资卡开户行'' AFTER position_id;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 添加工资卡卡号字段 -SET @columnname = 'bank_account'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column bank_account already exists in yz_tenant_employees" AS "";', - 'ALTER TABLE yz_tenant_employees ADD COLUMN bank_account VARCHAR(50) DEFAULT NULL COMMENT ''工资卡卡号'' AFTER bank_name;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 添加登录密码字段 -SET @columnname = 'password'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column password already exists in yz_tenant_employees" AS "";', - 'ALTER TABLE yz_tenant_employees ADD COLUMN password VARCHAR(255) DEFAULT NULL COMMENT ''登录密码(加密后)'' AFTER bank_account;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 添加盐值字段 -SET @columnname = 'salt'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column salt already exists in yz_tenant_employees" AS "";', - 'ALTER TABLE yz_tenant_employees ADD COLUMN salt VARCHAR(100) DEFAULT NULL COMMENT ''密码盐值'' AFTER password;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 添加最后登录时间字段 -SET @columnname = 'last_login_time'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column last_login_time already exists in yz_tenant_employees" AS "";', - 'ALTER TABLE yz_tenant_employees ADD COLUMN last_login_time DATETIME DEFAULT NULL COMMENT ''最后登录时间'' AFTER salt;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 添加最后登录IP字段 -SET @columnname = 'last_login_ip'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column last_login_ip already exists in yz_tenant_employees" AS "";', - 'ALTER TABLE yz_tenant_employees ADD COLUMN last_login_ip VARCHAR(50) DEFAULT NULL COMMENT ''最后登录IP'' AFTER last_login_time;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - diff --git a/server/database/add_knowledge_menus.sql b/server/database/add_knowledge_menus.sql deleted file mode 100644 index 5011e1c..0000000 --- a/server/database/add_knowledge_menus.sql +++ /dev/null @@ -1,65 +0,0 @@ --- 添加知识库管理相关的菜单项 --- 注意:需要先查询知识库菜单的ID(parent_id),假设为 11 - --- 如果知识库菜单ID为11,添加分类管理和标签管理菜单 --- 请根据实际数据库中的知识库菜单ID修改下面的 parent_id 值 - --- 查询知识库菜单ID(如果需要) --- SELECT id FROM yz_menus WHERE path = '/apps/knowledge'; - --- 添加分类管理菜单(如果不存在) -INSERT INTO yz_menus ( - name, - path, - parent_id, - icon, - `order`, - status, - component_path, - menu_type, - description -) -SELECT - '分类管理', - '/apps/knowledge/category', - id, - 'fa-solid fa-folder', - 2, - 1, - '@/views/apps/knowledge/category/index.vue', - 1, - '知识库分类管理' -FROM yz_menus -WHERE path = '/apps/knowledge' -AND NOT EXISTS ( - SELECT 1 FROM yz_menus WHERE path = '/apps/knowledge/category' -); - --- 添加标签管理菜单(如果不存在) -INSERT INTO yz_menus ( - name, - path, - parent_id, - icon, - `order`, - status, - component_path, - menu_type, - description -) -SELECT - '标签管理', - '/apps/knowledge/tag', - id, - 'fa-solid fa-tags', - 3, - 1, - '@/views/apps/knowledge/tag/index.vue', - 1, - '知识库标签管理' -FROM yz_menus -WHERE path = '/apps/knowledge' -AND NOT EXISTS ( - SELECT 1 FROM yz_menus WHERE path = '/apps/knowledge/tag' -); - diff --git a/server/database/add_menu_delete_time.sql b/server/database/add_menu_delete_time.sql deleted file mode 100644 index 83faa69..0000000 --- a/server/database/add_menu_delete_time.sql +++ /dev/null @@ -1,38 +0,0 @@ --- 为菜单表添加 delete_time 字段(软删除) --- 如果字段已存在,则不会重复添加 - -SET @dbname = DATABASE(); -SET @tablename = 'yz_menus'; -SET @columnname = 'delete_time'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column delete_time already exists in yz_menus" AS "";', - 'ALTER TABLE yz_menus ADD COLUMN delete_time DATETIME DEFAULT NULL COMMENT ''删除时间(软删除)'' AFTER update_time;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 添加索引以优化查询性能 -SET @indexname = 'idx_delete_time'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (INDEX_NAME = @indexname) - ) > 0, - 'SELECT "Index idx_delete_time already exists in yz_menus" AS "";', - 'ALTER TABLE yz_menus ADD INDEX idx_delete_time (delete_time);' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - diff --git a/server/database/add_organization_menu.sql b/server/database/add_organization_menu.sql deleted file mode 100644 index ad8a0ca..0000000 --- a/server/database/add_organization_menu.sql +++ /dev/null @@ -1,15 +0,0 @@ --- 添加组织架构管理菜单 --- 注意:请根据实际情况修改 parent_id(92 是 OA 模块的 ID,请确认) - -INSERT INTO yz_menus (name, path, parent_id, icon, `order`, status, component_path, menu_type, description) -VALUES ('组织架构', '/apps/oa/organization', 92, 'fa-solid fa-sitemap', 1, 1, '@/views/apps/oa/organization/index.vue', 1, '组织架构管理(部门与职位)') -ON DUPLICATE KEY UPDATE - name = VALUES(name), - path = VALUES(path), - icon = VALUES(icon), - `order` = VALUES(`order`), - status = VALUES(status), - component_path = VALUES(component_path), - menu_type = VALUES(menu_type), - description = VALUES(description); - diff --git a/server/database/check_role_permissions.sql b/server/database/check_role_permissions.sql deleted file mode 100644 index a839055..0000000 --- a/server/database/check_role_permissions.sql +++ /dev/null @@ -1,57 +0,0 @@ --- 检查角色权限数据 --- 查询角色ID为1的权限信息 - --- 1. 查看角色基本信息 -SELECT - role_id, - role_name, - menu_ids, - JSON_LENGTH(COALESCE(menu_ids, CAST('[]' AS JSON))) as menu_count, - tenant_id, - status -FROM yz_roles -WHERE role_id = 1; - --- 2. 查看所有角色的 menu_ids 字段 -SELECT - role_id, - role_name, - menu_ids, - JSON_LENGTH(COALESCE(menu_ids, CAST('[]' AS JSON))) as menu_count -FROM yz_roles -WHERE delete_time IS NULL -ORDER BY role_id; - --- 3. 查看菜单表中有权限标识的菜单 -SELECT - id, - name, - path, - permission, - menu_type, - parent_id -FROM yz_menus -WHERE delete_time IS NULL - AND permission IS NOT NULL - AND permission != '' -ORDER BY id -LIMIT 20; - --- 4. 如果 role_id=1 的 menu_ids 不为空,查看这些菜单的权限标识 --- 假设 menu_ids 是 [1,2,3],可以这样查询: --- SELECT DISTINCT permission --- FROM yz_menus --- WHERE id IN (1,2,3) --- AND delete_time IS NULL --- AND permission IS NOT NULL --- AND permission != ''; - --- 5. 查看 menu_ids 字段的原始JSON值(用于调试) -SELECT - role_id, - role_name, - menu_ids, - CAST(menu_ids AS CHAR) as menu_ids_str -FROM yz_roles -WHERE role_id = 1; - diff --git a/server/database/create_knowledge_tables.sql b/server/database/create_knowledge_tables.sql deleted file mode 100644 index 0519ecb..0000000 --- a/server/database/create_knowledge_tables.sql +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/server/database/create_missing_tables.sql b/server/database/create_missing_tables.sql deleted file mode 100644 index f67065d..0000000 --- a/server/database/create_missing_tables.sql +++ /dev/null @@ -1,178 +0,0 @@ --- 只创建缺失的表,不重复创建已存在的表 --- 适用于部分表已存在的情况 - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- 检查并创建用户表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_users ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID', - username VARCHAR(50) NOT NULL COMMENT '用户名', - password VARCHAR(255) NOT NULL COMMENT '加密后的密码', - salt VARCHAR(100) NOT NULL COMMENT '密码盐值', - email VARCHAR(100) DEFAULT NULL COMMENT '邮箱地址', - avatar VARCHAR(500) DEFAULT NULL COMMENT '头像URL', - nickname VARCHAR(50) DEFAULT NULL COMMENT '昵称', - role VARCHAR(20) DEFAULT 'user' COMMENT '用户角色:admin-管理员,user-普通用户', - status TINYINT DEFAULT 1 COMMENT '用户状态:0-禁用,1-启用', - last_login_time DATETIME DEFAULT NULL COMMENT '最后登录时间', - last_login_ip VARCHAR(45) DEFAULT NULL COMMENT '最后登录IP', - login_count INT DEFAULT 0 COMMENT '登录次数', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人', - - -- 索引 - UNIQUE KEY uk_username (username), - INDEX idx_email (email), - INDEX idx_role (role), - INDEX idx_status (status), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表'; - --- 检查并创建菜单表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_menus ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '菜单ID', - name VARCHAR(100) NOT NULL COMMENT '菜单名称', - path VARCHAR(255) NOT NULL COMMENT '菜单路径', - parent_id INT DEFAULT 0 COMMENT '父菜单ID,0表示顶级菜单', - level TINYINT DEFAULT 1 COMMENT '菜单层级:1-一级菜单,2-二级菜单,3-三级菜单', - full_path VARCHAR(500) DEFAULT NULL COMMENT '完整路径(包含所有父级路径)', - is_leaf TINYINT DEFAULT 0 COMMENT '是否叶子节点:0-非叶子节点,1-叶子节点', - has_children TINYINT DEFAULT 0 COMMENT '是否有子菜单:0-无子菜单,1-有子菜单', - children_count INT DEFAULT 0 COMMENT '子菜单数量', - icon VARCHAR(100) DEFAULT NULL COMMENT '菜单图标', - order_num INT DEFAULT 0 COMMENT '排序序号', - status TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用', - component_path VARCHAR(500) DEFAULT NULL COMMENT '组件路径', - is_external TINYINT DEFAULT 0 COMMENT '是否外部链接:0-内部路由,1-外部链接', - external_url VARCHAR(1000) DEFAULT NULL COMMENT '外部链接地址', - menu_type TINYINT DEFAULT 1 COMMENT '菜单类型:1-普通菜单,2-分组菜单,3-按钮菜单', - permission VARCHAR(200) DEFAULT NULL COMMENT '权限标识', - description VARCHAR(500) DEFAULT NULL COMMENT '菜单描述', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - create_by VARCHAR(100) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(100) DEFAULT NULL COMMENT '更新人', - - -- 索引 - INDEX idx_parent_id (parent_id), - INDEX idx_level (level), - INDEX idx_status (status), - INDEX idx_order (order_num), - INDEX idx_menu_type (menu_type), - INDEX idx_full_path (full_path(255)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='菜单表(增强版)'; - --- 检查并创建程序分类表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_program_category ( - category_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '分类ID', - category_name VARCHAR(100) NOT NULL COMMENT '分类名称', - category_desc VARCHAR(500) DEFAULT NULL COMMENT '分类描述', - parent_id INT DEFAULT 0 COMMENT '父分类ID,0表示顶级分类', - sort_order INT DEFAULT 0 COMMENT '排序序号,用于展示顺序', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - - -- 索引 - INDEX idx_parent_id (parent_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='程序分类表'; - --- 检查并创建程序信息表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_program_info ( - program_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '程序ID', - category_id INT NOT NULL COMMENT '所属分类ID', - program_name VARCHAR(200) NOT NULL COMMENT '程序名称', - program_desc TEXT COMMENT '程序描述', - jump_url VARCHAR(1000) NOT NULL COMMENT '跳转地址', - icon_url VARCHAR(1000) DEFAULT NULL COMMENT '程序图标地址', - version VARCHAR(50) DEFAULT NULL COMMENT '程序版本', - status TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用', - sort_order INT DEFAULT 0 COMMENT '排序序号,用于展示顺序', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - - -- 索引 - INDEX idx_category_id (category_id), - INDEX idx_status (status), - CONSTRAINT yz_fk_program_category FOREIGN KEY (category_id) REFERENCES yz_program_category (category_id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='程序信息表'; - --- 检查并创建租户表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_tenants ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '租户ID', - - -- 基本信息 - name VARCHAR(100) NOT NULL COMMENT '租户名称', - code VARCHAR(50) NOT NULL COMMENT '租户编码(唯一)', - owner VARCHAR(50) NOT NULL COMMENT '负责人', - phone VARCHAR(20) DEFAULT NULL COMMENT '联系电话', - email VARCHAR(100) DEFAULT NULL COMMENT '邮箱地址', - - -- 状态信息 - status VARCHAR(20) DEFAULT 'enabled' COMMENT '状态:enabled-启用,disabled-禁用', - audit_status VARCHAR(20) DEFAULT 'pending' COMMENT '审核状态:pending-待审核,approved-已通过,rejected-已拒绝', - - -- 审核信息 - audit_comment TEXT DEFAULT NULL COMMENT '审核意见', - audit_by VARCHAR(50) DEFAULT NULL COMMENT '审核人', - audit_time DATETIME DEFAULT NULL COMMENT '审核时间', - - -- 其他信息 - remark TEXT DEFAULT NULL COMMENT '备注', - - -- 时间戳 - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人', - - -- 索引 - UNIQUE KEY uk_code (code), - INDEX idx_name (name), - INDEX idx_owner (owner), - INDEX idx_status (status), - INDEX idx_audit_status (audit_status), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户表'; - --- 检查并创建文件表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_files ( - id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '文件ID', - tenant_id VARCHAR(64) NOT NULL COMMENT '租户ID', - user_id INT NOT NULL DEFAULT 0 COMMENT '用户ID', - - -- 文件基础信息 - file_name VARCHAR(255) NOT NULL COMMENT '文件名称', - original_name VARCHAR(255) NOT NULL COMMENT '原始文件名', - file_path VARCHAR(500) NOT NULL COMMENT '文件存储路径', - file_url VARCHAR(500) COMMENT '文件访问URL', - file_size BIGINT NOT NULL DEFAULT 0 COMMENT '文件大小(字节)', - file_type VARCHAR(50) NOT NULL COMMENT '文件类型', - file_ext VARCHAR(20) NOT NULL COMMENT '文件扩展名', - - -- 分类信息 - category VARCHAR(100) NOT NULL COMMENT '文件分类', - sub_category VARCHAR(100) COMMENT '子分类', - - -- 状态信息 - status TINYINT DEFAULT 1 COMMENT '状态(1:正常, 0:删除)', - is_public TINYINT DEFAULT 0 COMMENT '是否公开(1:是, 0:否)', - - -- 上传信息 - upload_by VARCHAR(100) NOT NULL COMMENT '上传人', - upload_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', - - -- 索引 - INDEX idx_tenant (tenant_id), - INDEX idx_user (user_id), - INDEX idx_category (category), - INDEX idx_upload_time (upload_time), - INDEX idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文件表'; - -SET FOREIGN_KEY_CHECKS = 1; - --- 完成创建 -SELECT 'Missing tables created successfully!' as message; diff --git a/server/database/create_oa_tables.sql b/server/database/create_oa_tables.sql deleted file mode 100644 index 352e138..0000000 --- a/server/database/create_oa_tables.sql +++ /dev/null @@ -1,163 +0,0 @@ --- 创建OA模块相关表(租户应用) --- 创建时间: 2025 --- 描述: 创建租户部门表、租户职位表和租户员工表 - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ============================================= --- 1. 创建租户部门表 --- ============================================= -CREATE TABLE IF NOT EXISTS yz_tenant_departments ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID', - tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID', - name VARCHAR(100) NOT NULL COMMENT '部门名称', - code VARCHAR(50) DEFAULT NULL COMMENT '部门编码', - parent_id INT DEFAULT 0 COMMENT '父部门ID,0表示顶级部门', - description TEXT DEFAULT NULL COMMENT '部门描述', - manager_id INT DEFAULT NULL COMMENT '部门经理ID', - sort_order INT DEFAULT 0 COMMENT '排序序号', - status TINYINT DEFAULT 1 COMMENT '状态:1-启用,0-禁用', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)', - - -- 索引 - INDEX idx_tenant_id (tenant_id), - INDEX idx_code (code), - INDEX idx_parent_id (parent_id), - INDEX idx_status (status), - INDEX idx_sort_order (sort_order) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户部门表'; - --- ============================================= --- 2. 创建租户职位表 --- ============================================= -CREATE TABLE IF NOT EXISTS yz_tenant_positions ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '职位ID', - tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID', - name VARCHAR(100) NOT NULL COMMENT '职位名称', - code VARCHAR(50) DEFAULT NULL COMMENT '职位编码', - department_id INT DEFAULT NULL COMMENT '所属部门ID', - level INT DEFAULT 0 COMMENT '职位级别', - description TEXT DEFAULT NULL COMMENT '职位描述', - sort_order INT DEFAULT 0 COMMENT '排序序号', - status TINYINT DEFAULT 1 COMMENT '状态:1-启用,0-禁用', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)', - - -- 索引 - INDEX idx_tenant_id (tenant_id), - INDEX idx_code (code), - INDEX idx_department_id (department_id), - INDEX idx_status (status), - INDEX idx_sort_order (sort_order) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户职位表'; - --- ============================================= --- 3. 创建租户员工表(如果不存在) --- ============================================= -CREATE TABLE IF NOT EXISTS yz_tenant_employees ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID', - tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID', - employee_no VARCHAR(50) NOT NULL COMMENT '工号', - name VARCHAR(50) NOT NULL COMMENT '姓名', - phone VARCHAR(20) DEFAULT NULL COMMENT '手机号', - email VARCHAR(100) DEFAULT NULL COMMENT '邮箱', - department_id INT DEFAULT NULL COMMENT '部门ID', - position_id INT DEFAULT NULL COMMENT '职位ID', - status TINYINT DEFAULT 1 COMMENT '状态:1-在职,0-离职', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)', - - -- 索引 - INDEX idx_tenant_id (tenant_id), - INDEX idx_employee_no (employee_no), - INDEX idx_name (name), - INDEX idx_department_id (department_id), - INDEX idx_position_id (position_id), - INDEX idx_status (status), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户员工表'; - --- ============================================= --- 4. 更新用户表,添加部门和职位字段(如果不存在) --- ============================================= --- 检查并添加 department_id 字段 -SET @dbname = DATABASE(); -SET @tablename = 'yz_users'; -SET @columnname = 'department_id'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column department_id already exists in yz_users" AS "";', - 'ALTER TABLE yz_users ADD COLUMN department_id INT DEFAULT NULL COMMENT ''部门ID'' AFTER role;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 检查并添加 position_id 字段 -SET @columnname = 'position_id'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column position_id already exists in yz_users" AS "";', - 'ALTER TABLE yz_users ADD COLUMN position_id INT DEFAULT NULL COMMENT ''职位ID'' AFTER department_id;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- ============================================= --- 5. 更新租户员工表,添加部门和职位字段(如果不存在) --- ============================================= --- 检查并添加 department_id 字段 -SET @tablename = 'yz_tenant_employees'; -SET @columnname = 'department_id'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column department_id already exists in yz_employees" AS "";', - 'ALTER TABLE yz_tenant_employees ADD COLUMN department_id INT DEFAULT NULL COMMENT ''部门ID'' AFTER email;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 检查并添加 position_id 字段 -SET @columnname = 'position_id'; -SET @preparedStatement = (SELECT IF( - ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE - (TABLE_SCHEMA = @dbname) - AND (TABLE_NAME = @tablename) - AND (COLUMN_NAME = @columnname) - ) > 0, - 'SELECT "Column position_id already exists in yz_tenant_employees" AS "";', - 'ALTER TABLE yz_tenant_employees ADD COLUMN position_id INT DEFAULT NULL COMMENT ''职位ID'' AFTER department_id;' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - -SET FOREIGN_KEY_CHECKS = 1; - diff --git a/server/database/drop_and_recreate_tables.sql b/server/database/drop_and_recreate_tables.sql deleted file mode 100644 index d88f46a..0000000 --- a/server/database/drop_and_recreate_tables.sql +++ /dev/null @@ -1,18 +0,0 @@ --- 删除现有表并重新创建 --- 注意:这会删除所有数据,请谨慎使用 - -SET FOREIGN_KEY_CHECKS = 0; - --- 删除现有表(按依赖关系顺序) -DROP TABLE IF EXISTS yz_user_role_relations; -DROP TABLE IF EXISTS yz_user_roles; -DROP TABLE IF EXISTS yz_files; -DROP TABLE IF EXISTS yz_program_info; -DROP TABLE IF EXISTS yz_program_category; -DROP TABLE IF EXISTS yz_menus; -DROP TABLE IF EXISTS yz_users; - -SET FOREIGN_KEY_CHECKS = 1; - --- 现在可以重新运行 init_database.sql -SELECT 'Tables dropped successfully. You can now run init_database.sql' as message; diff --git a/server/database/init_database.sql b/server/database/init_database.sql deleted file mode 100644 index 8647d9a..0000000 --- a/server/database/init_database.sql +++ /dev/null @@ -1,336 +0,0 @@ --- 云泽系统数据库初始化脚本 --- 创建时间: 2024 --- 描述: 包含所有系统表的创建和初始化数据 - --- 设置字符集 -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ============================================= --- 1. 用户相关表 --- ============================================= - --- 创建用户表 -CREATE TABLE yz_users ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID', - username VARCHAR(50) NOT NULL COMMENT '用户名', - password VARCHAR(255) NOT NULL COMMENT '加密后的密码', - salt VARCHAR(100) NOT NULL COMMENT '密码盐值', - email VARCHAR(100) DEFAULT NULL COMMENT '邮箱地址', - avatar VARCHAR(500) DEFAULT NULL COMMENT '头像URL', - nickname VARCHAR(50) DEFAULT NULL COMMENT '昵称', - role VARCHAR(20) DEFAULT 'user' COMMENT '用户角色:admin-管理员,user-普通用户', - status TINYINT DEFAULT 1 COMMENT '用户状态:0-禁用,1-启用', - last_login_time DATETIME DEFAULT NULL COMMENT '最后登录时间', - last_login_ip VARCHAR(45) DEFAULT NULL COMMENT '最后登录IP', - login_count INT DEFAULT 0 COMMENT '登录次数', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人', - - -- 索引 - UNIQUE KEY uk_username (username), - INDEX idx_email (email), - INDEX idx_role (role), - INDEX idx_status (status), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表'; - --- ============================================= --- 2. 菜单相关表 --- ============================================= - --- 创建菜单表(增强版,包含完整父子级别参数) -CREATE TABLE yz_menus ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '菜单ID', - name VARCHAR(100) NOT NULL COMMENT '菜单名称', - path VARCHAR(255) NOT NULL COMMENT '菜单路径', - parent_id INT DEFAULT 0 COMMENT '父菜单ID,0表示顶级菜单', - icon VARCHAR(100) DEFAULT NULL COMMENT '菜单图标', - `order` INT DEFAULT 0 COMMENT '排序序号', - `status` TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用', - component_path VARCHAR(500) DEFAULT NULL COMMENT '组件路径', - is_external TINYINT DEFAULT 0 COMMENT '是否外部链接:0-内部路由,1-外部链接', - external_url VARCHAR(1000) DEFAULT NULL COMMENT '外部链接地址', - menu_type TINYINT DEFAULT 1 COMMENT '菜单类型:1-普通菜单,2-分组菜单,3-按钮菜单', - permission VARCHAR(200) DEFAULT NULL COMMENT '权限标识', - description VARCHAR(500) DEFAULT NULL COMMENT '菜单描述', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - create_by VARCHAR(100) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(100) DEFAULT NULL COMMENT '更新人', - - -- 索引 - INDEX idx_parent_id (parent_id), - INDEX idx_status (`status`), - INDEX idx_order (`order`), - INDEX idx_menu_type (menu_type) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='菜单表(增强版)'; - --- ============================================= --- 3. 程序管理相关表 --- ============================================= - --- 创建程序分类表 -CREATE TABLE yz_program_category ( - category_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '分类ID', - category_name VARCHAR(100) NOT NULL COMMENT '分类名称', - category_desc VARCHAR(500) DEFAULT NULL COMMENT '分类描述', - parent_id INT DEFAULT 0 COMMENT '父分类ID,0表示顶级分类', - sort_order INT DEFAULT 0 COMMENT '排序序号,用于展示顺序', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - - -- 索引 - INDEX idx_parent_id (parent_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='程序分类表'; - --- 创建程序信息表 -CREATE TABLE yz_program_info ( - program_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '程序ID', - category_id INT NOT NULL COMMENT '所属分类ID', - program_name VARCHAR(200) NOT NULL COMMENT '程序名称', - program_desc TEXT COMMENT '程序描述', - jump_url VARCHAR(1000) NOT NULL COMMENT '跳转地址', - icon_url VARCHAR(1000) DEFAULT NULL COMMENT '程序图标地址', - version VARCHAR(50) DEFAULT NULL COMMENT '程序版本', - status TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用', - sort_order INT DEFAULT 0 COMMENT '排序序号,用于展示顺序', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - - -- 索引 - INDEX idx_category_id (category_id), - INDEX idx_status (status), - CONSTRAINT yz_fk_program_category FOREIGN KEY (category_id) REFERENCES yz_program_category (category_id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='程序信息表'; - --- ============================================= --- 4. 租户管理相关表 --- ============================================= - --- 创建租户表 -CREATE TABLE yz_tenants ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '租户ID', - - -- 基本信息 - name VARCHAR(100) NOT NULL COMMENT '租户名称', - code VARCHAR(50) NOT NULL COMMENT '租户编码(唯一)', - owner VARCHAR(50) NOT NULL COMMENT '负责人', - phone VARCHAR(20) DEFAULT NULL COMMENT '联系电话', - email VARCHAR(100) DEFAULT NULL COMMENT '邮箱地址', - - -- 状态信息 - status VARCHAR(20) DEFAULT 'enabled' COMMENT '状态:enabled-启用,disabled-禁用', - audit_status VARCHAR(20) DEFAULT 'pending' COMMENT '审核状态:pending-待审核,approved-已通过,rejected-已拒绝', - - -- 审核信息 - audit_comment TEXT DEFAULT NULL COMMENT '审核意见', - audit_by VARCHAR(50) DEFAULT NULL COMMENT '审核人', - audit_time DATETIME DEFAULT NULL COMMENT '审核时间', - - -- 其他信息 - remark TEXT DEFAULT NULL COMMENT '备注', - - -- 时间戳 - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人', - - -- 索引 - UNIQUE KEY uk_code (code), - INDEX idx_name (name), - INDEX idx_owner (owner), - INDEX idx_status (status), - INDEX idx_audit_status (audit_status), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户表'; - --- ============================================= --- 5. 文件管理相关表 --- ============================================= - --- 创建文件表 -CREATE TABLE yz_files ( - id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '文件ID', - tenant_id VARCHAR(64) NOT NULL COMMENT '租户ID', - user_id INT NOT NULL DEFAULT 0 COMMENT '用户ID', - - -- 文件基础信息 - file_name VARCHAR(255) NOT NULL COMMENT '文件名称', - original_name VARCHAR(255) NOT NULL COMMENT '原始文件名', - file_path VARCHAR(500) NOT NULL COMMENT '文件存储路径', - file_url VARCHAR(500) COMMENT '文件访问URL', - file_size BIGINT NOT NULL DEFAULT 0 COMMENT '文件大小(字节)', - file_type VARCHAR(50) NOT NULL COMMENT '文件类型', - file_ext VARCHAR(20) NOT NULL COMMENT '文件扩展名', - - -- 分类信息 - category VARCHAR(100) NOT NULL COMMENT '文件分类', - sub_category VARCHAR(100) COMMENT '子分类', - - -- 状态信息 - status TINYINT DEFAULT 1 COMMENT '状态(1:正常, 0:删除)', - is_public TINYINT DEFAULT 0 COMMENT '是否公开(1:是, 0:否)', - - -- 上传信息 - upload_by VARCHAR(100) NOT NULL COMMENT '上传人', - upload_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', - - -- 索引 - INDEX idx_tenant (tenant_id), - INDEX idx_user (user_id), - INDEX idx_category (category), - INDEX idx_upload_time (upload_time), - INDEX idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文件表'; - --- ============================================= --- 6. 插入初始数据 --- ============================================= - --- 插入默认租户数据 -INSERT INTO yz_tenants (name, code, owner, phone, email, status, audit_status, audit_comment, audit_by, audit_time, remark, create_by) VALUES -('默认租户', 'default', 'admin', '13800138000', 'admin@yunzer.com', 'enabled', 'approved', '系统默认租户,自动通过审核', 'system', NOW(), '系统默认租户,用于初始化数据', 'system'), -('示例租户A', 'demo-a', '张三', '13900139000', 'zhangsan@demo.com', 'enabled', 'approved', '资料完整,审核通过', 'admin', DATE_SUB(NOW(), INTERVAL 30 DAY), '演示租户A,用于展示功能', 'admin'), -('示例租户B', 'demo-b', '李四', '13700137000', 'lisi@demo.com', 'enabled', 'pending', NULL, NULL, NULL, '待审核租户,资料已提交', 'admin'); - --- 插入默认管理员用户 --- 注意:实际使用时需要生成真实的加密密码和盐值 -INSERT INTO yz_users (username, password, salt, email, nickname, role, status, create_by) VALUES -('admin', 'encrypted_password_here', 'salt_here', 'admin@yunzer.com', '超级管理员', 'admin', 1, 'system'), -('test', 'encrypted_password_here', 'salt_here', 'test@yunzer.com', '测试用户', 'user', 1, 'system'); - --- 插入默认菜单数据 -INSERT INTO yz_menus (name, path, parent_id, icon, `order`, status, component_path, menu_type, description) VALUES -('仪表盘', '/dashboard', 0, 'fa-solid fa-gauge', 1, 1, '@/views/dashboard/index.vue', 1, '系统仪表盘,展示关键指标'), -('系统管理', '/system', 0, 'fa-solid fa-screwdriver-wrench', 4, 1, null, 2, '系统管理功能模块'), -('系统设置', '/settings', 0, 'fa-solid fa-gear', 99, 1, '@/views/settings/index.vue', 1, '系统参数设置'), -('应用管理', '/apps', 0, 'fa-solid fa-gear', 4, 1, null, 2, '应用管理功能模块'), -('文件管理', '/system/files', 2, 'fa-solid fa-folder', 3, 1, '@/views/system/files/index.vue', 1, '文件管理系统'), -('租户管理', '/system/tenant', 2, 'fas fa-user-friends', 2, 1, '@/views/system/tenant/index.vue', 1, '租户管理'), -('用户管理', '/system/users', 2, 'fa-solid fa-user', 1, 1, '@/views/system/users/index.vue', 1, '用户信息管理'), -('角色管理', '/system/roles', 2, 'fa-solid fa-user-tag', 2, 1, '@/views/system/roles/index.vue', 1, '角色权限管理'), -('权限管理', '/system/permissions', 2, 'fa-solid fa-key', 2, 1, '@/views/system/permissions/index.vue', 1, '权限管理'), -('菜单管理', '/system/menus', 2, 'fa-solid fa-bars-progress', 2, 1, '@/views/system/menus/manager.vue', 1, '菜单权限管理'), -('程序管理', '/system/programs', 2, 'fa-solid fa-grip', 3, 1, '@/views/system/programs/index.vue', 1, '程序功能管理'), -('知识库', '/apps/knowledge', 4, 'fa-solid fa-book', 1, 1, '@/views/apps/knowledge/index.vue', 1, '知识库管理'), -('编辑', '/apps/knowledge/edit', 11, '', 1, 1, '@/views/apps/knowledge/components/edit.vue', 1, '知识库编辑'), -('详情', '/apps/knowledge/detail', 11, '', 1, 1, '@/views/apps/knowledge/components/detail.vue', 1, '知识库详情'); - --- ============================================= --- 4. 知识库相关表 --- ============================================= - --- 创建知识库分类表 -CREATE TABLE IF NOT EXISTS yz_knowledge_category ( - category_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '分类ID', - category_name VARCHAR(100) NOT NULL COMMENT '分类名称', - category_desc VARCHAR(500) DEFAULT NULL COMMENT '分类描述', - parent_id INT DEFAULT 0 COMMENT '父分类ID,0表示顶级分类', - sort_order INT DEFAULT 0 COMMENT '排序序号', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - INDEX idx_parent_id (parent_id), - INDEX idx_sort_order (sort_order) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库分类表'; - --- 创建知识库标签表 -CREATE TABLE IF NOT EXISTS yz_knowledge_tags ( - tag_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '标签ID', - tag_name VARCHAR(50) NOT NULL COMMENT '标签名称', - tag_color VARCHAR(20) DEFAULT NULL COMMENT '标签颜色', - usage_count INT DEFAULT 0 COMMENT '使用次数', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - UNIQUE KEY uk_tag_name (tag_name), - INDEX idx_usage_count (usage_count) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库标签表'; - --- 创建知识库内容表 -CREATE TABLE IF NOT EXISTS yz_knowledge ( - knowledge_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '知识ID', - title VARCHAR(200) NOT NULL COMMENT '标题', - category_id INT DEFAULT NULL COMMENT '分类ID', - tags TEXT COMMENT '标签JSON数组,存储标签名称', - author VARCHAR(50) NOT NULL COMMENT '作者', - content LONGTEXT COMMENT '正文内容(富文本)', - summary VARCHAR(500) DEFAULT NULL COMMENT '摘要', - cover_url VARCHAR(500) DEFAULT NULL COMMENT '封面图片URL', - status TINYINT DEFAULT 0 COMMENT '状态:0-草稿,1-已发布,2-已归档', - view_count INT DEFAULT 0 COMMENT '查看次数', - like_count INT DEFAULT 0 COMMENT '点赞数', - is_recommend TINYINT DEFAULT 0 COMMENT '是否推荐:0-否,1-是', - is_top TINYINT DEFAULT 0 COMMENT '是否置顶:0-否,1-是', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人', - INDEX idx_category_id (category_id), - INDEX idx_author (author), - INDEX idx_title (title), - INDEX idx_status (status), - INDEX idx_create_time (create_time), - INDEX idx_view_count (view_count), - INDEX idx_is_recommend (is_recommend), - INDEX idx_is_top (is_top) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库内容表'; - --- 创建知识库收藏表 -CREATE TABLE IF NOT EXISTS yz_knowledge_favorites ( - favorite_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '收藏ID', - knowledge_id INT NOT NULL COMMENT '知识ID', - user_id INT NOT NULL COMMENT '用户ID', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - UNIQUE KEY uk_knowledge_user (knowledge_id, user_id), - INDEX idx_user_id (user_id), - CONSTRAINT yz_fk_fav_knowledge FOREIGN KEY (knowledge_id) REFERENCES yz_knowledge (knowledge_id) ON DELETE CASCADE, - CONSTRAINT yz_fk_fav_user FOREIGN KEY (user_id) REFERENCES yz_users (id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库收藏表'; - --- 插入默认知识库分类 -INSERT INTO yz_knowledge_category (category_name, category_desc, parent_id, sort_order) VALUES -('技术文档', '技术相关的文档资料', 0, 1), -('产品手册', '产品使用手册和说明', 0, 2), -('用户指南', '用户操作指南和教程', 0, 3), -('常见问题', '常见问题解答', 0, 4), -('API文档', 'API接口文档', 0, 5), -('Vue', 'Vue框架相关', 1, 101), -('React', 'React框架相关', 1, 102), -('Go', 'Go语言相关', 1, 103); - --- 插入默认知识库标签 -INSERT INTO yz_knowledge_tags (tag_name, tag_color, usage_count) VALUES -('Vue', '#4CAF50', 0), -('React', '#42A5F5', 0), -('TypeScript', '#3178C6', 0), -('Element Plus', '#409EFF', 0), -('Vue Router', '#4FC08D', 0), -('Pinia', '#FFD54F', 0), -('Go', '#00ADD8', 0), -('Python', '#3776AB', 0); - --- 插入默认程序分类 -INSERT INTO yz_program_category (category_name, category_desc, parent_id, sort_order) VALUES -('办公软件', '办公相关程序', 0, 1), -('开发工具', '开发相关工具', 0, 2), -('系统工具', '系统管理工具', 0, 3); - --- 插入默认程序信息 -INSERT INTO yz_program_info (category_id, program_name, program_desc, jump_url, icon_url, version, status, sort_order) VALUES -(1, '文档编辑器', '在线文档编辑工具', '/editor', '/icons/editor.png', '1.0.0', 1, 1), -(2, '代码编辑器', '在线代码编辑工具', '/code', '/icons/code.png', '1.0.0', 1, 1), -(3, '系统监控', '系统状态监控工具', '/monitor', '/icons/monitor.png', '1.0.0', 1, 1); - --- 恢复外键检查 -SET FOREIGN_KEY_CHECKS = 1; - --- 完成初始化 -SELECT 'Database initialization completed successfully!' as message; - - - - - diff --git a/server/database/migrate_role_permissions_simple.sql b/server/database/migrate_role_permissions_simple.sql deleted file mode 100644 index f99a5c1..0000000 --- a/server/database/migrate_role_permissions_simple.sql +++ /dev/null @@ -1,124 +0,0 @@ --- ============================================= --- 角色权限迁移脚本(简化版,不使用存储过程) --- 将 yz_role_menus 表中的权限数据迁移到 yz_roles 表的 menu_ids 字段(JSON数组) --- ============================================= - -SET NAMES utf8mb4; - --- ============================================= --- 步骤1: 在 yz_roles 表中添加 menu_ids 字段(JSON类型存储菜单ID数组) --- ============================================= - --- 检查字段是否已存在,如果不存在则添加 -SET @exist := (SELECT COUNT(*) FROM information_schema.columns - WHERE table_schema = DATABASE() - AND table_name = 'yz_roles' - AND column_name = 'menu_ids'); - -SET @sqlstmt := IF(@exist = 0, - 'ALTER TABLE yz_roles ADD COLUMN menu_ids JSON NULL COMMENT ''菜单权限ID数组,JSON格式存储'' AFTER description', - 'SELECT ''字段 menu_ids 已存在,跳过添加'' AS message'); - -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- ============================================= --- 步骤2: 从 yz_role_menus 表迁移数据到 yz_roles.menu_ids --- ============================================= - --- 检查 yz_role_menus 表是否存在 -SET @table_exists := (SELECT COUNT(*) FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name = 'yz_role_menus'); - --- 如果表存在,执行迁移 --- 注意:需要分步执行,因为PREPARE不能执行多语句 - --- 2.1 创建临时表存储每个角色的菜单ID数组 --- 使用 GROUP_CONCAT 和 CONCAT 来构建JSON数组(兼容性更好) --- 处理 NULL 情况:如果 GROUP_CONCAT 返回 NULL,则使用空数组 '[]' -SET @sqlstmt := IF(@table_exists > 0, - 'CREATE TEMPORARY TABLE temp_role_menu_ids AS - SELECT - role_id, - IFNULL(CONCAT(''['', GROUP_CONCAT(menu_id ORDER BY menu_id SEPARATOR '',''), '']''), ''[]'') as menu_ids_json - FROM yz_role_menus - GROUP BY role_id', - 'SELECT ''表 yz_role_menus 不存在,跳过数据迁移'' AS message'); - -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 2.2 更新 yz_roles 表的 menu_ids 字段 --- 将字符串转换为 JSON 类型 -SET @sqlstmt := IF(@table_exists > 0, - 'UPDATE yz_roles r - INNER JOIN temp_role_menu_ids t ON r.role_id = t.role_id - SET r.menu_ids = CAST(t.menu_ids_json AS JSON)', - 'SELECT ''跳过更新'' AS message'); - -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 2.3 删除临时表 -SET @sqlstmt := IF(@table_exists > 0, - 'DROP TEMPORARY TABLE IF EXISTS temp_role_menu_ids', - 'SELECT ''跳过删除临时表'' AS message'); - -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 2.4 对于没有权限的角色,确保设置为空数组(如果还没有设置) --- 使用 CAST 将字符串转换为 JSON 类型 -UPDATE yz_roles -SET menu_ids = CAST('[]' AS JSON) -WHERE menu_ids IS NULL; - --- ============================================= --- 步骤3: 验证迁移结果 --- ============================================= - -SELECT - r.role_id, - r.role_name, - r.menu_ids, - JSON_LENGTH(COALESCE(r.menu_ids, CAST('[]' AS JSON))) as menu_count, - (SELECT COUNT(*) FROM yz_role_menus WHERE role_id = r.role_id) as old_count -FROM yz_roles r -WHERE r.delete_time IS NULL -ORDER BY r.role_id; - --- ============================================= --- 步骤4: 备份旧表(可选,建议先备份) --- ============================================= - --- 检查 yz_role_menus 表是否存在 -SET @table_exists := (SELECT COUNT(*) FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name = 'yz_role_menus'); - --- 检查备份表是否已存在 -SET @backup_exists := (SELECT COUNT(*) FROM information_schema.tables - WHERE table_schema = DATABASE() - AND table_name = 'yz_role_menus_backup'); - --- 如果原表存在且备份表不存在,则创建备份 -SET @sqlstmt := IF(@table_exists > 0 AND @backup_exists = 0, - 'CREATE TABLE yz_role_menus_backup AS SELECT * FROM yz_role_menus', - IF(@backup_exists > 0, - 'SELECT ''备份表 yz_role_menus_backup 已存在,跳过备份'' AS message', - 'SELECT ''表 yz_role_menus 不存在,无需备份'' AS message')); - -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- ============================================= --- 注意:迁移完成后,需要确认数据正确,然后可以删除 yz_role_menus 表 --- 删除命令:DROP TABLE IF EXISTS yz_role_menus; --- ============================================= - diff --git a/server/database/migrate_role_permissions_to_json.sql b/server/database/migrate_role_permissions_to_json.sql deleted file mode 100644 index 45c43d3..0000000 --- a/server/database/migrate_role_permissions_to_json.sql +++ /dev/null @@ -1,109 +0,0 @@ --- ============================================= --- 角色权限迁移脚本 --- 将 yz_role_menus 表中的权限数据迁移到 yz_roles 表的 menu_ids 字段(JSON数组) --- 执行时间: 2025 --- ============================================= - -SET NAMES utf8mb4; - --- ============================================= --- 步骤1: 在 yz_roles 表中添加 menu_ids 字段(JSON类型存储菜单ID数组) --- ============================================= - --- 检查字段是否已存在 -SET @exist := (SELECT COUNT(*) FROM information_schema.columns - WHERE table_schema = DATABASE() - AND table_name = 'yz_roles' - AND column_name = 'menu_ids'); - -SET @sqlstmt := IF(@exist = 0, - 'ALTER TABLE yz_roles ADD COLUMN menu_ids JSON NULL COMMENT "菜单权限ID数组,JSON格式存储" AFTER description', - 'SELECT "字段 menu_ids 已存在" AS message'); - -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- ============================================= --- 步骤2: 从 yz_role_menus 表迁移数据到 yz_roles.menu_ids --- ============================================= - --- 使用临时存储过程迁移数据 -DELIMITER $$ - -DROP PROCEDURE IF EXISTS migrate_role_permissions$$ - -CREATE PROCEDURE migrate_role_permissions() -BEGIN - DECLARE done INT DEFAULT FALSE; - DECLARE v_role_id INT; - DECLARE v_menu_ids JSON; - DECLARE cur CURSOR FOR - SELECT role_id, JSON_ARRAYAGG(menu_id ORDER BY menu_id) as menu_ids - FROM yz_role_menus - GROUP BY role_id; - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - OPEN cur; - - read_loop: LOOP - FETCH cur INTO v_role_id, v_menu_ids; - IF done THEN - LEAVE read_loop; - END IF; - - -- 更新角色表的 menu_ids 字段 - UPDATE yz_roles - SET menu_ids = v_menu_ids - WHERE role_id = v_role_id; - - END LOOP; - - CLOSE cur; - - -- 对于没有权限的角色,设置为空数组 - UPDATE yz_roles - SET menu_ids = JSON_ARRAY() - WHERE menu_ids IS NULL; - - SELECT '数据迁移完成' AS message; -END$$ - -DELIMITER ; - --- 执行迁移 -CALL migrate_role_permissions(); - --- 删除临时存储过程 -DROP PROCEDURE IF EXISTS migrate_role_permissions; - --- ============================================= --- 步骤3: 验证迁移结果 --- ============================================= - --- 查看迁移后的数据 -SELECT - r.role_id, - r.role_name, - r.menu_ids, - JSON_LENGTH(r.menu_ids) as menu_count, - (SELECT COUNT(*) FROM yz_role_menus WHERE role_id = r.role_id) as old_count -FROM yz_roles r -WHERE r.delete_time IS NULL -ORDER BY r.role_id; - --- ============================================= --- 步骤4: 备份旧表(可选,建议先备份) --- ============================================= - --- 创建备份表 -CREATE TABLE IF NOT EXISTS yz_role_menus_backup AS -SELECT * FROM yz_role_menus; - -SELECT '备份表 yz_role_menus_backup 创建完成' AS message; - --- ============================================= --- 注意:迁移完成后,需要确认数据正确,然后可以删除 yz_role_menus 表 --- 删除命令:DROP TABLE IF EXISTS yz_role_menus; --- ============================================= - diff --git a/server/database/performance_indexes.sql b/server/database/performance_indexes.sql deleted file mode 100644 index 8bcfdd9..0000000 --- a/server/database/performance_indexes.sql +++ /dev/null @@ -1,192 +0,0 @@ --- 性能优化索引脚本 --- 创建时间: 2025 --- 描述: 为常用查询字段添加索引,提升查询性能 - -SET NAMES utf8mb4; - --- ============================================= --- 1. 部门表 (yz_tenant_departments) 索引优化 --- ============================================= - --- 检查并添加 tenant_id 索引(如果不存在) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_tenant_departments' - AND index_name = 'idx_tenant_id'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_tenant_id ON yz_tenant_departments(tenant_id)', - 'SELECT "索引 idx_tenant_id 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加 delete_time 索引(如果不存在) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_tenant_departments' - AND index_name = 'idx_delete_time'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_delete_time ON yz_tenant_departments(delete_time)', - 'SELECT "索引 idx_delete_time 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加复合索引 (tenant_id, delete_time) 用于常用查询 -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_tenant_departments' - AND index_name = 'idx_tenant_delete'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_tenant_delete ON yz_tenant_departments(tenant_id, delete_time)', - 'SELECT "索引 idx_tenant_delete 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加 parent_id 索引(用于树形结构查询) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_tenant_departments' - AND index_name = 'idx_parent_id'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_parent_id ON yz_tenant_departments(parent_id)', - 'SELECT "索引 idx_parent_id 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- ============================================= --- 2. 职位表 (yz_tenant_positions) 索引优化 --- ============================================= - --- 检查并添加 tenant_id 索引(如果不存在) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_tenant_positions' - AND index_name = 'idx_tenant_id'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_tenant_id ON yz_tenant_positions(tenant_id)', - 'SELECT "索引 idx_tenant_id 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加 delete_time 索引(如果不存在) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_tenant_positions' - AND index_name = 'idx_delete_time'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_delete_time ON yz_tenant_positions(delete_time)', - 'SELECT "索引 idx_delete_time 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加 department_id 索引(用于按部门查询职位) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_tenant_positions' - AND index_name = 'idx_department_id'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_department_id ON yz_tenant_positions(department_id)', - 'SELECT "索引 idx_department_id 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加复合索引 (department_id, delete_time, status) 用于常用查询 -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_tenant_positions' - AND index_name = 'idx_dept_delete_status'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_dept_delete_status ON yz_tenant_positions(department_id, delete_time, status)', - 'SELECT "索引 idx_dept_delete_status 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- ============================================= --- 3. 角色表 (yz_roles) 索引优化 --- ============================================= - --- 检查并添加 tenant_id 索引(如果不存在) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_roles' - AND index_name = 'idx_tenant_id'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_tenant_id ON yz_roles(tenant_id)', - 'SELECT "索引 idx_tenant_id 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加 delete_time 索引(如果不存在) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_roles' - AND index_name = 'idx_delete_time'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_delete_time ON yz_roles(delete_time)', - 'SELECT "索引 idx_delete_time 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- ============================================= --- 4. 员工表 (yz_employees) 索引优化 --- ============================================= - --- 检查并添加 tenant_id 索引(如果不存在) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_employees' - AND index_name = 'idx_tenant_id'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_tenant_id ON yz_employees(tenant_id)', - 'SELECT "索引 idx_tenant_id 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加 delete_time 索引(如果不存在) -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_employees' - AND index_name = 'idx_delete_time'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_delete_time ON yz_employees(delete_time)', - 'SELECT "索引 idx_delete_time 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加 department_id 索引 -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_employees' - AND index_name = 'idx_department_id'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_department_id ON yz_employees(department_id)', - 'SELECT "索引 idx_department_id 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 检查并添加 position_id 索引 -SET @exist := (SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'yz_employees' - AND index_name = 'idx_position_id'); -SET @sqlstmt := IF(@exist = 0, - 'CREATE INDEX idx_position_id ON yz_employees(position_id)', - 'SELECT "索引 idx_position_id 已存在" AS message'); -PREPARE stmt FROM @sqlstmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - -SELECT '性能优化索引创建完成!' AS message; - diff --git a/server/database/performance_indexes_simple.sql b/server/database/performance_indexes_simple.sql deleted file mode 100644 index 0b424b9..0000000 --- a/server/database/performance_indexes_simple.sql +++ /dev/null @@ -1,67 +0,0 @@ --- 性能优化索引脚本(简化版) --- 创建时间: 2025 --- 描述: 为常用查询字段添加索引,提升查询性能 --- 注意: 如果索引已存在会报错,可以忽略或手动删除重复的索引 - -SET NAMES utf8mb4; - --- ============================================= --- 1. 部门表 (yz_tenant_departments) 索引优化 --- ============================================= - --- 添加 tenant_id 索引 -CREATE INDEX IF NOT EXISTS idx_tenant_id ON yz_tenant_departments(tenant_id); - --- 添加 delete_time 索引 -CREATE INDEX IF NOT EXISTS idx_delete_time ON yz_tenant_departments(delete_time); - --- 添加复合索引 (tenant_id, delete_time) 用于常用查询 -CREATE INDEX IF NOT EXISTS idx_tenant_delete ON yz_tenant_departments(tenant_id, delete_time); - --- 添加 parent_id 索引(用于树形结构查询) -CREATE INDEX IF NOT EXISTS idx_parent_id ON yz_tenant_departments(parent_id); - --- ============================================= --- 2. 职位表 (yz_tenant_positions) 索引优化 --- ============================================= - --- 添加 tenant_id 索引 -CREATE INDEX IF NOT EXISTS idx_tenant_id ON yz_tenant_positions(tenant_id); - --- 添加 delete_time 索引 -CREATE INDEX IF NOT EXISTS idx_delete_time ON yz_tenant_positions(delete_time); - --- 添加 department_id 索引(用于按部门查询职位) -CREATE INDEX IF NOT EXISTS idx_department_id ON yz_tenant_positions(department_id); - --- 添加复合索引 (department_id, delete_time, status) 用于常用查询 -CREATE INDEX IF NOT EXISTS idx_dept_delete_status ON yz_tenant_positions(department_id, delete_time, status); - --- ============================================= --- 3. 角色表 (yz_roles) 索引优化 --- ============================================= - --- 添加 tenant_id 索引 -CREATE INDEX IF NOT EXISTS idx_tenant_id ON yz_roles(tenant_id); - --- 添加 delete_time 索引 -CREATE INDEX IF NOT EXISTS idx_delete_time ON yz_roles(delete_time); - --- ============================================= --- 4. 员工表 (yz_employees) 索引优化 --- ============================================= - --- 添加 tenant_id 索引 -CREATE INDEX IF NOT EXISTS idx_tenant_id ON yz_employees(tenant_id); - --- 添加 delete_time 索引 -CREATE INDEX IF NOT EXISTS idx_delete_time ON yz_employees(delete_time); - --- 添加 department_id 索引 -CREATE INDEX IF NOT EXISTS idx_department_id ON yz_employees(department_id); - --- 添加 position_id 索引 -CREATE INDEX IF NOT EXISTS idx_position_id ON yz_employees(position_id); - -SELECT '性能优化索引创建完成!' AS message; - diff --git a/server/database/rollback_role_permissions.sql b/server/database/rollback_role_permissions.sql deleted file mode 100644 index 9babf4f..0000000 --- a/server/database/rollback_role_permissions.sql +++ /dev/null @@ -1,41 +0,0 @@ --- ============================================= --- 角色权限回滚脚本 --- 如果迁移出现问题,可以从备份表恢复数据 --- ============================================= - -SET NAMES utf8mb4; - --- ============================================= --- 步骤1: 从备份表恢复数据到 yz_role_menus --- ============================================= - --- 如果 yz_role_menus 表被删除,先创建它 -CREATE TABLE IF NOT EXISTS yz_role_menus ( - id INT AUTO_INCREMENT PRIMARY KEY, - role_id INT NOT NULL, - menu_id INT NOT NULL, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - create_by VARCHAR(50) NULL, - UNIQUE KEY uk_role_menu (role_id, menu_id), - INDEX idx_role_id (role_id), - INDEX idx_menu_id (menu_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色菜单关联表(备份恢复)'; - --- 从备份表恢复数据 -INSERT INTO yz_role_menus (id, role_id, menu_id, create_time, create_by) -SELECT id, role_id, menu_id, create_time, create_by -FROM yz_role_menus_backup -ON DUPLICATE KEY UPDATE - menu_id = VALUES(menu_id), - create_time = VALUES(create_time), - create_by = VALUES(create_by); - -SELECT '数据恢复完成' AS message; - --- ============================================= --- 步骤2: 如果需要移除 menu_ids 字段(可选) --- ============================================= - --- 注意:如果确定要移除新字段,可以执行以下命令 --- ALTER TABLE yz_roles DROP COLUMN menu_ids; - diff --git a/server/database/yz_departments.sql b/server/database/yz_departments.sql deleted file mode 100644 index be51086..0000000 --- a/server/database/yz_departments.sql +++ /dev/null @@ -1,32 +0,0 @@ --- 创建部门表 --- 创建时间: 2025 --- 描述: OA系统部门管理表 - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- 检查并创建租户部门表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_tenant_departments ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID', - tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID', - name VARCHAR(100) NOT NULL COMMENT '部门名称', - code VARCHAR(50) DEFAULT NULL COMMENT '部门编码', - parent_id INT DEFAULT 0 COMMENT '父部门ID,0表示顶级部门', - description TEXT DEFAULT NULL COMMENT '部门描述', - manager_id INT DEFAULT NULL COMMENT '部门经理ID', - sort_order INT DEFAULT 0 COMMENT '排序序号', - status TINYINT DEFAULT 1 COMMENT '状态:1-启用,0-禁用', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)', - - -- 索引 - INDEX idx_tenant_id (tenant_id), - INDEX idx_code (code), - INDEX idx_parent_id (parent_id), - INDEX idx_status (status), - INDEX idx_sort_order (sort_order) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户部门表'; - -SET FOREIGN_KEY_CHECKS = 1; - diff --git a/server/database/yz_employees.sql b/server/database/yz_employees.sql deleted file mode 100644 index 4af14f1..0000000 --- a/server/database/yz_employees.sql +++ /dev/null @@ -1,34 +0,0 @@ --- 创建员工表 --- 创建时间: 2025 --- 描述: OA系统员工管理表 - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- 检查并创建租户员工表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_tenant_employees ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID', - tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID', - employee_no VARCHAR(50) NOT NULL COMMENT '工号', - name VARCHAR(50) NOT NULL COMMENT '姓名', - phone VARCHAR(20) DEFAULT NULL COMMENT '手机号', - email VARCHAR(100) DEFAULT NULL COMMENT '邮箱', - department_id INT DEFAULT NULL COMMENT '部门ID', - position_id INT DEFAULT NULL COMMENT '职位ID', - status TINYINT DEFAULT 1 COMMENT '状态:1-在职,0-离职', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)', - - -- 索引 - INDEX idx_tenant_id (tenant_id), - INDEX idx_employee_no (employee_no), - INDEX idx_name (name), - INDEX idx_department_id (department_id), - INDEX idx_position_id (position_id), - INDEX idx_status (status), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户员工表'; - -SET FOREIGN_KEY_CHECKS = 1; - diff --git a/server/database/yz_files.sql b/server/database/yz_files.sql deleted file mode 100644 index 01c7808..0000000 --- a/server/database/yz_files.sql +++ /dev/null @@ -1,33 +0,0 @@ -CREATE TABLE yz_files ( - id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '文件ID', - tenant_id VARCHAR(64) NOT NULL COMMENT '租户ID', - user_id INT NOT NULL DEFAULT 0 COMMENT '用户ID', - - -- 文件基础信息 - file_name VARCHAR(255) NOT NULL COMMENT '文件名称', - original_name VARCHAR(255) NOT NULL COMMENT '原始文件名', - file_path VARCHAR(500) NOT NULL COMMENT '文件存储路径', - file_url VARCHAR(500) COMMENT '文件访问URL', - file_size BIGINT NOT NULL DEFAULT 0 COMMENT '文件大小(字节)', - file_type VARCHAR(50) NOT NULL COMMENT '文件类型', - file_ext VARCHAR(20) NOT NULL COMMENT '文件扩展名', - - -- 分类信息 - category VARCHAR(100) NOT NULL COMMENT '文件分类', - sub_category VARCHAR(100) COMMENT '子分类', - - -- 状态信息 - status TINYINT DEFAULT 1 COMMENT '状态(1:正常, 0:删除)', - is_public TINYINT DEFAULT 0 COMMENT '是否公开(1:是, 0:否)', - - -- 上传信息 - upload_by VARCHAR(100) NOT NULL COMMENT '上传人', - upload_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', - - -- 索引 - INDEX idx_tenant (tenant_id), - INDEX idx_user (user_id), - INDEX idx_category (category), - INDEX idx_upload_time (upload_time), - INDEX idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文件表'; \ No newline at end of file diff --git a/server/database/yz_knowledge.sql b/server/database/yz_knowledge.sql deleted file mode 100644 index 25b0ff9..0000000 --- a/server/database/yz_knowledge.sql +++ /dev/null @@ -1,94 +0,0 @@ --- 创建知识库分类表 -CREATE TABLE `yz_knowledge_category` ( - `category_id` INT NOT NULL AUTO_INCREMENT COMMENT '分类ID', - `category_name` VARCHAR(100) NOT NULL COMMENT '分类名称', - `category_desc` VARCHAR(500) DEFAULT NULL COMMENT '分类描述', - `parent_id` INT DEFAULT 0 COMMENT '父分类ID,0表示顶级分类', - `sort_order` INT DEFAULT 0 COMMENT '排序序号', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`category_id`), - KEY `idx_parent_id` (`parent_id`), - KEY `idx_sort_order` (`sort_order`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库分类表'; - --- 创建知识库标签表 -CREATE TABLE `yz_knowledge_tags` ( - `tag_id` INT NOT NULL AUTO_INCREMENT COMMENT '标签ID', - `tag_name` VARCHAR(50) NOT NULL COMMENT '标签名称', - `tag_color` VARCHAR(20) DEFAULT NULL COMMENT '标签颜色', - `usage_count` INT DEFAULT 0 COMMENT '使用次数', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`tag_id`), - UNIQUE KEY `uk_tag_name` (`tag_name`), - KEY `idx_usage_count` (`usage_count`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库标签表'; - --- 创建知识库内容表 -CREATE TABLE `yz_knowledge` ( - `knowledge_id` INT NOT NULL AUTO_INCREMENT COMMENT '知识ID', - `title` VARCHAR(200) NOT NULL COMMENT '标题', - `category_id` INT DEFAULT NULL COMMENT '分类ID', - `tags` TEXT COMMENT '标签JSON数组,存储标签名称', - `author` VARCHAR(50) NOT NULL COMMENT '作者', - `content` LONGTEXT COMMENT '正文内容(富文本)', - `summary` VARCHAR(500) DEFAULT NULL COMMENT '摘要', - `cover_url` VARCHAR(500) DEFAULT NULL COMMENT '封面图片URL', - `status` TINYINT DEFAULT 0 COMMENT '状态:0-草稿,1-已发布,2-已归档', - `view_count` INT DEFAULT 0 COMMENT '查看次数', - `like_count` INT DEFAULT 0 COMMENT '点赞数', - `is_recommend` TINYINT DEFAULT 0 COMMENT '是否推荐:0-否,1-是', - `is_top` TINYINT DEFAULT 0 COMMENT '是否置顶:0-否,1-是', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` VARCHAR(50) DEFAULT NULL COMMENT '创建人', - `update_by` VARCHAR(50) DEFAULT NULL COMMENT '更新人', - PRIMARY KEY (`knowledge_id`), - KEY `idx_category_id` (`category_id`), - KEY `idx_author` (`author`), - KEY `idx_status` (`status`), - KEY `idx_create_time` (`create_time`), - KEY `idx_view_count` (`view_count`), - KEY `idx_is_recommend` (`is_recommend`), - KEY `idx_is_top` (`is_top`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库内容表'; - --- 创建知识库收藏表 -CREATE TABLE `yz_knowledge_favorites` ( - `favorite_id` INT NOT NULL AUTO_INCREMENT COMMENT '收藏ID', - `knowledge_id` INT NOT NULL COMMENT '知识ID', - `user_id` INT NOT NULL COMMENT '用户ID', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`favorite_id`), - UNIQUE KEY `uk_knowledge_user` (`knowledge_id`, `user_id`), - KEY `idx_user_id` (`user_id`), - CONSTRAINT `yz_fk_fav_knowledge` FOREIGN KEY (`knowledge_id`) REFERENCES `yz_knowledge` (`knowledge_id`) ON DELETE CASCADE, - CONSTRAINT `yz_fk_fav_user` FOREIGN KEY (`user_id`) REFERENCES `yz_users` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库收藏表'; - --- 插入默认分类 -INSERT INTO `yz_knowledge_category` (`category_name`, `category_desc`, `parent_id`, `sort_order`) VALUES -('技术文档', '技术相关的文档资料', 0, 1), -('产品手册', '产品使用手册和说明', 0, 2), -('用户指南', '用户操作指南和教程', 0, 3), -('常见问题', '常见问题解答', 0, 4), -('API文档', 'API接口文档', 0, 5), -('Vue', 'Vue框架相关', 1, 101), -('React', 'React框架相关', 1, 102), -('Go', 'Go语言相关', 1, 103); - --- 插入默认标签 -INSERT INTO `yz_knowledge_tags` (`tag_name`, `tag_color`, `usage_count`) VALUES -('Vue', '#4CAF50', 0), -('React', '#42A5F5', 0), -('TypeScript', '#3178C6', 0), -('Element Plus', '#409EFF', 0), -('Vue Router', '#4FC08D', 0), -('Pinia', '#FFD54F', 0), -('Go', '#00ADD8', 0), -('Python', '#3776AB', 0); - ---插入默认知识 -INSERT INTO `yz_knowledge` (`title`, `category_id`, `tags`, `author`, `content`, `summary`, `cover_url`, `status`, `view_count`, `like_count`, `is_recommend`, `is_top`, `create_time`, `update_time`, `create_by`, `update_by`) VALUES -('Vue', 1, 'Vue', 'admin', 'Vue is a progressive framework for building user interfaces.', 'Vue is a progressive framework for building user interfaces.', 'https://vuejs.org/images/logo.png', 1, 0, 0, 0, 0, '2021-01-01 00:00:00', '2021-01-01 00:00:00', 'admin', 'admin'); \ No newline at end of file diff --git a/server/database/yz_menus.sql b/server/database/yz_menus.sql deleted file mode 100644 index 293ce98..0000000 --- a/server/database/yz_menus.sql +++ /dev/null @@ -1,42 +0,0 @@ --- 创建菜单表(增强版,包含完整父子级别参数) -CREATE TABLE `yz_menus` ( - `id` INT NOT NULL AUTO_INCREMENT COMMENT '菜单ID', - `name` VARCHAR(100) NOT NULL COMMENT '菜单名称', - `path` VARCHAR(255) NOT NULL COMMENT '菜单路径', - `parent_id` INT DEFAULT 0 COMMENT '父菜单ID,0表示顶级菜单', - `level` TINYINT DEFAULT 1 COMMENT '菜单层级:1-一级菜单,2-二级菜单,3-三级菜单', - `full_path` VARCHAR(500) DEFAULT NULL COMMENT '完整路径(包含所有父级路径)', - `is_leaf` TINYINT DEFAULT 0 COMMENT '是否叶子节点:0-非叶子节点,1-叶子节点', - `has_children` TINYINT DEFAULT 0 COMMENT '是否有子菜单:0-无子菜单,1-有子菜单', - `children_count` INT DEFAULT 0 COMMENT '子菜单数量', - `icon` VARCHAR(100) DEFAULT NULL COMMENT '菜单图标', - `order` INT DEFAULT 0 COMMENT '排序序号', - `status` TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用', - `component_path` VARCHAR(500) DEFAULT NULL COMMENT '组件路径', - `is_external` TINYINT DEFAULT 0 COMMENT '是否外部链接:0-内部路由,1-外部链接', - `external_url` VARCHAR(1000) DEFAULT NULL COMMENT '外部链接地址', - `menu_type` TINYINT DEFAULT 1 COMMENT '菜单类型:1-普通菜单,2-分组菜单,3-按钮菜单', - `permission` VARCHAR(200) DEFAULT NULL COMMENT '权限标识', - `description` VARCHAR(500) DEFAULT NULL COMMENT '菜单描述', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` VARCHAR(100) DEFAULT NULL COMMENT '创建人', - `update_by` VARCHAR(100) DEFAULT NULL COMMENT '更新人', - PRIMARY KEY (`id`), - KEY `idx_parent_id` (`parent_id`), - KEY `idx_level` (`level`), - KEY `idx_status` (`status`), - KEY `idx_order` (`order`), - KEY `idx_menu_type` (`menu_type`), - KEY `idx_full_path` (`full_path`(255)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菜单表(增强版)'; - --- 插入默认菜单数据(包含完整父子级别参数) -INSERT INTO `yz_menus` (`name`, `path`, `parent_id`, `level`, `full_path`, `is_leaf`, `has_children`, `children_count`, `icon`, `order`, `status`, `component_path`, `menu_type`, `description`) VALUES -('仪表盘', '/dashboard', 0, 1, '/dashboard', 1, 0, 0, 'el-icon-monitor', 1, 1, '@/views/dashboard/index.vue', 1, '系统仪表盘,展示关键指标'), -('文件管理', '/files', 0, 1, '/files', 1, 0, 0, 'el-icon-folder', 3, 1, '@/views/files/index.vue', 1, '文件管理系统'), -('系统管理', '/system', 0, 1, '/system', 0, 1, 3, 'el-icon-setting', 4, 1, null, 2, '系统管理功能模块'), -('用户管理', '/system/users', 4, 2, '/system/users', 1, 0, 0, 'el-icon-user', 1, 1, '@/views/system/users/index.vue', 1, '用户信息管理'), -('菜单管理', '/system/menus', 4, 2, '/system/menus', 1, 0, 0, 'el-icon-menu', 2, 1, '@/views/system/menumanager.vue', 1, '菜单权限管理'), -('程序管理', '/system/programs', 4, 2, '/system/programs', 1, 0, 0, 'el-icon-cpu', 3, 1, '@/views/system/programs/index.vue', 1, '程序功能管理'), -('系统设置', '/settings', 0, 1, '/settings', 1, 0, 0, 'el-icon-setting', 99, 1, '@/views/settings/index.vue', 1, '系统参数设置'); \ No newline at end of file diff --git a/server/database/yz_positions.sql b/server/database/yz_positions.sql deleted file mode 100644 index 6135fab..0000000 --- a/server/database/yz_positions.sql +++ /dev/null @@ -1,32 +0,0 @@ --- 创建职位表 --- 创建时间: 2025 --- 描述: OA系统职位管理表 - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- 检查并创建租户职位表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_tenant_positions ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '职位ID', - tenant_id INT NOT NULL DEFAULT 0 COMMENT '租户ID', - name VARCHAR(100) NOT NULL COMMENT '职位名称', - code VARCHAR(50) DEFAULT NULL COMMENT '职位编码', - department_id INT DEFAULT NULL COMMENT '所属部门ID', - level INT DEFAULT 0 COMMENT '职位级别', - description TEXT DEFAULT NULL COMMENT '职位描述', - sort_order INT DEFAULT 0 COMMENT '排序序号', - status TINYINT DEFAULT 1 COMMENT '状态:1-启用,0-禁用', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - delete_time DATETIME DEFAULT NULL COMMENT '删除时间(软删除)', - - -- 索引 - INDEX idx_tenant_id (tenant_id), - INDEX idx_code (code), - INDEX idx_department_id (department_id), - INDEX idx_status (status), - INDEX idx_sort_order (sort_order) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户职位表'; - -SET FOREIGN_KEY_CHECKS = 1; - diff --git a/server/database/yz_program_tables.sql b/server/database/yz_program_tables.sql deleted file mode 100644 index 053dfd6..0000000 --- a/server/database/yz_program_tables.sql +++ /dev/null @@ -1,31 +0,0 @@ --- 创建程序分类表 -CREATE TABLE `yz_program_category` ( - `category_id` INT NOT NULL AUTO_INCREMENT COMMENT '分类ID', - `category_name` VARCHAR(100) NOT NULL COMMENT '分类名称', - `category_desc` VARCHAR(500) DEFAULT NULL COMMENT '分类描述', - `parent_id` INT DEFAULT 0 COMMENT '父分类ID,0表示顶级分类', - `sort_order` INT DEFAULT 0 COMMENT '排序序号,用于展示顺序', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`category_id`), - KEY `idx_parent_id` (`parent_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='程序分类表'; - --- 创建程序信息表 -CREATE TABLE `yz_program_info` ( - `program_id` INT NOT NULL AUTO_INCREMENT COMMENT '程序ID', - `category_id` INT NOT NULL COMMENT '所属分类ID', - `program_name` VARCHAR(200) NOT NULL COMMENT '程序名称', - `program_desc` TEXT COMMENT '程序描述', - `jump_url` VARCHAR(1000) NOT NULL COMMENT '跳转地址', - `icon_url` VARCHAR(1000) DEFAULT NULL COMMENT '程序图标地址', - `version` VARCHAR(50) DEFAULT NULL COMMENT '程序版本', - `status` TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用', - `sort_order` INT DEFAULT 0 COMMENT '排序序号,用于展示顺序', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`program_id`), - KEY `idx_category_id` (`category_id`), - KEY `idx_status` (`status`), - CONSTRAINT `yz_fk_program_category` FOREIGN KEY (`category_id`) REFERENCES `yz_program_category` (`category_id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='程序信息表'; diff --git a/server/database/yz_roles.sql b/server/database/yz_roles.sql deleted file mode 100644 index 67a6686..0000000 --- a/server/database/yz_roles.sql +++ /dev/null @@ -1,34 +0,0 @@ --- 创建角色表 -CREATE TABLE IF NOT EXISTS yz_roles ( - role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '角色ID', - tenant_id INT NOT NULL COMMENT '租户ID', - role_code VARCHAR(50) NOT NULL COMMENT '角色代码', - role_name VARCHAR(50) NOT NULL COMMENT '角色名称', - description VARCHAR(500) DEFAULT NULL COMMENT '角色描述', - status TINYINT DEFAULT 1 COMMENT '角色状态:0-禁用,1-启用', - sort_order INT DEFAULT 0 COMMENT '排序序号', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - delete_time DATETIME DEFAULT NULL COMMENT '删除时间', - create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人', - - -- 索引 - UNIQUE KEY uk_role_code (role_code), - INDEX idx_status (status), - INDEX idx_sort_order (sort_order), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色表'; - --- 插入默认角色数据 -INSERT INTO yz_roles (tenant_id, role_code, role_name, description, status, sort_order, create_by, delete_time) VALUES -(0, 'system_admin', '系统管理员', '系统超级管理员,拥有所有权限', 1, 1, 'system'), -(0, 'admin', '管理员', '普通管理员,拥有大部分管理权限', 1, 2, 'system'), -(0, 'user', '普通用户', '普通用户,拥有基础使用权限', 1, 3, 'system') -ON DUPLICATE KEY UPDATE - tenant_id = VALUES(tenant_id), - role_name = VALUES(role_name), - description = VALUES(description), - update_time = CURRENT_TIMESTAMP, - delete_time = NULL; - diff --git a/server/database/yz_tenants.sql b/server/database/yz_tenants.sql deleted file mode 100644 index c76e668..0000000 --- a/server/database/yz_tenants.sql +++ /dev/null @@ -1,111 +0,0 @@ --- 创建租户表 --- 创建时间: 2025 --- 描述: 云泽系统租户管理表 - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- 检查并创建租户表(如果不存在) -CREATE TABLE IF NOT EXISTS yz_tenants ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '租户ID', - - -- 基本信息 - name VARCHAR(100) NOT NULL COMMENT '租户名称', - code VARCHAR(50) NOT NULL COMMENT '租户编码(唯一)', - owner VARCHAR(50) NOT NULL COMMENT '负责人', - phone VARCHAR(20) DEFAULT NULL COMMENT '联系电话', - email VARCHAR(100) DEFAULT NULL COMMENT '邮箱地址', - - -- 状态信息 - status VARCHAR(20) DEFAULT 'enabled' COMMENT '状态:enabled-启用,disabled-禁用', - audit_status VARCHAR(20) DEFAULT 'pending' COMMENT '审核状态:pending-待审核,approved-已通过,rejected-已拒绝', - - -- 审核信息 - audit_comment TEXT DEFAULT NULL COMMENT '审核意见', - audit_by VARCHAR(50) DEFAULT NULL COMMENT '审核人', - audit_time DATETIME DEFAULT NULL COMMENT '审核时间', - - -- 其他信息 - remark TEXT DEFAULT NULL COMMENT '备注', - - -- 时间戳 - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人', - - -- 索引 - UNIQUE KEY uk_code (code), - INDEX idx_name (name), - INDEX idx_owner (owner), - INDEX idx_status (status), - INDEX idx_audit_status (audit_status), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='租户表'; - -SET FOREIGN_KEY_CHECKS = 1; - --- ============================================= --- 插入测试数据 --- ============================================= - --- 清空现有测试数据(可选,注释掉以保留现有数据) --- DELETE FROM yz_tenants WHERE id > 0; - --- 插入测试租户数据 -INSERT INTO yz_tenants ( - name, - code, - owner, - phone, - email, - status, - audit_status, - audit_comment, - audit_by, - audit_time, - remark, - create_by -) VALUES --- 默认租户(已通过审核) -('默认租户', 'default', 'admin', '13800138000', 'admin@yunzer.com', 'enabled', 'approved', '系统默认租户,自动通过审核', 'system', NOW(), '系统默认租户,用于初始化数据', 'system'), - --- 示例租户A(已通过审核) -('示例租户A', 'demo-a', '张三', '13900139000', 'zhangsan@demo.com', 'enabled', 'approved', '资料完整,审核通过', 'admin', DATE_SUB(NOW(), INTERVAL 30 DAY), '演示租户A,用于展示功能', 'admin'), - --- 示例租户B(待审核) -('示例租户B', 'demo-b', '李四', '13700137000', 'lisi@demo.com', 'enabled', 'pending', NULL, NULL, NULL, '待审核租户,资料已提交', 'admin'), - --- 新申请租户C(待审核) -('新申请租户C', 'new-tenant-c', '王五', '13600136000', 'wangwu@new.com', 'enabled', 'pending', NULL, NULL, NULL, '新申请的租户,等待审核', 'admin'), - --- 已拒绝租户D -('已拒绝租户D', 'rejected-tenant', '赵六', '13500135000', 'zhaoliu@reject.com', 'disabled', 'rejected', '申请资料不完整,缺少必要信息', 'admin', DATE_SUB(NOW(), INTERVAL 10 DAY), '申请被拒绝的租户示例', 'admin'), - --- 企业租户E(已通过) -('企业租户E', 'enterprise-e', '陈七', '13400134000', 'chenqi@enterprise.com', 'enabled', 'approved', '企业级用户,认证通过', 'admin', DATE_SUB(NOW(), INTERVAL 15 DAY), '大型企业客户租户', 'admin'), - --- 测试租户F(已通过) -('测试租户F', 'test-f', '刘八', '13300133000', 'liuba@test.com', 'enabled', 'approved', '测试环境使用,已通过', 'admin', DATE_SUB(NOW(), INTERVAL 5 DAY), '测试环境租户', 'admin'), - --- 禁用租户G -('禁用租户G', 'disabled-g', '周九', '13200132000', 'zhoujiu@disabled.com', 'disabled', 'approved', '已通过审核但被禁用', 'admin', DATE_SUB(NOW(), INTERVAL 20 DAY), '已禁用的租户示例', 'admin'), - --- 小公司租户H(待审核) -('小公司租户H', 'small-h', '吴十', '13100131000', 'wushi@small.com', 'enabled', 'pending', NULL, NULL, NULL, '小型公司申请,等待审核', 'admin'), - --- 个人开发者租户I(已通过) -('个人开发者I', 'developer-i', '郑十一', '13000130000', 'zhengshiyi@dev.com', 'enabled', 'approved', '个人开发者账户,审核通过', 'admin', DATE_SUB(NOW(), INTERVAL 8 DAY), '个人开发者租户', 'admin'); - --- 查询验证 -SELECT 'Tenants table created and test data inserted successfully!' as message; -SELECT COUNT(*) as total_tenants FROM yz_tenants; -SELECT - id, - name, - code, - status, - audit_status, - create_time -FROM yz_tenants -ORDER BY id; diff --git a/server/database/yz_users.sql b/server/database/yz_users.sql deleted file mode 100644 index ba4bcb7..0000000 --- a/server/database/yz_users.sql +++ /dev/null @@ -1,32 +0,0 @@ --- 创建用户表 -CREATE TABLE yz_users ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID', - username VARCHAR(50) NOT NULL COMMENT '用户名', - password VARCHAR(255) NOT NULL COMMENT '加密后的密码', - salt VARCHAR(100) NOT NULL COMMENT '密码盐值', - email VARCHAR(100) DEFAULT NULL COMMENT '邮箱地址', - avatar VARCHAR(500) DEFAULT NULL COMMENT '头像URL', - nickname VARCHAR(50) DEFAULT NULL COMMENT '昵称', - role VARCHAR(20) DEFAULT 'user' COMMENT '用户角色:admin-管理员,user-普通用户', - status TINYINT DEFAULT 1 COMMENT '用户状态:0-禁用,1-启用', - last_login_time DATETIME DEFAULT NULL COMMENT '最后登录时间', - last_login_ip VARCHAR(45) DEFAULT NULL COMMENT '最后登录IP', - login_count INT DEFAULT 0 COMMENT '登录次数', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人', - update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人', - - -- 索引 - UNIQUE KEY uk_username (username), - INDEX idx_email (email), - INDEX idx_role (role), - INDEX idx_status (status), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表'; - --- 插入默认管理员用户 --- 密码为 yunzer123,已使用scrypt算法加密 -INSERT INTO yz_users (username, password, salt, email, nickname, role, status, create_by) VALUES -('admin', 'encrypted_password_here', 'salt_here', 'admin@yunzer.com', '超级管理员', 'admin', 1, 'system'), -('test', 'encrypted_password_here', 'salt_here', 'test@yunzer.com', '测试用户', 'user', 1, 'system'); diff --git a/server/database/执行索引优化说明.md b/server/database/执行索引优化说明.md deleted file mode 100644 index 8809056..0000000 --- a/server/database/执行索引优化说明.md +++ /dev/null @@ -1,99 +0,0 @@ -# 执行数据库索引优化说明 - -## 方法一:使用数据库管理工具(推荐) - -### 使用 Navicat、DBeaver、phpMyAdmin 等工具 - -1. 连接到数据库: - - 主机:`43.133.71.191` - - 端口:`3308` - - 用户名:`gotest` - - 密码:`2nZhRdMPCNZrdzsd` - - 数据库:`gotest` - -2. 打开并执行以下文件之一: - - `server/database/performance_indexes_simple.sql` (推荐,简单版本) - - `server/database/performance_indexes.sql` (完整版本,包含存在性检查) - -## 方法二:使用 MySQL 命令行(如果已安装) - -### Windows PowerShell - -```powershell -# 方法 1: 使用 Get-Content 管道 -Get-Content server\database\performance_indexes_simple.sql | mysql -u gotest -p2nZhRdMPCNZrdzsd -h 43.133.71.191 -P 3308 gotest - -# 方法 2: 使用 source 命令(需要先登录 MySQL) -mysql -u gotest -p2nZhRdMPCNZrdzsd -h 43.133.71.191 -P 3308 gotest -# 然后在 MySQL 提示符下执行: -source server/database/performance_indexes_simple.sql -``` - -### Windows CMD - -```cmd -mysql -u gotest -p2nZhRdMPCNZrdzsd -h 43.133.71.191 -P 3308 gotest < server\database\performance_indexes_simple.sql -``` - -### Linux/Mac - -```bash -mysql -u gotest -p2nZhRdMPCNZrdzsd -h 43.133.71.191 -P 3308 gotest < server/database/performance_indexes_simple.sql -``` - -## 方法三:在 Go 代码中执行(临时方案) - -如果无法直接执行 SQL,可以在后端代码初始化时执行: - -```go -// 在 server/models/user.go 的 Init 函数中添加 -func Init(version string) { - // ... 现有代码 ... - - // 执行索引优化(可选,建议直接执行 SQL 文件) - // 这里可以添加执行索引创建的代码 -} -``` - -## 验证索引是否创建成功 - -执行以下 SQL 查询验证索引: - -```sql --- 查看部门表索引 -SHOW INDEX FROM yz_tenant_departments; - --- 查看职位表索引 -SHOW INDEX FROM yz_tenant_positions; - --- 查看角色表索引 -SHOW INDEX FROM yz_roles; - --- 查看员工表索引 -SHOW INDEX FROM yz_employees; -``` - -## 注意事项 - -1. **MySQL 版本要求**: - - `CREATE INDEX IF NOT EXISTS` 需要 MySQL 8.0.12+ - - 如果使用较低版本,请使用 `performance_indexes.sql`(包含存在性检查) - -2. **执行时间**: - - 索引创建可能需要几秒到几分钟,取决于数据量 - - 创建索引期间,表会锁定(通常很快) - -3. **索引已存在**: - - 如果索引已存在,`CREATE INDEX IF NOT EXISTS` 会忽略 - - 如果使用 `performance_indexes.sql`,会显示"索引已存在"的消息 - -4. **性能影响**: - - 索引创建后,查询性能会显著提升 - - 插入/更新操作可能稍慢(通常可忽略) - -## 预期效果 - -- 查询速度提升:**50-90%**(取决于数据量) -- 减少全表扫描 -- 优化 WHERE 和 JOIN 查询 - diff --git a/server/database/迁移说明.md b/server/database/迁移说明.md deleted file mode 100644 index 7db4247..0000000 --- a/server/database/迁移说明.md +++ /dev/null @@ -1,71 +0,0 @@ -# 角色权限迁移说明 - -## 概述 -将角色权限从关系表 `yz_role_menus` 迁移到 `yz_roles` 表的 JSON 数组字段 `menu_ids`。 - -## 迁移步骤 - -### 1. 执行迁移脚本 -```bash -mysql -u gotest -p gotest < server/database/migrate_role_permissions_to_json.sql -``` - -或者在 MySQL 客户端中执行: -```sql -source server/database/migrate_role_permissions_to_json.sql -``` - -### 2. 验证迁移结果 -迁移脚本会自动: -- 在 `yz_roles` 表中添加 `menu_ids` JSON 字段 -- 从 `yz_role_menus` 表迁移数据到 `menu_ids` 字段 -- 创建备份表 `yz_role_menus_backup` -- 验证迁移结果 - -### 3. 确认数据正确性 -执行以下查询验证数据: -```sql -SELECT - r.role_id, - r.role_name, - r.menu_ids, - JSON_LENGTH(r.menu_ids) as menu_count, - (SELECT COUNT(*) FROM yz_role_menus_backup WHERE role_id = r.role_id) as old_count -FROM yz_roles r -WHERE r.delete_time IS NULL -ORDER BY r.role_id; -``` - -### 4. 删除旧表(可选) -确认数据迁移正确后,可以删除旧的关系表: -```sql -DROP TABLE IF EXISTS yz_role_menus; -``` - -## 回滚方案 -如果迁移出现问题,可以使用回滚脚本: -```bash -mysql -u gotest -p gotest < server/database/rollback_role_permissions.sql -``` - -## 代码变更 -- `server/models/role.go`: 添加 `MenuIds` 字段和 JSON 序列化/反序列化方法 -- `server/models/permission.go`: 更新 `GetRoleMenus` 和 `AssignRolePermissions` 函数 - -## 注意事项 -1. **备份数据**:迁移前请确保已备份数据库 -2. **测试环境**:建议先在测试环境执行迁移 -3. **数据一致性**:迁移后请验证权限分配功能是否正常 -4. **性能影响**:JSON 字段查询性能可能略低于关系表,但简化了数据结构 - -## JSON 字段格式 -`menu_ids` 字段存储格式为 JSON 数组,例如: -```json -[1, 2, 3, 4, 5] -``` - -空数组表示该角色没有任何权限: -```json -[] -``` - diff --git a/server/docs/FILE_UPLOAD.md b/server/docs/FILE_UPLOAD.md deleted file mode 100644 index a9d5293..0000000 --- a/server/docs/FILE_UPLOAD.md +++ /dev/null @@ -1,203 +0,0 @@ -# 文件上传功能文档 - -## 概述 - -文件上传功能已完整实现,支持将文件保存到本地文件系统并记录到数据库中。 - -## 功能特性 - -1. **自动目录管理**:按年月日自动创建目录结构(如 `front/uploads/2024/01/15/`) -2. **唯一文件名**:使用时间戳生成唯一文件名,避免重名冲突 -3. **文件类型识别**:自动识别文件类型(图片、文档、视频、音频、压缩包等) -4. **数据库记录**:所有文件信息保存到 `yz_files` 表 -5. **用户关联**:自动关联当前登录用户 -6. **异常处理**:文件保存失败时自动清理已上传的文件 - -## 文件结构 - -``` -front/ -└── uploads/ - ├── 2024/ - │ ├── 01/ - │ │ ├── 15/ - │ │ │ ├── 20240115143045_example.jpg - │ │ │ └── 20240115143046_document.pdf -``` - -## API 接口 - -### 上传文件 - -**POST** `/api/files` - -**请求头**: -``` -Authorization: Bearer -Content-Type: multipart/form-data -``` - -**请求参数**: -- `file` (File, required): 上传的文件 -- `category` (String, optional): 文件分类,默认为"未分类" -- `tenant_id` (String, optional): 租户ID,默认为"default" - -**响应示例**: -```json -{ - "success": true, - "message": "文件上传成功", - "data": { - "id": 1, - "tenant_id": "default", - "user_id": 123, - "file_name": "example", - "original_name": "example.jpg", - "file_path": "uploads/2024/01/15/20240115143045_example.jpg", - "file_url": "/uploads/2024/01/15/20240115143045_example.jpg", - "file_size": 102400, - "file_type": "image", - "file_ext": ".jpg", - "category": "未分类", - "upload_by": "username", - "upload_time": "2024-01-15T14:30:45Z" - } -} -``` - -## 后端实现 - -### 路由配置 - -在 `server/routers/router.go` 中配置: - -```go -// 文件管理路由 -beego.Router("/api/files", &controllers.FileController{}, "get:GetAllFiles") -beego.Router("/api/files", &controllers.FileController{}, "post:Post") -beego.Router("/api/files/my", &controllers.FileController{}, "get:GetMyFiles") -beego.Router("/api/files/:id", &controllers.FileController{}, "get:GetFileById") -beego.Router("/api/files/:id", &controllers.FileController{}, "put:UpdateFile") -beego.Router("/api/files/:id", &controllers.FileController{}, "delete:DeleteFile") -``` - -### 控制器实现 - -`Post()` 方法位于 `server/controllers/file.go`: - -1. 验证用户登录状态 -2. 接收上传的文件 -3. 生成日期路径和唯一文件名 -4. 保存文件到本地 -5. 记录文件信息到数据库 -6. 返回文件信息 - -## 前端使用 - -### 基本用法 - -```typescript -import { fileAPI } from '@/api/file' - -// 创建 FormData -const formData = new FormData() -formData.append('file', fileObject) // fileObject 是 File 对象 -formData.append('category', '文档') -formData.append('tenant_id', 'tenant-001') - -// 上传文件 -try { - const response = await fileAPI.uploadFile(formData, { - category: '文档', - tenantId: 'tenant-001' - }) - console.log('上传成功:', response.data) -} catch (error) { - console.error('上传失败:', error) -} -``` - -### Element Plus 上传组件 - -```vue - - -
- 将文件拖到此处,或点击上传 -
-
- - -``` - -## 文件访问 - -上传后的文件可以通过以下URL访问: - -``` -http://localhost:8080/uploads/2024/01/15/20240115143045_example.jpg -``` - -注意:需要配置 Beego 的静态文件服务来提供上传文件的访问。 - -在 `server/conf/app.conf` 中添加: - -```conf -# 文件上传目录 -StaticDir = /uploads:../front/uploads -``` - -## 安全注意事项 - -1. **文件大小限制**:建议在前端和后端都添加文件大小限制 -2. **文件类型验证**:根据业务需求限制允许上传的文件类型 -3. **文件名安全**:避免用户控制文件名造成安全问题 -4. **权限控制**:确保只有授权用户可以上传文件 -5. **存储位置**:考虑使用对象存储服务(如 OSS、S3)替代本地存储 - -## 数据库字段说明 - -`yz_files` 表的主要字段: - -- `file_path`: 相对路径,用于存储和访问文件 -- `file_url`: 访问URL -- `original_name`: 用户上传时的原始文件名 -- `file_name`: 去除扩展名的文件名 -- `file_type`: 文件类型(image, document, video, audio, archive, other) -- `category`: 用户自定义分类 - diff --git a/server/docs/OA合并接口说明.md b/server/docs/OA合并接口说明.md deleted file mode 100644 index 1d635ff..0000000 --- a/server/docs/OA合并接口说明.md +++ /dev/null @@ -1,145 +0,0 @@ -# OA 基础数据合并接口说明 - -## 概述 - -为了减少网络请求次数,提升系统性能,新增了一个合并接口,用于一次性获取部门、职位、角色三类基础数据。 - -## 接口信息 - -### 接口路径 -``` -GET /api/oa/base-data/:tenantId -``` - -### 请求参数 -- `tenantId` (路径参数): 租户ID - -### 响应格式 -```json -{ - "code": 0, - "message": "获取基础数据成功", - "data": { - "departments": [ - { - "id": 1, - "tenant_id": 1, - "name": "技术部", - "code": "TECH", - "parent_id": 0, - "description": "技术部门", - "manager_id": 0, - "sort_order": 0, - "status": 1, - "create_time": "2024-01-01T00:00:00Z", - "update_time": "2024-01-01T00:00:00Z" - } - ], - "positions": [ - { - "id": 1, - "tenant_id": 1, - "name": "高级工程师", - "code": "SENIOR", - "department_id": 1, - "level": 3, - "description": "高级工程师职位", - "sort_order": 0, - "status": 1, - "create_time": "2024-01-01T00:00:00Z", - "update_time": "2024-01-01T00:00:00Z" - } - ], - "roles": [ - { - "roleId": 1, - "tenantId": 1, - "roleCode": "ADMIN", - "roleName": "管理员", - "description": "管理员角色", - "status": 1, - "sortOrder": 0, - "createTime": "2024-01-01T00:00:00Z", - "updateTime": "2024-01-01T00:00:00Z" - } - ] - } -} -``` - -## 实现细节 - -### 后端实现 - -#### Services 层 (`server/services/oa.go`) -- 使用 goroutine 并行查询三个数据源 -- 使用 channel 安全地传递查询结果 -- 任何查询失败都会返回错误 - -#### Controllers 层 (`server/controllers/oa.go`) -- 接收租户ID参数 -- 调用 services 层获取数据 -- 格式化返回数据 - -#### 路由配置 (`server/routers/router.go`) -- 路由:`/api/oa/base-data/:tenantId` -- 方法:GET - -### 前端实现 - -#### API 文件 (`pc/src/api/oa.js`) -- 封装了 `getOABaseData` 方法 - -#### Store 更新 (`pc/src/stores/oa.js`) -- `fetchAllBaseData` 方法优先使用合并接口 -- 如果合并接口失败,自动回退到分别请求三个接口 -- 保持缓存机制不变 - -## 性能优势 - -### 优化前 -- 前端需要发起 3 个独立的 HTTP 请求 -- 每次请求都有网络延迟 -- 总耗时 = 3 × 网络延迟 + 3 × 查询时间 - -### 优化后 -- 前端只需发起 1 个 HTTP 请求 -- 后端使用 goroutine 并行查询,总耗时 = 1 × 网络延迟 + max(查询时间) -- **性能提升**:减少 2 个网络请求,总耗时减少约 60-70% - -## 使用示例 - -### 前端使用 - -```javascript -import { useOAStore } from '@/stores/oa'; - -const oaStore = useOAStore(); - -// 页面初始化时,会自动使用合并接口 -onMounted(async () => { - await oaStore.fetchAllBaseData(); -}); -``` - -### 后端扩展 - -如果需要添加更多数据到合并接口,只需: - -1. 在 `OABaseData` 结构体中添加新字段 -2. 在 `GetOABaseData` 方法中添加新的查询逻辑 -3. 在 controller 中格式化返回新数据 - -## 兼容性 - -- 合并接口与原有的三个独立接口并存 -- 前端 Store 有自动回退机制,确保兼容性 -- 如果合并接口失败,会自动使用原有接口 - -## 注意事项 - -1. **租户隔离**:确保返回的数据属于指定租户 -2. **错误处理**:任何查询失败都会返回错误 -3. **数据一致性**:确保返回的数据是最新的 -4. **性能考虑**:后端使用并行查询,但仍需注意数据库性能 - diff --git a/server/docs/UPLOAD_PATH.md b/server/docs/UPLOAD_PATH.md deleted file mode 100644 index c8e4a23..0000000 --- a/server/docs/UPLOAD_PATH.md +++ /dev/null @@ -1,164 +0,0 @@ -# 文件上传路径说明 - -## 当前配置 - -### 文件保存路径 -文件保存在项目根目录的 `front/uploads/` 目录下,按照日期自动分类: - -``` -项目根目录/ -├── server/ -│ └── controllers/ -│ └── file.go (处理上传) -├── front/ -│ └── uploads/ ← 文件保存位置 -│ ├── 2024/ -│ │ └── 01/ -│ │ └── 15/ -│ │ └── 20240115143045_example.jpg -``` - -### 代码中的路径 - -在 `server/controllers/file.go` 的 `Post()` 方法中: - -```go -// 构造保存路径:../front/uploads/年/月/日/ -uploadDir := path.Join("..", "front", "uploads", datePath) -``` - -**说明**: -- `..` 表示从 server 目录向上一级到项目根目录 -- `front/uploads/` 是上传文件的根目录 -- `datePath` 是按日期自动生成的子目录(如 `2024/01/15`) - -### 静态文件访问配置 - -在 `server/conf/app.conf` 中: - -```conf -StaticDir = /uploads:../front/uploads -``` - -**说明**: -- `/uploads` 是 URL 访问路径 -- `../front/uploads` 是实际文件存储路径(相对于 server 目录) - -## 目录结构 - -### 保存到数据库的路径 -- `file_path`: `uploads/2024/01/15/20240115143045_example.jpg`(相对路径) -- `file_url`: `/uploads/2024/01/15/20240115143045_example.jpg`(URL 路径) - -### 实际文件系统路径 -``` -front/uploads/2024/01/15/20240115143045_example.jpg -``` - -### 访问 URL -``` -http://localhost:8080/uploads/2024/01/15/20240115143045_example.jpg -``` - -## 为什么使用相对路径 `..` - -由于项目结构是: -``` -yunzer_go/ -├── server/ ← 服务端代码 -└── front/ ← 前端代码和上传文件 - └── uploads/ -``` - -从 `server` 目录运行应用时,要访问 `front/uploads`,需要使用 `../front/uploads`。 - -## 如果路径不对怎么办? - -### 方案1:修改代码中的路径 -如果您的项目启动目录不同,可以修改 `file.go` 中的路径: - -```go -// 如果从项目根目录运行 -uploadDir := path.Join("front", "uploads", datePath) - -// 或者使用绝对路径 -uploadDir := path.Join("/path/to/project", "front", "uploads", datePath) -``` - -### 方案2:从配置文件读取 -在 `app.conf` 中添加配置: - -```conf -# 上传文件目录 -uploadDir = ../front/uploads -``` - -然后在代码中读取: - -```go -import "github.com/beego/beego/v2/server/web" - -uploadDir := path.Join( - web.AppConfig.String("uploadDir"), - datePath, -) -``` - -## 验证路径是否正确 - -### 1. 检查文件保存位置 -上传一个文件后,查看文件是否在正确的位置: - -```bash -ls front/uploads/ -``` - -应该看到按日期分类的文件夹和文件。 - -### 2. 检查数据库记录 -查看 `yz_files` 表中的 `file_path` 字段: - -```sql -SELECT file_path, file_url FROM yz_files ORDER BY upload_time DESC LIMIT 1; -``` - -应该看到类似: -``` -file_path: uploads/2024/01/15/20240115143045_example.jpg -file_url: /uploads/2024/01/15/20240115143045_example.jpg -``` - -### 3. 检查 URL 访问 -直接在浏览器访问: - -``` -http://localhost:8080/uploads/2024/01/15/文件名 -``` - -如果能看到文件,说明路径配置正确。 - -## 常见问题 - -### Q: 文件保存在了 server/front/uploads? -A: 修改代码中的路径为 `../front/uploads`(已经修改) - -### Q: 文件保存在了 front/front/uploads? -A: 检查当前工作目录,确保在 server 目录运行应用 - -### Q: 访问文件返回 404? -A: 检查 `app.conf` 中的 StaticDir 配置是否正确 - -### Q: 权限问题? -A: 确保应用有创建目录和写入文件的权限: -```bash -chmod 755 front/uploads -``` - -## 建议的改进 - -如果需要更可靠的路径处理,可以考虑: - -1. **使用绝对路径**:从配置文件或环境变量读取项目根目录 -2. **路径验证**:在应用启动时检查上传目录是否存在,不存在则创建 -3. **日志记录**:记录文件保存的完整路径,便于调试 - diff --git a/server/docs/file_api.md b/server/docs/file_api.md deleted file mode 100644 index eb41f81..0000000 --- a/server/docs/file_api.md +++ /dev/null @@ -1,328 +0,0 @@ -# 文件管理 API 文档 - -## 概述 - -文件管理模块提供对 `yz_files` 表的完整 CRUD 操作,支持文件信息的管理、搜索和统计功能。 - -## 数据库表结构 - -```sql -CREATE TABLE yz_files ( - id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '文件ID', - tenant_id VARCHAR(64) NOT NULL COMMENT '租户ID', - - -- 文件基础信息 - file_name VARCHAR(255) NOT NULL COMMENT '文件名称', - original_name VARCHAR(255) NOT NULL COMMENT '原始文件名', - file_path VARCHAR(500) NOT NULL COMMENT '文件存储路径', - file_url VARCHAR(500) COMMENT '文件访问URL', - file_size BIGINT NOT NULL DEFAULT 0 COMMENT '文件大小(字节)', - file_type VARCHAR(50) NOT NULL COMMENT '文件类型', - file_ext VARCHAR(20) NOT NULL COMMENT '文件扩展名', - - -- 分类信息 - category VARCHAR(100) NOT NULL COMMENT '文件分类', - sub_category VARCHAR(100) COMMENT '子分类', - - -- 状态信息 - status TINYINT DEFAULT 1 COMMENT '状态(1:正常, 0:删除)', - is_public TINYINT DEFAULT 0 COMMENT '是否公开(1:是, 0:否)', - - -- 上传信息 - upload_by VARCHAR(100) NOT NULL COMMENT '上传人', - upload_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', - - -- 索引 - INDEX idx_tenant (tenant_id), - INDEX idx_category (category), - INDEX idx_upload_time (upload_time), - INDEX idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文件表'; -``` - -## API 接口列表 - -### 1. 获取所有文件信息 - -**GET** `/api/files` - -**认证方式**: JWT认证 - -**参数**: 无 - -**响应**: -```json -{ - "success": true, - "message": "获取成功", - "data": [ - { - "id": 1, - "tenant_id": "default", - "user_id": 1, - "file_name": "example.pdf", - "original_name": "example.pdf", - "file_path": "/uploads/2024/01/01/example.pdf", - "file_url": "http://example.com/uploads/2024/01/01/example.pdf", - "file_size": 1024, - "file_type": "application/pdf", - "file_ext": "pdf", - "category": "文档", - "sub_category": "PDF", - "status": 1, - "is_public": 0, - "upload_by": "admin", - "upload_time": "2024-01-01T10:00:00Z" - } - ] -} -``` - -### 2. 获取当前用户的文件列表 - -**GET** `/api/files/my` - -**认证方式**: JWT认证 - -**参数**: 无 - -**说明**: 通过JWT token自动获取当前登录用户的文件列表 - -**响应**: -```json -{ - "success": true, - "message": "获取成功", - "data": [ - { - "id": 1, - "tenant_id": "default", - "user_id": 1, - "file_name": "example.pdf", - "original_name": "example.pdf", - "file_path": "/uploads/2024/01/01/example.pdf", - "file_url": "http://example.com/uploads/2024/01/01/example.pdf", - "file_size": 1024, - "file_type": "application/pdf", - "file_ext": "pdf", - "category": "文档", - "sub_category": "PDF", - "status": 1, - "is_public": 0, - "upload_by": "admin", - "upload_time": "2024-01-01T10:00:00Z" - } - ] -} -``` - -### 2. 根据ID获取文件信息 - -**GET** `/api/files/:id` - -**参数**: -- `id` (路径参数): 文件ID - -**响应**: 同获取所有文件接口 - -### 3. 创建文件信息 - -**POST** `/api/files` - -**请求体**: -```json -{ - "tenant_id": "tenant-001", - "file_name": "new-file.txt", - "original_name": "原始文件.txt", - "file_path": "/uploads/tenant-001/new-file.txt", - "file_url": "http://localhost:8080/uploads/tenant-001/new-file.txt", - "file_size": 1024, - "file_type": "text/plain", - "file_ext": "txt", - "category": "文本", - "sub_category": "TXT", - "status": 1, - "is_public": 0, - "upload_by": "admin" -} -``` - -**响应**: -```json -{ - "success": true, - "message": "创建文件成功", - "data": { - "id": 2, - ... // 创建的文件信息 - } -} -``` - -### 4. 更新文件信息 - -**PUT** `/api/files/:id` - -**参数**: -- `id` (路径参数): 文件ID - -**请求体**: 同创建文件接口 - -**响应**: 同创建文件接口 - -### 5. 删除文件(软删除) - -**DELETE** `/api/files/:id` - -**参数**: -- `id` (路径参数): 文件ID - -**响应**: -```json -{ - "success": true, - "message": "删除文件成功" -} -``` - -### 6. 硬删除文件 - -**DELETE** `/api/files/:id/hard` - -**参数**: -- `id` (路径参数): 文件ID - -**响应**: 同软删除接口 - -### 7. 根据租户ID获取文件 - -**GET** `/api/files/tenant?tenant_id=tenant-001` - -**参数**: -- `tenant_id` (查询参数): 租户ID - -**响应**: 同获取所有文件接口 - -### 8. 根据分类获取文件 - -**GET** `/api/files/category?category=文档` - -**参数**: -- `category` (查询参数): 文件分类 - -**响应**: 同获取所有文件接口 - -### 9. 根据状态获取文件 - -**GET** `/api/files/status?status=1` - -**参数**: -- `status` (查询参数): 文件状态 (1:正常, 0:删除) - -**响应**: 同获取所有文件接口 - -### 10. 获取文件统计信息 - -**GET** `/api/files/statistics?tenant_id=tenant-001` - -**参数**: -- `tenant_id` (查询参数): 租户ID - -**响应**: -```json -{ - "success": true, - "message": "获取统计信息成功", - "data": { - "total_count": 10, - "total_size": 10485760, - "category_stats": [ - { - "category": "文档", - "count": 5, - "size": 5242880 - }, - { - "category": "图片", - "count": 3, - "size": 3145728 - } - ] - } -} -``` - -### 11. 搜索文件 - -**GET** `/api/files/search?keyword=文档&tenant_id=tenant-001` - -**参数**: -- `keyword` (查询参数): 搜索关键词 -- `tenant_id` (查询参数): 租户ID - -**响应**: 同获取所有文件接口 - -### 12. 公开接口(无需认证) - -**GET** `/api/files/public` - -**参数**: 无 - -**响应**: 同获取所有文件接口 - -## 错误响应 - -所有接口在发生错误时返回统一的错误格式: - -```json -{ - "success": false, - "message": "错误描述", - "error": "详细错误信息" -} -``` - -## 状态码说明 - -- `200`: 请求成功 -- `400`: 参数错误 -- `404`: 资源不存在 -- `500`: 服务器内部错误 - -## 使用示例 - -### 前端调用示例(JavaScript) - -```javascript -// 获取所有文件 -const response = await fetch('/api/files'); -const result = await response.json(); - -// 创建文件 -const newFile = { - tenant_id: "tenant-001", - file_name: "example.txt", - original_name: "示例文件.txt", - file_path: "/uploads/example.txt", - file_type: "text/plain", - file_ext: "txt", - category: "文档", - upload_by: "user123" -}; - -const createResponse = await fetch('/api/files', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(newFile) -}); -``` - -## 注意事项 - -1. 所有需要认证的接口都需要在请求头中包含有效的 JWT Token -2. 文件上传功能需要配合文件存储服务实现 -3. 软删除只是标记文件状态为删除,实际数据仍然保留 -4. 硬删除会永久删除文件记录,请谨慎使用 \ No newline at end of file diff --git a/server/docs/性能优化说明.md b/server/docs/性能优化说明.md deleted file mode 100644 index 4c6bac0..0000000 --- a/server/docs/性能优化说明.md +++ /dev/null @@ -1,135 +0,0 @@ -# 后端接口性能优化说明 - -## 问题描述 - -后端接口请求响应慢,主要原因是: - -1. **数据库连接池未配置** - 每次请求都创建新的数据库连接 -2. **缺少数据库索引** - 常用查询字段(tenant_id, delete_time)没有索引 -3. **内存分配未优化** - Controller 层数据格式化时未预分配容量 -4. **网络延迟** - 使用远程数据库,网络延迟较高 - -## 优化措施 - -### 1. 数据库连接池配置 ✅ - -**位置**: `server/models/user.go` - -**优化内容**: -- 设置最大空闲连接数:`MaxIdleConns = 10` -- 设置最大打开连接数:`MaxOpenConns = 100` -- 设置连接最大生存时间:`ConnMaxLifetime = 1小时` -- 添加连接超时参数:`timeout=10s&readTimeout=30s&writeTimeout=30s` - -**效果**: -- 减少连接创建和销毁的开销 -- 复用数据库连接,提升响应速度 -- 避免连接泄漏 - -### 2. 数据库索引优化 ✅ - -**位置**: `server/database/performance_indexes.sql` - -**优化内容**: -- 为 `yz_tenant_departments` 表添加索引: - - `idx_tenant_id` - 租户ID索引 - - `idx_delete_time` - 删除时间索引 - - `idx_tenant_delete` - 复合索引 (tenant_id, delete_time) - - `idx_parent_id` - 父级ID索引(树形结构查询) - -- 为 `yz_tenant_positions` 表添加索引: - - `idx_tenant_id` - 租户ID索引 - - `idx_delete_time` - 删除时间索引 - - `idx_department_id` - 部门ID索引 - - `idx_dept_delete_status` - 复合索引 (department_id, delete_time, status) - -- 为 `yz_roles` 表添加索引: - - `idx_tenant_id` - 租户ID索引 - - `idx_delete_time` - 删除时间索引 - -- 为 `yz_employees` 表添加索引: - - `idx_tenant_id` - 租户ID索引 - - `idx_delete_time` - 删除时间索引 - - `idx_department_id` - 部门ID索引 - - `idx_position_id` - 职位ID索引 - -**执行方法**: -```bash -mysql -u gotest -p -h 43.133.71.191 -P 3308 gotest < server/database/performance_indexes.sql -``` - -**效果**: -- 查询速度提升 10-100 倍(取决于数据量) -- 减少全表扫描 -- 优化 WHERE 和 JOIN 查询 - -### 3. 内存分配优化 ✅ - -**位置**: `server/controllers/oa.go` - -**优化内容**: -- 预分配切片容量,避免多次扩容 -- 使用 `make([]map[string]interface{}, 0, count)` 替代 `make([]map[string]interface{}, 0)` - -**效果**: -- 减少内存分配次数 -- 降低 GC 压力 -- 提升响应速度约 5-10% - -### 4. 查询优化建议 - -**已实现**: -- 使用 `services.GetOABaseData()` 并行查询部门、职位、角色数据 -- 使用 goroutine 并发执行多个查询 - -**建议**: -- 对于大数据量查询,考虑实现分页 -- 对于频繁查询的数据,考虑添加 Redis 缓存层 -- 监控慢查询日志,持续优化 - -## 性能提升预期 - -- **连接池配置**: 提升 20-30% -- **数据库索引**: 提升 50-90%(取决于数据量) -- **内存优化**: 提升 5-10% -- **总体提升**: 预期提升 50-80% - -## 注意事项 - -1. **索引维护成本**: - - 索引会占用额外存储空间 - - 插入/更新操作会稍慢(通常可忽略) - - 建议定期检查索引使用情况 - -2. **连接池配置**: - - `MaxOpenConns` 应根据实际并发量调整 - - 过大的连接池可能导致数据库连接耗尽 - - 建议监控连接池使用情况 - -3. **远程数据库**: - - 网络延迟是主要瓶颈之一 - - 考虑使用 CDN 或数据库代理 - - 对于高并发场景,建议使用本地数据库或缓存 - -## 下一步优化建议 - -1. **添加 Redis 缓存层**: - - 缓存常用的基础数据(部门、职位、角色) - - 设置合理的过期时间(如 5 分钟) - - 减少数据库查询压力 - -2. **实现查询日志**: - - 记录慢查询(> 100ms) - - 分析查询模式 - - 持续优化 - -3. **数据库查询优化**: - - 使用 `SELECT` 只查询需要的字段 - - 避免 `SELECT *` - - 使用 `LIMIT` 限制结果集 - -4. **监控和告警**: - - 监控接口响应时间 - - 监控数据库连接池使用情况 - - 设置性能告警阈值 - diff --git a/server/models/permission.go b/server/models/permission.go index 37dafed..3070be8 100644 --- a/server/models/permission.go +++ b/server/models/permission.go @@ -50,33 +50,46 @@ func init() { func GetRoleMenus(roleId int) ([]int, error) { o := orm.NewOrm() var menuIdsJson sql.NullString + var menuIdsStr string // 方法1: 尝试使用 JSON_UNQUOTE 读取 JSON 字段 - err := o.Raw("SELECT IFNULL(JSON_UNQUOTE(JSON_EXTRACT(menu_ids, '$')), '[]') FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsJson) - - // 如果方法1失败或结果为空,尝试方法2: 直接 CAST - if err != nil { - fmt.Printf("方法1失败,尝试方法2: %v\n", err) - err = nil // 重置错误,尝试方法2 - } - - if err != nil || !menuIdsJson.Valid || menuIdsJson.String == "" || menuIdsJson.String == "[]" { - fmt.Printf("方法1结果无效,尝试方法2\n") - err2 := o.Raw("SELECT CAST(IFNULL(menu_ids, '[]') AS CHAR) FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsJson) + err := o.Raw("SELECT IFNULL(JSON_UNQUOTE(JSON_EXTRACT(menu_ids, '$')), '[]') FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsStr) + if err == nil && menuIdsStr != "" && menuIdsStr != "[]" { + menuIdsJson = sql.NullString{String: menuIdsStr, Valid: true} + fmt.Printf("GetRoleMenus: 方法1成功,角色 %d 的 menu_ids: %s\n", roleId, menuIdsStr[:min(100, len(menuIdsStr))]) + } else { + // 方法1失败,尝试方法2: 直接 CAST + if err != nil { + fmt.Printf("GetRoleMenus: 方法1失败 (%v),尝试方法2\n", err) + } else { + fmt.Printf("GetRoleMenus: 方法1结果为空,尝试方法2\n") + } + + // 重置变量 + menuIdsStr = "" + err2 := o.Raw("SELECT CAST(IFNULL(menu_ids, '[]') AS CHAR) FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsStr) if err2 != nil { // 如果角色不存在,返回空数组而不是错误(兼容性处理) if err2 == orm.ErrNoRows { - fmt.Printf("角色 %d 不存在\n", roleId) + fmt.Printf("GetRoleMenus: 角色 %d 不存在\n", roleId) return []int{}, nil } - fmt.Printf("读取角色 %d 的 menu_ids 失败: %v\n", roleId, err2) - return nil, fmt.Errorf("获取角色菜单失败: %v", err2) + fmt.Printf("GetRoleMenus: 方法2也失败,角色 %d 的 menu_ids 读取失败: %v\n", roleId, err2) + return []int{}, nil // 返回空数组而不是错误,保持兼容性 + } + + if menuIdsStr != "" && menuIdsStr != "[]" && menuIdsStr != "null" { + menuIdsJson = sql.NullString{String: menuIdsStr, Valid: true} + fmt.Printf("GetRoleMenus: 方法2成功,角色 %d 的 menu_ids: %s\n", roleId, menuIdsStr[:min(100, len(menuIdsStr))]) + } else { + fmt.Printf("GetRoleMenus: 方法2结果也为空,角色 %d 的 menu_ids 为空或 null\n", roleId) + return []int{}, nil } } // 如果 menuIdsJson 无效或为空,返回空数组 if !menuIdsJson.Valid || menuIdsJson.String == "" { - fmt.Printf("角色 %d 的 menu_ids 为空或无效\n", roleId) + fmt.Printf("GetRoleMenus: 角色 %d 的 menu_ids 最终为空或无效\n", roleId) return []int{}, nil } diff --git a/server/models/role.go b/server/models/role.go index 665945b..c244515 100644 --- a/server/models/role.go +++ b/server/models/role.go @@ -14,6 +14,7 @@ import ( type Role struct { RoleId int `orm:"pk;auto;column(role_id)" json:"roleId"` TenantId int `orm:"column(tenant_id)" json:"tenantId"` + Default int8 `orm:"column(default);default(2)" json:"default"` // 角色默认分配:1-只给租户1,2-所有租户可用,3-租户专属 RoleCode string `orm:"size(50);unique" json:"roleCode"` RoleName string `orm:"size(100)" json:"roleName"` Description string `orm:"type(text);null" json:"description"` @@ -90,6 +91,7 @@ func GetRoleById(roleId int) (*Role, error) { type roleResult struct { RoleId int TenantId int + Default int8 RoleCode string RoleName string Description string @@ -105,8 +107,8 @@ func GetRoleById(roleId int) (*Role, error) { var result roleResult // 先读取其他字段(不包括 menu_ids),因为 Beego ORM 可能无法直接读取 JSON 类型 - err := o.Raw("SELECT role_id, tenant_id, role_code, role_name, description, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow( - &result.RoleId, &result.TenantId, &result.RoleCode, &result.RoleName, &result.Description, + err := o.Raw("SELECT role_id, tenant_id, `default`, role_code, role_name, description, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow( + &result.RoleId, &result.TenantId, &result.Default, &result.RoleCode, &result.RoleName, &result.Description, &result.Status, &result.SortOrder, &result.CreateTime, &result.UpdateTime, &result.DeleteTime, &result.CreateBy, &result.UpdateBy, ) @@ -150,6 +152,7 @@ func GetRoleById(roleId int) (*Role, error) { role := &Role{ RoleId: result.RoleId, TenantId: result.TenantId, + Default: result.Default, RoleCode: result.RoleCode, RoleName: result.RoleName, Description: result.Description, @@ -178,6 +181,7 @@ func GetAllRoles() ([]*Role, error) { var results []struct { RoleId int TenantId int + Default int8 RoleCode string RoleName string Description string @@ -191,7 +195,7 @@ func GetAllRoles() ([]*Role, error) { UpdateBy string } - _, err := o.Raw("SELECT role_id, tenant_id, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE delete_time IS NULL ORDER BY sort_order ASC, role_id ASC").QueryRows(&results) + _, err := o.Raw("SELECT role_id, tenant_id, `default`, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE delete_time IS NULL ORDER BY sort_order ASC, role_id ASC").QueryRows(&results) if err != nil { return nil, err } @@ -200,6 +204,7 @@ func GetAllRoles() ([]*Role, error) { role := &Role{ RoleId: r.RoleId, TenantId: r.TenantId, + Default: r.Default, RoleCode: r.RoleCode, RoleName: r.RoleName, Description: r.Description, @@ -228,6 +233,7 @@ func GetRoleByTenantId(tenantId int) ([]*Role, error) { var results []struct { RoleId int TenantId int + Default int8 RoleCode string RoleName string Description string @@ -241,7 +247,7 @@ func GetRoleByTenantId(tenantId int) ([]*Role, error) { UpdateBy string } - _, err := o.Raw("SELECT role_id, tenant_id, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE (tenant_id = ? OR tenant_id = 0) AND delete_time IS NULL ORDER BY sort_order ASC, role_id ASC", tenantId).QueryRows(&results) + _, err := o.Raw("SELECT role_id, tenant_id, `default`, role_code, role_name, description, CAST(IFNULL(menu_ids, '[]') AS CHAR) as menu_ids, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE (tenant_id = ? OR tenant_id = 0) AND delete_time IS NULL ORDER BY sort_order ASC, role_id ASC", tenantId).QueryRows(&results) if err != nil { return nil, err } @@ -250,6 +256,7 @@ func GetRoleByTenantId(tenantId int) ([]*Role, error) { role := &Role{ RoleId: r.RoleId, TenantId: r.TenantId, + Default: r.Default, RoleCode: r.RoleCode, RoleName: r.RoleName, Description: r.Description, @@ -272,9 +279,31 @@ func GetRoleByTenantId(tenantId int) ([]*Role, error) { // GetRoleByCode 根据角色代码获取角色 func GetRoleByCode(roleCode string) (*Role, error) { o := orm.NewOrm() - var role Role - err := o.QueryTable("yz_roles").Filter("role_code", roleCode).Filter("delete_time__isnull", true).One(&role) + // 使用Raw查询以正确读取所有字段,包括 default 和 menu_ids + type roleResult struct { + RoleId int + TenantId int + Default int8 + RoleCode string + RoleName string + Description string + MenuIdsJson sql.NullString + Status int8 + SortOrder int + CreateTime time.Time + UpdateTime time.Time + DeleteTime *time.Time + CreateBy string + UpdateBy string + } + + var result roleResult + err := o.Raw("SELECT role_id, tenant_id, `default`, role_code, role_name, description, status, sort_order, create_time, update_time, delete_time, create_by, update_by FROM yz_roles WHERE role_code = ? AND delete_time IS NULL", roleCode).QueryRow( + &result.RoleId, &result.TenantId, &result.Default, &result.RoleCode, &result.RoleName, &result.Description, + &result.Status, &result.SortOrder, &result.CreateTime, &result.UpdateTime, + &result.DeleteTime, &result.CreateBy, &result.UpdateBy, + ) if err != nil { return nil, err } @@ -283,11 +312,28 @@ func GetRoleByCode(roleCode string) (*Role, error) { var menuIdsStr string err2 := o.Raw("SELECT IFNULL(JSON_UNQUOTE(JSON_EXTRACT(menu_ids, '$')), '[]') FROM yz_roles WHERE role_code = ? AND delete_time IS NULL", roleCode).QueryRow(&menuIdsStr) if err2 == nil && menuIdsStr != "" && menuIdsStr != "[]" { - role.MenuIdsJson = sql.NullString{String: menuIdsStr, Valid: true} + result.MenuIdsJson = sql.NullString{String: menuIdsStr, Valid: true} + } + + role := &Role{ + RoleId: result.RoleId, + TenantId: result.TenantId, + Default: result.Default, + RoleCode: result.RoleCode, + RoleName: result.RoleName, + Description: result.Description, + MenuIdsJson: result.MenuIdsJson, + Status: result.Status, + SortOrder: result.SortOrder, + CreateTime: result.CreateTime, + UpdateTime: result.UpdateTime, + DeleteTime: result.DeleteTime, + CreateBy: result.CreateBy, + UpdateBy: result.UpdateBy, } role.AfterRead() - return &role, nil + return role, nil } // CreateRole 创建角色 @@ -296,8 +342,17 @@ func CreateRole(role *Role) error { role.BeforeInsert() // 使用Raw插入以正确处理JSON字段,并获取插入后的ID - res, err := o.Raw("INSERT INTO yz_roles (tenant_id, role_code, role_name, description, menu_ids, status, sort_order, create_time, update_time, create_by, update_by) VALUES (?, ?, ?, ?, CAST(? AS JSON), ?, ?, NOW(), NOW(), ?, ?)", - role.TenantId, role.RoleCode, role.RoleName, role.Description, role.MenuIdsJson.String, role.Status, role.SortOrder, role.CreateBy, role.UpdateBy).Exec() + // 如果没有设置 default 值,根据 tenant_id 自动设置:tenant_id=0 时 default=2,否则 default=3 + defaultValue := role.Default + if defaultValue == 0 { + if role.TenantId == 0 { + defaultValue = 2 // 所有租户可用 + } else { + defaultValue = 3 // 租户专属 + } + } + res, err := o.Raw("INSERT INTO yz_roles (tenant_id, `default`, role_code, role_name, description, menu_ids, status, sort_order, create_time, update_time, create_by, update_by) VALUES (?, ?, ?, ?, ?, CAST(? AS JSON), ?, ?, NOW(), NOW(), ?, ?)", + role.TenantId, defaultValue, role.RoleCode, role.RoleName, role.Description, role.MenuIdsJson.String, role.Status, role.SortOrder, role.CreateBy, role.UpdateBy).Exec() if err != nil { return err } @@ -324,8 +379,17 @@ func UpdateRole(role *Role) error { role.BeforeUpdate() // 使用Raw更新以正确处理JSON字段 - _, err := o.Raw("UPDATE yz_roles SET tenant_id = ?, role_code = ?, role_name = ?, description = ?, menu_ids = CAST(? AS JSON), status = ?, sort_order = ?, update_time = NOW(), update_by = ? WHERE role_id = ? AND delete_time IS NULL", - role.TenantId, role.RoleCode, role.RoleName, role.Description, role.MenuIdsJson.String, role.Status, role.SortOrder, role.UpdateBy, role.RoleId).Exec() + // 如果没有设置 default 值,根据 tenant_id 自动设置:tenant_id=0 时 default=2,否则 default=3 + defaultValue := role.Default + if defaultValue == 0 { + if role.TenantId == 0 { + defaultValue = 2 // 所有租户可用 + } else { + defaultValue = 3 // 租户专属 + } + } + _, err := o.Raw("UPDATE yz_roles SET tenant_id = ?, `default` = ?, role_code = ?, role_name = ?, description = ?, menu_ids = CAST(? AS JSON), status = ?, sort_order = ?, update_time = NOW(), update_by = ? WHERE role_id = ? AND delete_time IS NULL", + role.TenantId, defaultValue, role.RoleCode, role.RoleName, role.Description, role.MenuIdsJson.String, role.Status, role.SortOrder, role.UpdateBy, role.RoleId).Exec() return err }