Kaynağa Gözat

初步优化

SpringSunYY 5 ay önce
ebeveyn
işleme
d82f9b2710

+ 182 - 147
ruoyi-ui/src/views/system/user/index.vue

@@ -27,7 +27,9 @@
       </el-col>
       <!--用户数据-->
       <el-col :span="20" :xs="24">
-        <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+        <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
+                 label-width="68px"
+        >
           <el-form-item label="用户名称" prop="userName">
             <el-input
               v-model="queryParams.userName"
@@ -87,7 +89,8 @@
               size="mini"
               @click="handleAdd"
               v-hasPermi="['system:user:add']"
-            >新增</el-button>
+            >新增
+            </el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button
@@ -98,7 +101,8 @@
               :disabled="single"
               @click="handleUpdate"
               v-hasPermi="['system:user:edit']"
-            >修改</el-button>
+            >修改
+            </el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button
@@ -109,7 +113,8 @@
               :disabled="multiple"
               @click="handleDelete"
               v-hasPermi="['system:user:remove']"
-            >删除</el-button>
+            >删除
+            </el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button
@@ -119,7 +124,8 @@
               size="mini"
               @click="handleImport"
               v-hasPermi="['system:user:import']"
-            >导入</el-button>
+            >导入
+            </el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button
@@ -129,18 +135,27 @@
               size="mini"
               @click="handleExport"
               v-hasPermi="['system:user:export']"
-            >导出</el-button>
+            >导出
+            </el-button>
           </el-col>
           <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
         </el-row>
 
         <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
-          <el-table-column type="selection" width="50" align="center" />
-          <el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
-          <el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
-          <el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
-          <el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
-          <el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
+          <el-table-column type="selection" width="50" align="center"/>
+          <el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible"/>
+          <el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[1].visible"
+                           :show-overflow-tooltip="true"
+          />
+          <el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[2].visible"
+                           :show-overflow-tooltip="true"
+          />
+          <el-table-column label="部门" align="center" key="deptName" prop="dept.deptName" v-if="columns[3].visible"
+                           :show-overflow-tooltip="true"
+          />
+          <el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber"
+                           v-if="columns[4].visible" width="120"
+          />
           <el-table-column label="状态" align="center" key="status" v-if="columns[5].visible">
             <template slot-scope="scope">
               <el-switch
@@ -169,23 +184,31 @@
                 icon="el-icon-edit"
                 @click="handleUpdate(scope.row)"
                 v-hasPermi="['system:user:edit']"
-              >修改</el-button>
+              >修改
+              </el-button>
               <el-button
                 size="mini"
                 type="text"
                 icon="el-icon-delete"
                 @click="handleDelete(scope.row)"
                 v-hasPermi="['system:user:remove']"
-              >删除</el-button>
-              <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:user:resetPwd', 'system:user:edit']">
+              >删除
+              </el-button>
+              <el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)"
+                           v-hasPermi="['system:user:resetPwd', 'system:user:edit']"
+              >
                 <span class="el-dropdown-link">
                   <i class="el-icon-d-arrow-right el-icon--right"></i>更多
                 </span>
                 <el-dropdown-menu slot="dropdown">
                   <el-dropdown-item command="handleResetPwd" icon="el-icon-key"
-                    v-hasPermi="['system:user:resetPwd']">重置密码</el-dropdown-item>
+                                    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>
+                                    v-hasPermi="['system:user:edit']"
+                  >分配角色
+                  </el-dropdown-item>
                 </el-dropdown-menu>
               </el-dropdown>
             </template>
@@ -208,36 +231,38 @@
         <el-row>
           <el-col :span="12">
             <el-form-item label="用户昵称" prop="nickName">
-              <el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
+              <el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30"/>
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="归属部门" prop="deptId">
-              <treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门" />
+              <treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门"/>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row>
           <el-col :span="12">
             <el-form-item label="手机号码" prop="phonenumber">
-              <el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
+              <el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11"/>
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="邮箱" prop="email">
-              <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
+              <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50"/>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row>
           <el-col :span="12">
             <el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
-              <el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
+              <el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30"/>
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
-              <el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password/>
+              <el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20"
+                        show-password
+              />
             </el-form-item>
           </el-col>
         </el-row>
@@ -261,7 +286,8 @@
                   v-for="dict in dict.type.sys_normal_disable"
                   :key="dict.value"
                   :label="dict.value"
-                >{{dict.label}}</el-radio>
+                >{{ dict.label }}
+                </el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
@@ -326,10 +352,14 @@
         <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
         <div class="el-upload__tip text-center" slot="tip">
           <div class="el-upload__tip" slot="tip">
-            <el-checkbox v-model="upload.updateSupport" /> 是否更新已经存在的用户数据
+            <el-checkbox v-model="upload.updateSupport"/>
+            是否更新已经存在的用户数据
           </div>
           <span>仅允许导入xls、xlsx格式文件。</span>
-          <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
+          <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;"
+                   @click="importTemplate"
+          >下载模板
+          </el-link>
         </div>
       </el-upload>
       <div slot="footer" class="dialog-footer">
@@ -341,14 +371,14 @@
 </template>
 
 <script>
-import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus } from "@/api/system/user";
-import { getToken } from "@/utils/auth";
-import { treeselect } from "@/api/system/dept";
-import Treeselect from "@riophae/vue-treeselect";
-import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus } from '@/api/system/user'
+import { getToken } from '@/utils/auth'
+import { treeselect } from '@/api/system/dept'
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
 
 export default {
-  name: "User",
+  name: 'User',
   dicts: ['sys_normal_disable', 'sys_user_sex'],
   components: { Treeselect },
   data() {
@@ -368,7 +398,7 @@ export default {
       // 用户表格数据
       userList: null,
       // 弹出层标题
-      title: "",
+      title: '',
       // 部门树选项
       deptOptions: undefined,
       // 是否显示弹出层
@@ -386,23 +416,23 @@ export default {
       // 表单参数
       form: {},
       defaultProps: {
-        children: "children",
-        label: "label"
+        children: 'children',
+        label: 'label'
       },
       // 用户导入参数
       upload: {
         // 是否显示弹出层(用户导入)
         open: false,
         // 弹出层标题(用户导入)
-        title: "",
+        title: '',
         // 是否禁用上传
         isUploading: false,
         // 是否更新已经存在的用户数据
         updateSupport: 0,
         // 设置上传的请求头部
-        headers: { Authorization: "Bearer " + getToken() },
+        headers: { Authorization: 'Bearer ' + getToken() },
         // 上传的地址
-        url: process.env.VUE_APP_BASE_API + "/system/user/importData"
+        url: process.env.VUE_APP_BASE_API + '/system/user/importData'
       },
       // 查询参数
       queryParams: {
@@ -426,88 +456,88 @@ export default {
       // 表单校验
       rules: {
         userName: [
-          { required: true, message: "用户名称不能为空", trigger: "blur" },
+          { required: true, message: '用户名称不能为空', trigger: 'blur' },
           { min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }
         ],
         nickName: [
-          { required: true, message: "用户昵称不能为空", trigger: "blur" }
+          { required: true, message: '用户昵称不能为空', trigger: 'blur' }
         ],
         password: [
-          { required: true, message: "用户密码不能为空", trigger: "blur" },
+          { required: true, message: '用户密码不能为空', trigger: 'blur' },
           { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
         ],
         email: [
           {
-            type: "email",
-            message: "请输入正确的邮箱地址",
-            trigger: ["blur", "change"]
+            type: 'email',
+            message: '请输入正确的邮箱地址',
+            trigger: ['blur', 'change']
           }
         ],
         phonenumber: [
           {
             pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
-            message: "请输入正确的手机号码",
-            trigger: "blur"
+            message: '请输入正确的手机号码',
+            trigger: 'blur'
           }
         ]
       }
-    };
+    }
   },
   watch: {
     // 根据名称筛选部门树
     deptName(val) {
-      this.$refs.tree.filter(val);
+      this.$refs.tree.filter(val)
     }
   },
   created() {
-    this.getList();
-    this.getTreeselect();
-    this.getConfigKey("sys.user.initPassword").then(response => {
-      this.initPassword = response.msg;
-    });
+    this.getList()
+    this.getTreeselect()
+    this.getConfigKey('sys.user.initPassword').then(response => {
+      this.initPassword = response.msg
+    })
   },
   methods: {
     /** 查询用户列表 */
     getList() {
-      this.loading = true;
+      this.loading = true
       listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
-          this.userList = response.rows;
-          this.total = response.total;
-          this.loading = false;
+          this.userList = response.rows
+          this.total = response.total
+          this.loading = false
         }
-      );
+      )
     },
     /** 查询部门下拉树结构 */
     getTreeselect() {
       treeselect().then(response => {
-        this.deptOptions = response.data;
-      });
+        this.deptOptions = response.data
+      })
     },
     // 筛选节点
     filterNode(value, data) {
-      if (!value) return true;
-      return data.label.indexOf(value) !== -1;
+      if (!value) return true
+      return data.label.indexOf(value) !== -1
     },
     // 节点单击事件
     handleNodeClick(data) {
-      this.queryParams.deptId = data.id;
-      this.handleQuery();
+      this.queryParams.deptId = data.id
+      this.handleQuery()
     },
     // 用户状态修改
     handleStatusChange(row) {
-      let text = row.status === "0" ? "启用" : "停用";
+      let text = row.status === '0' ? '启用' : '停用'
       this.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function() {
-        return changeUserStatus(row.userId, row.status);
+        return changeUserStatus(row.userId, row.status)
       }).then(() => {
-        this.$modal.msgSuccess(text + "成功");
+        this.$modal.msgSuccess(text + '成功')
       }).catch(function() {
-        row.status = row.status === "0" ? "1" : "0";
-      });
+        row.status = row.status === '0' ? '1' : '0'
+      })
     },
     // 取消按钮
     cancel() {
-      this.open = false;
-      this.reset();
+      this.open = false
+      this.reset()
     },
     // 表单重置
     reset() {
@@ -520,119 +550,125 @@ export default {
         phonenumber: undefined,
         email: undefined,
         sex: undefined,
-        status: "0",
+        status: '0',
         remark: undefined,
         postIds: [],
         roleIds: []
-      };
-      this.resetForm("form");
+      }
+      this.resetForm('form')
     },
     /** 搜索按钮操作 */
     handleQuery() {
-      this.queryParams.pageNum = 1;
-      this.getList();
+      this.queryParams.pageNum = 1
+      this.getList()
     },
     /** 重置按钮操作 */
     resetQuery() {
-      this.dateRange = [];
-      this.resetForm("queryForm");
-      this.handleQuery();
+      this.dateRange = []
+      this.resetForm('queryForm')
+      this.handleQuery()
     },
     // 多选框选中数据
     handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.userId);
