2025年11月6日
This commit is contained in:
parent
ed835d628c
commit
f4cef1779c
73
ruoyi-ui/src/api/psychology/permission.js
Normal file
73
ruoyi-ui/src/api/psychology/permission.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询权限列表
|
||||
export function listPermission(query) {
|
||||
return request({
|
||||
url: '/psychology/permission/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询权限详细
|
||||
export function getPermission(permissionId) {
|
||||
return request({
|
||||
url: '/psychology/permission/' + permissionId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据用户ID获取用户有权限访问的量表ID列表
|
||||
export function getUserScaleIds(userId) {
|
||||
return request({
|
||||
url: '/psychology/permission/user/' + userId + '/scales',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 检查用户是否有权限访问指定量表
|
||||
export function checkPermission(userId, scaleId) {
|
||||
return request({
|
||||
url: '/psychology/permission/check/' + userId + '/' + scaleId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增权限
|
||||
export function addPermission(data) {
|
||||
return request({
|
||||
url: '/psychology/permission',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改权限
|
||||
export function updatePermission(data) {
|
||||
return request({
|
||||
url: '/psychology/permission',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除权限
|
||||
export function delPermission(permissionIds) {
|
||||
return request({
|
||||
url: '/psychology/permission/' + permissionIds,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量分配用户量表权限
|
||||
export function assignUserScales(userId, scaleIds) {
|
||||
return request({
|
||||
url: '/psychology/permission/assign',
|
||||
method: 'post',
|
||||
data: {
|
||||
userId: userId,
|
||||
scaleIds: scaleIds
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -298,6 +298,28 @@ export const dynamicRoutes = [
|
|||
roles: ['admin']
|
||||
}
|
||||
},
|
||||
// 量表权限管理
|
||||
{
|
||||
path: 'permission',
|
||||
name: 'ScalePermission',
|
||||
component: () => import('@/views/psychology/permission/index'),
|
||||
meta: {
|
||||
title: '量表权限管理',
|
||||
icon: 'lock',
|
||||
roles: ['admin']
|
||||
}
|
||||
},
|
||||
// 用户量表权限分配
|
||||
{
|
||||
path: 'permission/user/:userId',
|
||||
name: 'UserScalePermission',
|
||||
component: () => import('@/views/psychology/permission/user'),
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: '分配量表权限',
|
||||
roles: ['admin']
|
||||
}
|
||||
},
|
||||
// 解释配置
|
||||
{
|
||||
path: 'interpretation',
|
||||
|
|
|
|||
|
|
@ -91,8 +91,8 @@ export default {
|
|||
loading: false,
|
||||
// 验证码开关
|
||||
captchaEnabled: true,
|
||||
// 注册开关
|
||||
register: false,
|
||||
// 注册开关(默认启用,实际应该从后端配置读取)
|
||||
register: true,
|
||||
redirect: undefined
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -110,9 +110,34 @@ export default {
|
|||
methods: {
|
||||
/** 加载量表列表 */
|
||||
loadScales() {
|
||||
listScale({ status: '0' }).then(response => {
|
||||
this.scaleList = response.rows.filter(scale => scale.itemCount > 0);
|
||||
});
|
||||
// 获取当前登录用户ID
|
||||
const userId = this.$store.getters.userId;
|
||||
|
||||
// 如果是管理员,显示所有量表;否则只显示有权限的量表
|
||||
if (userId === 1) {
|
||||
// 管理员显示所有量表
|
||||
listScale({ status: '0' }).then(response => {
|
||||
this.scaleList = response.rows.filter(scale => scale.itemCount > 0);
|
||||
});
|
||||
} else {
|
||||
// 普通用户只显示有权限的量表
|
||||
import('@/api/psychology/permission').then(module => {
|
||||
module.getUserScaleIds(userId).then(permissionResponse => {
|
||||
const allowedScaleIds = permissionResponse.data || [];
|
||||
if (allowedScaleIds.length === 0) {
|
||||
this.scaleList = [];
|
||||
this.$message.warning('您还没有被分配任何量表权限,请联系管理员');
|
||||
return;
|
||||
}
|
||||
// 获取所有量表,然后过滤
|
||||
listScale({ status: '0' }).then(response => {
|
||||
this.scaleList = response.rows.filter(scale =>
|
||||
scale.itemCount > 0 && allowedScaleIds.includes(scale.scaleId)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
/** 加载用户档案列表 */
|
||||
loadProfiles() {
|
||||
|
|
|
|||
|
|
@ -1,21 +1,39 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="量表ID" prop="scaleId">
|
||||
<el-input
|
||||
<el-form-item label="量表" prop="scaleId">
|
||||
<el-select
|
||||
v-model="queryParams.scaleId"
|
||||
placeholder="请输入量表ID"
|
||||
placeholder="请选择量表"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
filterable
|
||||
style="width: 200px;"
|
||||
@change="handleScaleChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="scale in scaleList"
|
||||
:key="scale.scaleId"
|
||||
:label="scale.scaleName"
|
||||
:value="scale.scaleId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="因子ID" prop="factorId">
|
||||
<el-input
|
||||
<el-form-item label="因子" prop="factorId">
|
||||
<el-select
|
||||
v-model="queryParams.factorId"
|
||||
placeholder="请输入因子ID"
|
||||
placeholder="请选择因子"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
filterable
|
||||
style="width: 200px;"
|
||||
:disabled="!queryParams.scaleId"
|
||||
>
|
||||
<el-option
|
||||
v-for="factor in factorList"
|
||||
:key="factor.factorId"
|
||||
:label="factor.factorName"
|
||||
:value="factor.factorId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="等级" prop="level">
|
||||
<el-input
|
||||
|
|
@ -70,8 +88,16 @@
|
|||
<el-table v-loading="loading" :data="interpretationList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="序号" align="center" prop="interpretationId" width="80" />
|
||||
<el-table-column label="量表ID" align="center" prop="scaleId" width="100" />
|
||||
<el-table-column label="因子ID" align="center" prop="factorId" width="100" />
|
||||
<el-table-column label="量表" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ getScaleName(scope.row.scaleId) || '通用' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="因子" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ getFactorName(scope.row.factorId) || '总体' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分数范围" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.scoreRangeMin }} - {{ scope.row.scoreRangeMax }}
|
||||
|
|
@ -118,11 +144,39 @@
|
|||
<!-- 添加或修改对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="量表ID" prop="scaleId">
|
||||
<el-input v-model="form.scaleId" placeholder="留空表示通用解释" />
|
||||
<el-form-item label="量表" prop="scaleId">
|
||||
<el-select
|
||||
v-model="form.scaleId"
|
||||
placeholder="请选择量表(留空表示通用解释)"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%;"
|
||||
@change="handleFormScaleChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="scale in scaleList"
|
||||
:key="scale.scaleId"
|
||||
:label="scale.scaleName"
|
||||
:value="scale.scaleId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="因子ID" prop="factorId">
|
||||
<el-input v-model="form.factorId" placeholder="留空表示总体解释" />
|
||||
<el-form-item label="因子" prop="factorId">
|
||||
<el-select
|
||||
v-model="form.factorId"
|
||||
placeholder="请选择因子(留空表示总体解释)"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%;"
|
||||
:disabled="!form.scaleId"
|
||||
>
|
||||
<el-option
|
||||
v-for="factor in formFactorList"
|
||||
:key="factor.factorId"
|
||||
:label="factor.factorName"
|
||||
:value="factor.factorId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
|
|
@ -164,8 +218,8 @@
|
|||
<!-- 查看对话框 -->
|
||||
<el-dialog title="解释详情" :visible.sync="viewOpen" width="800px" append-to-body>
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="量表ID">{{ viewForm.scaleId || '通用' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="因子ID">{{ viewForm.factorId || '总体' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="量表">{{ getScaleName(viewForm.scaleId) || '通用' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="因子">{{ getFactorName(viewForm.factorId) || '总体' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="分数范围">{{ viewForm.scoreRangeMin }} - {{ viewForm.scoreRangeMax }}</el-descriptions-item>
|
||||
<el-descriptions-item label="等级">{{ viewForm.level }}</el-descriptions-item>
|
||||
<el-descriptions-item label="等级名称">{{ viewForm.levelName }}</el-descriptions-item>
|
||||
|
|
@ -186,6 +240,8 @@
|
|||
|
||||
<script>
|
||||
import { listInterpretation, getInterpretation, addInterpretation, updateInterpretation, delInterpretation } from "@/api/psychology/interpretation";
|
||||
import { listScale } from "@/api/psychology/scale";
|
||||
import { listFactor } from "@/api/psychology/factor";
|
||||
|
||||
export default {
|
||||
name: "Interpretation",
|
||||
|
|
@ -223,6 +279,12 @@ export default {
|
|||
form: {},
|
||||
// 查看表单
|
||||
viewForm: {},
|
||||
// 量表列表
|
||||
scaleList: [],
|
||||
// 因子列表(用于搜索)
|
||||
factorList: [],
|
||||
// 因子列表(用于表单)
|
||||
formFactorList: [],
|
||||
// 表单校验
|
||||
rules: {
|
||||
interpretationTitle: [
|
||||
|
|
@ -232,9 +294,68 @@ export default {
|
|||
};
|
||||
},
|
||||
created() {
|
||||
this.loadScaleList();
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 加载量表列表 */
|
||||
loadScaleList() {
|
||||
listScale({ status: '0', pageNum: 1, pageSize: 1000 }).then(response => {
|
||||
this.scaleList = response.rows || [];
|
||||
});
|
||||
},
|
||||
/** 加载因子列表(用于搜索) */
|
||||
loadFactorList(scaleId) {
|
||||
if (!scaleId) {
|
||||
this.factorList = [];
|
||||
return;
|
||||
}
|
||||
listFactor(scaleId).then(response => {
|
||||
this.factorList = response.data || [];
|
||||
}).catch(() => {
|
||||
this.factorList = [];
|
||||
});
|
||||
},
|
||||
/** 加载因子列表(用于表单) */
|
||||
loadFormFactorList(scaleId) {
|
||||
if (!scaleId) {
|
||||
this.formFactorList = [];
|
||||
this.form.factorId = undefined;
|
||||
return;
|
||||
}
|
||||
listFactor(scaleId).then(response => {
|
||||
this.formFactorList = response.data || [];
|
||||
}).catch(() => {
|
||||
this.formFactorList = [];
|
||||
});
|
||||
},
|
||||
/** 搜索条件中量表变化 */
|
||||
handleScaleChange(scaleId) {
|
||||
this.queryParams.factorId = undefined;
|
||||
this.loadFactorList(scaleId);
|
||||
},
|
||||
/** 表单中量表变化 */
|
||||
handleFormScaleChange(scaleId) {
|
||||
this.form.factorId = undefined;
|
||||
this.loadFormFactorList(scaleId);
|
||||
},
|
||||
/** 获取量表名称 */
|
||||
getScaleName(scaleId) {
|
||||
if (!scaleId) return null;
|
||||
const scale = this.scaleList.find(s => s.scaleId === scaleId);
|
||||
return scale ? scale.scaleName : null;
|
||||
},
|
||||
/** 获取因子名称 */
|
||||
getFactorName(factorId) {
|
||||
if (!factorId) return null;
|
||||
// 从当前因子列表中查找
|
||||
const factor = this.factorList.find(f => f.factorId === factorId);
|
||||
if (factor) return factor.factorName;
|
||||
// 如果不在当前列表中,尝试从表单因子列表中查找
|
||||
const formFactor = this.formFactorList.find(f => f.factorId === factorId);
|
||||
if (formFactor) return formFactor.factorName;
|
||||
return null;
|
||||
},
|
||||
/** 查询解释列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
|
|
@ -242,6 +363,28 @@ export default {
|
|||
this.interpretationList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
// 加载所有解释中涉及的因子列表
|
||||
this.loadAllFactors();
|
||||
});
|
||||
},
|
||||
/** 加载所有解释中涉及的因子 */
|
||||
loadAllFactors() {
|
||||
// 获取所有唯一的量表ID
|
||||
const scaleIds = [...new Set(this.interpretationList
|
||||
.map(item => item.scaleId)
|
||||
.filter(id => id != null))];
|
||||
|
||||
// 为每个量表加载因子
|
||||
scaleIds.forEach(scaleId => {
|
||||
listFactor(scaleId).then(response => {
|
||||
const factors = response.data || [];
|
||||
// 合并到因子列表中(去重)
|
||||
factors.forEach(factor => {
|
||||
if (!this.factorList.find(f => f.factorId === factor.factorId)) {
|
||||
this.factorList.push(factor);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
|
|
@ -272,6 +415,10 @@ export default {
|
|||
const interpretationId = row.interpretationId ? row.interpretationId : this.ids[0];
|
||||
getInterpretation(interpretationId).then(response => {
|
||||
this.form = response.data;
|
||||
// 如果表单中有量表ID,加载对应的因子列表
|
||||
if (this.form.scaleId) {
|
||||
this.loadFormFactorList(this.form.scaleId);
|
||||
}
|
||||
this.open = true;
|
||||
this.title = "修改解释配置";
|
||||
});
|
||||
|
|
|
|||
310
ruoyi-ui/src/views/psychology/permission/index.vue
Normal file
310
ruoyi-ui/src/views/psychology/permission/index.vue
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="量表名称" prop="scaleId">
|
||||
<el-select v-model="queryParams.scaleId" placeholder="请选择量表" clearable style="width: 200px">
|
||||
<el-option
|
||||
v-for="scale in scaleList"
|
||||
:key="scale.scaleId"
|
||||
:label="scale.scaleName"
|
||||
:value="scale.scaleId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名称" prop="userId">
|
||||
<el-select v-model="queryParams.userId" placeholder="请选择用户" clearable filterable style="width: 200px">
|
||||
<el-option
|
||||
v-for="user in userList"
|
||||
:key="user.userId"
|
||||
:label="user.userName"
|
||||
:value="user.userId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="状态" clearable style="width: 120px">
|
||||
<el-option label="有效" value="0" />
|
||||
<el-option label="无效" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['psychology:permission:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['psychology:permission:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="permissionList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="权限ID" align="center" prop="permissionId" width="100" />
|
||||
<el-table-column label="量表名称" align="center" prop="scaleName" width="200" />
|
||||
<el-table-column label="用户名称" align="center" prop="userName" width="150" />
|
||||
<el-table-column label="部门名称" align="center" prop="deptName" width="150" />
|
||||
<el-table-column label="角色名称" align="center" prop="roleName" width="150" />
|
||||
<el-table-column label="班级名称" align="center" prop="className" width="150" />
|
||||
<el-table-column label="开始时间" align="center" prop="startTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" align="center" prop="endTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.status === '0'" type="success">有效</el-tag>
|
||||
<el-tag v-else type="danger">无效</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['psychology:permission:edit']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['psychology:permission:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改权限对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="量表" prop="scaleId">
|
||||
<el-select v-model="form.scaleId" placeholder="请选择量表" style="width: 100%;" filterable>
|
||||
<el-option
|
||||
v-for="scale in scaleList"
|
||||
:key="scale.scaleId"
|
||||
:label="scale.scaleName"
|
||||
:value="scale.scaleId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户" prop="userId">
|
||||
<el-select v-model="form.userId" placeholder="请选择用户(留空表示所有用户)" clearable filterable style="width: 100%;">
|
||||
<el-option
|
||||
v-for="user in userList"
|
||||
:key="user.userId"
|
||||
:label="user.userName"
|
||||
:value="user.userId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker
|
||||
v-model="form.startTime"
|
||||
type="datetime"
|
||||
placeholder="选择开始时间(留空表示无限制)"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
style="width: 100%;">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker
|
||||
v-model="form.endTime"
|
||||
type="datetime"
|
||||
placeholder="选择结束时间(留空表示无限制)"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
style="width: 100%;">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio label="0">有效</el-radio>
|
||||
<el-radio label="1">无效</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listPermission, getPermission, delPermission, addPermission, updatePermission } from "@/api/psychology/permission";
|
||||
import { listScale } from "@/api/psychology/scale";
|
||||
import { listUser } from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "PsyScalePermission",
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 权限表格数据
|
||||
permissionList: [],
|
||||
// 量表列表
|
||||
scaleList: [],
|
||||
// 用户列表
|
||||
userList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
scaleId: undefined,
|
||||
userId: undefined,
|
||||
status: undefined
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
scaleId: [
|
||||
{ required: true, message: "量表不能为空", trigger: "change" }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: "状态不能为空", trigger: "change" }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.loadScales();
|
||||
this.loadUsers();
|
||||
},
|
||||
methods: {
|
||||
/** 查询权限列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listPermission(this.queryParams).then(response => {
|
||||
this.permissionList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 加载量表列表 */
|
||||
loadScales() {
|
||||
listScale({ status: '0' }).then(response => {
|
||||
this.scaleList = response.rows || [];
|
||||
});
|
||||
},
|
||||
/** 加载用户列表 */
|
||||
loadUsers() {
|
||||
listUser({ status: '0' }).then(response => {
|
||||
this.userList = response.rows || [];
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
permissionId: undefined,
|
||||
scaleId: undefined,
|
||||
userId: undefined,
|
||||
deptId: undefined,
|
||||
roleId: undefined,
|
||||
className: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
status: "0",
|
||||
remark: undefined
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.permissionId);
|
||||
this.single = selection.length != 1;
|
||||
this.multiple = !selection.length;
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加量表权限";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const permissionId = row.permissionId || this.ids[0];
|
||||
getPermission(permissionId).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改量表权限";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
if (this.form.permissionId != undefined) {
|
||||
updatePermission(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
} else {
|
||||
addPermission(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const permissionIds = row.permissionId || this.ids;
|
||||
this.$modal.confirm('是否确认删除权限编号为"' + permissionIds + '"的数据项?').then(() => {
|
||||
return delPermission(permissionIds);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
118
ruoyi-ui/src/views/psychology/permission/user.vue
Normal file
118
ruoyi-ui/src/views/psychology/permission/user.vue
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-card shadow="never">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>为用户分配量表权限</span>
|
||||
<el-button style="float: right; padding: 3px 0" type="text" @click="handleBack">返回</el-button>
|
||||
</div>
|
||||
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="用户名称">
|
||||
<el-input v-model="userName" disabled style="width: 300px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="选择量表">
|
||||
<el-transfer
|
||||
v-model="selectedScaleIds"
|
||||
:data="scaleList"
|
||||
:titles="['可选量表', '已选量表']"
|
||||
:button-texts="['移除', '添加']"
|
||||
:format="{
|
||||
noChecked: '${total}',
|
||||
hasChecked: '${checked}/${total}'
|
||||
}"
|
||||
filterable
|
||||
filter-placeholder="搜索量表"
|
||||
style="text-align: left; display: inline-block"
|
||||
@change="handleChange">
|
||||
<span slot-scope="{ option }">{{ option.label }} ({{ option.itemCount }}题)</span>
|
||||
</el-transfer>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm" :loading="loading">保存</el-button>
|
||||
<el-button @click="handleBack">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { assignUserScales, getUserScaleIds } from "@/api/psychology/permission";
|
||||
import { listScale } from "@/api/psychology/scale";
|
||||
import { getUser } from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "UserScalePermission",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
userId: undefined,
|
||||
userName: "",
|
||||
scaleList: [],
|
||||
selectedScaleIds: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const userId = this.$route.params.userId;
|
||||
if (userId) {
|
||||
this.userId = parseInt(userId);
|
||||
this.loadUserInfo();
|
||||
this.loadScales();
|
||||
this.loadUserScales();
|
||||
} else {
|
||||
this.$modal.msgError("缺少用户ID参数");
|
||||
this.handleBack();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 加载用户信息 */
|
||||
loadUserInfo() {
|
||||
getUser(this.userId).then(response => {
|
||||
this.userName = response.data.userName;
|
||||
});
|
||||
},
|
||||
/** 加载量表列表 */
|
||||
loadScales() {
|
||||
listScale({ status: '0' }).then(response => {
|
||||
this.scaleList = (response.rows || []).map(scale => ({
|
||||
key: scale.scaleId,
|
||||
label: scale.scaleName,
|
||||
itemCount: scale.itemCount || 0
|
||||
}));
|
||||
});
|
||||
},
|
||||
/** 加载用户已有权限的量表 */
|
||||
loadUserScales() {
|
||||
getUserScaleIds(this.userId).then(response => {
|
||||
this.selectedScaleIds = response.data || [];
|
||||
});
|
||||
},
|
||||
/** 提交表单 */
|
||||
submitForm() {
|
||||
this.loading = true;
|
||||
assignUserScales(this.userId, this.selectedScaleIds).then(response => {
|
||||
this.$modal.msgSuccess("分配成功");
|
||||
this.loading = false;
|
||||
this.handleBack();
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 返回 */
|
||||
handleBack() {
|
||||
this.$router.push("/system/user");
|
||||
},
|
||||
/** 选择变化 */
|
||||
handleChange(value, direction, movedKeys) {
|
||||
// 可以在这里添加额外的逻辑
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -126,15 +126,67 @@
|
|||
</el-row>
|
||||
|
||||
<el-table :data="ruleList" border>
|
||||
<el-table-column label="题目ID" align="center" prop="itemId" width="100" />
|
||||
<el-table-column label="选项ID" align="center" prop="optionIds" width="200" />
|
||||
<el-table-column label="权重" align="center" prop="weight" width="100" />
|
||||
<el-table-column label="计算方式" align="center" prop="calculationType" width="120">
|
||||
<el-table-column label="题目" align="center" width="200">
|
||||
<template slot-scope="scope">
|
||||
{{ getCalculationTypeName(scope.row.calculationType) }}
|
||||
<el-select
|
||||
v-model="scope.row.itemId"
|
||||
placeholder="请选择题目"
|
||||
filterable
|
||||
style="width: 100%;"
|
||||
@change="handleRuleItemChange(scope.$index, scope.row.itemId)">
|
||||
<el-option
|
||||
v-for="item in itemList"
|
||||
:key="item.itemId"
|
||||
:label="item.itemNumber + '. ' + item.itemContent"
|
||||
:value="item.itemId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
|
||||
<el-table-column label="选项" align="center" width="250">
|
||||
<template slot-scope="scope">
|
||||
<el-select
|
||||
v-model="scope.row.optionIdsArray"
|
||||
placeholder="请选择选项(可选,留空表示所有选项)"
|
||||
multiple
|
||||
filterable
|
||||
clearable
|
||||
style="width: 100%;"
|
||||
:disabled="!scope.row.itemId"
|
||||
@change="handleRuleOptionChange(scope.$index)">
|
||||
<el-option
|
||||
v-for="option in getItemOptions(scope.row.itemId)"
|
||||
:key="option.optionId"
|
||||
:label="option.optionCode + '. ' + option.optionContent"
|
||||
:value="option.optionId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="权重" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.weight"
|
||||
:min="0"
|
||||
:max="10"
|
||||
:step="0.1"
|
||||
:precision="2"
|
||||
controls-position="right"
|
||||
style="width: 100%;">
|
||||
</el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计算方式" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.calculationType" style="width: 100%;">
|
||||
<el-option label="求和" value="sum" />
|
||||
<el-option label="平均" value="average" />
|
||||
<el-option label="最大" value="max" />
|
||||
<el-option label="最小" value="min" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
|
|
@ -158,6 +210,7 @@
|
|||
import { listFactor, getFactor, delFactor, addFactor, updateFactor } from "@/api/psychology/factor";
|
||||
import { listItem } from "@/api/psychology/item";
|
||||
import { listFactorRule, saveRules } from "@/api/psychology/factor";
|
||||
import { listOption } from "@/api/psychology/option";
|
||||
|
||||
export default {
|
||||
name: "ScaleFactor",
|
||||
|
|
@ -175,6 +228,8 @@ export default {
|
|||
factorList: [],
|
||||
// 题目列表
|
||||
itemList: [],
|
||||
// 选项列表缓存(key: itemId, value: optionList)
|
||||
optionListCache: {},
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
|
|
@ -311,7 +366,21 @@ export default {
|
|||
this.ruleOpen = true;
|
||||
// 加载已有规则
|
||||
listFactorRule(row.factorId).then(response => {
|
||||
this.ruleList = response.data || [];
|
||||
const rules = response.data || [];
|
||||
// 将optionIds字符串转换为数组,方便多选组件使用
|
||||
this.ruleList = rules.map(rule => ({
|
||||
...rule,
|
||||
optionIdsArray: rule.optionIds ? rule.optionIds.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id)) : []
|
||||
}));
|
||||
// 预加载所有规则中题目的选项
|
||||
const itemIds = [...new Set(rules.map(r => r.itemId).filter(id => id))];
|
||||
itemIds.forEach(itemId => {
|
||||
if (!this.optionListCache[itemId]) {
|
||||
listOption(itemId).then(optionRes => {
|
||||
this.$set(this.optionListCache, itemId, optionRes.data || []);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 新增规则 */
|
||||
|
|
@ -321,6 +390,7 @@ export default {
|
|||
factorId: this.currentFactorId,
|
||||
itemId: null,
|
||||
optionIds: '',
|
||||
optionIdsArray: [], // 用于多选组件的数组格式
|
||||
weight: 1.00,
|
||||
calculationType: 'sum'
|
||||
};
|
||||
|
|
@ -330,15 +400,47 @@ export default {
|
|||
handleDeleteRule(index) {
|
||||
this.ruleList.splice(index, 1);
|
||||
},
|
||||
/** 计算方式名称 */
|
||||
getCalculationTypeName(type) {
|
||||
const types = {
|
||||
sum: '求和',
|
||||
average: '平均',
|
||||
max: '最大',
|
||||
min: '最小'
|
||||
};
|
||||
return types[type] || type;
|
||||
/** 获取题目的选项列表 */
|
||||
getItemOptions(itemId) {
|
||||
if (!itemId) {
|
||||
return [];
|
||||
}
|
||||
// 如果缓存中有,直接返回
|
||||
if (this.optionListCache[itemId]) {
|
||||
return this.optionListCache[itemId];
|
||||
}
|
||||
// 否则异步加载选项(首次访问时可能返回空数组,但会触发加载)
|
||||
if (!this.optionListCache[itemId]) {
|
||||
listOption(itemId).then(response => {
|
||||
this.$set(this.optionListCache, itemId, response.data || []);
|
||||
}).catch(() => {
|
||||
this.$set(this.optionListCache, itemId, []);
|
||||
});
|
||||
}
|
||||
return this.optionListCache[itemId] || [];
|
||||
},
|
||||
/** 题目变化时处理 */
|
||||
handleRuleItemChange(index, itemId) {
|
||||
const rule = this.ruleList[index];
|
||||
// 清空选项选择
|
||||
rule.optionIdsArray = [];
|
||||
rule.optionIds = '';
|
||||
// 加载该题目的选项
|
||||
if (itemId) {
|
||||
listOption(itemId).then(response => {
|
||||
this.$set(this.optionListCache, itemId, response.data || []);
|
||||
});
|
||||
}
|
||||
},
|
||||
/** 选项变化时处理 */
|
||||
handleRuleOptionChange(index) {
|
||||
const rule = this.ruleList[index];
|
||||
// 将选项ID数组转换为逗号分隔的字符串
|
||||
if (rule.optionIdsArray && rule.optionIdsArray.length > 0) {
|
||||
rule.optionIds = rule.optionIdsArray.map(id => String(id)).join(',');
|
||||
} else {
|
||||
rule.optionIds = '';
|
||||
}
|
||||
},
|
||||
/** 保存规则 */
|
||||
submitRules() {
|
||||
|
|
@ -351,7 +453,20 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
saveRules(this.currentFactorId, this.ruleList).then(response => {
|
||||
// 准备保存的数据,将optionIdsArray转换为optionIds字符串
|
||||
const rulesToSave = this.ruleList.map(rule => {
|
||||
const ruleData = {
|
||||
ruleId: rule.ruleId,
|
||||
factorId: rule.factorId,
|
||||
itemId: rule.itemId,
|
||||
optionIds: rule.optionIds || (rule.optionIdsArray && rule.optionIdsArray.length > 0 ? rule.optionIdsArray.map(id => String(id)).join(',') : ''),
|
||||
weight: rule.weight || 1.00,
|
||||
calculationType: rule.calculationType || 'sum'
|
||||
};
|
||||
return ruleData;
|
||||
});
|
||||
|
||||
saveRules(this.currentFactorId, rulesToSave).then(response => {
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
this.ruleOpen = false;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -97,14 +97,19 @@
|
|||
/>
|
||||
<el-table-column label="量表类型" align="center" prop="scaleType" width="120">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.psy_scale_type" :value="scope.row.scaleType"/>
|
||||
<el-tag v-if="scope.row.scaleType" type="primary">
|
||||
{{ getDictLabel(dict.type.psy_scale_type, scope.row.scaleType) }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="题目数量" align="center" prop="itemCount" width="100" />
|
||||
<el-table-column label="预计时间(分)" align="center" prop="estimatedTime" width="120" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.psy_scale_status" :value="scope.row.status"/>
|
||||
<el-tag v-if="scope.row.status === '0'" type="success">正常</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === '1'" type="danger">停用</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="center" prop="sortOrder" width="80" />
|
||||
|
|
@ -178,9 +183,9 @@
|
|||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="量表类型" prop="scaleType">
|
||||
<el-select v-model="form.scaleType" placeholder="请选择量表类型">
|
||||
<el-select v-model="form.scaleType" placeholder="请选择量表类型" clearable>
|
||||
<el-option
|
||||
v-for="dict in dict.type.psy_scale_type"
|
||||
v-for="dict in uniqueScaleTypes"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
|
|
@ -195,7 +200,8 @@
|
|||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="题目数量" prop="itemCount">
|
||||
<el-input-number v-model="form.itemCount" :min="0" controls-position="right" />
|
||||
<el-input-number v-model="form.itemCount" :min="0" controls-position="right" :disabled="true" />
|
||||
<span style="margin-left: 10px; color: #909399; font-size: 12px;">(自动计算,根据实际题目数量)</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
|
|
@ -226,11 +232,8 @@
|
|||
<el-col :span="24">
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio
|
||||
v-for="dict in dict.type.psy_scale_status"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>{{dict.label}}</el-radio>
|
||||
<el-radio label="0">正常</el-radio>
|
||||
<el-radio label="1">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
|
@ -302,16 +305,35 @@ export default {
|
|||
scaleType: [
|
||||
{ required: true, message: "量表类型不能为空", trigger: "change" }
|
||||
],
|
||||
itemCount: [
|
||||
{ required: true, message: "题目数量不能为空", trigger: "blur" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** 去重后的量表类型列表 */
|
||||
uniqueScaleTypes() {
|
||||
if (!this.dict.type.psy_scale_type) {
|
||||
return [];
|
||||
}
|
||||
const seen = new Set();
|
||||
return this.dict.type.psy_scale_type.filter(dict => {
|
||||
if (seen.has(dict.value)) {
|
||||
return false;
|
||||
}
|
||||
seen.add(dict.value);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 获取字典标签 */
|
||||
getDictLabel(dictList, value) {
|
||||
if (!dictList || !value) return value;
|
||||
const dict = dictList.find(item => item.value === value);
|
||||
return dict ? dict.label : value;
|
||||
},
|
||||
/** 查询量表列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
|
|
|
|||
171
ruoyi-ui/src/views/system/menu/menuCleanup.vue
Normal file
171
ruoyi-ui/src/views/system/menu/menuCleanup.vue
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>菜单去重工具</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="menu-cleanup-container">
|
||||
<div class="button-group">
|
||||
<el-button type="primary" @click="checkDuplicateMenus" :loading="loading">
|
||||
<i class="el-icon-search"></i>
|
||||
检查重复菜单
|
||||
</el-button>
|
||||
<el-button type="success" @click="cleanupDuplicateMenus" :loading="loading" :disabled="!hasDuplicates">
|
||||
<i class="el-icon-delete"></i>
|
||||
执行菜单去重
|
||||
</el-button>
|
||||
<el-button @click="listPsychologyMenus" :loading="loading">
|
||||
<i class="el-icon-menu"></i>
|
||||
查看所有菜单
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
v-if="message"
|
||||
:title="message"
|
||||
:type="messageType"
|
||||
show-icon
|
||||
:closable="false"
|
||||
></el-alert>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" style="width: 100%; margin-top: 20px;">
|
||||
<el-table-column prop="menu_name" label="菜单名称" width="200"></el-table-column>
|
||||
<el-table-column prop="path" label="路由路径" width="250"></el-table-column>
|
||||
<el-table-column prop="component" label="组件路径" width="300"></el-table-column>
|
||||
<el-table-column prop="parent_id" label="父菜单ID" width="100"></el-table-column>
|
||||
<el-table-column prop="count" label="重复数量" width="100" v-if="showDuplicates"></el-table-column>
|
||||
<el-table-column prop="菜单ID列表" label="菜单ID列表" v-if="showDuplicates"></el-table-column>
|
||||
<el-table-column prop="menu_type" label="类型" width="80" v-if="!showDuplicates"></el-table-column>
|
||||
<el-table-column prop="visible" label="是否显示" width="100" v-if="!showDuplicates"></el-table-column>
|
||||
<el-table-column prop="order_num" label="排序" width="80" v-if="!showDuplicates"></el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-if="!loading && tableData.length === 0" class="empty-state">
|
||||
<el-empty description="暂无数据"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
export default {
|
||||
name: 'MenuCleanup',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
message: '',
|
||||
messageType: 'info',
|
||||
hasDuplicates: false,
|
||||
showDuplicates: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 检查重复菜单
|
||||
checkDuplicateMenus() {
|
||||
this.loading = true
|
||||
this.showDuplicates = true
|
||||
axios.post('/system/menu/cleanup/check').then(res => {
|
||||
if (res.code === 200) {
|
||||
this.tableData = res.data
|
||||
this.hasDuplicates = this.tableData.length > 0
|
||||
if (this.hasDuplicates) {
|
||||
this.message = `发现 ${this.tableData.length} 组重复菜单!`
|
||||
this.messageType = 'warning'
|
||||
} else {
|
||||
this.message = '未发现重复菜单!'
|
||||
this.messageType = 'success'
|
||||
}
|
||||
} else {
|
||||
this.message = res.msg || '检查失败'
|
||||
this.messageType = 'error'
|
||||
this.hasDuplicates = false
|
||||
}
|
||||
}).catch(err => {
|
||||
this.message = '请求失败:' + err
|
||||
this.messageType = 'error'
|
||||
this.hasDuplicates = false
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 执行菜单去重
|
||||
cleanupDuplicateMenus() {
|
||||
this.$confirm('确定要清理所有重复菜单吗?清理操作不可撤销!', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.loading = true
|
||||
axios.post('/system/menu/cleanup/cleanup').then(res => {
|
||||
if (res.code === 200) {
|
||||
this.message = res.msg || '清理成功'
|
||||
this.messageType = 'success'
|
||||
this.checkDuplicateMenus() // 清理后重新检查
|
||||
} else {
|
||||
this.message = res.msg || '清理失败'
|
||||
this.messageType = 'error'
|
||||
}
|
||||
}).catch(err => {
|
||||
this.message = '请求失败:' + err
|
||||
this.messageType = 'error'
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 列出所有心理学相关菜单
|
||||
listPsychologyMenus() {
|
||||
this.loading = true
|
||||
this.showDuplicates = false
|
||||
axios.post('/system/menu/cleanup/list').then(res => {
|
||||
if (res.code === 200) {
|
||||
this.tableData = res.data
|
||||
this.message = `共查询到 ${this.tableData.length} 个心理学相关菜单`
|
||||
this.messageType = 'info'
|
||||
} else {
|
||||
this.message = res.msg || '查询失败'
|
||||
this.messageType = 'error'
|
||||
}
|
||||
}).catch(err => {
|
||||
this.message = '请求失败:' + err
|
||||
this.messageType = 'error'
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu-cleanup-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.button-group .el-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
margin-top: 50px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -82,6 +82,7 @@
|
|||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="handleResetPwd" icon="el-icon-key" v-hasPermi="['system:user:resetPwd']">重置密码</el-dropdown-item>
|
||||
<el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check" v-hasPermi="['system:user:edit']">分配角色</el-dropdown-item>
|
||||
<el-dropdown-item command="handleAuthScale" icon="el-icon-document" v-hasPermi="['psychology:permission:edit']">分配量表权限</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
|
@ -432,6 +433,9 @@ export default {
|
|||
case "handleAuthRole":
|
||||
this.handleAuthRole(row)
|
||||
break
|
||||
case "handleAuthScale":
|
||||
this.handleAuthScale(row)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
@ -486,6 +490,10 @@ export default {
|
|||
const userId = row.userId
|
||||
this.$router.push("/system/user-auth/role/" + userId)
|
||||
},
|
||||
/** 分配量表权限操作 */
|
||||
handleAuthScale: function(row) {
|
||||
this.$router.push("/psychology/permission/user/" + row.userId)
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm: function() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,156 @@
|
|||
package com.ddnai.admin.controller;
|
||||
|
||||
import com.ddnai.common.core.controller.BaseController;
|
||||
import com.ddnai.common.core.domain.AjaxResult;
|
||||
import com.ddnai.common.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 菜单去重清理控制器
|
||||
* 用于检测和清理系统中重复的菜单
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/menu/cleanup")
|
||||
public class MenuCleanupController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 检查重复菜单
|
||||
*/
|
||||
@PostMapping("/check")
|
||||
public AjaxResult checkDuplicateMenus()
|
||||
{
|
||||
// 查找所有重复的菜单(基于menu_name和path组合)
|
||||
String sql = "SELECT " +
|
||||
" menu_name AS '菜单名称'," +
|
||||
" path AS '路由路径'," +
|
||||
" component AS '组件路径'," +
|
||||
" parent_id AS '父菜单ID'," +
|
||||
" COUNT(*) AS '重复数量'," +
|
||||
" GROUP_CONCAT(menu_id ORDER BY menu_id SEPARATOR ', ') AS '菜单ID列表' " +
|
||||
"FROM sys_menu " +
|
||||
"WHERE menu_name LIKE '%心理%' " +
|
||||
" OR menu_name LIKE '%量表%' " +
|
||||
" OR menu_name LIKE '%题目%' " +
|
||||
" OR menu_name LIKE '%因子%' " +
|
||||
" OR menu_name LIKE '%测评%' " +
|
||||
" OR menu_name LIKE '%报告%' " +
|
||||
" OR menu_name LIKE '%解释%' " +
|
||||
" OR menu_name LIKE '%档案%' " +
|
||||
" OR menu_name LIKE '%问卷%' " +
|
||||
" OR menu_name LIKE '%网站%' " +
|
||||
" OR menu_name LIKE '%栏目%' " +
|
||||
" OR menu_name LIKE '%评论%' " +
|
||||
" OR menu_name LIKE '%预警%' " +
|
||||
"GROUP BY menu_name, path, component, parent_id " +
|
||||
"HAVING COUNT(*) > 1 " +
|
||||
"ORDER BY COUNT(*) DESC, menu_name";
|
||||
|
||||
List<Map<String, Object>> duplicates = jdbcTemplate.queryForList(sql);
|
||||
return AjaxResult.success(duplicates);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行菜单去重
|
||||
*/
|
||||
@PostMapping("/cleanup")
|
||||
public AjaxResult cleanupDuplicateMenus()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 删除"心理测评管理"目录的重复项(保留第一个)
|
||||
int deleteCount1 = jdbcTemplate.update(
|
||||
"DELETE t1 FROM sys_menu t1 " +
|
||||
"INNER JOIN sys_menu t2 " +
|
||||
"WHERE t1.menu_name = '心理测评管理' " +
|
||||
" AND t1.parent_id = 0 " +
|
||||
" AND t2.menu_name = '心理测评管理' " +
|
||||
" AND t2.parent_id = 0 " +
|
||||
" AND t1.menu_id > t2.menu_id"
|
||||
);
|
||||
|
||||
// 删除"心理网站管理"目录的重复项
|
||||
int deleteCount2 = jdbcTemplate.update(
|
||||
"DELETE t1 FROM sys_menu t1 " +
|
||||
"INNER JOIN sys_menu t2 " +
|
||||
"WHERE t1.menu_name = '心理网站管理' " +
|
||||
" AND t1.parent_id = 0 " +
|
||||
" AND t2.menu_name = '心理网站管理' " +
|
||||
" AND t2.parent_id = 0 " +
|
||||
" AND t1.menu_id > t2.menu_id"
|
||||
);
|
||||
|
||||
// 删除其他重复菜单(基于path和component)
|
||||
int deleteCount3 = jdbcTemplate.update(
|
||||
"DELETE t1 FROM sys_menu t1 " +
|
||||
"INNER JOIN sys_menu t2 " +
|
||||
"WHERE t1.path = t2.path " +
|
||||
" AND t1.component = t2.component " +
|
||||
" AND t1.menu_name = t2.menu_name " +
|
||||
" AND t1.menu_id > t2.menu_id " +
|
||||
" AND (t1.menu_name LIKE '%心理%' " +
|
||||
" OR t1.menu_name LIKE '%量表%' " +
|
||||
" OR t1.menu_name LIKE '%题目%' " +
|
||||
" OR t1.menu_name LIKE '%因子%' " +
|
||||
" OR t1.menu_name LIKE '%测评%' " +
|
||||
" OR t1.menu_name LIKE '%网站%' " +
|
||||
" OR t1.menu_name LIKE '%栏目%' " +
|
||||
" OR t1.menu_name LIKE '%评论%' " +
|
||||
" OR t1.menu_name LIKE '%预警%' " +
|
||||
" OR t1.menu_name LIKE '%问卷%' " +
|
||||
" OR t1.menu_name LIKE '%档案%')"
|
||||
);
|
||||
|
||||
int totalDeleted = deleteCount1 + deleteCount2 + deleteCount3;
|
||||
return AjaxResult.success("菜单去重完成,共删除 " + totalDeleted + " 个重复菜单");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return AjaxResult.error("菜单去重失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有心理学相关菜单(按层级)
|
||||
*/
|
||||
@PostMapping("/list")
|
||||
public AjaxResult listPsychologyMenus()
|
||||
{
|
||||
String sql = "SELECT " +
|
||||
" menu_id, " +
|
||||
" menu_name AS '菜单名称', " +
|
||||
" parent_id AS '父菜单ID', " +
|
||||
" path AS '路由路径', " +
|
||||
" component AS '组件路径', " +
|
||||
" menu_type AS '类型', " +
|
||||
" visible AS '是否显示', " +
|
||||
" order_num AS '排序' " +
|
||||
"FROM sys_menu " +
|
||||
"WHERE menu_name LIKE '%心理%' " +
|
||||
" OR menu_name LIKE '%量表%' " +
|
||||
" OR menu_name LIKE '%题目%' " +
|
||||
" OR menu_name LIKE '%因子%' " +
|
||||
" OR menu_name LIKE '%测评%' " +
|
||||
" OR menu_name LIKE '%报告%' " +
|
||||
" OR menu_name LIKE '%解释%' " +
|
||||
" OR menu_name LIKE '%档案%' " +
|
||||
" OR menu_name LIKE '%问卷%' " +
|
||||
" OR menu_name LIKE '%网站%' " +
|
||||
" OR menu_name LIKE '%栏目%' " +
|
||||
" OR menu_name LIKE '%评论%' " +
|
||||
" OR menu_name LIKE '%预警%' " +
|
||||
"ORDER BY parent_id, order_num, menu_id";
|
||||
|
||||
List<Map<String, Object>> menus = jdbcTemplate.queryForList(sql);
|
||||
return AjaxResult.success(menus);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package com.ddnai.web.controller.psychology;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ddnai.common.annotation.Log;
|
||||
import com.ddnai.common.core.controller.BaseController;
|
||||
import com.ddnai.common.core.domain.AjaxResult;
|
||||
import com.ddnai.common.core.page.TableDataInfo;
|
||||
import com.ddnai.common.enums.BusinessType;
|
||||
import com.ddnai.system.domain.psychology.PsyScalePermission;
|
||||
import com.ddnai.system.service.psychology.IPsyScalePermissionService;
|
||||
|
||||
/**
|
||||
* 量表权限 信息操作处理
|
||||
*
|
||||
* @author ddnai
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/psychology/permission")
|
||||
public class PsyScalePermissionController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IPsyScalePermissionService permissionService;
|
||||
|
||||
/**
|
||||
* 获取权限列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('psychology:permission:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PsyScalePermission permission)
|
||||
{
|
||||
startPage();
|
||||
List<PsyScalePermission> list = permissionService.selectPermissionList(permission);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据权限ID获取详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('psychology:permission:query')")
|
||||
@GetMapping(value = "/{permissionId}")
|
||||
public AjaxResult getInfo(@PathVariable Long permissionId)
|
||||
{
|
||||
return success(permissionService.selectPermissionById(permissionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID获取用户有权限访问的量表ID列表
|
||||
*/
|
||||
@GetMapping("/user/{userId}/scales")
|
||||
public AjaxResult getUserScaleIds(@PathVariable Long userId)
|
||||
{
|
||||
List<Long> scaleIds = permissionService.selectScaleIdsByUserId(userId);
|
||||
return success(scaleIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有权限访问指定量表
|
||||
*/
|
||||
@GetMapping("/check/{userId}/{scaleId}")
|
||||
public AjaxResult checkPermission(@PathVariable Long userId, @PathVariable Long scaleId)
|
||||
{
|
||||
boolean hasPermission = permissionService.checkUserScalePermission(userId, scaleId);
|
||||
return success(hasPermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增权限
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('psychology:permission:add')")
|
||||
@Log(title = "量表权限", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@Validated @RequestBody PsyScalePermission permission)
|
||||
{
|
||||
permission.setCreateBy(getUsername());
|
||||
return toAjax(permissionService.insertPermission(permission));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改权限
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('psychology:permission:edit')")
|
||||
@Log(title = "量表权限", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@Validated @RequestBody PsyScalePermission permission)
|
||||
{
|
||||
permission.setUpdateBy(getUsername());
|
||||
return toAjax(permissionService.updatePermission(permission));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除权限
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('psychology:permission:remove')")
|
||||
@Log(title = "量表权限", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{permissionIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] permissionIds)
|
||||
{
|
||||
return toAjax(permissionService.deletePermissionByIds(permissionIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量分配用户量表权限
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('psychology:permission:edit')")
|
||||
@Log(title = "量表权限", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/assign")
|
||||
public AjaxResult assignUserScales(@RequestBody java.util.Map<String, Object> params)
|
||||
{
|
||||
Long userId = Long.valueOf(params.get("userId").toString());
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Long> scaleIdList = (List<Long>) params.get("scaleIds");
|
||||
Long[] scaleIds = scaleIdList != null ? scaleIdList.toArray(new Long[0]) : new Long[0];
|
||||
return toAjax(permissionService.batchAssignUserScalePermission(userId, scaleIds));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.ddnai.admin.test;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONReader;
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.ddnai.common.core.domain.model.LoginUser;
|
||||
import com.ddnai.common.core.domain.entity.SysUser;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Fastjson序列化测试类
|
||||
*/
|
||||
@SpringBootTest
|
||||
@RunWith(SpringRunner.class)
|
||||
public class FastJsonTest {
|
||||
|
||||
@Test
|
||||
public void testLoginUserSerialization() {
|
||||
// 创建一个简单的LoginUser对象
|
||||
LoginUser loginUser = new LoginUser();
|
||||
loginUser.setUsername("admin");
|
||||
loginUser.setUserId(1L);
|
||||
loginUser.setBrowser("Chrome");
|
||||
loginUser.setOs("Windows");
|
||||
loginUser.setIpaddr("127.0.0.1");
|
||||
|
||||
// 设置权限
|
||||
Set<String> permissions = new HashSet<>();
|
||||
permissions.add("*:*:*");
|
||||
loginUser.setPermissions(permissions);
|
||||
|
||||
// 设置SysUser对象
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUserId(1L);
|
||||
sysUser.setUserName("admin");
|
||||
sysUser.setNickName("管理员");
|
||||
sysUser.setStatus("0");
|
||||
loginUser.setUser(sysUser);
|
||||
|
||||
System.out.println("=== 开始序列化测试 ===");
|
||||
|
||||
try {
|
||||
// 序列化
|
||||
String json = JSON.toJSONString(loginUser, JSONWriter.Feature.WriteClassName);
|
||||
System.out.println("序列化结果: " + json);
|
||||
|
||||
// 反序列化 - 直接指定类型
|
||||
LoginUser deserializedUser = JSON.parseObject(json, LoginUser.class, JSONReader.Feature.SupportAutoType);
|
||||
System.out.println("反序列化成功! 用户名: " + deserializedUser.getUsername());
|
||||
System.out.println("反序列化成功! 嵌套用户ID: " + deserializedUser.getUser().getUserId());
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("序列化/反序列化异常: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("=== 测试结束 ===");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package com.ddnai.admin.test;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ddnai.common.core.domain.entity.SysUser;
|
||||
import com.ddnai.common.core.domain.model.LoginUser;
|
||||
import org.junit.Test;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 测试LoginUser类的序列化和反序列化
|
||||
*/
|
||||
public class LoginUserSerializationTest {
|
||||
|
||||
@Test
|
||||
public void testSerializationAndDeserialization() {
|
||||
// 创建测试对象
|
||||
SysUser user = new SysUser();
|
||||
user.setUserId(1L);
|
||||
user.setUserName("admin");
|
||||
user.setNickName("管理员");
|
||||
|
||||
Set<String> permissions = new HashSet<>();
|
||||
permissions.add("*:*:*");
|
||||
|
||||
LoginUser loginUser = new LoginUser(user, permissions);
|
||||
loginUser.setUserId(1L);
|
||||
loginUser.setDeptId(103L);
|
||||
loginUser.setToken("test-token");
|
||||
loginUser.setLoginTime(System.currentTimeMillis());
|
||||
loginUser.setExpireTime(System.currentTimeMillis() + 3600000);
|
||||
loginUser.setIpaddr("127.0.0.1");
|
||||
loginUser.setLoginLocation("内网IP");
|
||||
loginUser.setBrowser("Chrome");
|
||||
loginUser.setOs("Windows 10");
|
||||
|
||||
// 序列化
|
||||
String jsonString = JSON.toJSONString(loginUser, JSONWriter.Feature.WriteClassName);
|
||||
System.out.println("序列化结果:");
|
||||
System.out.println(jsonString);
|
||||
|
||||
// 反序列化
|
||||
try {
|
||||
LoginUser deserializedUser = JSON.parseObject(jsonString, LoginUser.class);
|
||||
System.out.println("\n反序列化结果:");
|
||||
System.out.println("用户名: " + deserializedUser.getUsername());
|
||||
System.out.println("用户ID: " + deserializedUser.getUserId());
|
||||
System.out.println("部门ID: " + deserializedUser.getDeptId());
|
||||
System.out.println("Token: " + deserializedUser.getToken());
|
||||
System.out.println("\n反序列化成功!");
|
||||
} catch (Exception e) {
|
||||
System.err.println("\n反序列化失败:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
package com.ddnai.admin.tool;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单去重工具
|
||||
* 用于检测和清理数据库中重复的菜单
|
||||
* 可以直接运行此类来执行菜单去重
|
||||
*/
|
||||
public class MenuCleanupTool {
|
||||
|
||||
// 数据库连接信息
|
||||
private static final String URL = "jdbc:mysql://localhost:3306/ry_news?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
|
||||
private static final String USERNAME = "root";
|
||||
private static final String PASSWORD = "password"; // 请根据实际情况修改密码
|
||||
|
||||
public static void main(String[] args) {
|
||||
Connection conn = null;
|
||||
|
||||
try {
|
||||
// 加载数据库驱动
|
||||
Class.forName("com.mysql.cj.jdbc.Driver");
|
||||
|
||||
// 建立数据库连接
|
||||
System.out.println("正在连接数据库...");
|
||||
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
|
||||
System.out.println("数据库连接成功!");
|
||||
|
||||
// 1. 先检查重复菜单
|
||||
System.out.println("\n===============================================");
|
||||
System.out.println("开始检查重复菜单...");
|
||||
List<DuplicateMenuInfo> duplicates = checkDuplicateMenus(conn);
|
||||
|
||||
if (duplicates.isEmpty()) {
|
||||
System.out.println("未发现重复菜单!");
|
||||
} else {
|
||||
System.out.println("发现 " + duplicates.size() + " 组重复菜单:");
|
||||
for (DuplicateMenuInfo info : duplicates) {
|
||||
System.out.println("- 菜单名称: " + info.getMenuName() + ", 重复数量: " + info.getCount() + ", 菜单ID: " + info.getMenuIds());
|
||||
}
|
||||
|
||||
// 2. 执行去重操作
|
||||
System.out.println("\n===============================================");
|
||||
System.out.println("开始执行菜单去重...");
|
||||
int totalDeleted = cleanupDuplicateMenus(conn);
|
||||
System.out.println("\n菜单去重完成!共删除 " + totalDeleted + " 个重复菜单。");
|
||||
|
||||
// 3. 验证去重结果
|
||||
System.out.println("\n===============================================");
|
||||
System.out.println("验证去重结果...");
|
||||
List<DuplicateMenuInfo> afterCleanup = checkDuplicateMenus(conn);
|
||||
if (afterCleanup.isEmpty()) {
|
||||
System.out.println("验证成功!所有重复菜单已被清理。");
|
||||
} else {
|
||||
System.out.println("仍有 " + afterCleanup.size() + " 组重复菜单未被清理,请检查。");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("执行菜单去重时出错:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
// 关闭数据库连接
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.close();
|
||||
System.out.println("\n数据库连接已关闭。");
|
||||
} catch (SQLException e) {
|
||||
System.err.println("关闭数据库连接时出错:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查重复菜单
|
||||
*/
|
||||
private static List<DuplicateMenuInfo> checkDuplicateMenus(Connection conn) throws SQLException {
|
||||
List<DuplicateMenuInfo> duplicates = new ArrayList<>();
|
||||
|
||||
String sql = "SELECT " +
|
||||
" menu_name, " +
|
||||
" COUNT(*) AS count, " +
|
||||
" GROUP_CONCAT(menu_id ORDER BY menu_id SEPARATOR ', ') AS menu_ids " +
|
||||
"FROM sys_menu " +
|
||||
"WHERE menu_name LIKE '%心理%' " +
|
||||
" OR menu_name LIKE '%量表%' " +
|
||||
" OR menu_name LIKE '%题目%' " +
|
||||
" OR menu_name LIKE '%因子%' " +
|
||||
" OR menu_name LIKE '%测评%' " +
|
||||
" OR menu_name LIKE '%报告%' " +
|
||||
" OR menu_name LIKE '%解释%' " +
|
||||
" OR menu_name LIKE '%档案%' " +
|
||||
" OR menu_name LIKE '%问卷%' " +
|
||||
" OR menu_name LIKE '%网站%' " +
|
||||
" OR menu_name LIKE '%栏目%' " +
|
||||
" OR menu_name LIKE '%评论%' " +
|
||||
" OR menu_name LIKE '%预警%' " +
|
||||
"GROUP BY menu_name, path, component, parent_id " +
|
||||
"HAVING COUNT(*) > 1 " +
|
||||
"ORDER BY COUNT(*) DESC, menu_name";
|
||||
|
||||
try (PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
ResultSet rs = stmt.executeQuery()) {
|
||||
|
||||
while (rs.next()) {
|
||||
DuplicateMenuInfo info = new DuplicateMenuInfo();
|
||||
info.setMenuName(rs.getString("menu_name"));
|
||||
info.setCount(rs.getInt("count"));
|
||||
info.setMenuIds(rs.getString("menu_ids"));
|
||||
duplicates.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
return duplicates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行菜单去重
|
||||
*/
|
||||
private static int cleanupDuplicateMenus(Connection conn) throws SQLException {
|
||||
int totalDeleted = 0;
|
||||
|
||||
// 1. 删除"心理测评管理"目录的重复项(保留第一个)
|
||||
String sql1 = "DELETE t1 FROM sys_menu t1 " +
|
||||
"INNER JOIN sys_menu t2 " +
|
||||
"WHERE t1.menu_name = '心理测评管理' " +
|
||||
" AND t1.parent_id = 0 " +
|
||||
" AND t2.menu_name = '心理测评管理' " +
|
||||
" AND t2.parent_id = 0 " +
|
||||
" AND t1.menu_id > t2.menu_id";
|
||||
|
||||
try (PreparedStatement stmt = conn.prepareStatement(sql1)) {
|
||||
int deleted1 = stmt.executeUpdate();
|
||||
totalDeleted += deleted1;
|
||||
System.out.println("删除重复的'心理测评管理'目录: " + deleted1 + " 个");
|
||||
}
|
||||
|
||||
// 2. 删除"心理网站管理"目录的重复项
|
||||
String sql2 = "DELETE t1 FROM sys_menu t1 " +
|
||||
"INNER JOIN sys_menu t2 " +
|
||||
"WHERE t1.menu_name = '心理网站管理' " +
|
||||
" AND t1.parent_id = 0 " +
|
||||
" AND t2.menu_name = '心理网站管理' " +
|
||||
" AND t2.parent_id = 0 " +
|
||||
" AND t1.menu_id > t2.menu_id";
|
||||
|
||||
try (PreparedStatement stmt = conn.prepareStatement(sql2)) {
|
||||
int deleted2 = stmt.executeUpdate();
|
||||
totalDeleted += deleted2;
|
||||
System.out.println("删除重复的'心理网站管理'目录: " + deleted2 + " 个");
|
||||
}
|
||||
|
||||
// 3. 删除其他重复菜单(基于path和component)
|
||||
String sql3 = "DELETE t1 FROM sys_menu t1 " +
|
||||
"INNER JOIN sys_menu t2 " +
|
||||
"WHERE t1.path = t2.path " +
|
||||
" AND t1.component = t2.component " +
|
||||
" AND t1.menu_name = t2.menu_name " +
|
||||
" AND t1.menu_id > t2.menu_id " +
|
||||
" AND (t1.menu_name LIKE '%心理%' " +
|
||||
" OR t1.menu_name LIKE '%量表%' " +
|
||||
" OR t1.menu_name LIKE '%题目%' " +
|
||||
" OR t1.menu_name LIKE '%因子%' " +
|
||||
" OR t1.menu_name LIKE '%测评%' " +
|
||||
" OR t1.menu_name LIKE '%网站%' " +
|
||||
" OR t1.menu_name LIKE '%栏目%' " +
|
||||
" OR t1.menu_name LIKE '%评论%' " +
|
||||
" OR t1.menu_name LIKE '%预警%' " +
|
||||
" OR t1.menu_name LIKE '%问卷%' " +
|
||||
" OR t1.menu_name LIKE '%档案%')";
|
||||
|
||||
try (PreparedStatement stmt = conn.prepareStatement(sql3)) {
|
||||
int deleted3 = stmt.executeUpdate();
|
||||
totalDeleted += deleted3;
|
||||
System.out.println("删除其他重复菜单: " + deleted3 + " 个");
|
||||
}
|
||||
|
||||
return totalDeleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重复菜单信息类
|
||||
*/
|
||||
static class DuplicateMenuInfo {
|
||||
private String menuName;
|
||||
private int count;
|
||||
private String menuIds;
|
||||
|
||||
public String getMenuName() {
|
||||
return menuName;
|
||||
}
|
||||
|
||||
public void setMenuName(String menuName) {
|
||||
this.menuName = menuName;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public String getMenuIds() {
|
||||
return menuIds;
|
||||
}
|
||||
|
||||
public void setMenuIds(String menuIds) {
|
||||
this.menuIds = menuIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
package com.ddnai.system.domain.psychology;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ddnai.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 量表权限表 psy_scale_permission
|
||||
*
|
||||
* @author ddnai
|
||||
*/
|
||||
public class PsyScalePermission extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 权限ID */
|
||||
private Long permissionId;
|
||||
|
||||
/** 量表ID */
|
||||
private Long scaleId;
|
||||
|
||||
/** 部门ID(关联sys_dept,为空表示所有部门) */
|
||||
private Long deptId;
|
||||
|
||||
/** 角色ID(关联sys_role,为空表示所有角色) */
|
||||
private Long roleId;
|
||||
|
||||
/** 用户ID(关联sys_user,为空表示所有用户) */
|
||||
private Long userId;
|
||||
|
||||
/** 班级名称(特殊字段,用于罪犯管理) */
|
||||
private String className;
|
||||
|
||||
/** 开始时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private java.util.Date startTime;
|
||||
|
||||
/** 结束时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private java.util.Date endTime;
|
||||
|
||||
/** 状态(0有效 1无效) */
|
||||
private String status;
|
||||
|
||||
/** 量表名称(关联查询字段,不存储在表中) */
|
||||
private String scaleName;
|
||||
|
||||
/** 部门名称(关联查询字段,不存储在表中) */
|
||||
private String deptName;
|
||||
|
||||
/** 角色名称(关联查询字段,不存储在表中) */
|
||||
private String roleName;
|
||||
|
||||
/** 用户名称(关联查询字段,不存储在表中) */
|
||||
private String userName;
|
||||
|
||||
public Long getPermissionId()
|
||||
{
|
||||
return permissionId;
|
||||
}
|
||||
|
||||
public void setPermissionId(Long permissionId)
|
||||
{
|
||||
this.permissionId = permissionId;
|
||||
}
|
||||
|
||||
public Long getScaleId()
|
||||
{
|
||||
return scaleId;
|
||||
}
|
||||
|
||||
public void setScaleId(Long scaleId)
|
||||
{
|
||||
this.scaleId = scaleId;
|
||||
}
|
||||
|
||||
public Long getDeptId()
|
||||
{
|
||||
return deptId;
|
||||
}
|
||||
|
||||
public void setDeptId(Long deptId)
|
||||
{
|
||||
this.deptId = deptId;
|
||||
}
|
||||
|
||||
public Long getRoleId()
|
||||
{
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId)
|
||||
{
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public Long getUserId()
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getClassName()
|
||||
{
|
||||
return className;
|
||||
}
|
||||
|
||||
public void setClassName(String className)
|
||||
{
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public java.util.Date getStartTime()
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(java.util.Date startTime)
|
||||
{
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public java.util.Date getEndTime()
|
||||
{
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setEndTime(java.util.Date endTime)
|
||||
{
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getScaleName()
|
||||
{
|
||||
return scaleName;
|
||||
}
|
||||
|
||||
public void setScaleName(String scaleName)
|
||||
{
|
||||
this.scaleName = scaleName;
|
||||
}
|
||||
|
||||
public String getDeptName()
|
||||
{
|
||||
return deptName;
|
||||
}
|
||||
|
||||
public void setDeptName(String deptName)
|
||||
{
|
||||
this.deptName = deptName;
|
||||
}
|
||||
|
||||
public String getRoleName()
|
||||
{
|
||||
return roleName;
|
||||
}
|
||||
|
||||
public void setRoleName(String roleName)
|
||||
{
|
||||
this.roleName = roleName;
|
||||
}
|
||||
|
||||
public String getUserName()
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName)
|
||||
{
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("permissionId", getPermissionId())
|
||||
.append("scaleId", getScaleId())
|
||||
.append("deptId", getDeptId())
|
||||
.append("roleId", getRoleId())
|
||||
.append("userId", getUserId())
|
||||
.append("className", getClassName())
|
||||
.append("startTime", getStartTime())
|
||||
.append("endTime", getEndTime())
|
||||
.append("status", getStatus())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package com.ddnai.system.mapper.psychology;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ddnai.system.domain.psychology.PsyScalePermission;
|
||||
|
||||
/**
|
||||
* 量表权限表 数据层
|
||||
*
|
||||
* @author ddnai
|
||||
*/
|
||||
public interface PsyScalePermissionMapper
|
||||
{
|
||||
/**
|
||||
* 查询量表权限信息
|
||||
*
|
||||
* @param permissionId 权限ID
|
||||
* @return 权限信息
|
||||
*/
|
||||
public PsyScalePermission selectPermissionById(Long permissionId);
|
||||
|
||||
/**
|
||||
* 查询量表权限列表
|
||||
*
|
||||
* @param permission 权限信息
|
||||
* @return 权限集合
|
||||
*/
|
||||
public List<PsyScalePermission> selectPermissionList(PsyScalePermission permission);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询用户有权限访问的量表ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 量表ID列表
|
||||
*/
|
||||
public List<Long> selectScaleIdsByUserId(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 根据用户ID和量表ID检查用户是否有权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scaleId 量表ID
|
||||
* @return 权限数量(>0表示有权限)
|
||||
*/
|
||||
public int checkUserScalePermission(@Param("userId") Long userId, @Param("scaleId") Long scaleId);
|
||||
|
||||
/**
|
||||
* 新增量表权限
|
||||
*
|
||||
* @param permission 权限信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertPermission(PsyScalePermission permission);
|
||||
|
||||
/**
|
||||
* 修改量表权限
|
||||
*
|
||||
* @param permission 权限信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int updatePermission(PsyScalePermission permission);
|
||||
|
||||
/**
|
||||
* 删除量表权限
|
||||
*
|
||||
* @param permissionId 权限ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePermissionById(Long permissionId);
|
||||
|
||||
/**
|
||||
* 批量删除量表权限
|
||||
*
|
||||
* @param permissionIds 需要删除的权限ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePermissionByIds(Long[] permissionIds);
|
||||
|
||||
/**
|
||||
* 根据用户ID删除该用户的所有权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePermissionByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据量表ID删除该量表的所有权限
|
||||
*
|
||||
* @param scaleId 量表ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePermissionByScaleId(Long scaleId);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package com.ddnai.system.service.impl.psychology;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.ddnai.common.utils.SecurityUtils;
|
||||
import com.ddnai.system.domain.psychology.PsyScalePermission;
|
||||
import com.ddnai.system.mapper.psychology.PsyScalePermissionMapper;
|
||||
import com.ddnai.system.service.psychology.IPsyScalePermissionService;
|
||||
|
||||
/**
|
||||
* 量表权限 服务层实现
|
||||
*
|
||||
* @author ddnai
|
||||
*/
|
||||
@Service
|
||||
public class PsyScalePermissionServiceImpl implements IPsyScalePermissionService
|
||||
{
|
||||
@Autowired
|
||||
private PsyScalePermissionMapper permissionMapper;
|
||||
|
||||
/**
|
||||
* 查询量表权限信息
|
||||
*
|
||||
* @param permissionId 权限ID
|
||||
* @return 权限信息
|
||||
*/
|
||||
@Override
|
||||
public PsyScalePermission selectPermissionById(Long permissionId)
|
||||
{
|
||||
return permissionMapper.selectPermissionById(permissionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询量表权限列表
|
||||
*
|
||||
* @param permission 权限信息
|
||||
* @return 权限集合
|
||||
*/
|
||||
@Override
|
||||
public List<PsyScalePermission> selectPermissionList(PsyScalePermission permission)
|
||||
{
|
||||
return permissionMapper.selectPermissionList(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID查询用户有权限访问的量表ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 量表ID列表
|
||||
*/
|
||||
@Override
|
||||
public List<Long> selectScaleIdsByUserId(Long userId)
|
||||
{
|
||||
return permissionMapper.selectScaleIdsByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有权限访问指定量表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scaleId 量表ID
|
||||
* @return 是否有权限
|
||||
*/
|
||||
@Override
|
||||
public boolean checkUserScalePermission(Long userId, Long scaleId)
|
||||
{
|
||||
// 管理员拥有所有权限
|
||||
if (userId != null && userId == 1L)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
int count = permissionMapper.checkUserScalePermission(userId, scaleId);
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增量表权限
|
||||
*
|
||||
* @param permission 权限信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertPermission(PsyScalePermission permission)
|
||||
{
|
||||
if (permission.getStatus() == null)
|
||||
{
|
||||
permission.setStatus("0");
|
||||
}
|
||||
return permissionMapper.insertPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改量表权限
|
||||
*
|
||||
* @param permission 权限信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updatePermission(PsyScalePermission permission)
|
||||
{
|
||||
return permissionMapper.updatePermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除量表权限
|
||||
*
|
||||
* @param permissionIds 需要删除的权限ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deletePermissionByIds(Long[] permissionIds)
|
||||
{
|
||||
return permissionMapper.deletePermissionByIds(permissionIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID删除该用户的所有权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deletePermissionByUserId(Long userId)
|
||||
{
|
||||
return permissionMapper.deletePermissionByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据量表ID删除该量表的所有权限
|
||||
*
|
||||
* @param scaleId 量表ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deletePermissionByScaleId(Long scaleId)
|
||||
{
|
||||
return permissionMapper.deletePermissionByScaleId(scaleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量分配用户量表权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scaleIds 量表ID数组
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int batchAssignUserScalePermission(Long userId, Long[] scaleIds)
|
||||
{
|
||||
// 先删除该用户的所有现有权限
|
||||
permissionMapper.deletePermissionByUserId(userId);
|
||||
|
||||
// 批量插入新权限
|
||||
int count = 0;
|
||||
if (scaleIds != null && scaleIds.length > 0)
|
||||
{
|
||||
String username = SecurityUtils.getUsername();
|
||||
for (Long scaleId : scaleIds)
|
||||
{
|
||||
PsyScalePermission permission = new PsyScalePermission();
|
||||
permission.setUserId(userId);
|
||||
permission.setScaleId(scaleId);
|
||||
permission.setStatus("0");
|
||||
permission.setCreateBy(username);
|
||||
count += permissionMapper.insertPermission(permission);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package com.ddnai.system.service.psychology;
|
||||
|
||||
import java.util.List;
|
||||
import com.ddnai.system.domain.psychology.PsyScalePermission;
|
||||
|
||||
/**
|
||||
* 量表权限 服务层
|
||||
*
|
||||
* @author ddnai
|
||||
*/
|
||||
public interface IPsyScalePermissionService
|
||||
{
|
||||
/**
|
||||
* 查询量表权限信息
|
||||
*
|
||||
* @param permissionId 权限ID
|
||||
* @return 权限信息
|
||||
*/
|
||||
public PsyScalePermission selectPermissionById(Long permissionId);
|
||||
|
||||
/**
|
||||
* 查询量表权限列表
|
||||
*
|
||||
* @param permission 权限信息
|
||||
* @return 权限集合
|
||||
*/
|
||||
public List<PsyScalePermission> selectPermissionList(PsyScalePermission permission);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询用户有权限访问的量表ID列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 量表ID列表
|
||||
*/
|
||||
public List<Long> selectScaleIdsByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 检查用户是否有权限访问指定量表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scaleId 量表ID
|
||||
* @return 是否有权限
|
||||
*/
|
||||
public boolean checkUserScalePermission(Long userId, Long scaleId);
|
||||
|
||||
/**
|
||||
* 新增量表权限
|
||||
*
|
||||
* @param permission 权限信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertPermission(PsyScalePermission permission);
|
||||
|
||||
/**
|
||||
* 修改量表权限
|
||||
*
|
||||
* @param permission 权限信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int updatePermission(PsyScalePermission permission);
|
||||
|
||||
/**
|
||||
* 批量删除量表权限
|
||||
*
|
||||
* @param permissionIds 需要删除的权限ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePermissionByIds(Long[] permissionIds);
|
||||
|
||||
/**
|
||||
* 根据用户ID删除该用户的所有权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePermissionByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据量表ID删除该量表的所有权限
|
||||
*
|
||||
* @param scaleId 量表ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePermissionByScaleId(Long scaleId);
|
||||
|
||||
/**
|
||||
* 批量分配用户量表权限
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param scaleIds 量表ID数组
|
||||
* @return 结果
|
||||
*/
|
||||
public int batchAssignUserScalePermission(Long userId, Long[] scaleIds);
|
||||
}
|
||||
|
||||
|
|
@ -29,40 +29,46 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</resultMap>
|
||||
|
||||
<sql id="selectScaleVo">
|
||||
select scale_id, scale_code, scale_name, scale_en_name, scale_type, scale_version,
|
||||
scale_intro, scale_description, item_count, estimated_time, target_population,
|
||||
author, source, reference, status, sort_order, create_by, create_time,
|
||||
update_by, update_time, remark
|
||||
from psy_scale
|
||||
select s.scale_id, s.scale_code, s.scale_name, s.scale_en_name, s.scale_type, s.scale_version,
|
||||
s.scale_intro, s.scale_description,
|
||||
COALESCE(COUNT(i.item_id), 0) as item_count,
|
||||
s.estimated_time, s.target_population,
|
||||
s.author, s.source, s.reference, s.status, s.sort_order, s.create_by, s.create_time,
|
||||
s.update_by, s.update_time, s.remark
|
||||
from psy_scale s
|
||||
left join psy_scale_item i on s.scale_id = i.scale_id
|
||||
</sql>
|
||||
|
||||
<select id="selectScaleById" parameterType="Long" resultMap="PsyScaleResult">
|
||||
<include refid="selectScaleVo"/>
|
||||
where scale_id = #{scaleId}
|
||||
where s.scale_id = #{scaleId}
|
||||
group by s.scale_id
|
||||
</select>
|
||||
|
||||
<select id="selectScaleByCode" parameterType="String" resultMap="PsyScaleResult">
|
||||
<include refid="selectScaleVo"/>
|
||||
where scale_code = #{scaleCode}
|
||||
where s.scale_code = #{scaleCode}
|
||||
group by s.scale_id
|
||||
</select>
|
||||
|
||||
<select id="selectScaleList" parameterType="com.ddnai.system.domain.psychology.PsyScale" resultMap="PsyScaleResult">
|
||||
<include refid="selectScaleVo"/>
|
||||
<where>
|
||||
<if test="scaleName != null and scaleName != ''">
|
||||
AND scale_name like concat('%', #{scaleName}, '%')
|
||||
AND s.scale_name like concat('%', #{scaleName}, '%')
|
||||
</if>
|
||||
<if test="scaleCode != null and scaleCode != ''">
|
||||
AND scale_code like concat('%', #{scaleCode}, '%')
|
||||
AND s.scale_code like concat('%', #{scaleCode}, '%')
|
||||
</if>
|
||||
<if test="scaleType != null and scaleType != ''">
|
||||
AND scale_type = #{scaleType}
|
||||
AND s.scale_type = #{scaleType}
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
AND status = #{status}
|
||||
AND s.status = #{status}
|
||||
</if>
|
||||
</where>
|
||||
order by sort_order, create_time desc
|
||||
group by s.scale_id
|
||||
order by s.sort_order, s.create_time desc
|
||||
</select>
|
||||
|
||||
<insert id="insertScale" parameterType="com.ddnai.system.domain.psychology.PsyScale">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ddnai.system.mapper.psychology.PsyScalePermissionMapper">
|
||||
|
||||
<resultMap type="com.ddnai.system.domain.psychology.PsyScalePermission" id="PsyScalePermissionResult">
|
||||
<result property="permissionId" column="permission_id" />
|
||||
<result property="scaleId" column="scale_id" />
|
||||
<result property="deptId" column="dept_id" />
|
||||
<result property="roleId" column="role_id" />
|
||||
<result property="userId" column="user_id" />
|
||||
<result property="className" column="class_name" />
|
||||
<result property="startTime" column="start_time" />
|
||||
<result property="endTime" column="end_time" />
|
||||
<result property="status" column="status" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="scaleName" column="scale_name" />
|
||||
<result property="deptName" column="dept_name" />
|
||||
<result property="roleName" column="role_name" />
|
||||
<result property="userName" column="user_name" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectPermissionVo">
|
||||
select p.permission_id, p.scale_id, p.dept_id, p.role_id, p.user_id, p.class_name,
|
||||
p.start_time, p.end_time, p.status, p.create_by, p.create_time, p.update_by, p.update_time, p.remark,
|
||||
s.scale_name, d.dept_name, r.role_name, u.user_name
|
||||
from psy_scale_permission p
|
||||
left join psy_scale s on p.scale_id = s.scale_id
|
||||
left join sys_dept d on p.dept_id = d.dept_id
|
||||
left join sys_role r on p.role_id = r.role_id
|
||||
left join sys_user u on p.user_id = u.user_id
|
||||
</sql>
|
||||
|
||||
<select id="selectPermissionById" parameterType="Long" resultMap="PsyScalePermissionResult">
|
||||
<include refid="selectPermissionVo"/>
|
||||
where p.permission_id = #{permissionId}
|
||||
</select>
|
||||
|
||||
<select id="selectPermissionList" parameterType="com.ddnai.system.domain.psychology.PsyScalePermission" resultMap="PsyScalePermissionResult">
|
||||
<include refid="selectPermissionVo"/>
|
||||
<where>
|
||||
<if test="scaleId != null">
|
||||
AND p.scale_id = #{scaleId}
|
||||
</if>
|
||||
<if test="deptId != null">
|
||||
AND p.dept_id = #{deptId}
|
||||
</if>
|
||||
<if test="roleId != null">
|
||||
AND p.role_id = #{roleId}
|
||||
</if>
|
||||
<if test="userId != null">
|
||||
AND p.user_id = #{userId}
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
AND p.status = #{status}
|
||||
</if>
|
||||
</where>
|
||||
order by p.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectScaleIdsByUserId" parameterType="Long" resultType="Long">
|
||||
select distinct p.scale_id
|
||||
from psy_scale_permission p
|
||||
where p.status = '0'
|
||||
and (
|
||||
-- 用户直接权限
|
||||
(p.user_id = #{userId})
|
||||
-- 或者角色权限(需要关联用户角色表)
|
||||
or (p.role_id is not null and p.role_id in (
|
||||
select role_id from sys_user_role where user_id = #{userId}
|
||||
))
|
||||
-- 或者部门权限(需要关联用户部门)
|
||||
or (p.dept_id is not null and p.dept_id in (
|
||||
select dept_id from sys_user where user_id = #{userId}
|
||||
))
|
||||
-- 或者所有用户权限(user_id, role_id, dept_id 都为空)
|
||||
or (p.user_id is null and p.role_id is null and p.dept_id is null)
|
||||
)
|
||||
-- 检查时间范围
|
||||
and (p.start_time is null or p.start_time <= now())
|
||||
and (p.end_time is null or p.end_time >= now())
|
||||
</select>
|
||||
|
||||
<select id="checkUserScalePermission" resultType="int">
|
||||
select count(1)
|
||||
from psy_scale_permission p
|
||||
where p.scale_id = #{scaleId}
|
||||
and p.status = '0'
|
||||
and (
|
||||
-- 用户直接权限
|
||||
(p.user_id = #{userId})
|
||||
-- 或者角色权限
|
||||
or (p.role_id is not null and p.role_id in (
|
||||
select role_id from sys_user_role where user_id = #{userId}
|
||||
))
|
||||
-- 或者部门权限
|
||||
or (p.dept_id is not null and p.dept_id in (
|
||||
select dept_id from sys_user where user_id = #{userId}
|
||||
))
|
||||
-- 或者所有用户权限
|
||||
or (p.user_id is null and p.role_id is null and p.dept_id is null)
|
||||
)
|
||||
-- 检查时间范围
|
||||
and (p.start_time is null or p.start_time <= now())
|
||||
and (p.end_time is null or p.end_time >= now())
|
||||
</select>
|
||||
|
||||
<insert id="insertPermission" parameterType="com.ddnai.system.domain.psychology.PsyScalePermission" useGeneratedKeys="true" keyProperty="permissionId">
|
||||
insert into psy_scale_permission (
|
||||
<if test="scaleId != null">scale_id, </if>
|
||||
<if test="deptId != null">dept_id, </if>
|
||||
<if test="roleId != null">role_id, </if>
|
||||
<if test="userId != null">user_id, </if>
|
||||
<if test="className != null and className != ''">class_name, </if>
|
||||
<if test="startTime != null">start_time, </if>
|
||||
<if test="endTime != null">end_time, </if>
|
||||
<if test="status != null and status != ''">status, </if>
|
||||
<if test="remark != null">remark,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time
|
||||
)values(
|
||||
<if test="scaleId != null">#{scaleId}, </if>
|
||||
<if test="deptId != null">#{deptId}, </if>
|
||||
<if test="roleId != null">#{roleId}, </if>
|
||||
<if test="userId != null">#{userId}, </if>
|
||||
<if test="className != null and className != ''">#{className}, </if>
|
||||
<if test="startTime != null">#{startTime}, </if>
|
||||
<if test="endTime != null">#{endTime}, </if>
|
||||
<if test="status != null and status != ''">#{status}, </if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updatePermission" parameterType="com.ddnai.system.domain.psychology.PsyScalePermission">
|
||||
update psy_scale_permission
|
||||
<set>
|
||||
<if test="scaleId != null">scale_id = #{scaleId}, </if>
|
||||
<if test="deptId != null">dept_id = #{deptId}, </if>
|
||||
<if test="roleId != null">role_id = #{roleId}, </if>
|
||||
<if test="userId != null">user_id = #{userId}, </if>
|
||||
<if test="className != null and className != ''">class_name = #{className}, </if>
|
||||
<if test="startTime != null">start_time = #{startTime}, </if>
|
||||
<if test="endTime != null">end_time = #{endTime}, </if>
|
||||
<if test="status != null and status != ''">status = #{status}, </if>
|
||||
<if test="remark != null">remark = #{remark}, </if>
|
||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where permission_id = #{permissionId}
|
||||
</update>
|
||||
|
||||
<delete id="deletePermissionById" parameterType="Long">
|
||||
delete from psy_scale_permission where permission_id = #{permissionId}
|
||||
</delete>
|
||||
|
||||
<delete id="deletePermissionByIds" parameterType="String">
|
||||
delete from psy_scale_permission where permission_id in
|
||||
<foreach item="permissionId" collection="array" open="(" separator="," close=")">
|
||||
#{permissionId}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<delete id="deletePermissionByUserId" parameterType="Long">
|
||||
delete from psy_scale_permission where user_id = #{userId}
|
||||
</delete>
|
||||
|
||||
<delete id="deletePermissionByScaleId" parameterType="Long">
|
||||
delete from psy_scale_permission where scale_id = #{scaleId}
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
|
||||
141
sql/cleanup_duplicate_menus_enhanced.sql
Normal file
141
sql/cleanup_duplicate_menus_enhanced.sql
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
-- ========================================
|
||||
-- 增强版清理重复菜单SQL脚本
|
||||
-- 用途:彻底删除数据库中重复的心理学相关菜单
|
||||
-- 注意:执行前请备份数据库!
|
||||
-- ========================================
|
||||
USE ry_news;
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- ========================================
|
||||
-- 第一步:删除重复的父菜单(目录)
|
||||
-- ========================================
|
||||
-- 删除重复的"心理测评管理"目录(保留menu_id最小的)
|
||||
DELETE t1 FROM sys_menu t1
|
||||
INNER JOIN sys_menu t2
|
||||
WHERE t1.menu_name = '心理测评管理'
|
||||
AND t1.parent_id = 0
|
||||
AND t2.menu_name = '心理测评管理'
|
||||
AND t2.parent_id = 0
|
||||
AND t1.menu_id > t2.menu_id;
|
||||
|
||||
-- 删除重复的"心理网站管理"目录
|
||||
DELETE t1 FROM sys_menu t1
|
||||
INNER JOIN sys_menu t2
|
||||
WHERE t1.menu_name = '心理网站管理'
|
||||
AND t1.parent_id = 0
|
||||
AND t2.menu_name = '心理网站管理'
|
||||
AND t2.parent_id = 0
|
||||
AND t1.menu_id > t2.menu_id;
|
||||
|
||||
-- ========================================
|
||||
-- 第二步:删除基于menu_name和parent_id的重复菜单
|
||||
-- ========================================
|
||||
-- 对于相同名称和父菜单的重复项,保留最小的menu_id
|
||||
DELETE t1 FROM sys_menu t1
|
||||
INNER JOIN sys_menu t2
|
||||
WHERE t1.menu_name = t2.menu_name
|
||||
AND t1.parent_id = t2.parent_id
|
||||
AND t1.menu_id > t2.menu_id
|
||||
AND (t1.menu_name LIKE '%心理%'
|
||||
OR t1.menu_name LIKE '%量表%'
|
||||
OR t1.menu_name LIKE '%题目%'
|
||||
OR t1.menu_name LIKE '%因子%'
|
||||
OR t1.menu_name LIKE '%测评%'
|
||||
OR t1.menu_name LIKE '%报告%'
|
||||
OR t1.menu_name LIKE '%解释%'
|
||||
OR t1.menu_name LIKE '%档案%'
|
||||
OR t1.menu_name LIKE '%问卷%'
|
||||
OR t1.menu_name LIKE '%网站%'
|
||||
OR t1.menu_name LIKE '%栏目%'
|
||||
OR t1.menu_name LIKE '%评论%'
|
||||
OR t1.menu_name LIKE '%预警%'
|
||||
OR t1.menu_name LIKE '%规则%');
|
||||
|
||||
-- ========================================
|
||||
-- 第三步:删除基于path和component的重复菜单
|
||||
-- ========================================
|
||||
-- 删除所有重复菜单(保留menu_id最小的)
|
||||
DELETE t1 FROM sys_menu t1
|
||||
INNER JOIN sys_menu t2
|
||||
WHERE t1.path = t2.path
|
||||
AND (t1.component = t2.component OR (t1.component IS NULL AND t2.component IS NULL))
|
||||
AND t1.menu_name = t2.menu_name
|
||||
AND t1.parent_id = t2.parent_id
|
||||
AND t1.menu_id > t2.menu_id
|
||||
AND (t1.menu_name LIKE '%心理%'
|
||||
OR t1.menu_name LIKE '%量表%'
|
||||
OR t1.menu_name LIKE '%题目%'
|
||||
OR t1.menu_name LIKE '%因子%'
|
||||
OR t1.menu_name LIKE '%测评%'
|
||||
OR t1.menu_name LIKE '%报告%'
|
||||
OR t1.menu_name LIKE '%解释%'
|
||||
OR t1.menu_name LIKE '%档案%'
|
||||
OR t1.menu_name LIKE '%问卷%'
|
||||
OR t1.menu_name LIKE '%网站%'
|
||||
OR t1.menu_name LIKE '%栏目%'
|
||||
OR t1.menu_name LIKE '%评论%'
|
||||
OR t1.menu_name LIKE '%预警%'
|
||||
OR t1.menu_name LIKE '%规则%');
|
||||
|
||||
-- ========================================
|
||||
-- 第四步:清理孤立的子菜单(父菜单已被删除)
|
||||
-- ========================================
|
||||
-- 删除那些父菜单ID不存在于sys_menu表中的子菜单
|
||||
DELETE FROM sys_menu
|
||||
WHERE parent_id > 0
|
||||
AND parent_id NOT IN (SELECT menu_id FROM (SELECT menu_id FROM sys_menu) AS temp)
|
||||
AND (menu_name LIKE '%心理%'
|
||||
OR menu_name LIKE '%量表%'
|
||||
OR menu_name LIKE '%题目%'
|
||||
OR menu_name LIKE '%因子%'
|
||||
OR menu_name LIKE '%测评%'
|
||||
OR menu_name LIKE '%报告%'
|
||||
OR menu_name LIKE '%解释%'
|
||||
OR menu_name LIKE '%档案%'
|
||||
OR menu_name LIKE '%问卷%'
|
||||
OR menu_name LIKE '%网站%'
|
||||
OR menu_name LIKE '%栏目%'
|
||||
OR menu_name LIKE '%评论%'
|
||||
OR menu_name LIKE '%预警%'
|
||||
OR menu_name LIKE '%规则%');
|
||||
|
||||
-- ========================================
|
||||
-- 第五步:清理角色菜单关联表中的孤立记录
|
||||
-- ========================================
|
||||
-- 删除指向已删除菜单的角色菜单关联
|
||||
DELETE FROM sys_role_menu
|
||||
WHERE menu_id NOT IN (SELECT menu_id FROM (SELECT menu_id FROM sys_menu) AS temp2);
|
||||
|
||||
-- ========================================
|
||||
-- 第六步:验证清理结果
|
||||
-- ========================================
|
||||
SELECT '清理完成!' AS result;
|
||||
|
||||
-- 检查是否还有重复菜单
|
||||
SELECT
|
||||
menu_name AS '菜单名称',
|
||||
path AS '路由路径',
|
||||
component AS '组件路径',
|
||||
parent_id AS '父菜单ID',
|
||||
COUNT(*) AS '剩余数量'
|
||||
FROM sys_menu
|
||||
WHERE menu_name LIKE '%心理%'
|
||||
OR menu_name LIKE '%量表%'
|
||||
OR menu_name LIKE '%题目%'
|
||||
OR menu_name LIKE '%因子%'
|
||||
OR menu_name LIKE '%测评%'
|
||||
OR menu_name LIKE '%报告%'
|
||||
OR menu_name LIKE '%解释%'
|
||||
OR menu_name LIKE '%档案%'
|
||||
OR menu_name LIKE '%问卷%'
|
||||
OR menu_name LIKE '%网站%'
|
||||
OR menu_name LIKE '%栏目%'
|
||||
OR menu_name LIKE '%评论%'
|
||||
OR menu_name LIKE '%预警%'
|
||||
OR menu_name LIKE '%规则%'
|
||||
GROUP BY menu_name, path, component, parent_id
|
||||
HAVING COUNT(*) > 1;
|
||||
|
||||
-- 如果没有输出,说明没有重复菜单了
|
||||
SELECT '如果上面的查询没有返回结果,说明所有重复菜单已清理完成!' AS message;
|
||||
|
||||
58
sql/enable_register_and_permission_menu.sql
Normal file
58
sql/enable_register_and_permission_menu.sql
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
-- ========================================
|
||||
-- 启用用户注册功能和添加量表权限管理菜单
|
||||
-- ========================================
|
||||
USE ry_news;
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- ========================================
|
||||
-- 1. 启用用户注册功能
|
||||
-- ========================================
|
||||
-- 检查并更新注册功能配置
|
||||
INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, remark)
|
||||
VALUES ('用户注册开关', 'sys.account.registerUser', 'true', 'Y', 'admin', NOW(), '是否开启用户注册功能(true开启 false关闭)')
|
||||
ON DUPLICATE KEY UPDATE config_value = 'true', update_by = 'admin', update_time = NOW();
|
||||
|
||||
-- ========================================
|
||||
-- 2. 量表权限管理菜单配置
|
||||
-- ========================================
|
||||
-- 量表权限管理主菜单
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '量表权限管理', menu_id, 11, 'permission', 'psychology/permission/index', 1, 0, 'C', '0', '0', 'psychology:permission:list', 'lock', 'admin', NOW(), '量表权限管理菜单'
|
||||
FROM sys_menu WHERE menu_name = '心理测评管理' AND parent_id = 0 LIMIT 1;
|
||||
|
||||
-- 量表权限管理按钮权限
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '权限查询', menu_id, 1, '', '', 1, 0, 'F', '0', '0', 'psychology:permission:query', '#', 'admin', NOW(), ''
|
||||
FROM sys_menu WHERE menu_name = '量表权限管理' LIMIT 1;
|
||||
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '权限新增', menu_id, 2, '', '', 1, 0, 'F', '0', '0', 'psychology:permission:add', '#', 'admin', NOW(), ''
|
||||
FROM sys_menu WHERE menu_name = '量表权限管理' LIMIT 1;
|
||||
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '权限修改', menu_id, 3, '', '', 1, 0, 'F', '0', '0', 'psychology:permission:edit', '#', 'admin', NOW(), ''
|
||||
FROM sys_menu WHERE menu_name = '量表权限管理' LIMIT 1;
|
||||
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '权限删除', menu_id, 4, '', '', 1, 0, 'F', '0', '0', 'psychology:permission:remove', '#', 'admin', NOW(), ''
|
||||
FROM sys_menu WHERE menu_name = '量表权限管理' LIMIT 1;
|
||||
|
||||
-- ========================================
|
||||
-- 3. 为管理员角色分配量表权限管理菜单权限
|
||||
-- ========================================
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`)
|
||||
SELECT 1, menu_id FROM sys_menu
|
||||
WHERE (menu_name = '量表权限管理'
|
||||
OR menu_name LIKE '%权限查询%'
|
||||
OR menu_name LIKE '%权限新增%'
|
||||
OR menu_name LIKE '%权限修改%'
|
||||
OR menu_name LIKE '%权限删除%')
|
||||
AND menu_name LIKE '%权限%';
|
||||
|
||||
-- ========================================
|
||||
-- 验证配置
|
||||
-- ========================================
|
||||
SELECT '用户注册功能和量表权限管理菜单配置完成!' AS result;
|
||||
SELECT config_value AS register_enabled FROM sys_config WHERE config_key = 'sys.account.registerUser';
|
||||
SELECT COUNT(*) AS permission_menu_count FROM sys_menu WHERE menu_name LIKE '%量表权限%' OR menu_name LIKE '%权限%';
|
||||
|
||||
38
sql/psy_scale_permission_menu.sql
Normal file
38
sql/psy_scale_permission_menu.sql
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
-- ========================================
|
||||
-- 量表权限管理菜单配置
|
||||
-- ========================================
|
||||
USE ry_news;
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- ========================================
|
||||
-- 量表权限管理
|
||||
-- ========================================
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '量表权限管理', menu_id, 11, 'permission', 'psychology/permission/index', 1, 0, 'C', '0', '0', 'psychology:permission:list', 'lock', 'admin', NOW(), '量表权限管理菜单'
|
||||
FROM sys_menu WHERE menu_name = '心理测评管理' AND parent_id = 0 LIMIT 1;
|
||||
|
||||
-- 量表权限管理按钮权限
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '权限查询', menu_id, 1, '', '', 1, 0, 'F', '0', '0', 'psychology:permission:query', '#', 'admin', NOW(), ''
|
||||
FROM sys_menu WHERE menu_name = '量表权限管理' LIMIT 1;
|
||||
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '权限新增', menu_id, 2, '', '', 1, 0, 'F', '0', '0', 'psychology:permission:add', '#', 'admin', NOW(), ''
|
||||
FROM sys_menu WHERE menu_name = '量表权限管理' LIMIT 1;
|
||||
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '权限修改', menu_id, 3, '', '', 1, 0, 'F', '0', '0', 'psychology:permission:edit', '#', 'admin', NOW(), ''
|
||||
FROM sys_menu WHERE menu_name = '量表权限管理' LIMIT 1;
|
||||
|
||||
INSERT IGNORE INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `remark`)
|
||||
SELECT '权限删除', menu_id, 4, '', '', 1, 0, 'F', '0', '0', 'psychology:permission:remove', '#', 'admin', NOW(), ''
|
||||
FROM sys_menu WHERE menu_name = '量表权限管理' LIMIT 1;
|
||||
|
||||
-- ========================================
|
||||
-- 为管理员角色分配权限
|
||||
-- ========================================
|
||||
INSERT IGNORE INTO `sys_role_menu` (`role_id`, `menu_id`)
|
||||
SELECT 1, menu_id FROM sys_menu WHERE menu_name = '量表权限管理' OR menu_name LIKE '%权限查询%' OR menu_name LIKE '%权限新增%' OR menu_name LIKE '%权限修改%' OR menu_name LIKE '%权限删除%';
|
||||
|
||||
SELECT '量表权限管理菜单配置完成!' AS result;
|
||||
|
||||
301
z_Project change/14-用户注册和量表权限管理功能开发.md
Normal file
301
z_Project change/14-用户注册和量表权限管理功能开发.md
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
# 14-用户注册和量表权限管理功能开发
|
||||
|
||||
## 功能概述
|
||||
|
||||
本功能模块实现了用户自主注册和量表权限管理功能,允许用户自行注册账号,管理员可以为用户分配量表访问权限,普通用户只能访问被分配权限的量表进行测评。
|
||||
|
||||
## 开发时间
|
||||
|
||||
2025-11-06
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 1. 用户注册功能
|
||||
- 用户可以通过注册页面自行注册账号
|
||||
- 注册功能基于RuoYi框架现有的注册功能
|
||||
- 注册成功后,用户可以使用注册的账号登录系统
|
||||
|
||||
### 2. 量表权限管理
|
||||
- 管理员可以管理用户对量表的访问权限
|
||||
- 支持按用户、角色、部门分配权限
|
||||
- 支持权限的有效期设置(开始时间、结束时间)
|
||||
- 支持权限状态管理(有效/无效)
|
||||
|
||||
### 3. 权限过滤
|
||||
- 普通用户在选择量表时,只能看到被分配权限的量表
|
||||
- 管理员可以看到所有量表
|
||||
- 如果用户没有分配任何权限,会提示联系管理员
|
||||
|
||||
## 数据库设计
|
||||
|
||||
### 量表权限表(psy_scale_permission)
|
||||
|
||||
该表已在 `sql/psychological_system_complete.sql` 中定义:
|
||||
|
||||
```sql
|
||||
CREATE TABLE `psy_scale_permission` (
|
||||
`permission_id` bigint NOT NULL AUTO_INCREMENT COMMENT '权限ID',
|
||||
`scale_id` bigint NOT NULL COMMENT '量表ID',
|
||||
`dept_id` bigint DEFAULT NULL COMMENT '部门ID(关联sys_dept,为空表示所有部门)',
|
||||
`role_id` bigint DEFAULT NULL COMMENT '角色ID(关联sys_role,为空表示所有角色)',
|
||||
`user_id` bigint DEFAULT NULL COMMENT '用户ID(关联sys_user,为空表示所有用户)',
|
||||
`class_name` varchar(100) DEFAULT NULL COMMENT '班级名称(特殊字段,用于罪犯管理)',
|
||||
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
|
||||
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
|
||||
`status` char(1) DEFAULT '0' COMMENT '状态(0有效 1无效)',
|
||||
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
PRIMARY KEY (`permission_id`),
|
||||
KEY `idx_scale_id` (`scale_id`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='量表权限表';
|
||||
```
|
||||
|
||||
## 后端开发
|
||||
|
||||
### 1. 实体类
|
||||
|
||||
**文件**: `ry-news-system/src/main/java/com/ddnai/system/domain/psychology/PsyScalePermission.java`
|
||||
|
||||
- 定义了量表权限的所有字段
|
||||
- 包含关联查询字段(scaleName, deptName, roleName, userName)
|
||||
|
||||
### 2. Mapper接口和XML
|
||||
|
||||
**文件**:
|
||||
- `ry-news-system/src/main/java/com/ddnai/system/mapper/psychology/PsyScalePermissionMapper.java`
|
||||
- `ry-news-system/src/main/resources/mapper/system/psychology/PsyScalePermissionMapper.xml`
|
||||
|
||||
**核心方法**:
|
||||
- `selectPermissionList`: 查询权限列表
|
||||
- `selectScaleIdsByUserId`: 根据用户ID查询用户有权限访问的量表ID列表
|
||||
- `checkUserScalePermission`: 检查用户是否有权限访问指定量表
|
||||
- `batchAssignUserScalePermission`: 批量分配用户量表权限
|
||||
|
||||
### 3. Service接口和实现
|
||||
|
||||
**文件**:
|
||||
- `ry-news-system/src/main/java/com/ddnai/system/service/psychology/IPsyScalePermissionService.java`
|
||||
- `ry-news-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyScalePermissionServiceImpl.java`
|
||||
|
||||
**核心功能**:
|
||||
- 权限查询和管理
|
||||
- 用户权限检查(管理员自动拥有所有权限)
|
||||
- 批量权限分配
|
||||
|
||||
### 4. Controller
|
||||
|
||||
**文件**: `ry-news-admin/src/main/java/com/ddnai/web/controller/psychology/PsyScalePermissionController.java`
|
||||
|
||||
**API接口**:
|
||||
- `GET /psychology/permission/list` - 获取权限列表
|
||||
- `GET /psychology/permission/{permissionId}` - 获取权限详情
|
||||
- `GET /psychology/permission/user/{userId}/scales` - 获取用户有权限的量表ID列表
|
||||
- `GET /psychology/permission/check/{userId}/{scaleId}` - 检查用户是否有权限
|
||||
- `POST /psychology/permission` - 新增权限
|
||||
- `PUT /psychology/permission` - 修改权限
|
||||
- `DELETE /psychology/permission/{permissionIds}` - 删除权限
|
||||
- `POST /psychology/permission/assign` - 批量分配用户量表权限
|
||||
|
||||
## 前端开发
|
||||
|
||||
### 1. API文件
|
||||
|
||||
**文件**: `ruoyi-ui/src/api/psychology/permission.js`
|
||||
|
||||
提供了所有权限相关的API调用方法。
|
||||
|
||||
### 2. 量表权限管理页面
|
||||
|
||||
**文件**: `ruoyi-ui/src/views/psychology/permission/index.vue`
|
||||
|
||||
**功能**:
|
||||
- 权限列表查询(支持按量表、用户、状态筛选)
|
||||
- 新增权限
|
||||
- 修改权限
|
||||
- 删除权限
|
||||
- 显示权限详细信息(量表名称、用户名称、部门、角色、时间范围等)
|
||||
|
||||
### 3. 用户量表权限分配页面
|
||||
|
||||
**文件**: `ruoyi-ui/src/views/psychology/permission/user.vue`
|
||||
|
||||
**功能**:
|
||||
- 使用Transfer组件进行量表权限分配
|
||||
- 显示用户名称
|
||||
- 支持搜索和筛选量表
|
||||
- 批量保存权限
|
||||
|
||||
### 4. 用户管理页面集成
|
||||
|
||||
**文件**: `ruoyi-ui/src/views/system/user/index.vue`
|
||||
|
||||
**修改**:
|
||||
- 在用户操作下拉菜单中添加"分配量表权限"选项
|
||||
- 点击后跳转到用户量表权限分配页面
|
||||
|
||||
### 5. 量表选择页面权限过滤
|
||||
|
||||
**文件**: `ruoyi-ui/src/views/psychology/assessment/start.vue`
|
||||
|
||||
**修改**:
|
||||
- 管理员(userId === 1)显示所有量表
|
||||
- 普通用户只显示有权限的量表
|
||||
- 如果用户没有权限,显示提示信息
|
||||
|
||||
### 6. 路由配置
|
||||
|
||||
**文件**: `ruoyi-ui/src/router/index.js`
|
||||
|
||||
**新增路由**:
|
||||
- `/psychology/permission` - 量表权限管理
|
||||
- `/psychology/permission/user/:userId` - 用户量表权限分配
|
||||
|
||||
## 菜单配置
|
||||
|
||||
在 `sql/psychological_system_complete.sql` 中添加了量表权限管理的菜单配置:
|
||||
|
||||
- **菜单名称**: 量表权限管理
|
||||
- **路径**: `permission`
|
||||
- **组件**: `psychology/permission/index`
|
||||
- **权限标识**: `psychology:permission:list`
|
||||
|
||||
**按钮权限**:
|
||||
- `psychology:permission:query` - 权限查询
|
||||
- `psychology:permission:add` - 权限新增
|
||||
- `psychology:permission:edit` - 权限修改
|
||||
- `psychology:permission:remove` - 权限删除
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 用户注册
|
||||
|
||||
1. 访问系统注册页面(通常在登录页面有注册链接)
|
||||
2. 填写用户名、密码、确认密码
|
||||
3. 输入验证码(如果启用)
|
||||
4. 点击注册按钮
|
||||
5. 注册成功后,使用注册的账号登录系统
|
||||
|
||||
### 2. 管理员分配量表权限
|
||||
|
||||
#### 方法一:通过量表权限管理页面
|
||||
|
||||
1. 登录管理员账号
|
||||
2. 进入"心理测评管理" -> "量表权限管理"
|
||||
3. 点击"新增"按钮
|
||||
4. 选择量表、用户(可选)、开始时间、结束时间、状态
|
||||
5. 点击"确定"保存
|
||||
|
||||
#### 方法二:通过用户管理页面
|
||||
|
||||
1. 登录管理员账号
|
||||
2. 进入"系统管理" -> "用户管理"
|
||||
3. 找到要分配权限的用户
|
||||
4. 点击操作列的"更多" -> "分配量表权限"
|
||||
5. 在权限分配页面,使用Transfer组件选择要分配的量表
|
||||
6. 点击"保存"按钮
|
||||
|
||||
### 3. 用户选择量表进行测评
|
||||
|
||||
1. 用户登录系统
|
||||
2. 进入"心理测评管理" -> "测评管理"
|
||||
3. 点击"开始测评"按钮
|
||||
4. 在量表选择页面,只能看到被分配权限的量表
|
||||
5. 选择量表后,继续完成测评流程
|
||||
|
||||
## 权限规则
|
||||
|
||||
### 权限匹配规则
|
||||
|
||||
系统按以下优先级检查用户是否有权限访问量表:
|
||||
|
||||
1. **用户直接权限**: 如果存在 `user_id = 当前用户ID` 的权限记录
|
||||
2. **角色权限**: 如果存在 `role_id = 用户角色ID` 的权限记录
|
||||
3. **部门权限**: 如果存在 `dept_id = 用户部门ID` 的权限记录
|
||||
4. **全局权限**: 如果存在 `user_id`, `role_id`, `dept_id` 都为空的权限记录(表示所有用户)
|
||||
|
||||
### 时间范围检查
|
||||
|
||||
- 如果权限设置了 `start_time`,当前时间必须 >= `start_time`
|
||||
- 如果权限设置了 `end_time`,当前时间必须 <= `end_time`
|
||||
- 如果时间字段为空,表示无时间限制
|
||||
|
||||
### 状态检查
|
||||
|
||||
- 只有 `status = '0'`(有效)的权限才会生效
|
||||
|
||||
### 管理员权限
|
||||
|
||||
- 管理员(userId = 1)自动拥有所有量表的访问权限,不受权限表限制
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **注册功能**: 需要确保系统配置中启用了注册功能(`sys.account.registerUser = true`)
|
||||
2. **权限分配**: 建议在用户注册后,管理员及时为用户分配量表权限
|
||||
3. **权限清理**: 删除用户时,会自动删除该用户的所有权限(通过外键约束)
|
||||
4. **权限冲突**: 如果用户同时拥有多种类型的权限(用户、角色、部门),只要满足其中一种即可访问
|
||||
5. **性能优化**: 权限查询使用了索引,但在大量用户和量表的情况下,建议定期清理无效权限
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 后端文件
|
||||
- `ry-news-system/src/main/java/com/ddnai/system/domain/psychology/PsyScalePermission.java`
|
||||
- `ry-news-system/src/main/java/com/ddnai/system/mapper/psychology/PsyScalePermissionMapper.java`
|
||||
- `ry-news-system/src/main/resources/mapper/system/psychology/PsyScalePermissionMapper.xml`
|
||||
- `ry-news-system/src/main/java/com/ddnai/system/service/psychology/IPsyScalePermissionService.java`
|
||||
- `ry-news-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyScalePermissionServiceImpl.java`
|
||||
- `ry-news-admin/src/main/java/com/ddnai/web/controller/psychology/PsyScalePermissionController.java`
|
||||
|
||||
### 前端文件
|
||||
- `ruoyi-ui/src/api/psychology/permission.js`
|
||||
- `ruoyi-ui/src/views/psychology/permission/index.vue`
|
||||
- `ruoyi-ui/src/views/psychology/permission/user.vue`
|
||||
- `ruoyi-ui/src/views/system/user/index.vue` (修改)
|
||||
- `ruoyi-ui/src/views/psychology/assessment/start.vue` (修改)
|
||||
- `ruoyi-ui/src/router/index.js` (修改)
|
||||
|
||||
### 配置文件
|
||||
- `sql/psychological_system_complete.sql` (菜单配置)
|
||||
|
||||
## 测试建议
|
||||
|
||||
1. **注册功能测试**
|
||||
- 测试正常注册流程
|
||||
- 测试用户名重复、密码格式验证
|
||||
- 测试验证码功能
|
||||
|
||||
2. **权限分配测试**
|
||||
- 测试通过权限管理页面分配权限
|
||||
- 测试通过用户管理页面分配权限
|
||||
- 测试批量分配权限
|
||||
- 测试权限的时间范围限制
|
||||
|
||||
3. **权限过滤测试**
|
||||
- 测试普通用户只能看到有权限的量表
|
||||
- 测试管理员可以看到所有量表
|
||||
- 测试无权限用户的提示信息
|
||||
|
||||
4. **权限检查测试**
|
||||
- 测试用户直接权限
|
||||
- 测试角色权限
|
||||
- 测试部门权限
|
||||
- 测试全局权限
|
||||
- 测试权限过期情况
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **批量操作**: 支持批量为用户分配多个量表权限
|
||||
2. **权限模板**: 支持创建权限模板,快速为多个用户分配相同权限
|
||||
3. **权限统计**: 添加权限使用统计功能
|
||||
4. **权限审批**: 支持权限申请和审批流程
|
||||
5. **权限继承**: 支持部门权限继承到子部门
|
||||
6. **权限导出**: 支持导出用户权限列表
|
||||
|
||||
## 总结
|
||||
|
||||
本功能模块完整实现了用户注册和量表权限管理功能,管理员可以灵活地为用户分配量表访问权限,普通用户只能访问被分配权限的量表,有效控制了系统的访问范围。功能基于RuoYi框架开发,遵循了框架的设计规范和代码风格,易于维护和扩展。
|
||||
|
||||
399
z_Project change/15-新量表导入完整操作指南.md
Normal file
399
z_Project change/15-新量表导入完整操作指南.md
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
# 新量表导入完整操作指南
|
||||
|
||||
## 📋 概述
|
||||
|
||||
本文档详细说明如何将一个新的心理量表完整导入到系统中,包括所有必要的配置步骤。
|
||||
|
||||
## 🎯 导入流程概览
|
||||
|
||||
```
|
||||
1. 创建量表基本信息
|
||||
↓
|
||||
2. 添加题目(题目管理)
|
||||
↓
|
||||
3. 为每个题目添加选项(选项管理)
|
||||
↓
|
||||
4. 添加因子(因子管理)
|
||||
↓
|
||||
5. 配置因子计分规则(计分规则管理)
|
||||
↓
|
||||
6. 配置结果解释(解释配置)
|
||||
↓
|
||||
7. 配置预警规则(可选,预警规则配置)
|
||||
↓
|
||||
8. 启用量表并测试
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 详细操作步骤
|
||||
|
||||
### 步骤1:创建量表基本信息
|
||||
|
||||
**路径**:`心理测评管理` → `量表管理`
|
||||
|
||||
**操作步骤**:
|
||||
1. 点击 **"新增"** 按钮
|
||||
2. 填写量表基本信息:
|
||||
- **量表编码**:唯一标识,如 `SCL_90`、`EPQ_001`(必填)
|
||||
- **量表名称**:如 `症状自评量表SCL-90`(必填)
|
||||
- **量表类型**:选择类型(情绪、人格、行为等)
|
||||
- **量表简介**:简短介绍
|
||||
- **量表描述**:详细描述
|
||||
- **题目数量**:预计题目数(如 `90`)
|
||||
- **预计完成时间**:分钟数(如 `30`)
|
||||
- **适用人群**:如 `一般人群`、`青少年`等
|
||||
- **作者**:量表作者
|
||||
- **来源**:量表来源
|
||||
- **状态**:选择 `正常`(启用)或 `停用`
|
||||
3. 点击 **"确定"** 保存
|
||||
|
||||
**注意事项**:
|
||||
- 量表编码必须唯一
|
||||
- 状态选择 `正常` 后,量表才会在测评选择页面显示
|
||||
- 题目数量可以后续修改
|
||||
|
||||
---
|
||||
|
||||
### 步骤2:添加题目(题目管理)
|
||||
|
||||
**路径**:`心理测评管理` → `量表管理` → 点击量表行的 **"题目管理"** 按钮
|
||||
|
||||
**操作步骤**:
|
||||
1. 在量表列表中找到刚创建的量表
|
||||
2. 点击该行的 **"题目管理"** 按钮
|
||||
3. 进入题目管理页面后,点击 **"新增"** 按钮
|
||||
4. 填写题目信息:
|
||||
- **题目序号**:如 `1`、`2`、`3`...(必填,用于排序)
|
||||
- **题目内容**:题目的完整文字内容(必填)
|
||||
- **题目类型**:选择 `单选题` 或 `多选题`
|
||||
- **是否必答**:选择 `是` 或 `否`
|
||||
- **排序**:数字越小越靠前(可选)
|
||||
5. 点击 **"确定"** 保存
|
||||
6. 重复步骤3-5,添加所有题目
|
||||
|
||||
**批量添加技巧**:
|
||||
- 可以一次性添加多个题目,然后逐个配置选项
|
||||
- 建议先添加所有题目,再统一配置选项
|
||||
|
||||
**注意事项**:
|
||||
- 题目序号建议连续,从1开始
|
||||
- 题目内容支持富文本(如果需要)
|
||||
- 题目类型决定了后续选项的配置方式
|
||||
|
||||
---
|
||||
|
||||
### 步骤3:为每个题目添加选项(选项管理)
|
||||
|
||||
**路径**:在题目管理页面,点击题目行的 **"选项管理"** 按钮
|
||||
|
||||
**操作步骤**:
|
||||
1. 在题目列表中,找到要配置选项的题目
|
||||
2. 点击该行的 **"选项管理"** 按钮
|
||||
3. 在弹出的对话框中,点击 **"新增"** 按钮
|
||||
4. 填写选项信息:
|
||||
- **选项编码**:如 `A`、`B`、`C`、`D` 或 `1`、`2`、`3`、`4`(必填)
|
||||
- **选项内容**:选项的文字内容(必填)
|
||||
- **选项分数**:该选项对应的分数(必填,如 `0`、`1`、`2`、`3`)
|
||||
- **排序**:数字越小越靠前(可选)
|
||||
5. 点击 **"确定"** 保存
|
||||
6. 重复步骤3-5,为该题目添加所有选项
|
||||
7. 点击对话框的 **"关闭"** 按钮
|
||||
8. 重复步骤1-7,为所有题目配置选项
|
||||
|
||||
**选项配置示例**:
|
||||
|
||||
**单选题示例**(如焦虑自评量表):
|
||||
- 选项A:没有或很少时间(分数:1)
|
||||
- 选项B:小部分时间(分数:2)
|
||||
- 选项C:相当多时间(分数:3)
|
||||
- 选项D:绝大部分或全部时间(分数:4)
|
||||
|
||||
**多选题示例**(如症状自评量表):
|
||||
- 选项A:没有(分数:0)
|
||||
- 选项B:很轻(分数:1)
|
||||
- 选项C:中等(分数:2)
|
||||
- 选项D:偏重(分数:3)
|
||||
- 选项E:严重(分数:4)
|
||||
|
||||
**注意事项**:
|
||||
- 选项编码在同一题目内必须唯一
|
||||
- 选项分数用于后续计分,必须准确
|
||||
- 单选题通常每个选项对应一个分数
|
||||
- 多选题可能需要累加分数或取最高分
|
||||
|
||||
---
|
||||
|
||||
### 步骤4:添加因子(因子管理)
|
||||
|
||||
**路径**:`心理测评管理` → `量表管理` → 点击量表行的 **"因子管理"** 按钮
|
||||
|
||||
**操作步骤**:
|
||||
1. 在量表列表中找到量表
|
||||
2. 点击该行的 **"因子管理"** 按钮
|
||||
3. 进入因子管理页面后,点击 **"新增"** 按钮
|
||||
4. 填写因子信息:
|
||||
- **因子编码**:如 `F1`、`F2`、`F3` 或 `躯体化`、`强迫`等(必填)
|
||||
- **因子名称**:如 `躯体化因子`、`强迫症状因子`(必填)
|
||||
- **因子描述**:因子的详细说明(可选)
|
||||
- **排序**:数字越小越靠前(可选)
|
||||
5. 点击 **"确定"** 保存
|
||||
6. 重复步骤3-5,添加所有因子
|
||||
|
||||
**因子配置示例**(如SCL-90量表):
|
||||
- 因子1:躯体化
|
||||
- 因子2:强迫症状
|
||||
- 因子3:人际关系敏感
|
||||
- 因子4:抑郁
|
||||
- 因子5:焦虑
|
||||
- 因子6:敌对
|
||||
- 因子7:恐怖
|
||||
- 因子8:偏执
|
||||
- 因子9:精神病性
|
||||
|
||||
**注意事项**:
|
||||
- 因子编码在同一量表内必须唯一
|
||||
- 因子用于后续的计分规则配置和结果解释
|
||||
- 如果量表没有因子(只有总分),可以跳过此步骤
|
||||
|
||||
---
|
||||
|
||||
### 步骤5:配置因子计分规则(计分规则管理)
|
||||
|
||||
**路径**:在因子管理页面,点击因子行的 **"计分规则"** 按钮
|
||||
|
||||
**操作步骤**:
|
||||
1. 在因子列表中,找到要配置计分规则的因子
|
||||
2. 点击该行的 **"计分规则"** 按钮
|
||||
3. 在弹出的对话框中,点击 **"新增规则"** 按钮
|
||||
4. 在表格中配置规则:
|
||||
- **题目**:从下拉框中选择题目(显示题目序号和内容)
|
||||
- **选项**:从下拉框中选择选项(多选,可选,留空表示所有选项)
|
||||
- **权重**:该规则在计分中的权重(0-10,默认1.00)
|
||||
- **计算方式**:选择 `求和`、`平均`、`最大`、`最小`
|
||||
5. 重复步骤3-4,为该因子添加所有计分规则
|
||||
6. 点击 **"保存"** 按钮保存所有规则
|
||||
7. 重复步骤1-6,为所有因子配置计分规则
|
||||
|
||||
**计分规则配置示例**:
|
||||
|
||||
**示例1:简单求和**(如SCL-90的躯体化因子):
|
||||
- 规则1:题目1 → 所有选项 → 权重1.00 → 计算方式:求和
|
||||
- 规则2:题目4 → 所有选项 → 权重1.00 → 计算方式:求和
|
||||
- 规则3:题目12 → 所有选项 → 权重1.00 → 计算方式:求和
|
||||
- ...(包含所有属于该因子的题目)
|
||||
|
||||
**示例2:加权求和**(如某些量表的因子):
|
||||
- 规则1:题目1 → 选项A,B → 权重2.00 → 计算方式:求和
|
||||
- 规则2:题目2 → 所有选项 → 权重1.00 → 计算方式:求和
|
||||
|
||||
**注意事项**:
|
||||
- 计分规则决定了因子分数的计算方式
|
||||
- 如果某个题目属于多个因子,需要为每个因子都配置规则
|
||||
- 计算方式:
|
||||
- **求和**:所有选中选项的分数之和 × 权重
|
||||
- **平均**:所有选中选项的分数平均值 × 权重
|
||||
- **最大**:所有选中选项的分数最大值 × 权重
|
||||
- **最小**:所有选中选项的分数最小值 × 权重
|
||||
|
||||
---
|
||||
|
||||
### 步骤6:配置结果解释(解释配置)
|
||||
|
||||
**路径**:`心理测评管理` → `解释配置`
|
||||
|
||||
**操作步骤**:
|
||||
1. 进入解释配置页面
|
||||
2. 点击 **"新增"** 按钮
|
||||
3. 填写解释信息:
|
||||
- **量表**:从下拉框中选择量表(可选,留空表示通用解释)
|
||||
- **因子**:从下拉框中选择因子(可选,留空表示总体解释;需先选择量表)
|
||||
- **分数下限**:该解释适用的最低分数(必填)
|
||||
- **分数上限**:该解释适用的最高分数(必填)
|
||||
- **等级**:如 `低`、`中`、`高`(可选)
|
||||
- **等级名称**:如 `轻度`、`中度`、`重度`(可选)
|
||||
- **解释标题**:解释的标题(必填)
|
||||
- **解释内容**:详细的解释说明(可选)
|
||||
- **建议指导**:针对该分数段的建议或指导(可选)
|
||||
- **排序**:数字越小越靠前(可选)
|
||||
4. 点击 **"确定"** 保存
|
||||
5. 重复步骤2-4,为所有分数段配置解释
|
||||
|
||||
**结果解释配置示例**(如SCL-90的躯体化因子):
|
||||
- 解释1:量表=SCL-90,因子=躯体化,分数范围=0-10,等级=低,解释标题="正常范围"
|
||||
- 解释2:量表=SCL-90,因子=躯体化,分数范围=11-20,等级=中,解释标题="轻度症状"
|
||||
- 解释3:量表=SCL-90,因子=躯体化,分数范围=21-30,等级=高,解释标题="中度症状"
|
||||
- 解释4:量表=SCL-90,因子=躯体化,分数范围=31-40,等级=高,解释标题="重度症状"
|
||||
|
||||
**注意事项**:
|
||||
- 分数范围不能重叠(系统会根据分数自动匹配解释)
|
||||
- 可以为量表总体配置解释(因子留空)
|
||||
- 可以为特定因子配置解释(选择因子)
|
||||
- 可以为通用情况配置解释(量表和因子都留空)
|
||||
|
||||
---
|
||||
|
||||
### 步骤7:配置预警规则(可选)
|
||||
|
||||
**路径**:`心理测评管理` → `预警规则配置`
|
||||
|
||||
**操作步骤**:
|
||||
1. 进入预警规则配置页面
|
||||
2. 点击 **"新增"** 按钮
|
||||
3. 填写预警规则信息:
|
||||
- **量表**:从下拉框中选择量表(必填)
|
||||
- **因子**:从下拉框中选择因子(可选,留空表示针对总分)
|
||||
- **规则名称**:如 `重度抑郁预警`(必填)
|
||||
- **预警等级**:选择 `低`、`中`、`高`、`紧急`(必填)
|
||||
- **分数下限**:触发预警的最低分数(可选)
|
||||
- **分数上限**:触发预警的最高分数(可选)
|
||||
- **百分位下限**:触发预警的最低百分位(可选)
|
||||
- **百分位上限**:触发预警的最高百分位(可选)
|
||||
- **自动解除**:是否自动解除预警(可选)
|
||||
- **解除条件**:自动解除的条件(可选)
|
||||
- **状态**:选择 `正常`(启用)或 `停用`
|
||||
4. 点击 **"确定"** 保存
|
||||
5. 重复步骤2-4,配置所有预警规则
|
||||
|
||||
**预警规则配置示例**:
|
||||
- 规则1:量表=SCL-90,因子=抑郁,预警等级=高,分数范围=30-40,状态=正常
|
||||
- 规则2:量表=SCL-90,因子=焦虑,预警等级=紧急,分数范围=35-40,状态=正常
|
||||
|
||||
**注意事项**:
|
||||
- 预警规则用于在测评结果异常时自动向管理员发出警告
|
||||
- 可以针对特定因子配置,也可以针对总分配置
|
||||
- 预警等级决定了警告的严重程度
|
||||
- 只有状态为 `正常` 的规则才会生效
|
||||
|
||||
---
|
||||
|
||||
### 步骤8:启用量表并测试
|
||||
|
||||
**操作步骤**:
|
||||
1. 返回 `量表管理` 页面
|
||||
2. 确认量表状态为 `正常`(如果为 `停用`,点击修改改为 `正常`)
|
||||
3. 确认所有配置已完成:
|
||||
- ✅ 量表基本信息已填写
|
||||
- ✅ 题目已添加(至少1题)
|
||||
- ✅ 每个题目都有选项(至少1个选项)
|
||||
- ✅ 因子已添加(如果有因子)
|
||||
- ✅ 因子计分规则已配置(如果有因子)
|
||||
- ✅ 结果解释已配置(至少1条)
|
||||
4. 测试量表:
|
||||
- 进入 `测评管理` → `开始测评`
|
||||
- 选择刚创建的量表
|
||||
- 填写被测评人信息
|
||||
- 开始答题,完成所有题目
|
||||
- 提交测评
|
||||
- 查看生成的报告,验证:
|
||||
- 分数计算是否正确
|
||||
- 因子分数是否正确(如果有因子)
|
||||
- 结果解释是否正确显示
|
||||
- 预警是否正常触发(如果配置了预警规则)
|
||||
|
||||
---
|
||||
|
||||
## 📊 配置检查清单
|
||||
|
||||
在完成量表导入后,使用以下清单检查配置是否完整:
|
||||
|
||||
### 基本信息
|
||||
- [ ] 量表编码已填写且唯一
|
||||
- [ ] 量表名称已填写
|
||||
- [ ] 量表状态为 `正常`
|
||||
- [ ] 题目数量与实际题目数一致
|
||||
|
||||
### 题目配置
|
||||
- [ ] 至少添加了1个题目
|
||||
- [ ] 所有题目都有题目序号
|
||||
- [ ] 所有题目都有题目内容
|
||||
- [ ] 题目类型已选择(单选/多选)
|
||||
|
||||
### 选项配置
|
||||
- [ ] 每个题目至少添加了1个选项
|
||||
- [ ] 所有选项都有选项编码
|
||||
- [ ] 所有选项都有选项内容
|
||||
- [ ] 所有选项都有选项分数
|
||||
|
||||
### 因子配置(如果有因子)
|
||||
- [ ] 至少添加了1个因子
|
||||
- [ ] 所有因子都有因子编码
|
||||
- [ ] 所有因子都有因子名称
|
||||
|
||||
### 计分规则配置(如果有因子)
|
||||
- [ ] 每个因子至少配置了1条计分规则
|
||||
- [ ] 计分规则中的题目都已选择
|
||||
- [ ] 计分规则中的权重已设置
|
||||
- [ ] 计分规则中的计算方式已选择
|
||||
|
||||
### 结果解释配置
|
||||
- [ ] 至少配置了1条结果解释
|
||||
- [ ] 结果解释的分数范围已设置
|
||||
- [ ] 结果解释的解释标题已填写
|
||||
- [ ] 结果解释的解释内容已填写(建议)
|
||||
|
||||
### 预警规则配置(可选)
|
||||
- [ ] 预警规则已配置(如果需要)
|
||||
- [ ] 预警规则的量表已选择
|
||||
- [ ] 预警规则的预警等级已选择
|
||||
- [ ] 预警规则的状态为 `正常`
|
||||
|
||||
---
|
||||
|
||||
## 🔍 常见问题
|
||||
|
||||
### Q1:量表创建后,在测评选择页面看不到?
|
||||
**A**:检查以下几点:
|
||||
- 量表状态是否为 `正常`
|
||||
- 量表是否有至少1个题目
|
||||
- 刷新浏览器页面
|
||||
|
||||
### Q2:题目添加后,选项管理按钮点击没反应?
|
||||
**A**:检查以下几点:
|
||||
- 题目是否已保存
|
||||
- 浏览器控制台是否有错误
|
||||
- 尝试刷新页面
|
||||
|
||||
### Q3:因子计分规则配置后,分数计算不正确?
|
||||
**A**:检查以下几点:
|
||||
- 选项分数是否正确
|
||||
- 计分规则中的题目是否选择正确
|
||||
- 计分规则中的计算方式是否合适
|
||||
- 权重设置是否正确
|
||||
|
||||
### Q4:结果解释不显示?
|
||||
**A**:检查以下几点:
|
||||
- 是否配置了结果解释
|
||||
- 结果解释的分数范围是否覆盖了实际分数
|
||||
- 结果解释的量表和因子是否匹配
|
||||
|
||||
### Q5:预警规则不触发?
|
||||
**A**:检查以下几点:
|
||||
- 预警规则的状态是否为 `正常`
|
||||
- 预警规则的分数范围是否设置正确
|
||||
- 测评结果是否满足预警条件
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [功能使用指南](./使用指南-功能测试说明.md)
|
||||
- [用户注册和量表权限管理功能开发](./14-用户注册和量表权限管理功能开发.md)
|
||||
|
||||
---
|
||||
|
||||
## ✅ 完成标志
|
||||
|
||||
当以下所有条件满足时,量表导入完成:
|
||||
|
||||
1. ✅ 量表在测评选择页面可以正常选择
|
||||
2. ✅ 可以正常开始测评并答题
|
||||
3. ✅ 提交测评后可以正常生成报告
|
||||
4. ✅ 报告中的分数计算正确
|
||||
5. ✅ 报告中的结果解释正确显示
|
||||
6. ✅ 预警规则正常触发(如果配置了)
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-06
|
||||
|
||||
Loading…
Reference in New Issue
Block a user