批量更新
This commit is contained in:
parent
6a9b5d413a
commit
873a2e297a
@ -108,7 +108,7 @@ async function refreshCache() {
|
|||||||
// 触发菜单刷新事件,通知CommonAside组件刷新菜单
|
// 触发菜单刷新事件,通知CommonAside组件刷新菜单
|
||||||
window.dispatchEvent(new CustomEvent('menu-cache-refreshed'));
|
window.dispatchEvent(new CustomEvent('menu-cache-refreshed'));
|
||||||
|
|
||||||
ElMessage.success('菜单缓存和路由更新成功');
|
ElMessage.success('更新成功');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to refresh cache', error);
|
console.error('Failed to refresh cache', error);
|
||||||
ElMessage.error('更新缓存失败,请检查网络连接');
|
ElMessage.error('更新缓存失败,请检查网络连接');
|
||||||
|
|||||||
@ -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 进行数据管理,大幅提升了性能和代码质量。
|
|
||||||
|
|
||||||
@ -69,6 +69,7 @@
|
|||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:loading="loadingRoles"
|
:loading="loadingRoles"
|
||||||
clearable
|
clearable
|
||||||
|
:key="`role-${filteredRoleList.length}-${form.tenant_id}-${form.role}`"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="role in filteredRoleList"
|
v-for="role in filteredRoleList"
|
||||||
@ -83,7 +84,7 @@
|
|||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
<div v-if="filteredRoleList.length === 0 && !loadingRoles" style="color: #999; font-size: 12px; margin-top: 4px;">
|
<div v-if="filteredRoleList.length === 0 && !loadingRoles" style="color: #999; font-size: 12px; margin-top: 4px;">
|
||||||
暂无可用角色
|
暂无可用角色(当前租户ID: {{ form.tenant_id }})
|
||||||
</div>
|
</div>
|
||||||
<div v-if="form.role && !hasValidRole" style="color: #f56c6c; font-size: 12px; margin-top: 4px;">
|
<div v-if="form.role && !hasValidRole" style="color: #f56c6c; font-size: 12px; margin-top: 4px;">
|
||||||
警告:当前选择的角色ID ({{ form.role }}) 在角色列表中不存在
|
警告:当前选择的角色ID ({{ form.role }}) 在角色列表中不存在
|
||||||
@ -139,32 +140,79 @@ const props = defineProps<{
|
|||||||
loadingRoles: boolean;
|
loadingRoles: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// 根据当前员工的 tenant_id 过滤角色列表
|
// 根据当前员工的 tenant_id 和角色的 default 字段过滤角色列表
|
||||||
const filteredRoleList = computed(() => {
|
const filteredRoleList = computed(() => {
|
||||||
if (!props.roleList || props.roleList.length === 0) {
|
if (!props.roleList || props.roleList.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前员工的 tenant_id
|
// 获取当前员工的 tenant_id(优先使用 form.value.tenant_id,因为表单数据可能已更新)
|
||||||
const currentTenantId = props.formData?.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) {
|
if (currentTenantId === null || currentTenantId === undefined) {
|
||||||
// 如果没有 tenant_id,返回所有角色(不应该发生,但做个兼容)
|
// 如果没有 tenant_id,返回所有角色(不应该发生,但做个兼容)
|
||||||
|
console.log('filteredRoleList - 没有 tenant_id,返回所有角色');
|
||||||
return props.roleList;
|
return props.roleList;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过滤角色:显示当前租户的角色(tenant_id 匹配)和公共角色(tenant_id 为 0)
|
const currentTenantIdNum = Number(currentTenantId);
|
||||||
return props.roleList.filter((role: any) => {
|
|
||||||
|
// 过滤角色:根据 default 字段和 tenant_id 进行筛选
|
||||||
|
// default = 1: 只匹配给 tenant_id = 1 的租户
|
||||||
|
// default = 2: 分配给所有租户使用
|
||||||
|
// default = 3: 租户单独设置的,给自己用(tenant_id 匹配当前租户)
|
||||||
|
const filtered = props.roleList.filter((role: any) => {
|
||||||
// 兼容不同的字段名:tenantId 或 tenant_id
|
// 兼容不同的字段名:tenantId 或 tenant_id
|
||||||
const roleTenantId = role.tenantId !== undefined ? role.tenantId : (role.tenant_id !== undefined ? role.tenant_id : null);
|
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 roleTenantIdNum = roleTenantId !== null && roleTenantId !== undefined ? Number(roleTenantId) : null;
|
||||||
|
const roleDefaultNum = Number(roleDefault);
|
||||||
|
|
||||||
// 显示当前租户的角色或公共角色(tenant_id 为 0)
|
let match = false;
|
||||||
return roleTenantIdNum === currentTenantIdNum || roleTenantIdNum === 0;
|
|
||||||
|
// 情况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,
|
department_id: props.formData.department_id ? Number(props.formData.department_id) : null,
|
||||||
position_id: props.formData.position_id ? Number(props.formData.position_id) : null,
|
position_id: props.formData.position_id ? Number(props.formData.position_id) : null,
|
||||||
role: props.formData.role ? Number(props.formData.role) : 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,确保组件能正确渲染
|
// 再等待一个tick,确保组件能正确渲染
|
||||||
await nextTick();
|
await nextTick();
|
||||||
} else if (val && !props.isEdit) {
|
} else if (val && !props.isEdit) {
|
||||||
// 新增时重置表单
|
// 新增时重置表单
|
||||||
|
// 从 props.formData 获取 tenant_id(如果有的话)
|
||||||
|
const tenantId = props.formData?.tenant_id || null;
|
||||||
form.value = {
|
form.value = {
|
||||||
id: null,
|
id: null,
|
||||||
employeeNo: "",
|
employeeNo: "",
|
||||||
@ -284,8 +340,11 @@ watch(() => props.visible, async (val) => {
|
|||||||
bank_name: "",
|
bank_name: "",
|
||||||
bank_account: "",
|
bank_account: "",
|
||||||
status: 1,
|
status: 1,
|
||||||
tenant_id: null,
|
tenant_id: tenantId ? Number(tenantId) : null,
|
||||||
};
|
};
|
||||||
|
console.log('EmployeeEdit - 新增模式,设置表单数据:', {
|
||||||
|
tenant_id: form.value.tenant_id
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -63,7 +63,7 @@
|
|||||||
width="180"
|
width="180"
|
||||||
align="center"
|
align="center"
|
||||||
/>
|
/>
|
||||||
<el-table-column label="操作" width="280" align="center" fixed="right">
|
<el-table-column label="操作" width="340" align="center" fixed="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
@ -79,6 +79,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
size="small"
|
size="small"
|
||||||
type="danger"
|
type="danger"
|
||||||
|
:disabled="scope.row.role === 5"
|
||||||
@click="handleDelete(scope.row)"
|
@click="handleDelete(scope.row)"
|
||||||
>删除</el-button>
|
>删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -164,15 +164,15 @@ const handleCollapseAll = () => {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
|
|
||||||
.el-button {
|
// .el-button {
|
||||||
padding: 0;
|
// padding: 0;
|
||||||
font-size: 13px;
|
// font-size: 13px;
|
||||||
color: var(--el-color-primary);
|
// color: var(--el-color-primary);
|
||||||
|
|
||||||
&:hover {
|
// &:hover {
|
||||||
color: var(--el-color-primary-light-3);
|
// color: var(--el-color-primary-light-3);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -458,6 +458,12 @@ const submitForm = async (formData: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (employee: Employee) => {
|
const handleDelete = async (employee: Employee) => {
|
||||||
|
// 如果角色ID是5,不允许删除
|
||||||
|
if (employee.role === 5) {
|
||||||
|
ElMessage.warning("该员工的角色不允许删除");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ElMessageBox.confirm("确认删除该员工?", "提示", {
|
ElMessageBox.confirm("确认删除该员工?", "提示", {
|
||||||
confirmButtonText: "确定",
|
confirmButtonText: "确定",
|
||||||
cancelButtonText: "取消",
|
cancelButtonText: "取消",
|
||||||
|
|||||||
@ -273,7 +273,13 @@ const handleTaskChange = (task: any) => {
|
|||||||
// 初始化图表
|
// 初始化图表
|
||||||
onMounted(() => {
|
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",
|
type: "line",
|
||||||
data: {
|
data: {
|
||||||
labels: ["1月", "2月", "3月", "4月", "5月", "6月", "7月"],
|
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",
|
type: "bar",
|
||||||
data: {
|
data: {
|
||||||
labels: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
|
labels: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
|
||||||
|
|||||||
@ -85,6 +85,7 @@ func (c *OAController) GetOABaseData() {
|
|||||||
roleList = append(roleList, map[string]interface{}{
|
roleList = append(roleList, map[string]interface{}{
|
||||||
"roleId": role.RoleId,
|
"roleId": role.RoleId,
|
||||||
"tenantId": role.TenantId,
|
"tenantId": role.TenantId,
|
||||||
|
"default": role.Default,
|
||||||
"roleCode": role.RoleCode,
|
"roleCode": role.RoleCode,
|
||||||
"roleName": role.RoleName,
|
"roleName": role.RoleName,
|
||||||
"description": role.Description,
|
"description": role.Description,
|
||||||
|
|||||||
@ -29,6 +29,8 @@ func (c *RoleController) GetAllRoles() {
|
|||||||
for _, role := range roles {
|
for _, role := range roles {
|
||||||
roleList = append(roleList, map[string]interface{}{
|
roleList = append(roleList, map[string]interface{}{
|
||||||
"roleId": role.RoleId,
|
"roleId": role.RoleId,
|
||||||
|
"tenantId": role.TenantId,
|
||||||
|
"default": role.Default,
|
||||||
"roleCode": role.RoleCode,
|
"roleCode": role.RoleCode,
|
||||||
"roleName": role.RoleName,
|
"roleName": role.RoleName,
|
||||||
"description": role.Description,
|
"description": role.Description,
|
||||||
@ -124,10 +126,30 @@ func (c *RoleController) GetRoleByTenantId() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
return
|
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{}{
|
c.Data["json"] = map[string]interface{}{
|
||||||
"code": 0,
|
"code": 0,
|
||||||
"message": "获取角色列表成功",
|
"message": "获取角色列表成功",
|
||||||
"data": roles,
|
"data": roleList,
|
||||||
}
|
}
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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'
|
|
||||||
);
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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);
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
@ -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;
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
-- =============================================
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
-- =============================================
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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='文件表';
|
|
||||||
@ -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');
|
|
||||||
@ -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, '系统参数设置');
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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='程序信息表';
|
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
@ -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');
|
|
||||||
@ -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 查询
|
|
||||||
|
|
||||||
@ -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
|
|
||||||
[]
|
|
||||||
```
|
|
||||||
|
|
||||||
@ -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 <token>
|
|
||||||
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
|
|
||||||
<el-upload
|
|
||||||
ref="uploadRef"
|
|
||||||
drag
|
|
||||||
:action="uploadUrl"
|
|
||||||
:headers="uploadHeaders"
|
|
||||||
:data="{ category: uploadForm.category }"
|
|
||||||
:on-success="handleUploadSuccess"
|
|
||||||
:on-error="handleUploadError"
|
|
||||||
:before-upload="beforeUpload"
|
|
||||||
multiple
|
|
||||||
>
|
|
||||||
<el-icon><upload-filled /></el-icon>
|
|
||||||
<div class="el-upload__text">
|
|
||||||
将文件拖到此处,或<em>点击上传</em>
|
|
||||||
</div>
|
|
||||||
</el-upload>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const uploadUrl = computed(() => {
|
|
||||||
const baseUrl = import.meta.env.VITE_API_BASE_URL
|
|
||||||
return `${baseUrl}/api/files`
|
|
||||||
})
|
|
||||||
|
|
||||||
const uploadHeaders = computed(() => {
|
|
||||||
const token = localStorage.getItem('token')
|
|
||||||
return {
|
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleUploadSuccess = (response) => {
|
|
||||||
console.log('上传成功:', response)
|
|
||||||
}
|
|
||||||
|
|
||||||
const beforeUpload = (file) => {
|
|
||||||
const maxSize = 10 * 1024 * 1024 // 10MB
|
|
||||||
if (file.size > maxSize) {
|
|
||||||
ElMessage.error('文件大小不能超过 10MB!')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 文件访问
|
|
||||||
|
|
||||||
上传后的文件可以通过以下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`: 用户自定义分类
|
|
||||||
|
|
||||||
@ -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. **性能考虑**:后端使用并行查询,但仍需注意数据库性能
|
|
||||||
|
|
||||||
@ -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. **日志记录**:记录文件保存的完整路径,便于调试
|
|
||||||
|
|
||||||
@ -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. 硬删除会永久删除文件记录,请谨慎使用
|
|
||||||
@ -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. **监控和告警**:
|
|
||||||
- 监控接口响应时间
|
|
||||||
- 监控数据库连接池使用情况
|
|
||||||
- 设置性能告警阈值
|
|
||||||
|
|
||||||
@ -50,33 +50,46 @@ func init() {
|
|||||||
func GetRoleMenus(roleId int) ([]int, error) {
|
func GetRoleMenus(roleId int) ([]int, error) {
|
||||||
o := orm.NewOrm()
|
o := orm.NewOrm()
|
||||||
var menuIdsJson sql.NullString
|
var menuIdsJson sql.NullString
|
||||||
|
var menuIdsStr string
|
||||||
|
|
||||||
// 方法1: 尝试使用 JSON_UNQUOTE 读取 JSON 字段
|
// 方法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)
|
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 != "[]" {
|
||||||
// 如果方法1失败或结果为空,尝试方法2: 直接 CAST
|
menuIdsJson = sql.NullString{String: menuIdsStr, Valid: true}
|
||||||
if err != nil {
|
fmt.Printf("GetRoleMenus: 方法1成功,角色 %d 的 menu_ids: %s\n", roleId, menuIdsStr[:min(100, len(menuIdsStr))])
|
||||||
fmt.Printf("方法1失败,尝试方法2: %v\n", err)
|
} else {
|
||||||
err = nil // 重置错误,尝试方法2
|
// 方法1失败,尝试方法2: 直接 CAST
|
||||||
}
|
if err != nil {
|
||||||
|
fmt.Printf("GetRoleMenus: 方法1失败 (%v),尝试方法2\n", err)
|
||||||
if err != nil || !menuIdsJson.Valid || menuIdsJson.String == "" || menuIdsJson.String == "[]" {
|
} else {
|
||||||
fmt.Printf("方法1结果无效,尝试方法2\n")
|
fmt.Printf("GetRoleMenus: 方法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)
|
}
|
||||||
|
|
||||||
|
// 重置变量
|
||||||
|
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 != nil {
|
||||||
// 如果角色不存在,返回空数组而不是错误(兼容性处理)
|
// 如果角色不存在,返回空数组而不是错误(兼容性处理)
|
||||||
if err2 == orm.ErrNoRows {
|
if err2 == orm.ErrNoRows {
|
||||||
fmt.Printf("角色 %d 不存在\n", roleId)
|
fmt.Printf("GetRoleMenus: 角色 %d 不存在\n", roleId)
|
||||||
return []int{}, nil
|
return []int{}, nil
|
||||||
}
|
}
|
||||||
fmt.Printf("读取角色 %d 的 menu_ids 失败: %v\n", roleId, err2)
|
fmt.Printf("GetRoleMenus: 方法2也失败,角色 %d 的 menu_ids 读取失败: %v\n", roleId, err2)
|
||||||
return nil, fmt.Errorf("获取角色菜单失败: %v", 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 无效或为空,返回空数组
|
// 如果 menuIdsJson 无效或为空,返回空数组
|
||||||
if !menuIdsJson.Valid || menuIdsJson.String == "" {
|
if !menuIdsJson.Valid || menuIdsJson.String == "" {
|
||||||
fmt.Printf("角色 %d 的 menu_ids 为空或无效\n", roleId)
|
fmt.Printf("GetRoleMenus: 角色 %d 的 menu_ids 最终为空或无效\n", roleId)
|
||||||
return []int{}, nil
|
return []int{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import (
|
|||||||
type Role struct {
|
type Role struct {
|
||||||
RoleId int `orm:"pk;auto;column(role_id)" json:"roleId"`
|
RoleId int `orm:"pk;auto;column(role_id)" json:"roleId"`
|
||||||
TenantId int `orm:"column(tenant_id)" json:"tenantId"`
|
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"`
|
RoleCode string `orm:"size(50);unique" json:"roleCode"`
|
||||||
RoleName string `orm:"size(100)" json:"roleName"`
|
RoleName string `orm:"size(100)" json:"roleName"`
|
||||||
Description string `orm:"type(text);null" json:"description"`
|
Description string `orm:"type(text);null" json:"description"`
|
||||||
@ -90,6 +91,7 @@ func GetRoleById(roleId int) (*Role, error) {
|
|||||||
type roleResult struct {
|
type roleResult struct {
|
||||||
RoleId int
|
RoleId int
|
||||||
TenantId int
|
TenantId int
|
||||||
|
Default int8
|
||||||
RoleCode string
|
RoleCode string
|
||||||
RoleName string
|
RoleName string
|
||||||
Description string
|
Description string
|
||||||
@ -105,8 +107,8 @@ func GetRoleById(roleId int) (*Role, error) {
|
|||||||
|
|
||||||
var result roleResult
|
var result roleResult
|
||||||
// 先读取其他字段(不包括 menu_ids),因为 Beego ORM 可能无法直接读取 JSON 类型
|
// 先读取其他字段(不包括 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(
|
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.RoleCode, &result.RoleName, &result.Description,
|
&result.RoleId, &result.TenantId, &result.Default, &result.RoleCode, &result.RoleName, &result.Description,
|
||||||
&result.Status, &result.SortOrder, &result.CreateTime, &result.UpdateTime,
|
&result.Status, &result.SortOrder, &result.CreateTime, &result.UpdateTime,
|
||||||
&result.DeleteTime, &result.CreateBy, &result.UpdateBy,
|
&result.DeleteTime, &result.CreateBy, &result.UpdateBy,
|
||||||
)
|
)
|
||||||
@ -150,6 +152,7 @@ func GetRoleById(roleId int) (*Role, error) {
|
|||||||
role := &Role{
|
role := &Role{
|
||||||
RoleId: result.RoleId,
|
RoleId: result.RoleId,
|
||||||
TenantId: result.TenantId,
|
TenantId: result.TenantId,
|
||||||
|
Default: result.Default,
|
||||||
RoleCode: result.RoleCode,
|
RoleCode: result.RoleCode,
|
||||||
RoleName: result.RoleName,
|
RoleName: result.RoleName,
|
||||||
Description: result.Description,
|
Description: result.Description,
|
||||||
@ -178,6 +181,7 @@ func GetAllRoles() ([]*Role, error) {
|
|||||||
var results []struct {
|
var results []struct {
|
||||||
RoleId int
|
RoleId int
|
||||||
TenantId int
|
TenantId int
|
||||||
|
Default int8
|
||||||
RoleCode string
|
RoleCode string
|
||||||
RoleName string
|
RoleName string
|
||||||
Description string
|
Description string
|
||||||
@ -191,7 +195,7 @@ func GetAllRoles() ([]*Role, error) {
|
|||||||
UpdateBy string
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -200,6 +204,7 @@ func GetAllRoles() ([]*Role, error) {
|
|||||||
role := &Role{
|
role := &Role{
|
||||||
RoleId: r.RoleId,
|
RoleId: r.RoleId,
|
||||||
TenantId: r.TenantId,
|
TenantId: r.TenantId,
|
||||||
|
Default: r.Default,
|
||||||
RoleCode: r.RoleCode,
|
RoleCode: r.RoleCode,
|
||||||
RoleName: r.RoleName,
|
RoleName: r.RoleName,
|
||||||
Description: r.Description,
|
Description: r.Description,
|
||||||
@ -228,6 +233,7 @@ func GetRoleByTenantId(tenantId int) ([]*Role, error) {
|
|||||||
var results []struct {
|
var results []struct {
|
||||||
RoleId int
|
RoleId int
|
||||||
TenantId int
|
TenantId int
|
||||||
|
Default int8
|
||||||
RoleCode string
|
RoleCode string
|
||||||
RoleName string
|
RoleName string
|
||||||
Description string
|
Description string
|
||||||
@ -241,7 +247,7 @@ func GetRoleByTenantId(tenantId int) ([]*Role, error) {
|
|||||||
UpdateBy string
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -250,6 +256,7 @@ func GetRoleByTenantId(tenantId int) ([]*Role, error) {
|
|||||||
role := &Role{
|
role := &Role{
|
||||||
RoleId: r.RoleId,
|
RoleId: r.RoleId,
|
||||||
TenantId: r.TenantId,
|
TenantId: r.TenantId,
|
||||||
|
Default: r.Default,
|
||||||
RoleCode: r.RoleCode,
|
RoleCode: r.RoleCode,
|
||||||
RoleName: r.RoleName,
|
RoleName: r.RoleName,
|
||||||
Description: r.Description,
|
Description: r.Description,
|
||||||
@ -272,9 +279,31 @@ func GetRoleByTenantId(tenantId int) ([]*Role, error) {
|
|||||||
// GetRoleByCode 根据角色代码获取角色
|
// GetRoleByCode 根据角色代码获取角色
|
||||||
func GetRoleByCode(roleCode string) (*Role, error) {
|
func GetRoleByCode(roleCode string) (*Role, error) {
|
||||||
o := orm.NewOrm()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -283,11 +312,28 @@ func GetRoleByCode(roleCode string) (*Role, error) {
|
|||||||
var menuIdsStr string
|
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)
|
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 != "[]" {
|
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()
|
role.AfterRead()
|
||||||
return &role, nil
|
return role, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRole 创建角色
|
// CreateRole 创建角色
|
||||||
@ -296,8 +342,17 @@ func CreateRole(role *Role) error {
|
|||||||
role.BeforeInsert()
|
role.BeforeInsert()
|
||||||
|
|
||||||
// 使用Raw插入以正确处理JSON字段,并获取插入后的ID
|
// 使用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(), ?, ?)",
|
// 如果没有设置 default 值,根据 tenant_id 自动设置:tenant_id=0 时 default=2,否则 default=3
|
||||||
role.TenantId, role.RoleCode, role.RoleName, role.Description, role.MenuIdsJson.String, role.Status, role.SortOrder, role.CreateBy, role.UpdateBy).Exec()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -324,8 +379,17 @@ func UpdateRole(role *Role) error {
|
|||||||
role.BeforeUpdate()
|
role.BeforeUpdate()
|
||||||
|
|
||||||
// 使用Raw更新以正确处理JSON字段
|
// 使用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",
|
// 如果没有设置 default 值,根据 tenant_id 自动设置:tenant_id=0 时 default=2,否则 default=3
|
||||||
role.TenantId, role.RoleCode, role.RoleName, role.Description, role.MenuIdsJson.String, role.Status, role.SortOrder, role.UpdateBy, role.RoleId).Exec()
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user