-      this.single = selection.length != 1;
-      this.multiple = !selection.length;
+      this.ids = selection.map(item => item.userId)
+      this.single = selection.length != 1
+      this.multiple = !selection.length
     },
     // 更多操作触发
     handleCommand(command, row) {
       switch (command) {
-        case "handleResetPwd":
-          this.handleResetPwd(row);
-          break;
-        case "handleAuthRole":
-          this.handleAuthRole(row);
-          break;
+        case 'handleResetPwd':
+          this.handleResetPwd(row)
+          break
+        case 'handleAuthRole':
+          this.handleAuthRole(row)
+          break
         default:
-          break;
+          break
       }
     },
     /** 新增按钮操作 */
     handleAdd() {
-      this.reset();
-      this.getTreeselect();
+      this.reset()
+      this.getTreeselect()
       getUser().then(response => {
-        this.postOptions = response.posts;
-        this.roleOptions = response.roles;
-        this.open = true;
-        this.title = "添加用户";
-        this.form.password = this.initPassword;
-      });
+        this.postOptions = response.posts
+        this.roleOptions = response.roles
+        this.open = true
+        this.title = '添加用户'
+        this.form.password = this.initPassword
+      })
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
-      this.reset();
-      this.getTreeselect();
-      const userId = row.userId || this.ids;
+      this.reset()
+      this.getTreeselect()
+      const userId = row.userId || this.ids
       getUser(userId).then(response => {
-        this.form = response.data;
-        this.postOptions = response.posts;
-        this.roleOptions = response.roles;
-        this.form.postIds = response.postIds;
-        this.form.roleIds = response.roleIds;
-        this.open = true;
-        this.title = "修改用户";
-        this.form.password = "";
-      });
+        this.form = response.data
+        this.postOptions = response.posts
+        this.roleOptions = response.roles
+        if (response.postIds) {
+          this.form.postIds = response.postIds
+        }
+        if (response.data.roles) {
+          this.form.roleIds = response.data.roles.map(item => item.roleId)
+        }
+        this.open = true
+        this.title = '修改用户'
+        this.form.password = ''
+      })
     },
     /** 重置密码按钮操作 */
     handleResetPwd(row) {
-      this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
-        confirmButtonText: "确定",
-        cancelButtonText: "取消",
+      this.$prompt('请输入"' + row.userName + '"的新密码', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
         closeOnClickModal: false,
         inputPattern: /^.{5,20}$/,
-        inputErrorMessage: "用户密码长度必须介于 5 和 20 之间"
+        inputErrorMessage: '用户密码长度必须介于 5 和 20 之间'
       }).then(({ value }) => {
-          resetUserPwd(row.userId, value).then(response => {
-            this.$modal.msgSuccess("修改成功,新密码是:" + value);
-          });
-        }).catch(() => {});
+        resetUserPwd(row.userId, value).then(response => {
+          this.$modal.msgSuccess('修改成功,新密码是:' + value)
+        })
+      }).catch(() => {
+      })
     },
     /** 分配角色操作 */
     handleAuthRole: function(row) {
-      const userId = row.userId;
-      this.$router.push("/system/user-auth/role/" + userId);
+      const userId = row.userId
+      this.$router.push('/system/user-auth/role/' + userId)
     },
     /** 提交按钮 */
     submitForm: function() {
-      this.$refs["form"].validate(valid => {
+      this.$refs['form'].validate(valid => {
         if (valid) {
           if (this.form.userId != undefined) {
             updateUser(this.form).then(response => {
-              this.$modal.msgSuccess("修改成功");
-              this.open = false;
-              this.getList();
-            });
+              this.$modal.msgSuccess('修改成功')
+              this.open = false
+              this.getList()
+            })
           } else {
             addUser(this.form).then(response => {
-              this.$modal.msgSuccess("新增成功");
-              this.open = false;
-              this.getList();
-            });
+              this.$modal.msgSuccess('新增成功')
+              this.open = false
+              this.getList()
+            })
           }
         }
-      });
+      })
     },
     /** 删除按钮操作 */
     handleDelete(row) {
-      const userIds = row.userId || this.ids;
+      const userIds = row.userId || this.ids
       this.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function() {
-        return delUser(userIds);
+        return delUser(userIds)
       }).then(() => {
-        this.getList();
-        this.$modal.msgSuccess("删除成功");
-      }).catch(() => {});
+        this.getList()
+        this.$modal.msgSuccess('删除成功')
+      }).catch(() => {
+      })
     },
     /** 导出按钮操作 */
     handleExport() {
@@ -642,30 +678,29 @@ export default {
     },
     /** 导入按钮操作 */
     handleImport() {
-      this.upload.title = "用户导入";
-      this.upload.open = true;
+      this.upload.title = '用户导入'
+      this.upload.open = true
     },
     /** 下载模板操作 */
     importTemplate() {
-      this.download('system/user/importTemplate', {
-      }, `user_template_${new Date().getTime()}.xlsx`)
+      this.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`)
     },
     // 文件上传中处理
     handleFileUploadProgress(event, file, fileList) {
-      this.upload.isUploading = true;
+      this.upload.isUploading = true
     },
     // 文件上传成功处理
     handleFileSuccess(response, file, fileList) {
-      this.upload.open = false;
-      this.upload.isUploading = false;
-      this.$refs.upload.clearFiles();
-      this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
-      this.getList();
+      this.upload.open = false
+      this.upload.isUploading = false
+      this.$refs.upload.clearFiles()
+      this.$alert('<div style=\'overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;\'>' + response.msg + '</div>', '导入结果', { dangerouslyUseHTMLString: true })
+      this.getList()
     },
     // 提交上传文件
     submitFileForm() {
-      this.$refs.upload.submit();
+      this.$refs.upload.submit()
     }
   }
-};
-</script>
+}
+</script>

+ 4 - 4
ruoyi_admin/config/app.yml

@@ -7,9 +7,9 @@ ruoyi:
   api:
     version: '/'
   env: 'dev'
-  profile: 'dev'
+  profile: 'G:/ruoyi/uploadPath'
 
 token:
-    header: 'Authorization'
-    secret: 'abcdefghijklmnopqrstuvwxyz'
-    expireTime: 30
+  header: 'Authorization'
+  secret: 'abcdefghijklmnopqrstuvwxyz'
+  expireTime: 30

+ 29 - 60
ruoyi_admin/controller/common/common.py

@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# @Author  : YY
+# @Author  : shaw-lee
 
 import os
 import time
@@ -12,8 +12,8 @@ from werkzeug.exceptions import NotFound
 from ruoyi_common.config import RuoYiConfig
 from ruoyi_common.constant import Constants
 from ruoyi_common.descriptor.serializer import JsonSerializer
-from ruoyi_common.descriptor.validator import FileUploadValidator, QueryValidator
-from ruoyi_common.base.model import AjaxResponse
+from ruoyi_common.descriptor.validator import FileValidator, QueryValidator
+from ruoyi_common.base.model import AjaxResponse, MultiFile
 from ruoyi_common.utils import FileUploadUtil, FileUtil, StringUtil
 from ... import reg
 
@@ -22,14 +22,14 @@ from ... import reg
 @QueryValidator()
 @JsonSerializer()
 def common_download(
-    file_name:Annotated[str,Field(min_length=1,max_length=100)],
-    delete:Annotated[bool,Field(annotations=bool,default=False)],
+        file_name: Annotated[str, Field(min_length=1, max_length=100)],
+        delete: Annotated[bool, Field(annotations=bool, default=False)],
 ):
-    file_path = RuoYiConfig().download_path + file_name
+    file_path = RuoYiConfig.download_path + file_name
     download_name = time.time() * 1000 + file_name[file_name.index("_") + 1:]
     try:
         response = send_from_directory(
-            directory=RuoYiConfig().download_path,
+            directory=RuoYiConfig.download_path,
             path=file_name,
             as_attachment=True,
             download_name=download_name,
@@ -43,63 +43,33 @@ def common_download(
     return response
 
 
-@reg.api.route('/profile/<path:resource>')
+@reg.api.route('/common/upload')
+@FileValidator()
 @JsonSerializer()
-def profile_resource(resource:str):
-    """
-    通过 /profile/** 访问上传资源
-    例如:/profile/uploads/dev/upload/2025/11/11/xxx.jpg
-    """
-    # 安全处理相对路径
-    safe_path = resource.replace("..", "").lstrip("/\\")
-    try:
-        directory = os.path.dirname(safe_path) or "."
-        filename = os.path.basename(safe_path)
-        response = send_from_directory(
-            directory=directory,
-            path=filename,
-            as_attachment=False,
-            download_name=filename,
-        )
-    except NotFound:
-        return AjaxResponse.from_error("文件不存在")
-    except Exception:
-        return AjaxResponse.from_error("读取文件失败")
-    return response
-
-
-@reg.api.route('/common/upload', methods=['POST'])
-@FileUploadValidator()
-@JsonSerializer()
-def common_upload():
-    file:FileStorage = request.files.get('file')
-    if not file:
-        return AjaxResponse.from_error("上传文件不能为空")
-    file_name = FileUploadUtil.upload(file, RuoYiConfig().upload_path)
+def common_upload(file: MultiFile):
+    file: FileStorage = file.one()
+    file_name = FileUploadUtil.upload(file, RuoYiConfig.upload_path)
     url = request.host_url[:-1] + file_name
     new_file_name = FileUploadUtil.get_filename(file_name)
     original_filename = file.filename
     ajax_response = AjaxResponse.from_success()
-    # 兼容前端组件字段(期待 fileName)
-    setattr(ajax_response,"url",url)
-    setattr(ajax_response,"fileName",file_name)
-    setattr(ajax_response,"newFileName",new_file_name)
-    setattr(ajax_response,"originalFilename",original_filename)
+    setattr(ajax_response, "url", url)
+    setattr(ajax_response, "file_name", file_name)
+    setattr(ajax_response, "new_file_name", new_file_name)
+    setattr(ajax_response, "original_filename", original_filename)
     return ajax_response
 
 
-@reg.api.route('/common/uploads', methods=['POST'])
-@FileUploadValidator()
+@reg.api.route('/common/uploads')
+@FileValidator()
 @JsonSerializer()
-def common_uploads():
+def common_uploads(files: MultiFile):
     file_names = []
     urls = []
     new_file_names = []
     original_filenames = []
-    if not request.files:
-        return AjaxResponse.from_error("上传文件不能为空")
-    for _,file in request.files.items():
-        file_name = FileUploadUtil.upload(file, RuoYiConfig().upload_path)
+    for _, file in files.items():
+        file_name = FileUploadUtil.upload(file, RuoYiConfig.upload_path)
         file_names.append(file_name)
         url = request.host_url[:-1] + file_name
         urls.append(url)
@@ -108,11 +78,10 @@ def common_uploads():
         original_filename = file.filename
         original_filenames.append(original_filename)
     ajax_response = AjaxResponse.from_success()
-    # 兼容 camelCase
-    setattr(ajax_response,"urls", ",".join(urls))
-    setattr(ajax_response,"fileNames", ",".join(file_names))
-    setattr(ajax_response,"newFileNames", ",".join(new_file_names))
-    setattr(ajax_response,"originalFilenames", ",".join(original_filenames))
+    setattr(ajax_response, "urls", urls.join(","))
+    setattr(ajax_response, "file_names", file_names.join(","))
+    setattr(ajax_response, "new_file_names", new_file_names.join(","))
+    setattr(ajax_response, "original_filenames", original_filenames.join(","))
     return ajax_response
 
 
@@ -120,17 +89,17 @@ def common_uploads():
 @QueryValidator()
 @JsonSerializer()
 def common_download_resource(
-    resource:Annotated[str,Field(annotation=str,min_length=1,max_length=100)]
+        resource: Annotated[str, Field(annotation=str, min_length=1, max_length=100)]
 ):
-    download_path = RuoYiConfig().download_path + StringUtil.substring_after(resource,Constants.RESOURCE_PREFIX)
+    download_path = RuoYiConfig.download_path + StringUtil.substring_after(resource, Constants.RESOURCE_PREFIX)
     download_name = os.path.basename(download_path)
     try:
         response = send_from_directory(
-            directory=RuoYiConfig().download_path,
+            directory=RuoYiConfig.download_path,
             path=download_path,
             as_attachment=True,
             download_name=download_name,
-            )
+        )
     except NotFound as e:
         return AjaxResponse.from_error("文件不存在")
     except Exception as e:

+ 10 - 10
ruoyi_admin/controller/system/profile.py

@@ -46,7 +46,7 @@ def system_user_profile_update(dto:SysUser):
     user: SysUser = login_user.user
     dto.user_name = user.user_name
     if dto.phonenumber and UserConstants.NOT_UNIQUE == \
-        SysUserService.check_phone_unique(dto):
+            SysUserService.check_phone_unique(dto):
         return AjaxResponse.from_error(msg=f"修改用户'{user.user_name}'失败,手机号码已存在")
     if dto.email and UserConstants.NOT_UNIQUE == SysUserService.check_email_unique(dto):
         return AjaxResponse.from_error(msg=f"修改用户'{user.user_name}'失败,邮箱账号已存在")
@@ -69,25 +69,25 @@ def system_user_profile_update(dto:SysUser):
 @Log(title = "个人信息", business_type = BusinessType.UPDATE)
 @JsonSerializer()
 def system_user_profile_update_pwd(
-    old_password:Annotated[SecretStr, Field(..., example='admin')],
-    new_password:Annotated[SecretStr, Field(..., example='admin123')]
+        old_password:Annotated[SecretStr, Field(..., example='admin')],
+        new_password:Annotated[SecretStr, Field(..., example='admin123')]
 ):
     '''
         修改密码
     '''
     login_user: LoginUser = SecurityUtil.get_login_user()
     if not SecurityUtil.matches_password(
-        old_password.get_secret_value(), login_user.user.password
+            old_password.get_secret_value(), login_user.user.password
     ):
         return AjaxResponse.from_error(msg="修改密码失败,旧密码错误")
     if SecurityUtil.matches_password(
-        new_password.get_secret_value(), login_user.user.password
+            new_password.get_secret_value(), login_user.user.password
     ):
         return AjaxResponse.from_error(msg="修改密码失败,新密码不能与旧密码相同")
     if SysUserService.reset_user_pwd(
-        login_user.user.user_name, 
-        SecurityUtil.encrypt_password(new_password.get_secret_value())
-        ):
+            login_user.user.user_name,
+            SecurityUtil.encrypt_password(new_password.get_secret_value())
+    ):
         login_user.user.password = SecurityUtil.encrypt_password(
             new_password.get_secret_value()
         )
@@ -111,9 +111,9 @@ def system_user_profile_avatar(file:MultiFile):
         login_user: LoginUser = SecurityUtil.get_login_user()
         avatar_path = FileUploadUtil.upload(file,RuoYiConfig.profile)
         num = SysUserService.update_user_avatar(
-            login_user.user_name, 
+            login_user.user_name,
             avatar_path
-            )
+        )
         if num > 0:
             ajax_response = AjaxResponse.from_success()
             setattr(ajax_response, "img_url", avatar_path)

+ 61 - 60
ruoyi_admin/controller/system/user.py

@@ -2,27 +2,28 @@
 # @Author  : YY
 
 from typing import List, Optional
+
+from flask_login import login_required
 from pydantic import BeforeValidator, Field
 from typing_extensions import Annotated
-from werkzeug.datastructures import ImmutableMultiDict, FileStorage
-from flask_login import login_required
+from werkzeug.datastructures import FileStorage
 
-from ruoyi_common.base.transformer import ids_to_list
 from ruoyi_common.base.model import AjaxResponse, TableResponse
+from ruoyi_common.base.transformer import ids_to_list
 from ruoyi_common.constant import UserConstants
-from ruoyi_common.utils import security_util as SecurityUtil
-from ruoyi_common.utils.base import ExcelUtil
-from ruoyi_common.domain.entity import SysUser, SysRole
-from ruoyi_common.domain.enum import BusinessType
 from ruoyi_common.descriptor.serializer import BaseSerializer, JsonSerializer
 from ruoyi_common.descriptor.validator import FileDownloadValidator, \
     FileUploadValidator, QueryValidator, BodyValidator, PathValidator
+from ruoyi_common.domain.entity import SysUser, SysRole
+from ruoyi_common.domain.enum import BusinessType
+from ruoyi_common.utils import security_util as SecurityUtil
+from ruoyi_common.utils.base import ExcelUtil
+from ruoyi_framework.descriptor.log import Log
+from ruoyi_framework.descriptor.permission import HasPerm, PreAuthorize
 from ruoyi_system.domain.entity import SysPost
+from ruoyi_system.service import SysUserService
 from ruoyi_system.service.sys_post import SysPostService
 from ruoyi_system.service.sys_role import SysRoleService
-from ruoyi_system.service import SysUserService
-from ruoyi_framework.descriptor.permission import HasPerm, PreAuthorize
-from ruoyi_framework.descriptor.log import Log
 from ... import reg
 
 
@@ -30,7 +31,7 @@ from ... import reg
 @QueryValidator(is_page=True)
 @PreAuthorize(HasPerm("system:user:list"))
 @JsonSerializer()
-def system_user_list(dto:SysUser):
+def system_user_list(dto: SysUser):
     '''
         获取用户列表
     '''
@@ -44,50 +45,50 @@ def system_user_list(dto:SysUser):
 @PathValidator()
 @PreAuthorize(HasPerm("system:user:query"))
 @JsonSerializer()
-def system_get_user(id:Optional[int]=None):
+def system_get_user(id: Optional[int] = None):
     '''
         获取用户详情
     '''
     SysUserService.check_user_data_scope(id)
     ajax_response = AjaxResponse.from_success()
-    roles:List[SysRole] = SysRoleService.select_role_all()
-    posts:List[SysPost] = SysPostService.select_post_all()
+    roles: List[SysRole] = SysRoleService.select_role_all()
+    posts: List[SysPost] = SysPostService.select_post_all()
     if not SecurityUtil.is_admin(id):
         roles = [role for role in roles if not role.is_admin()]
-    setattr(ajax_response,"roles",roles)
-    setattr(ajax_response,"posts",posts)
+    setattr(ajax_response, "roles", roles)
+    setattr(ajax_response, "posts", posts)
     if id:
         user = SysUserService.select_user_by_id(id)
-        setattr(ajax_response,"data",user)
+        setattr(ajax_response, "data", user)
         post_ids = SysPostService.select_post_list_by_user_id(id)
-        setattr(ajax_response,"post_ids",post_ids)
-        setattr(ajax_response,"role_ids",user.role_ids)
+        setattr(ajax_response, "postIds", post_ids)
+        setattr(ajax_response, "role_ids", user.role_ids)
     return ajax_response
 
 
 @reg.api.route("/system/user", methods=["POST"])
 @BodyValidator()
 @PreAuthorize(HasPerm("system:user:add"))
-@Log(title="用户管理",business_type=BusinessType.INSERT)
+@Log(title="用户管理", business_type=BusinessType.INSERT)
 @JsonSerializer()
-def system_create_user(dto:SysUser):
+def system_create_user(dto: SysUser):
     '''
         新增用户
     '''
     if SysUserService.check_user_name_unique(dto) \
-        == UserConstants.NOT_UNIQUE:
+            == UserConstants.NOT_UNIQUE:
         return AjaxResponse.from_error(
             f"新增用户'{dto.user_name}'失败,登录账号已存在"
         )
     elif dto.phonenumber \
-        and SysUserService.check_phone_unique(dto) \
-        == UserConstants.NOT_UNIQUE:
+            and SysUserService.check_phone_unique(dto) \
+            == UserConstants.NOT_UNIQUE:
         return AjaxResponse.from_error(
             f"新增用户'{dto.phonenumber}'失败,手机号码已存在"
         )
     elif dto.email \
-        and SysUserService.check_email_unique(dto) \
-        == UserConstants.NOT_UNIQUE:
+            and SysUserService.check_email_unique(dto) \
+            == UserConstants.NOT_UNIQUE:
         return AjaxResponse.from_error(
             f"新增用户'{dto.email}'失败,邮箱已存在"
         )
@@ -100,23 +101,23 @@ def system_create_user(dto:SysUser):
 @reg.api.route("/system/user", methods=["PUT"])
 @BodyValidator()
 @PreAuthorize(HasPerm("system:user:edit"))
-@Log(title="用户管理",business_type=BusinessType.UPDATE)
+@Log(title="用户管理", business_type=BusinessType.UPDATE)
 @JsonSerializer()
-def system_update_user(dto:SysUser):
+def system_update_user(dto: SysUser):
     '''
         修改用户
     '''
     SysUserService.check_user_allowed(dto)
     SysUserService.check_user_data_scope(dto.user_id)
     if dto.phonenumber \
-        and SysUserService.check_phone_unique(dto.phonenumber) \
-        == UserConstants.NOT_UNIQUE:
+            and SysUserService.check_phone_unique(dto) \
+            == UserConstants.NOT_UNIQUE:
         return AjaxResponse.from_error(
             f"新增用户'{dto.phonenumber}'失败,手机号码已存在"
         )
     elif dto.email \
-        and SysUserService.check_email_unique(dto.email) \
-        == UserConstants.NOT_UNIQUE:
+            and SysUserService.check_email_unique(dto.email) \
+            == UserConstants.NOT_UNIQUE:
         return AjaxResponse.from_error(
             f"新增用户'{dto.email}'失败,邮箱已存在"
         )
@@ -129,10 +130,10 @@ def system_update_user(dto:SysUser):
 @reg.api.route("/system/user/<ids>", methods=["DELETE"])
 @PathValidator()
 @PreAuthorize(HasPerm("system:user:remove"))
-@Log(title="用户管理",business_type=BusinessType.DELETE)
+@Log(title="用户管理", business_type=BusinessType.DELETE)
 @JsonSerializer()
 def system_delete_users(
-    ids: Annotated[List[int],BeforeValidator(ids_to_list)]
+        ids: Annotated[List[int], BeforeValidator(ids_to_list)]
 ):
     '''
         删除用户
@@ -147,9 +148,9 @@ def system_delete_users(
 @reg.api.route("/system/user/export", methods=["POST"])
 @FileDownloadValidator()
 @PreAuthorize(HasPerm("system:user:export"))
-@Log(title="用户管理",business_type=BusinessType.EXPORT)
+@Log(title="用户管理", business_type=BusinessType.EXPORT)
 @BaseSerializer()
-def system_user_export(dto:SysUser):
+def system_user_export(dto: SysUser):
     '''
         导出用户数据
     '''
@@ -161,19 +162,19 @@ def system_user_export(dto:SysUser):
 @reg.api.route("/system/user/importData", methods=["POST"])
 @FileUploadValidator()
 @PreAuthorize(HasPerm("system:user:import"))
-@Log(title="用户管理",business_type=BusinessType.IMPORT)
+@Log(title="用户管理", business_type=BusinessType.IMPORT)
 @JsonSerializer()
 def system_user_importdata(
-    file:List[FileStorage],
-    update_support: Annotated[bool,BeforeValidator(lambda x:x!="0")]
+        file: List[FileStorage],
+        update_support: Annotated[bool, BeforeValidator(lambda x: x != "0")]
 ):
     '''
         导入用户模板
     '''
     file = file[0]
     excel_util = ExcelUtil(SysUser)
-    datas = excel_util.import_file(file,sheetname="用户数据")
-    msg = SysUserService.import_user(datas,update_support)
+    datas = excel_util.import_file(file, sheetname="用户数据")
+    msg = SysUserService.import_user(datas, update_support)
     return AjaxResponse.from_success(msg=msg)
 
 
@@ -186,14 +187,14 @@ def system_user_importtemplate():
     '''
     excel_util = ExcelUtil(SysUser)
     return excel_util.import_template_response(sheetname="用户数据")
-    
-    
+
+
 @reg.api.route("/system/user/resetPwd", methods=["PUT"])
 @BodyValidator()
 @PreAuthorize(HasPerm("system:user:resetPwd"))
-@Log(title="用户管理",business_type=BusinessType.UPDATE)
+@Log(title="用户管理", business_type=BusinessType.UPDATE)
 @JsonSerializer()
-def system_update_user_resetpwd(dto:SysUser):
+def system_update_user_resetpwd(dto: SysUser):
     '''
         重置密码
     '''
@@ -209,9 +210,9 @@ def system_update_user_resetpwd(dto:SysUser):
 @reg.api.route("/system/user/changeStatus", methods=["PUT"])
 @BodyValidator()
 @PreAuthorize(HasPerm("system:user:edit"))
-@Log(title="用户管理",business_type=BusinessType.UPDATE)
+@Log(title="用户管理", business_type=BusinessType.UPDATE)
 @JsonSerializer()
-def system_update_user_changestatus(dto:SysUser):
+def system_update_user_changestatus(dto: SysUser):
     '''
         修改用户状态
     '''
@@ -221,38 +222,38 @@ def system_update_user_changestatus(dto:SysUser):
     flag = SysUserService.update_user_status(dto)
     ajax_response = AjaxResponse.from_success() if flag else AjaxResponse.from_error()
     return ajax_response
-    
-    
+
+
 @reg.api.route("/system/user/authRole/<int:id>", methods=["GET"])
 @PathValidator()
 @PreAuthorize(HasPerm("system:user:query"))
 @JsonSerializer()
-def system_get_user_authrole(id:int):
+def system_get_user_authrole(id: int):
     '''
         获取用户授权角色
     '''
-    sysuser:SysUser = SysUserService.select_user_by_id(id)
-    roles:List[SysRole] = SysRoleService.select_role_list_by_user_id(id)
+    sysuser: SysUser = SysUserService.select_user_by_id(id)
+    roles: List[SysRole] = SysRoleService.select_role_list_by_user_id(id)
     if not sysuser.is_admin():
         roles = [role for role in roles if not role.is_admin()]
     ajax_response = AjaxResponse.from_success() if sysuser else AjaxResponse.from_error()
-    setattr(ajax_response,"user",sysuser)
-    setattr(ajax_response,"roles",roles)
+    setattr(ajax_response, "user", sysuser)
+    setattr(ajax_response, "roles", roles)
     return ajax_response
-    
-    
+
+
 @reg.api.route("/system/user/authRole", methods=["PUT"])
 @BodyValidator()
 @PreAuthorize(HasPerm("system:user:edit"))
-@Log(title="用户管理",business_type=BusinessType.GRANT)
+@Log(title="用户管理", business_type=BusinessType.GRANT)
 @JsonSerializer()
 def system_update_user_authrole(
-    user_id:Annotated[int,Field(gt=0)],
-    role_ids:Annotated[List[int],Field(default_factory=List)]
+        user_id: Annotated[int, Field(gt=0)],
+        role_ids: Annotated[List[int], Field(default_factory=List)]
 ):
     '''
         授权用户角色
     '''
     SysUserService.check_user_data_scope(user_id)
-    SysUserService.update_user_roles(user_id,role_ids)
+    SysUserService.update_user_roles(user_id, role_ids)
     return AjaxResponse.from_success()

+ 7 - 7
ruoyi_common/config.py

@@ -2,12 +2,12 @@
 # @Author  : YY
 
 from ruoyi_common.ruoyi.config import CONFIG_CACHE
-        
+
 
 class RuoYiConfig:
-    
-    profile = CONFIG_CACHE.get("ruoyi.profile", "dev")
-    
+
+    profile = CONFIG_CACHE.get("ruoyi.profile")
+
     @property
     def upload_path(self) -> str:
         """
@@ -17,7 +17,7 @@ class RuoYiConfig:
             str: 上传路径
         """
         return f"uploads/{self.profile}/upload"
-    
+
     @property
     def download_path(self) -> str:
         """
@@ -27,7 +27,7 @@ class RuoYiConfig:
             str: 下载路径
         """
         return f"uploads/{self.profile}/download/"
-    
+
     @property
     def avatar_path(self) -> str:
         """
@@ -37,7 +37,7 @@ class RuoYiConfig:
             str: 头像路径
         """
         return f"uploads/{self.profile}/avatar"
-    
+
     @property
     def import_path(self) -> str:
         """

+ 42 - 43
ruoyi_common/descriptor/validator.py

@@ -18,7 +18,7 @@ from ruoyi_common.base.schema_vo import ArbitrarySchemaFactory, \
 
 
 class AbcValidatorFunction(ABC):
-    
+
     @abstractmethod
     def validate_unbound_parameters(self):
         raise NotImplementedError()
@@ -26,14 +26,14 @@ class AbcValidatorFunction(ABC):
     @abstractmethod
     def validate_function(self):
         raise NotImplementedError()
-    
+
     @abstractmethod
     def __call__(self, *args: Any, **kwargs: Any) -> Any:
         raise NotImplementedError()
-    
+
 
 class ValidatorScopeFunction(AbcValidatorFunction):
-    
+
     def __init__(self,func:Callable):
         self.func = func
         self.sig = inspect.signature(self.func)
@@ -48,11 +48,11 @@ class ValidatorScopeFunction(AbcValidatorFunction):
     @property
     def unbound_model(self):
         return self._unbound_model
-    
+
     def _validate_kind(self,kind):
         if kind != inspect.Parameter.POSITIONAL_OR_KEYWORD:
             raise Exception("参数必须是位置参数")
-    
+
     def validate_unbound_parameters(self):
         index = 0
         for key in self.sig.parameters:
@@ -68,18 +68,18 @@ class ValidatorScopeFunction(AbcValidatorFunction):
                 self._unbound_fields[key] = \
                     FieldInfo.from_annotation(param.annotation)
             index += 1
-        
+
     def validate_function(self):
         self.func = validate_call(self.func)
-            
+
     def __call__(self, *args: Any, **kwargs: Any) -> Any:
         self.args = args if args else ()
         self.kwargs = kwargs if kwargs else {}
         return self.func(*self.args, **self.kwargs)
-    
+
 
 class ValidatorViewFunction(AbcValidatorFunction):
-        
+
     def __init__(self,func:Callable):
         self.func = func
         self.sig = inspect.signature(self.func)
@@ -94,11 +94,11 @@ class ValidatorViewFunction(AbcValidatorFunction):
     @property
     def unbound_model(self):
         return self._unbound_model
-    
+
     def _validate_kind(self,kind):
         if kind != inspect.Parameter.POSITIONAL_OR_KEYWORD:
             raise Exception("参数必须是位置参数")
-    
+
     def validate_unbound_parameters(self):
         index = 0
         for key in self.sig.parameters:
@@ -127,7 +127,7 @@ class ValidatorViewFunction(AbcValidatorFunction):
                     self._unbound_fields[key] = \
                         FieldInfo.from_annotation(param.annotation)
             index += 1
-        
+
     def validate_function(self):
         if self._schema_factory:
             if self._unbound_model:
@@ -143,21 +143,21 @@ class ValidatorViewFunction(AbcValidatorFunction):
             self.func = validate_call(self.func)
 
     def unbound_data(
-        self, 
-        data_parser: BaseReqParser
-        ):
+            self,
+            data_parser: BaseReqParser
+    ):
         self._data_parser = data_parser
         if self._schema_factory and self._data_parser:
             self._data_parser.prepare_factory(self._schema_factory)
-    
+
     def unbound_schema(
-        self, 
-        schema_factory:Optional[BaseSchemaFactory], 
-        ):
+            self,
+            schema_factory:Optional[BaseSchemaFactory],
+    ):
         self._schema_factory = schema_factory
         self.validate_unbound_parameters()
         self.validate_function()
-    
+
     def bound_data(self, args:Tuple=(), kwargs:Dict={}):
         if self._unbound_model:
             key, bo_model = self._unbound_model
@@ -167,7 +167,7 @@ class ValidatorViewFunction(AbcValidatorFunction):
             data = self._data_parser.data()
             kwargs.clear()
             kwargs.update(data)
-            
+
     def __call__(self, *args: Any, **kwargs: Any) -> Any:
         self.args = args if args else ()
         self.kwargs = kwargs if kwargs else {}
@@ -189,28 +189,28 @@ class ValidatorViewFunction(AbcValidatorFunction):
 
 @dataclass
 class BaseValidator:
-    
+
     data_parser:ClassVar = None
-    
+
     schema_factory:ClassVar = None
-    
+
     vo_context:VoValidatorContext = field(init=False)
-    
+
     def __call__(self, func):
-        
+
         view_function = ValidatorViewFunction(func)
         view_function.unbound_schema(self.schema_factory)
         view_function.unbound_data(self.data_parser)
-        
+
         @wraps(func)
         def wrapper(*args, **kwargs):
             return view_function(*args, **kwargs)
         return wrapper
-    
+
 
 @dataclass
 class PathValidator(BaseValidator):
-    
+
     def __post_init__(self):
         self.schema_factory = PathSchemaFactory()
         self.data_parser = PathReqParser()
@@ -218,15 +218,15 @@ class PathValidator(BaseValidator):
 
 @dataclass
 class QueryValidator(BaseValidator):
-    
+
     is_page: bool = False
-    
+
     include:Optional[Set[str]] = field(default=None)
-    
+
     exclude:Optional[Set[str]] = field(default=None)
-    
+
     extra_fields:Optional[Dict[str, FieldInfo]] = field(default=None)
-    
+
     def __post_init__(self):
         vo_context = VoValidatorContext(
             exclude_data_alias=True,
@@ -241,14 +241,14 @@ class QueryValidator(BaseValidator):
             extra_allowed_fields=self.extra_fields
         )
         self.data_parser = QueryReqParser(vo_context)
-        
+
 
 @dataclass
 class BodyValidator(BaseValidator):
-    
+
     include:Optional[Set[str]] = field(default=None)
     exclude:Optional[Set[str]] = field(default=None)
-    
+
     def __post_init__(self):
         vo_context = VoValidatorContext(
             include_fields=self.include,
@@ -260,7 +260,7 @@ class BodyValidator(BaseValidator):
 
 @dataclass
 class FileDownloadValidator(BaseValidator):
-    
+
     def __post_init__(self):
         vo_context = VoValidatorContext(
             exclude_data_alias=True,
@@ -272,7 +272,7 @@ class FileDownloadValidator(BaseValidator):
 
 @dataclass
 class FileUploadValidator(BaseValidator):
-        
+
     def __post_init__(self):
         self.schema_factory = ArbitrarySchemaFactory()
         self.data_parser = UploadFileFormReqParser(
@@ -282,10 +282,9 @@ class FileUploadValidator(BaseValidator):
 
 @dataclass
 class FileValidator(BaseValidator):
-    
+
     include:Optional[Set[str]] = field(default=None)
-    
+
     def __post_init__(self):
         self.schema_factory = ArbitrarySchemaFactory()
         self.data_parser = UploadFileFormReqParser()
-        

+ 49 - 64
ruoyi_common/domain/entity.py

@@ -17,7 +17,6 @@ from ruoyi_common.base.transformer import int_to_str, to_datetime, str_to_int
 
 
 class LoginUser(BaseModel, UserMixin):
-
     model_config = strict_base_config.copy()
 
     user_id: int
@@ -67,21 +66,20 @@ class LoginUser(BaseModel, UserMixin):
 
 
 class SysUser(AuditEntity):
-
     user_id: Annotated[
         int,
         BeforeValidator(str_to_int),
-        Field(gt=0,default=None),
+        Field(gt=0, default=None),
         VoField(query=True),
-        ExcelField(name="用户序号",cell_type="numeric",prompt="用户编号")
+        ExcelField(name="用户序号", cell_type="numeric", prompt="用户编号")
     ]
 
     dept_id: Annotated[
         int,
         BeforeValidator(str_to_int),
-        Field(gt=0,default=None),
+        Field(gt=0, default=None),
         VoField(query=True),
-        ExcelField(name="部门编号",action="import")
+        ExcelField(name="部门编号", action="import")
     ]
 
     user_name: Annotated[
@@ -115,14 +113,14 @@ class SysUser(AuditEntity):
     sex: Annotated[
         Optional[str],
         Field(default=None),
-        ExcelField(name="用户性别",converter="0=男,1=女,2=未知")
+        ExcelField(name="用户性别", converter="0=男,1=女,2=未知")
     ]
 
     avatar: Optional[str] = None
 
     password: Annotated[
         str,
-        Field(default=None,exclude=True)
+        Field(default=None, exclude=True)
     ]
 
     salt: Annotated[
@@ -135,7 +133,7 @@ class SysUser(AuditEntity):
         Optional[str],
         Field(default=None),
         VoField(query=True),
-        ExcelField(name="帐号状态",converter="0=正常,1=停用")
+        ExcelField(name="帐号状态", converter="0=正常,1=停用")
     ]
 
     del_flag: Optional[str] = None
@@ -143,7 +141,7 @@ class SysUser(AuditEntity):
     login_ip: Annotated[
         Optional[str],
         Field(default=None),
-        ExcelField(name="最后登录IP",action="export")
+        ExcelField(name="最后登录IP", action="export")
     ]
 
     login_date: Annotated[
@@ -162,8 +160,8 @@ class SysUser(AuditEntity):
         Field(default=None),
         VoField(body=False),
         ExcelFields(
-            ExcelAccess(name="部门名称",width=20,attr="dept_name"),
-            ExcelAccess(name="部门负责人",width=20,attr="leader")
+            ExcelAccess(name="部门名称", width=20, attr="dept_name"),
+            ExcelAccess(name="部门负责人", width=20, attr="leader")
         )
     ]
 
@@ -184,32 +182,24 @@ class SysUser(AuditEntity):
     ]
 
     def is_admin(self) -> bool:
-        # 检查用户是否拥有管理员角色
-        if self.user_id == 1:
-            return True
-        if self.roles:
-            for role in self.roles:
-                if role.role_key == "admin":
-                    return True
-        return False
+        return True if self.user_id and self.user_id == 1 else False
 
 
 class SysRole(AuditEntity):
-
     role_id: Annotated[
         int,
         BeforeValidator(str_to_int),
-        Field(gt=0,default=None,vo=VoAccess(query=True))
+        Field(gt=0, default=None, vo=VoAccess(query=True))
     ]
 
     role_name: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     role_key: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     role_sort: Annotated[
@@ -220,33 +210,33 @@ class SysRole(AuditEntity):
 
     data_scope: Optional[str] = None
 
-    menu_check_strictly: Annotated[Optional[bool],Strict(False)] = None
+    menu_check_strictly: Annotated[Optional[bool], Strict(False)] = None
 
-    dept_check_strictly: Annotated[Optional[bool],Strict(False)] = None
+    dept_check_strictly: Annotated[Optional[bool], Strict(False)] = None
 
     status: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     del_flag: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(body=False))
+        Field(default=None, vo=VoAccess(body=False))
     ]
 
     flag: Annotated[
         Optional[bool],
-        Field(default=None,vo=VoAccess(body=False))
+        Field(default=None, vo=VoAccess(body=False))
     ]
 
     menu_ids: Annotated[
         Optional[List[int]],
-        Field(default=[],vo=VoAccess(body=False))
+        Field(default=[], vo=VoAccess(body=False))
     ]
 
     dept_ids: Annotated[
         Optional[List[int]],
-        Field(default=[],vo=VoAccess(body=False))
+        Field(default=[], vo=VoAccess(body=False))
     ]
 
     def is_admin(self) -> bool:
@@ -254,21 +244,20 @@ class SysRole(AuditEntity):
 
 
 class SysMenu(AuditEntity):
-
     menu_id: Annotated[
         int,
         BeforeValidator(str_to_int),
-        Field(gt=0,default=None,vo=VoAccess(query=True))
+        Field(gt=0, default=None, vo=VoAccess(query=True))
     ]
 
     menu_name: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     parent_name: Annotated[
         Optional[int],
-        Field(default=None,exclude=True,vo=VoAccess(body=False))
+        Field(default=None, exclude=True, vo=VoAccess(body=False))
     ]
 
     parent_id: Optional[int] = None
@@ -282,25 +271,25 @@ class SysMenu(AuditEntity):
     query: Optional[str] = None
 
     is_frame: Annotated[str,
-        BeforeValidator(int_to_str),
-        Field(default=None)
+    BeforeValidator(int_to_str),
+    Field(default=None)
     ]
 
     is_cache: Annotated[str,
-        BeforeValidator(int_to_str),
-        Field(default=None)
+    BeforeValidator(int_to_str),
+    Field(default=None)
     ]
 
     menu_type: Optional[str] = None
 
     visible: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     status: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     perms: Optional[str] = None
@@ -309,62 +298,60 @@ class SysMenu(AuditEntity):
 
     children: Annotated[
         List["SysMenu"] | NoneType,
-        Field(default=[],exclude=True,vo=VoAccess(body=False))
+        Field(default=[], exclude=True, vo=VoAccess(body=False))
     ]
 
 
 class SysDictType(AuditEntity):
-
     dict_id: Annotated[
         int,
         BeforeValidator(str_to_int),
-        Field(default=None,ge=0,vo=VoAccess(query=True))
+        Field(default=None, ge=0, vo=VoAccess(query=True))
     ]
 
     dict_name: Annotated[
         str,
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     dict_type: Annotated[
         str,
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     status: Annotated[
         str,
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
 
 class SysDictData(AuditEntity):
-
     dict_code: Annotated[
         int,
         BeforeValidator(str_to_int),
-        Field(default=None,ge=0,vo=VoAccess(query=True))
+        Field(default=None, ge=0, vo=VoAccess(query=True))
     ]
 
-    dict_sort: Annotated[int,Field(default=None)]
+    dict_sort: Annotated[int, Field(default=None)]
 
     dict_label: Annotated[
         str,
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     dict_value: Annotated[
         str,
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     dict_type: Annotated[
         str,
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     css_class: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(body=False))
+        Field(default=None, vo=VoAccess(body=False))
     ]
 
     list_class: Annotated[
@@ -372,33 +359,32 @@ class SysDictData(AuditEntity):
         Field(default=None)
     ]
 
-    is_default: Annotated[str,Field(default=None)]
+    is_default: Annotated[str, Field(default=None)]
 
     status: Annotated[
         str,
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
 
 class SysDept(AuditEntity):
-
     dept_id: Annotated[
         int,
         BeforeValidator(str_to_int),
-        Field(gt=0,default=None,vo=VoAccess(query=True))
+        Field(gt=0, default=None, vo=VoAccess(query=True))
     ]
 
     parent_id: Annotated[
         Optional[int],
         BeforeValidator(str_to_int),
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     ancestors: Optional[str] = None
 
     dept_name: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     order_num: Optional[int] = None
@@ -411,18 +397,17 @@ class SysDept(AuditEntity):
 
     status: Annotated[
         Optional[str],
-        Field(default=None,vo=VoAccess(query=True))
+        Field(default=None, vo=VoAccess(query=True))
     ]
 
     del_flag: Optional[str] = None
 
-    parent_name: Annotated[Optional[str],Field(default=None,exclude=True,vo=VoAccess(body=False))]
+    parent_name: Annotated[Optional[str], Field(default=None, exclude=True, vo=VoAccess(body=False))]
 
-    children: Annotated[List["SysDept"],Field(default=[],vo=VoAccess(body=False))]
+    children: Annotated[List["SysDept"], Field(default=[], vo=VoAccess(body=False))]
 
 
 class TreeSelect(BaseModel):
-
     # 节点ID
     id: Annotated[int, Field(default=None)]
 

+ 9 - 9
ruoyi_common/ruoyi/config.py

@@ -13,18 +13,18 @@ CONFIG_CACHE = dict()
 
 
 class RuoYiConfigLoader(object):
-    
+
     pname = "config"
     name = "app.yml"
     name_tmpl = "app-{}.yml"
-    
+
     def __init__(self, root):
         self._root = root
         self._raw_data = {}
         config_file = self._generate_main_config()
         self.load_config(config_file)
         self.load_config_from_cache()
-    
+
     def load_config_from_cache(self):
         '''
         从缓存配置env配置中加载配置
@@ -34,12 +34,12 @@ class RuoYiConfigLoader(object):
             config_file = os.path.join(self._root, self.pname, self.name_tmpl.format(env))
             if not os.path.exists(config_file):
                 raise FileNotFoundError(f"Config file {config_file} not found")
-            self.load_config(config_file)        
-    
+            self.load_config(config_file)
+
     def _generate_main_config(self) -> str:
         '''
         生成配置文件
-        
+
         Returns:
             str: 配置文件路径
         '''
@@ -48,7 +48,7 @@ class RuoYiConfigLoader(object):
         if not os.path.exists(config_path):
             os.mkdir(config_path)
         return config_file
-    
+
     def load_config(self, file):
         '''
         加载配置文件参数
@@ -60,7 +60,7 @@ class RuoYiConfigLoader(object):
         flatten_data = DictUtil.flatten(data)
         formated_data = DictUtil.format_value(flatten_data)
         CONFIG_CACHE.update(formated_data)
-    
+
     @property
     def cache(self) -> Dict:
         """
@@ -70,7 +70,7 @@ class RuoYiConfigLoader(object):
             Dict: 缓存配置
         """
         return CONFIG_CACHE
-    
+
     def set_app(self,app:Flask):
         """
         关联flask应用

Dosya farkı çok büyük olduğundan ihmal edildi
+ 170 - 178
ruoyi_common/utils/base.py


+ 26 - 28
ruoyi_common/utils/security_util.py

@@ -76,33 +76,6 @@ def get_login_user() -> LoginUser:
     except Exception:
         raise UtilException("获取用户信息异常", HttpStatus.UNAUTHORIZED)
 
-def encrypt_password(password:str) -> str:
-    """
-    加密密码
-
-    Args:
-        password (str): 原始密码
-
-    Returns:
-        str: 加密后的密码
-    """
-    salt = bcrypt.gensalt(rounds=10,prefix=b'2a')
-    bcrypt_password = bcrypt.hashpw(password.encode('utf-8'), salt)
-    return bcrypt_password
-
-def matches_password(raw_password:str, encoded_password:str) -> bool:
-    """
-    验证密码是否匹配
-
-    Args:
-        raw_password (str): 原始密码
-        encoded_password (str): 加密后的密码
-
-    Returns:
-        bool: 密码是否匹配
-    """
-    return bcrypt.checkpw(raw_password.encode('utf-8'), encoded_password.encode('utf-8'))
-
 def is_admin(user_id) -> bool:
     """
     判断用户是否为管理员
@@ -115,6 +88,27 @@ def is_admin(user_id) -> bool:
     """
     return user_id is not None and user_id == 1
 
+def is_user_admin(user) -> bool:
+    """
+    通过用户对象判断是否为管理员
+    
+    Args:
+        user: 用户对象
+        
+    Returns:
+        bool: 是否为管理员
+    """
+    # 检查是否为超级管理员用户
+    if is_admin(user.user_id):
+        return True
+    
+    # 检查用户角色中是否包含admin角色
+    if hasattr(user, 'roles') and user.roles:
+        for role in user.roles:
+            if hasattr(role, 'role_key') and role.role_key == 'admin':
+                return True
+    return False
+
 def login_user_is_admin() -> bool:
     """
     判断当前登录用户是否为管理员
@@ -122,4 +116,8 @@ def login_user_is_admin() -> bool:
     Returns:
         bool: 当前登录用户是否为管理员
     """
-    return is_admin(get_user_id())
+    try:
+        user = get_login_user().user
+        return is_user_admin(user)
+    except:
+        return is_admin(get_user_id())

+ 20 - 4
ruoyi_framework/descriptor/datascope.py

@@ -74,9 +74,27 @@ class DataScope:
         login_user:LoginUser = SecurityUtil.get_login_user()
         if login_user:
             current_user:SysUser = login_user.user
-            if not SecurityUtil.is_admin(login_user.user_id):
+            # 检查用户是否为超级管理员(用户ID为1或者具有admin角色)
+            if not SecurityUtil.is_admin(login_user.user_id) and not self.is_user_admin(current_user):
                 self.filter_data_scope(current_user)
     
+    def is_user_admin(self, user: SysUser) -> bool:
+        """
+        判断用户是否为管理员(通过角色判断)
+        
+        Args:
+            user (SysUser): 用户对象
+            
+        Returns:
+            bool: 是否为管理员
+        """
+        if user.roles:
+            for role in user.roles:
+                # 如果用户有任何一个角色的role_key为admin,则认为是超级管理员
+                if hasattr(role, 'role_key') and role.role_key == 'admin':
+                    return True
+        return False
+    
     def filter_data_scope(self,user: SysUser):
         """
         过滤数据权限范围
@@ -140,6 +158,4 @@ class DataScope:
             else:
                 print(ValueError("Invalid data_scope value: {}".format(role.data_scope)))
         data_scope = or_(*criterions)
-        criterian_meta.scope = data_scope
-
-
+        criterian_meta.scope = data_scope

+ 42 - 37
ruoyi_system/mapper/sys_post.py

@@ -13,23 +13,22 @@ from ruoyi_system.domain.po import SysPostPo, SysUserPo, SysUserPostPo
 
 
 class SysPostMapper:
-    
     """
     岗位的数据访问层
     """
-    
+
     default_fields = {
-        'post_id', 'post_name', 'post_code', 'post_sort', 'status', 
+        'post_id', 'post_name', 'post_code', 'post_sort', 'status',
         'create_by', 'create_time', 'remark'
     }
-    
+
     default_columns = ColumnEntityList(SysPostPo, default_fields, False)
-    
+
     @classmethod
     def select_post_list(cls, post: SysPost) -> List[SysPost]:
         """
         根据条件,查询岗位列表
-        
+
         Args:
             post (SysPost): 岗位条件
         Returns:
@@ -42,31 +41,31 @@ class SysPostMapper:
             criterions.append(SysPostPo.post_name.like(f'%{post.post_name}%'))
         if post.status:
             criterions.append(SysPostPo.post_status == post.status)
-            
+
         stmt = select(*cls.default_columns).where(*criterions)
         if "criterian_meta" in g and g.criterian_meta.page:
             g.criterian_meta.page.stmt = stmt
-        
+
         rows = db.session.execute(stmt).all()
-        return [cls.default_columns.cast(row,SysPost) for row in rows]
+        return [cls.default_columns.cast(row, SysPost) for row in rows]
 
     @classmethod
     def select_post_all(cls) -> List[SysPost]:
         """
         查询所有岗位
-        
+
         Returns:
             List[SysPost]: 岗位列表
         """
         stmt = select(*cls.default_columns)
         rows = db.session.execute(stmt).all()
-        return [cls.default_columns.cast(row,SysPost) for row in rows]
+        return [cls.default_columns.cast(row, SysPost) for row in rows]
 
     @classmethod
     def select_post_by_id(cls, post_id: int) -> Optional[SysPost]:
         """
         通过岗位ID查询岗位信息
-        
+
         Args:
             post_id (int): 岗位ID
         Returns:
@@ -74,29 +73,29 @@ class SysPostMapper:
         """
         stmt = select(*cls.default_columns).where(SysPostPo.post_id == post_id)
         row = db.session.execute(stmt).one_or_none()
-        return cls.default_columns.cast(row,SysPost) if row else None
+        return cls.default_columns.cast(row, SysPost) if row else None
 
     @classmethod
     def select_post_list_by_user_id(cls, user_id: int) -> List[int]:
         """
         根据用户ID,查询岗位ID列表
-        
+
         Args:
             user_id (int): 用户ID
         Returns:
             List[int]: 岗位ID列表
-        """        
+        """
         stmt = select(SysPostPo.post_id).select_from(SysPostPo) \
             .join(SysUserPostPo, SysUserPostPo.post_id == SysPostPo.post_id) \
-           .join(SysUserPo, SysUserPo.user_id == SysUserPostPo.user_id) \
-           .where(SysUserPo.user_id == user_id)
+            .join(SysUserPo, SysUserPo.user_id == SysUserPostPo.user_id) \
+            .where(SysUserPo.user_id == user_id)
         return db.session.execute(stmt).scalars().all()
 
     @classmethod
     def select_posts_by_user_name(cls, user_name: str) -> List[SysPost]:
         """
         根据用户名,查询岗位列表
-        
+
         Args:
             user_name (str): 用户名
         Returns:
@@ -104,16 +103,16 @@ class SysPostMapper:
         """
         fields = {"post_id", "post_name", "post_code"}
         columns = ColumnEntityList(SysPostPo, fields, False)
-        
+
         stmt = select(*columns).select_from(SysPostPo) \
             .join(SysUserPostPo, SysUserPostPo.post_id == SysPostPo.id) \
             .join(SysUserPo, SysUserPo.user_id == SysUserPostPo.user_id) \
             .where(SysUserPo.user_name == user_name)
-            
+
         rows = db.session.execute(stmt).all()
         eos = list()
         for row in rows:
-            eos.append(columns.cast(row,SysPost))
+            eos.append(columns.cast(row, SysPost))
         return eos
 
     @classmethod
@@ -121,7 +120,7 @@ class SysPostMapper:
     def delete_post_by_id(cls, post_id: int) -> int:
         """
         删除岗位信息
-        
+
         Args:
             post_id (int): 岗位ID
         Returns:
@@ -136,7 +135,7 @@ class SysPostMapper:
     def delete_post_by_ids(cls, post_ids: List[int]) -> int:
         """
         批量删除岗位信息
-        
+
         Args:
             post_ids (List[int]): 岗位ID列表
         Returns:
@@ -151,18 +150,18 @@ class SysPostMapper:
     def update_post(cls, post: SysPost) -> int:
         """
         修改岗位信息
-        
+
         Args:
             post (SysPost): 岗位信息
         Returns:
             int: 影响行数
         """
         fields = {
-            "post_name", "post_code", "post_sort", "status", "remark", 
+            "post_name", "post_code", "post_sort", "status", "remark",
             "update_by", "update_time"
         }
         data = post.model_dump(
-            include=fields, 
+            include=fields,
             exclude_none=True,
             exclude_unset=True
         )
@@ -175,18 +174,18 @@ class SysPostMapper:
     def insert_post(cls, post: SysPost) -> int:
         """
         新增岗位信息
-        
+
         Args:
             post (SysPost): 岗位信息
         Returns:
             int: 新增记录的ID
         """
         fields = {
-            "post_id", "post_name", "post_code", "post_sort", "status", 
+            "post_id", "post_name", "post_code", "post_sort", "status",
             "remark", "create_by", "create_time"
         }
         data = post.model_dump(
-            include=fields, 
+            include=fields,
             exclude_none=True,
             exclude_unset=True
         )
@@ -198,32 +197,38 @@ class SysPostMapper:
     def check_post_name_unique(cls, post_name: str) -> Optional[SysPost]:
         """
         校验岗位名称
-        
+
         Args:
             post_name (str): 岗位名称
-            
-        Returns:    
+
+        Returns:
             Optional[SysPost]: 岗位信息
         """
         stmt = select(*cls.default_columns) \
             .where(SysPostPo.post_name == post_name) \
             .limit(1)
         row = db.session.execute(stmt).one_or_none()
-        return cls.default_columns.cast(row,SysPost) if row else None
+        return cls.default_columns.cast(row, SysPost) if row else None
 
     @classmethod
     def check_post_code_unique(cls, post_code: str) -> Optional[SysPost]:
         """
         校验岗位编码
-        
+
         Args:
             post_code (str): 岗位编码
-            
-        Returns:    
+
+        Returns:
             Optional[SysPost]: 岗位信息
         """
         stmt = select(*cls.default_columns) \
             .where(SysPostPo.post_code == post_code) \
             .limit(1)
         row = db.session.execute(stmt).one_or_none()
-        return cls.default_columns.cast(row,SysPost) if row else None
+        return cls.default_columns.cast(row, SysPost) if row else None
+
+    @classmethod
+    def delete_user_post_by_user_id(cls, user_id):
+        stmt = delete(SysUserPostPo) \
+            .where(SysUserPostPo.user_id == user_id)
+        return db.session.execute(stmt).rowcount

+ 52 - 41
ruoyi_system/mapper/sys_role.py

@@ -3,7 +3,7 @@
 
 from typing import List, Optional
 from flask import g
-from sqlalchemy import func, select,update,insert
+from sqlalchemy import func, select, update, insert, delete
 
 from ruoyi_common.base.model import ExtraModel
 from ruoyi_common.domain.entity import SysRole
@@ -13,19 +13,19 @@ from ruoyi_common.sqlalchemy.transaction import Transactional
 from ruoyi_system.domain.po import SysDeptPo, SysRolePo, SysUserPo, SysUserRolePo
 
 class SysRoleMapper:
-    
+
     """
     角色数据访问层
     """
-    
+
     default_fields = {
         "role_id", "role_name", "role_key", "role_sort", "data_scope", \
         "menu_check_strictly", "dept_check_strictly", "status", "del_flag", \
         "create_time", "remark"
     }
-    
+
     default_columns = ColumnEntityList(SysRolePo, default_fields)
-    
+
     default_select = select(*default_columns).distinct() \
         .outerjoin(SysUserRolePo, SysUserRolePo.role_id == SysRolePo.role_id) \
         .outerjoin(SysUserPo,SysUserPo.user_id == SysUserRolePo.user_id) \
@@ -35,14 +35,14 @@ class SysRoleMapper:
     def select_role_list(cls, role: SysRole) -> List[SysRole]:
         """
         根据条件,查询角色列表
-        
+
         Args:
             role: SysRole: 角色查询条件
-        
+
         Returns:
             List[SysRole]: 角色列表
         """
-        
+
         criterions = [SysRolePo.del_flag=="0"]
         if role.role_id and role.role_id != 0:
             criterions.append(SysRolePo.role_id==role.role_id)
@@ -59,13 +59,13 @@ class SysRoleMapper:
                 criterions.append(SysRolePo.create_time <= extra.end_time)
         if "criterian_meta" in g and g.criterian_meta.scope:
             criterions.append(g.criterian_meta.scope)
-        
+
         stmt = cls.default_select.where(*criterions)
         if "criterian_meta" in g and g.criterian_meta.page:
             g.criterian_meta.page.stmt = stmt
-                
+
         rows = db.session.execute(stmt).all()
-        
+
         eos = list()
         for row in rows:
             role = cls.default_columns.cast(row,SysRole)
@@ -76,10 +76,10 @@ class SysRoleMapper:
     def select_role_permission_by_user_id(cls, user_id: int) -> List[SysRole]:
         """
         根据用户ID,查询角色
-        
+
         Args:
             user_id: int: 用户ID
-        
+
         Returns:
             List[SysRole]: 角色列表
         """
@@ -97,7 +97,7 @@ class SysRoleMapper:
     def select_role_all(cls) -> List[SysRole]:
         """
         查询所有角色
-        
+
         Returns:
             List[SysRole]: 角色列表
         """
@@ -108,16 +108,16 @@ class SysRoleMapper:
             role = cls.default_columns.cast(row,SysRole)
             eos.append(role)
         return eos
-    
+
     @classmethod
     def select_role_list_by_user_name(cls, user_name: str) -> List[SysRole]:
         """
         根据用户名,查询角色列表
-        
+
         Args:
             user_name: str: 用户名
-        
-        Returns:    
+
+        Returns:
             List[SysRole]: 角色列表
         """
         criterions = [SysRolePo.del_flag=="0"]
@@ -134,10 +134,10 @@ class SysRoleMapper:
     def select_role_list_by_user_id(cls, user_id: int) -> List[int]:
         """
         根据用户ID,查询角色选择框列表
-        
+
         Args:
             user_id: int: 用户ID
-        
+
         Returns:
             List[int]: 角色ID列表
         """
@@ -151,13 +151,13 @@ class SysRoleMapper:
     def select_role_by_id(cls, role_id: int) -> Optional[SysRole]:
         """
         根据角色ID,查询角色
-        
+
         Args:
             role_id: int: 角色ID
-        
+
         Returns:
             Optional[SysRole]: 角色
-        """        
+        """
         stmt = cls.default_select.where(SysRolePo.role_id==role_id)
         row = db.session.execute(stmt).one_or_none()
         return cls.default_columns.cast(row,SysRole) if row else None
@@ -166,25 +166,25 @@ class SysRoleMapper:
     def count_user_role_by_role_id(cls, role_id: int) -> int:
         """
         根据角色ID,查询角色使用数量
-        
+
         Args:
             role_id: int: 角色ID
-        
+
         Returns:
             int: 角色使用数量
         """
         stmt = select(func.count()).select_from(SysUserRolePo) \
             .where(SysUserRolePo.role_id==role_id)
         return db.session.execute(stmt).scalar() or 0
-        
+
     @classmethod
     def check_role_name_unique(cls, role_name:str) -> Optional[SysRole]:
         """
         检查角色名称是否唯一
-        
+
         Args:
             role_name: str: 角色名称
-        
+
         Returns:
             Optional[SysRole]: 角色
         """
@@ -198,10 +198,10 @@ class SysRoleMapper:
     def check_role_key_unique(cls, role_key:str) -> Optional[SysRolePo]:
         """
         校验角色权限是否唯一
-        
+
         Args:
             role_key: str: 角色权限
-        
+
         Returns:
             Optional[SysRolePo]: 角色
         """
@@ -210,16 +210,16 @@ class SysRoleMapper:
         stmt = cls.default_select.where(*criterions).limit(1)
         row = db.session.execute(stmt).one_or_none()
         return cls.default_columns.cast(row,SysRole) if row else None
-    
+
     @classmethod
     @Transactional(db.session)
     def insert_role(cls, role: SysRole) -> int:
         """
         新增角色信息
-        
+
         Args:
             role: SysRole: 角色信息
-        
+
         Returns:
             int: 新增记录的ID
         """
@@ -233,20 +233,20 @@ class SysRoleMapper:
             exclude_unset=True,
             exclude_none=True
         )
-        
+
         stmt = insert(SysRolePo).values(data)
         out = db.session.execute(stmt).inserted_primary_key
         return out[0] if out else 0
-            
+
     @classmethod
     @Transactional(db.session)
     def update_role(cls, role: SysRole) -> int:
         """
         修改角色信息
-        
+
         Args:
             role: SysRole: 角色信息
-        
+
         Returns:
             int: 修改数量
         """
@@ -268,10 +268,10 @@ class SysRoleMapper:
     def delete_role_by_id(cls, role_id: int) -> int:
         """
         根据角色ID,删除角色
-        
+
         Args:
             role_id: int: 角色ID
-        
+
         Returns:
             int: 删除数量
         """
@@ -284,13 +284,24 @@ class SysRoleMapper:
     def delete_role_by_ids(cls, role_ids: List[int]) -> int:
         """
         批量删除角色信息
-        
+
         Args:
             role_ids: List[int]: 角色ID列表
-        
+
         Returns:
             int: 删除数量
         """
         stmt = update(SysRolePo).where(SysRolePo.role_id.in_(role_ids)) \
             .values(del_flag="2")
         return db.session.execute(stmt).rowcount
+
+    @classmethod
+    def delete_user_role_by_user_id(cls, user_id):
+        """
+        通过用户ID,删除用户
+
+        Args:
+            user_id: int: 用户ID
+        """
+        stmt = delete(SysUserRolePo).where(SysUserRolePo.user_id==user_id)
+        return db.session.execute(stmt).rowcount

+ 90 - 79
ruoyi_system/mapper/sys_user.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 # @Author  : YY
 
-from typing import List, Optional 
+from typing import List, Optional
 from flask import g
 from sqlalchemy import and_, or_, func, insert, select, update
 
@@ -12,23 +12,23 @@ from ruoyi_common.sqlalchemy.transaction import Transactional
 from ruoyi_system.domain.po import SysDeptPo, SysRolePo, SysUserPo, \
     SysUserRolePo
 from ruoyi_admin.ext import db
+from ruoyi_common.utils import security_util as SecurityUtil
 
 
 class SysUserMapper:
-    
     """
     用户数据访问层
     """
 
     default_fields = {
         "user_id", "dept_id", "user_name", "nick_name", "email", "phonenumber",
-        "avatar", "status", "password", "sex", "del_flag", "login_ip", 
+        "avatar", "status", "password", "sex", "del_flag", "login_ip",
         "login_date", "create_by", "create_time", "update_by", "update_time",
         "remark"
     }
-    
+
     default_columns = ColumnEntityList(SysUserPo, default_fields, False)
-    
+
     @classmethod
     def select_user_list(cls, user: SysUser) -> List[SysUser]:
         """
@@ -40,45 +40,52 @@ class SysUserMapper:
         Returns:
             用户信息列表
         """
-        dept_vo_fields = {"dept_name","leader"}
+        print(user)
+        dept_vo_fields = {"dept_name", "leader"}
         user_columns = ColumnEntityList(SysUserPo, cls.default_fields)
         dept_columns = ColumnEntityList(SysDeptPo, dept_vo_fields)
-                
-        criterions = [SysUserPo.del_flag=="0"]
+
+        criterions = [SysUserPo.del_flag == "0"]
         if user.user_id is not None and user.user_id != 0:
-            criterions.append(SysUserPo.user_id==user.user_id)
+            criterions.append(SysUserPo.user_id == user.user_id)
         if user.user_name is not None and user.user_name != '':
             criterions.append(SysUserPo.user_name.like(f"%{user.user_name}%"))
         if user.status is not None and user.status != 0:
-            criterions.append(SysUserPo.status==user.status)
+            criterions.append(SysUserPo.status == user.status)
         if user.phonenumber is not None and user.phonenumber != '':
             criterions.append(SysUserPo.phonenumber.like(f"%{user.phonenumber}%"))
         if user.dept_id is not None and user.dept_id != 0:
             subquery = select(SysUserPo.dept_id).where(or_(
-                SysUserPo.dept_id==user.dept_id,
+                SysUserPo.dept_id == user.dept_id,
                 func.find_in_set(user.dept_id, SysDeptPo.ancestors)
             )).subquery()
             criterions.append(SysUserPo.dept_id.in_(subquery))
         if g.criterian_meta.extra:
-            extra:ExtraModel = g.criterian_meta.extra
+            extra: ExtraModel = g.criterian_meta.extra
             if extra.start_time and extra.end_time:
                 criterions.append(SysUserPo.create_time >= extra.start_time)
                 criterions.append(SysUserPo.create_time <= extra.end_time)
-        if g.criterian_meta.scope:
+        # 检查是否需要应用数据范围过滤
+        # 只有当用户不是超级管理员时才应用数据范围过滤
+        login_user = SecurityUtil.get_login_user()
+        if g.criterian_meta.scope and (not login_user or not SecurityUtil.is_user_admin(login_user.user)):
             criterions.append(g.criterian_meta.scope)
-        
+
         stmt = select(*user_columns, *dept_columns) \
-            .join(SysDeptPo,SysUserPo.dept_id==SysDeptPo.dept_id) \
+            .join(SysDeptPo, SysUserPo.dept_id == SysDeptPo.dept_id) \
             .where(*criterions)
+        print("Generated SQL:")
+        print(stmt.compile(db.session.bind, compile_kwargs={"literal_binds": True}))
+
         if g.criterian_meta.page:
-            g.criterian_meta.page.stmt = stmt
+                    g.criterian_meta.page.stmt = stmt
 
         rows = db.session.execute(stmt).all()
-        
+
         eos = list()
         for row in rows:
-            user = user_columns.cast(row,SysUser)
-            user.dept = dept_columns.cast(row,SysDept)
+            user = user_columns.cast(row, SysUser)
+            user.dept = dept_columns.cast(row, SysDept)
             eos.append(user)
         return eos
 
@@ -94,25 +101,27 @@ class SysUserMapper:
             List[SysUser]: 用户信息列表
         """
         fields = {"user_id", "dept_id", "user_name", "nick_name", "email", \
-            "phonenumber", "status", "create_time"}
+                  "phonenumber", "status", "create_time"}
         columns = ColumnEntityList(SysUserPo, fields, alia_prefix=False)
-        
-        criterions = [SysUserPo.del_flag=="0"]
+
+        criterions = [SysUserPo.del_flag == "0"]
         if user.user_name:
             criterions.append(SysUserPo.user_name.like(f"%{user.user_name}%"))
         if user.phonenumber:
             criterions.append(SysUserPo.phonenumber.like(f"%{user.phonenumber}%"))
-        if "criterian_meta" in g and g.criterian_meta.scope:
+        # 检查是否需要应用数据范围过滤
+        login_user = SecurityUtil.get_login_user()
+        if "criterian_meta" in g and g.criterian_meta.scope and (not login_user or not SecurityUtil.is_user_admin(login_user.user)):
             criterions.append(g.criterian_meta.scope)
-        
+
         stmt = select(*columns).distinct() \
-            .join(SysDeptPo, SysUserPo.dept_id==SysDeptPo.dept_id) \
-            .join(SysUserRolePo, SysUserPo.user_id==SysUserRolePo.user_id) \
-            .join(SysRolePo, SysUserRolePo.role_id==SysRolePo.role_id) \
+            .join(SysDeptPo, SysUserPo.dept_id == SysDeptPo.dept_id) \
+            .join(SysUserRolePo, SysUserPo.user_id == SysUserRolePo.user_id) \
+            .join(SysRolePo, SysUserRolePo.role_id == SysRolePo.role_id) \
             .where(*criterions)
         rows = db.session.execute(stmt).all()
-        
-        return [columns.cast(row,SysUser) for row in rows]
+
+        return [columns.cast(row, SysUser) for row in rows]
 
     @classmethod
     def select_unallocated_list(cls, user: SysUser) -> List[SysUser]:
@@ -130,16 +139,16 @@ class SysUserMapper:
             "phonenumber", "status", "create_time"
         }
         columns = ColumnEntityList(SysUserPo, fields, False)
-        
-        criterions = [SysUserPo.del_flag=="0"]
+
+        criterions = [SysUserPo.del_flag == "0"]
         subquery = select(SysUserPo.user_id) \
             .join(SysUserRolePo, and_(
-                SysUserPo.user_id==SysUserRolePo.user_id,
-                SysUserRolePo.role_id==user.role_id
-            )) \
+            SysUserPo.user_id == SysUserRolePo.user_id,
+            SysUserRolePo.role_id == user.role_id
+        )) \
             .subquery()
         criterions.append(or_(
-            SysRolePo.role_id!=user.role_id,
+            SysRolePo.role_id != user.role_id,
             SysRolePo.role_id.is_(None)
         ))
         criterions.append(SysUserPo.user_id.notin_(subquery))
@@ -147,17 +156,19 @@ class SysUserMapper:
             criterions.append(SysUserPo.user_name.like(f"%{user.user_name}%"))
         if user.phonenumber:
             criterions.append(SysUserPo.phonenumber.like(f"%{user.phonenumber}%"))
-        if "criterian_meta" in g and g.criterian_meta.scope:
+        # 检查是否需要应用数据范围过滤
+        login_user = SecurityUtil.get_login_user()
+        if "criterian_meta" in g and g.criterian_meta.scope and (not login_user or not SecurityUtil.is_user_admin(login_user.user)):
             criterions.append(g.criterian_meta.scope)
-        
+
         stmt = select(*columns).distinct() \
-            .join(SysDeptPo, SysUserPo.dept_id==SysDeptPo.dept_id) \
-            .join(SysUserRolePo, SysUserPo.user_id==SysUserRolePo.user_id) \
-            .join(SysRolePo, SysUserRolePo.role_id==SysRolePo.role_id) \
+            .join(SysDeptPo, SysUserPo.dept_id == SysDeptPo.dept_id) \
+            .join(SysUserRolePo, SysUserPo.user_id == SysUserRolePo.user_id) \
+            .join(SysRolePo, SysUserRolePo.role_id == SysRolePo.role_id) \
             .where(*criterions)
         rows = db.session.execute(stmt).all()
 
-        return [columns.cast(row,SysUser) for row in rows]
+        return [columns.cast(row, SysUser) for row in rows]
 
     @classmethod
     def select_user_by_user_name(cls, user_name: str) -> Optional[SysUser]:
@@ -184,13 +195,13 @@ class SysUserMapper:
             Optional[SysUser]: 用户信息
         """
         return cls.select_user_by_unique_map("user_id", user_id)
-    
+
     @classmethod
     def select_user_by_unique_map(
-        cls, 
-        key: str, 
-        value: int|str
-        ) -> Optional[SysUser]:
+            cls,
+            key: str,
+            value: int | str
+    ) -> Optional[SysUser]:
         """
         通过含有唯一键的条件,查询用户
 
@@ -202,36 +213,36 @@ class SysUserMapper:
             Optional[SysUser]: 用户信息
         """
         dept_vo_fields = {
-            "dept_id", "parent_id", "dept_name", "order_num", "leader", 
+            "dept_id", "parent_id", "dept_name", "order_num", "leader",
             "status"
         }
         role_vo_fields = {
-            "role_id", "role_name", "role_key", "role_sort", "data_scope", 
+            "role_id", "role_name", "role_key", "role_sort", "data_scope",
             "status"
         }
-        
+
         user_columns = ColumnEntityList(SysUserPo, cls.default_fields)
         dept_columns = ColumnEntityList(SysDeptPo, dept_vo_fields)
         role_columns = ColumnEntityList(SysRolePo, role_vo_fields)
-        
+
         column = getattr(SysUserPo, key)
-        stmt = select(*user_columns,*dept_columns,*role_columns).distinct() \
-            .join(SysDeptPo, SysUserPo.dept_id==SysDeptPo.dept_id) \
-            .join(SysUserRolePo, SysUserPo.user_id==SysUserRolePo.user_id) \
-            .join(SysRolePo, SysUserRolePo.role_id==SysRolePo.role_id) \
-            .where(column==value)
+        stmt = select(*user_columns, *dept_columns, *role_columns).distinct() \
+            .join(SysDeptPo, SysUserPo.dept_id == SysDeptPo.dept_id) \
+            .join(SysUserRolePo, SysUserPo.user_id == SysUserRolePo.user_id) \
+            .join(SysRolePo, SysUserRolePo.role_id == SysRolePo.role_id) \
+            .where(column == value)
         rows = db.session.execute(stmt).all()
-        
+
         eo_tmp = {}
         for row in rows:
             if key in eo_tmp:
                 user = eo_tmp[key]
             else:
-                user = user_columns.cast(row,SysUser)
-                user.dept = dept_columns.cast(row,SysDept)
+                user = user_columns.cast(row, SysUser)
+                user.dept = dept_columns.cast(row, SysDept)
                 eo_tmp[key] = user
-            user.roles.append(role_columns.cast(row,SysRole))
-            
+            user.roles.append(role_columns.cast(row, SysRole))
+
         return eo_tmp[key] if rows else None
 
     @classmethod
@@ -273,14 +284,14 @@ class SysUserMapper:
         """
         fields = {
             "dept_id", "user_name", "nick_name", "email", "avatar", "login_ip",
-            "phonenumber", "sex", "password", "login_date", "status", "update_by", 
+            "phonenumber", "sex", "password", "login_date", "status", "update_by",
             "remark"
         }
         data = user.model_dump(
-            include=fields,exclude_unset=True,exclude_none=True
+            include=fields, exclude_unset=True, exclude_none=True
         )
         stmt = update(SysUserPo) \
-            .where(SysUserPo.user_id==user.user_id) \
+            .where(SysUserPo.user_id == user.user_id) \
             .values(data)
         return db.session.execute(stmt).rowcount
 
@@ -298,10 +309,10 @@ class SysUserMapper:
             int: 修改数量
         """
         stmt = update(SysUserPo) \
-            .where(SysUserPo.user_name==user_name) \
+            .where(SysUserPo.user_name == user_name) \
             .values(**{'avatar': avatar})
         return db.session.execute(stmt).rowcount
-    
+
     @classmethod
     @Transactional(db.session)
     def reset_user_pwd(cls, user_name: str, password: str) -> int:
@@ -316,7 +327,7 @@ class SysUserMapper:
             int: 修改数量
         """
         stmt = update(SysUserPo) \
-            .where(SysUserPo.user_name==user_name) \
+            .where(SysUserPo.user_name == user_name) \
             .values(password=password)
         return db.session.execute(stmt).rowcount
 
@@ -332,11 +343,11 @@ class SysUserMapper:
         Returns:
             int: 删除数量
         """
-        stmt = update(SysUserPo).where(SysUserPo.user_id==user_id) \
+        stmt = update(SysUserPo).where(SysUserPo.user_id == user_id) \
             .values(del_flag="2")
         num = db.session.execute(stmt).rowcount
         return num
-    
+
     @classmethod
     @Transactional(db.session)
     def delete_user_by_ids(cls, user_ids: List[int]) -> int:
@@ -366,7 +377,7 @@ class SysUserMapper:
             int: 0-唯一,大于0-已存在
         """
         stmt = select(func.count()).select_from(SysUserPo) \
-            .where(SysUserPo.user_name==user_name)
+            .where(SysUserPo.user_name == user_name)
         return db.session.execute(stmt).scalar() or 0
 
     @classmethod
@@ -380,15 +391,15 @@ class SysUserMapper:
         Returns:
             Optional[SysUser]: 用户信息
         """
-        fields = {"user_id","phonenumber"}
+        fields = {"user_id", "phonenumber"}
         columns = ColumnEntityList(
             SysUserPo, fields, alia_prefix=False
-            )
-        
+        )
+
         stmt = select(*columns) \
-            .where(SysUserPo.phonenumber==phone_number)
+            .where(SysUserPo.phonenumber == phone_number)
         row = db.session.execute(stmt).one_or_none()
-        return columns.cast(row,SysUser) if row else None
+        return columns.cast(row, SysUser) if row else None
 
     @classmethod
     def check_email_unique(cls, email: str) -> Optional[SysUser]:
@@ -401,11 +412,11 @@ class SysUserMapper:
         Returns:
             Optional[SysUser]: 用户信息
         """
-        fields = {"user_id","email"}
+        fields = {"user_id", "email"}
         columns = ColumnEntityList(
             SysUserPo, fields, alia_prefix=False
-            )
-        
-        stmt = select(*columns).where(SysUserPo.email==email)
+        )
+
+        stmt = select(*columns).where(SysUserPo.email == email)
         row = db.session.execute(stmt).one_or_none()
-        return columns.cast(row,SysUser) if row else None
+        return columns.cast(row, SysUser) if row else None

+ 60 - 52
ruoyi_system/service/sys_user.py

@@ -21,7 +21,7 @@ from ruoyi_system.service.sys_config import SysConfigService
 
 
 class SysUserService:
-    
+
     @classmethod
     @DataScope(dept=True, user=True)
     def select_user_list(cls, query: SysUser) -> List[SysUser]:
@@ -35,7 +35,7 @@ class SysUserService:
             List[SysUser]: 用户列表
         """
         return SysUserMapper.select_user_list(query)
-        
+
     @classmethod
     @DataScope(dept=True, user=True)
     def select_allocated_list(cls, query: SysUser) -> List[SysUser]:
@@ -49,7 +49,7 @@ class SysUserService:
             List[SysUser]: 已分配用户列表
         """
         return SysUserMapper.select_allocated_list(query)
-        
+
     @classmethod
     @DataScope(dept=True, user=True)
     def select_unallocated_list(cls, query: SysUser) -> List[SysUser]:
@@ -63,7 +63,7 @@ class SysUserService:
             List[SysUser]: 已分配用户列表
         """
         return SysUserMapper.select_unallocated_list(query)
-        
+
     @classmethod
     def select_user_by_user_name(cls, user_name: str) -> Optional[SysUser]:
         """
@@ -76,7 +76,7 @@ class SysUserService:
             Optional[SysUser]: 用户信息
         """
         return SysUserMapper.select_user_by_user_name(user_name)
-        
+
     @classmethod
     def select_user_by_id(cls, user_id: int) -> Optional[SysUser]:
         """
@@ -89,25 +89,25 @@ class SysUserService:
             Optional[SysUser]: 用户信息
         """
         return SysUserMapper.select_user_by_id(user_id)
-    
+
     @classmethod
     def select_user_role_group(cls, user_name: str) -> str:
         """
         查询用户角色组
-        
+
         Args:
             user_name (str): 用户名
-        
+
         Returns:
             str: 角色组
         """
-        eos:List[SysRole] = SysRoleMapper.select_role_list_by_user_name(user_name)
+        eos: List[SysRole] = SysRoleMapper.select_role_list_by_user_name(user_name)
         if not eos:
             return StringUtil.EMPTY
         return ",".join([eo.role_name for eo in eos])
-    
+
     @classmethod
-    def select_user_post_group(cls, user_name: str)->str:
+    def select_user_post_group(cls, user_name: str) -> str:
         """
         查询用户岗位组
 
@@ -117,13 +117,13 @@ class SysUserService:
         Returns:
             str: 岗位组
         """
-        eos:List[SysPost] = SysPostMapper.select_posts_by_user_name(user_name)
+        eos: List[SysPost] = SysPostMapper.select_posts_by_user_name(user_name)
         if not eos:
             return StringUtil.EMPTY
         return ",".join([eo.post_name for eo in eos])
-    
+
     @classmethod
-    def check_user_name_unique(cls, user: SysUser) -> Literal["0","1"]:
+    def check_user_name_unique(cls, user: SysUser) -> Literal["0", "1"]:
         """
         校验用户名是否唯一
 
@@ -138,9 +138,9 @@ class SysUserService:
         if num > 0:
             return UserConstants.NOT_UNIQUE
         return UserConstants.UNIQUE
-    
+
     @classmethod
-    def check_phone_unique(cls, user: SysUser) -> Literal["0","1"]:
+    def check_phone_unique(cls, user: SysUser) -> Literal["0", "1"]:
         """
         校验手机号是否唯一
 
@@ -151,13 +151,13 @@ class SysUserService:
             str: 唯一标识符, 0-唯一, 1-不唯一
         """
         user_id = -1 if user.user_id is None else user.user_id
-        eo:SysUser = SysUserMapper.check_phone_unique(user.phonenumber)
+        eo: SysUser = SysUserMapper.check_phone_unique(user.phonenumber)
         if eo and eo.user_id != user_id:
             return UserConstants.NOT_UNIQUE
         return UserConstants.UNIQUE
-    
+
     @classmethod
-    def check_email_unique(cls, user: SysUser) -> Literal["0","1"]:
+    def check_email_unique(cls, user: SysUser) -> str:
         """
         校验邮箱是否唯一
 
@@ -168,11 +168,11 @@ class SysUserService:
             str: 唯一标识符, 0-唯一, 1-不唯一
         """
         user_email = -1 if user.email is None else user.email
-        eo:SysUser = SysUserMapper.check_email_unique(user.email)
+        eo: SysUser = SysUserMapper.check_email_unique(user.email)
         if eo and eo.email != user_email:
             return UserConstants.NOT_UNIQUE
         return UserConstants.UNIQUE
-    
+
     @classmethod
     def check_user_allowed(cls, user: SysUser):
         """
@@ -185,9 +185,9 @@ class SysUserService:
             ServiceException: 超级管理员用户不允许操作
 
         """
-        if user.is_admin:
+        if user.is_admin():
             raise ServiceException("不允许操作超级管理员用户")
-    
+
     @classmethod
     def check_user_data_scope(cls, user_id: Optional[int]):
         """
@@ -204,7 +204,7 @@ class SysUserService:
             users: List[SysUser] = cls.select_user_list(user)
             if not users:
                 raise ServiceException("没有权限访问用户数据")
-            
+
     @classmethod
     @Transactional(db.session)
     def insert_user(cls, user: SysUser) -> bool:
@@ -222,7 +222,7 @@ class SysUserService:
         cls.insert_user_post_by_user(user)
         cls.insert_user_role_by_user(user)
         return last_pid > 0
-    
+
     @classmethod
     def register_user(cls, user: SysUser) -> bool:
         """
@@ -236,8 +236,9 @@ class SysUserService:
         """
         flag = SysUserMapper.insert_user(user)
         return flag > 0
-    
+
     @classmethod
+    @Transactional(db.session)
     def update_user(cls, user: SysUser) -> bool:
         """
         更新用户
@@ -248,21 +249,28 @@ class SysUserService:
         Returns:
             bool: 操作结果
         """
+        # 删除用户角色关联
+        SysRoleMapper.delete_user_role_by_user_id(user.user_id)
+        # 新增用户和角色的关联
+        cls.insert_user_role_by_user(user)
+        # 删除用户岗位关联
+        SysUserPostMapper.delete_user_post_by_user_id(user.user_id)
+        # 新增用户岗位关联
+        cls.insert_user_post_by_user(user)
         return SysUserMapper.update_user(user)
 
     @classmethod
     @Transactional(db.session)
-    def insert_user_auth(cls, user_id:int, role_ids:List[int]):
+    def insert_user_auth(cls, user_id: int, role_ids: List[int]):
         """
         新增用户角色
-        
+
         Args:
             user_id: 用户id
             role_ids: 角色id列表
         """
-        SysUserMapper.delete_user_role_by_user_id(user_id)
         cls.insert_user_role(user_id, role_ids)
-    
+
     @classmethod
     def delete_users_by_id(cls, id: int) -> bool:
         """
@@ -275,7 +283,7 @@ class SysUserService:
             bool: 操作结果
         """
         return SysUserMapper.delete_user_by_id(id) > 0
-        
+
     @classmethod
     def delete_users_by_ids(cls, ids: List[int]) -> bool:
         """
@@ -288,7 +296,7 @@ class SysUserService:
             bool: 操作结果
         """
         return SysUserMapper.delete_user_by_ids(ids) > 0
-    
+
     @classmethod
     def update_user_status(cls, user: SysUser) -> bool:
         """
@@ -316,7 +324,7 @@ class SysUserService:
         return SysUserMapper.update_user(user) > 0
 
     @classmethod
-    def update_user_avatar(cls, user_name:str, avatar:str) -> bool:
+    def update_user_avatar(cls, user_name: str, avatar: str) -> bool:
         """
         更新用户头像
 
@@ -341,9 +349,9 @@ class SysUserService:
             bool: 操作结果
         """
         return SysUserMapper.update_user(user) > 0
-    
+
     @classmethod
-    def reset_user_pwd(cls, username:str, password:str) -> bool:
+    def reset_user_pwd(cls, username: str, password: str) -> bool:
         """
         重置用户密码
 
@@ -355,23 +363,23 @@ class SysUserService:
             bool: 操作结果
         """
         return SysUserMapper.reset_user_pwd(username, password) > 0
-  
+
     @classmethod
     def insert_user_role_by_user(cls, user: SysUser):
         """
         新增用户角色
-        
+
         Args:
             user: 用户信息
         """
         cls.insert_user_role(user.user_id, user.role_ids)
-    
+
     @classmethod
     @Transactional(db.session)
-    def insert_user_role(cls, user_id:int, role_ids:List[int]):
+    def insert_user_role(cls, user_id: int, role_ids: List[int]):
         """
         新增用户角色
-        
+
         Args:
             user_id: 用户id
             role_ids: 角色id列表
@@ -380,25 +388,25 @@ class SysUserService:
             lists = [
                 SysUserRole(user_id=user_id, role_id=role_id)
                 for role_id in role_ids
-                ]
+            ]
             SysUserRoleMapper.batch_user_role(lists)
-    
+
     @classmethod
     def insert_user_post_by_user(cls, user: SysUser):
         """
         新增用户岗位
-        
+
         Args:
             user: 用户信息
         """
         cls.insert_user_post(user.user_id, user.post_ids)
-        
+
     @classmethod
     @Transactional(db.session)
-    def insert_user_post(cls, user_id:int, post_ids:List[int]):
+    def insert_user_post(cls, user_id: int, post_ids: List[int]):
         """
         新增用户岗位
-        
+
         Args:
             user_id: 用户id
             post_ids: 岗位id列表
@@ -407,7 +415,7 @@ class SysUserService:
             lists = [
                 SysUserPost(user_id=user_id, post_id=post_id)
                 for post_id in post_ids
-                ]
+            ]
             SysUserPostMapper.batch_user_post(lists)
 
     @classmethod
@@ -415,20 +423,20 @@ class SysUserService:
     def update_user_roles(cls, user_id: int, role_ids: List[int]) -> bool:
         """
         更新用户角色
-        
+
         Args:
             user_id: 用户id
             role_ids: 角色id列表
-        
+
         Returns:
             bool: 操作结果
         """
         SysUserRoleMapper.delete_user_role_by_user_id(user_id)
         cls.insert_user_role(user_id, role_ids)
         return True
-                
+
     @classmethod
-    def import_user(cls, users: List[SysUser],is_update:bool=False) -> str:
+    def import_user(cls, users: List[SysUser], is_update: bool = False) -> str:
         """
         导入用户
 
@@ -477,5 +485,5 @@ class SysUserService:
             raise ServiceException(fail_msg)
         else:
             success_msg = f"恭喜您,数据已全部导入成功!共 {success_count} 条,数据如下:" \
-                + success_msg
+                          + success_msg
         return success_msg

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor