Переглянути джерело

Merge remote-tracking branch 'origin/test_dev' into test

# Conflicts:
#	marriageAdmin-vue/src/views/admin/AdminUserList.vue
#	marriageAdmin-vue/src/views/points-product/PointsProductList.vue
#	service/admin/src/main/java/com/zhentao/service/AdminSecurityService.java
YH_0525 1 місяць тому
батько
коміт
e311d26348
72 змінених файлів з 5604 додано та 451 видалено
  1. 496 198
      marriageAdmin-vue/src/views/admin/AdminUserList.vue
  2. 431 239
      marriageAdmin-vue/src/views/points-product/PointsProductList.vue
  3. 53 0
      service/Essential/src/main/java/com/zhentao/exception/CustomErrorController.java
  4. 120 0
      service/Essential/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java
  5. 26 0
      service/admin/src/main/java/com/zhentao/config/MyBatisPlusConfig.java
  6. 122 0
      service/admin/src/main/java/com/zhentao/config/PermissionInterceptor.java
  7. 16 0
      service/admin/src/main/java/com/zhentao/config/WebMvcConfig.java
  8. 390 0
      service/admin/src/main/java/com/zhentao/controller/AdminUserController.java
  9. 196 0
      service/admin/src/main/java/com/zhentao/controller/PointsProductController.java
  10. 136 0
      service/admin/src/main/java/com/zhentao/entity/AdminPermission.java
  11. 104 0
      service/admin/src/main/java/com/zhentao/entity/AdminRole.java
  12. 80 0
      service/admin/src/main/java/com/zhentao/entity/AdminRolePermission.java
  13. 80 0
      service/admin/src/main/java/com/zhentao/entity/AdminUserRole.java
  14. 76 0
      service/admin/src/main/java/com/zhentao/entity/Permission.java
  15. 56 0
      service/admin/src/main/java/com/zhentao/entity/Role.java
  16. 40 0
      service/admin/src/main/java/com/zhentao/entity/RolePermission.java
  17. 40 0
      service/admin/src/main/java/com/zhentao/entity/UserRole.java
  18. 53 0
      service/admin/src/main/java/com/zhentao/exception/CustomErrorController.java
  19. 103 0
      service/admin/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java
  20. 18 0
      service/admin/src/main/java/com/zhentao/mapper/AdminPermissionMapper.java
  21. 18 0
      service/admin/src/main/java/com/zhentao/mapper/AdminRoleMapper.java
  22. 18 0
      service/admin/src/main/java/com/zhentao/mapper/AdminRolePermissionMapper.java
  23. 30 0
      service/admin/src/main/java/com/zhentao/mapper/AdminUserRoleMapper.java
  24. 31 0
      service/admin/src/main/java/com/zhentao/mapper/PermissionMapper.java
  25. 13 0
      service/admin/src/main/java/com/zhentao/mapper/RoleMapper.java
  26. 26 0
      service/admin/src/main/java/com/zhentao/mapper/RolePermissionMapper.java
  27. 26 0
      service/admin/src/main/java/com/zhentao/mapper/UserRoleMapper.java
  28. 83 0
      service/admin/src/main/java/com/zhentao/security/AdminUserDetails.java
  29. 188 0
      service/admin/src/main/java/com/zhentao/security/AdminUserDetailsService.java
  30. 81 0
      service/admin/src/main/java/com/zhentao/security/JwtAuthenticationFilter.java
  31. 85 0
      service/admin/src/main/java/com/zhentao/security/SecurityConfig.java
  32. 13 0
      service/admin/src/main/java/com/zhentao/service/AdminPermissionService.java
  33. 13 0
      service/admin/src/main/java/com/zhentao/service/AdminRolePermissionService.java
  34. 13 0
      service/admin/src/main/java/com/zhentao/service/AdminRoleService.java
  35. 110 14
      service/admin/src/main/java/com/zhentao/service/AdminSecurityService.java
  36. 13 0
      service/admin/src/main/java/com/zhentao/service/AdminUserRoleService.java
  37. 13 0
      service/admin/src/main/java/com/zhentao/service/AdminUserService.java
  38. 53 0
      service/admin/src/main/java/com/zhentao/service/PermissionService.java
  39. 43 0
      service/admin/src/main/java/com/zhentao/service/PointsProductService.java
  40. 49 0
      service/admin/src/main/java/com/zhentao/service/RoleService.java
  41. 59 0
      service/admin/src/main/java/com/zhentao/service/UserService.java
  42. 22 0
      service/admin/src/main/java/com/zhentao/service/impl/AdminPermissionServiceImpl.java
  43. 22 0
      service/admin/src/main/java/com/zhentao/service/impl/AdminRolePermissionServiceImpl.java
  44. 22 0
      service/admin/src/main/java/com/zhentao/service/impl/AdminRoleServiceImpl.java
  45. 22 0
      service/admin/src/main/java/com/zhentao/service/impl/AdminUserRoleServiceImpl.java
  46. 22 0
      service/admin/src/main/java/com/zhentao/service/impl/AdminUserServiceImpl.java
  47. 165 0
      service/admin/src/main/java/com/zhentao/service/impl/PermissionServiceImpl.java
  48. 123 0
      service/admin/src/main/java/com/zhentao/service/impl/PointsProductServiceImpl.java
  49. 115 0
      service/admin/src/main/java/com/zhentao/service/impl/RoleServiceImpl.java
  50. 207 0
      service/admin/src/main/java/com/zhentao/service/impl/UserServiceImpl.java
  51. 109 0
      service/admin/src/main/java/com/zhentao/util/JwtUtil.java
  52. 61 0
      service/admin/src/main/java/com/zhentao/util/PasswordUtil.java
  53. 25 0
      service/admin/src/main/resources/com/zhentao/mapper/AdminPermissionMapper.xml
  54. 21 0
      service/admin/src/main/resources/com/zhentao/mapper/AdminRoleMapper.xml
  55. 17 0
      service/admin/src/main/resources/com/zhentao/mapper/AdminRolePermissionMapper.xml
  56. 25 0
      service/admin/src/main/resources/com/zhentao/mapper/AdminUserMapper.xml
  57. 30 0
      service/admin/src/main/resources/com/zhentao/mapper/AdminUserRoleMapper.xml
  58. 27 0
      service/admin/src/main/resources/com/zhentao/mapper/PointsProductMapper.xml
  59. 87 0
      service/admin/src/main/resources/db/schema.sql
  60. 37 0
      service/admin/src/main/resources/mapper/PermissionMapper.xml
  61. 19 0
      service/admin/src/main/resources/mapper/RolePermissionMapper.xml
  62. 19 0
      service/admin/src/main/resources/mapper/UserRoleMapper.xml
  63. 53 0
      service/dynamic/src/main/java/com/zhentao/exception/CustomErrorController.java
  64. 120 0
      service/dynamic/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java
  65. 26 0
      service/homePage/src/main/java/com/zhentao/config/MyBatisPlusConfig.java
  66. 53 0
      service/homePage/src/main/java/com/zhentao/exception/CustomErrorController.java
  67. 120 0
      service/homePage/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java
  68. 92 0
      service/randomMatch/src/main/java/com/zhentao/common/Result.java
  69. 53 0
      service/randomMatch/src/main/java/com/zhentao/exception/CustomErrorController.java
  70. 103 0
      service/randomMatch/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java
  71. 53 0
      service/websocket/src/main/java/com/zhentao/exception/CustomErrorController.java
  72. 120 0
      service/websocket/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java

+ 496 - 198
marriageAdmin-vue/src/views/admin/AdminUserList.vue

@@ -1,45 +1,72 @@
 <template>
-  <div class="admin-user-container">
-    <el-card class="box-card">
-      <template #header>
-        <div class="card-header">
-          <span>管理员列表</span>
-          <el-button type="primary" @click="handleAdd">
-            <el-icon><Plus /></el-icon>
-            添加管理员
-          </el-button>
-        </div>
-      </template>
-
-      <!-- 搜索栏 -->
-      <el-form :inline="true" :model="searchForm" class="search-form">
-        <el-form-item label="用户名">
-          <el-input v-model="searchForm.username" placeholder="请输入用户名" clearable />
-        </el-form-item>
-        <el-form-item label="状态">
-          <el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
-            <el-option label="启用" :value="1" />
-            <el-option label="禁用" :value="0" />
-          </el-select>
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" @click="handleSearch">搜索</el-button>
-          <el-button @click="handleReset">重置</el-button>
-        </el-form-item>
-      </el-form>
-
-      <!-- 表格 -->
-      <el-table :data="tableData" v-loading="loading" stripe>
-        <el-table-column prop="id" label="ID" width="80" />
+  <div class="admin-user-list-container">
+    <h2 class="page-title">管理员管理</h2>
+    
+    <!-- 操作栏 -->
+    <el-card shadow="never" class="toolbar-card">
+      <el-row :gutter="20">
+        <el-col :span="18">
+          <el-space wrap>
+            <el-button type="primary" icon="Plus" @click="handleCreate">
+              注册管理员
+            </el-button>
+            <el-button icon="Refresh" @click="loadAdminUserList">刷新</el-button>
+          </el-space>
+        </el-col>
+        <el-col :span="6">
+          <el-input
+            v-model="searchQuery"
+            placeholder="搜索用户名、姓名、手机号"
+            clearable
+            @clear="loadAdminUserList"
+            @keyup.enter="loadAdminUserList"
+          >
+            <template #append>
+              <el-button icon="Search" @click="loadAdminUserList" />
+            </template>
+          </el-input>
+        </el-col>
+      </el-row>
+    </el-card>
+    
+    <!-- 管理员列表 -->
+    <el-card shadow="never" class="table-card">
+      <el-table
+        v-loading="loading"
+        :data="adminUserList"
+        stripe
+        style="width: 100%"
+      >
+        <template #empty>
+          <div class="custom-empty-state">
+            <el-icon class="empty-icon"><UserFilled /></el-icon>
+            <div class="empty-text">暂无管理员数据</div>
+            <div class="empty-hint">点击"注册管理员"按钮开始创建</div>
+          </div>
+        </template>
+        <el-table-column type="index" label="序号" width="60" />
+        
         <el-table-column prop="username" label="用户名" width="150" />
-        <el-table-column prop="nickname" label="昵称" width="150" />
-        <el-table-column prop="role" label="角色" width="120">
+        
+        <el-table-column prop="realName" label="真实姓名" width="120" />
+        
+        <el-table-column prop="phone" label="手机号" width="130" />
+        
+        <el-table-column prop="email" label="邮箱" width="200" />
+        
+        <el-table-column label="角色" width="150">
           <template #default="{ row }">
-            <el-tag :type="row.role === 'super' ? 'danger' : 'primary'">
-              {{ row.role === 'super' ? '超级管理员' : '普通管理员' }}
+            <el-tag
+              v-for="role in row.roles"
+              :key="role.id"
+              :type="role.roleCode === 'SUPER_ADMIN' ? 'danger' : 'primary'"
+              style="margin-right: 5px"
+            >
+              {{ role.roleName }}
             </el-tag>
           </template>
         </el-table-column>
+        
         <el-table-column prop="status" label="状态" width="100">
           <template #default="{ row }">
             <el-tag :type="row.status === 1 ? 'success' : 'danger'">
@@ -47,104 +74,193 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="180" />
-        <el-table-column prop="lastLoginTime" label="最后登录" width="180" />
-        <el-table-column label="操作" fixed="right" width="200">
+        
+        <el-table-column prop="createTime" label="创建时间" width="180">
+          <template #default="{ row }">
+            {{ formatDateTime(row.createTime) }}
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="lastLoginTime" label="最后登录" width="180">
           <template #default="{ row }">
-            <el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
-            <el-button 
-              :type="row.status === 1 ? 'warning' : 'success'" 
-              size="small" 
-              @click="handleToggleStatus(row)"
+            {{ row.lastLoginTime ? formatDateTime(row.lastLoginTime) : '从未登录' }}
+          </template>
+        </el-table-column>
+        
+        <el-table-column label="操作" width="280" fixed="right">
+          <template #default="{ row }">
+            <el-button
+              type="primary"
+              link
+              size="small"
+              @click="handleEdit(row)"
+            >
+              编辑
+            </el-button>
+            <el-button
+              v-if="row.status === 1"
+              type="warning"
+              link
+              size="small"
+              :disabled="isSuperAdmin(row) || isCurrentUser(row)"
+              @click="handleDisable(row)"
+            >
+              禁用
+            </el-button>
+            <el-button
+              v-else
+              type="success"
+              link
+              size="small"
+              :disabled="isSuperAdmin(row)"
+              @click="handleEnable(row)"
             >
-              {{ row.status === 1 ? '禁用' : '启用' }}
+              启用
             </el-button>
-            <el-button type="danger" size="small" @click="handleDelete(row)" :disabled="row.role === 'super'">
+            <el-button
+              type="danger"
+              link
+              size="small"
+              :disabled="isSuperAdmin(row)"
+              @click="handleDelete(row)"
+            >
               删除
             </el-button>
           </template>
         </el-table-column>
       </el-table>
-
+      
       <!-- 分页 -->
-      <el-pagination
-        v-model:current-page="pagination.page"
-        v-model:page-size="pagination.pageSize"
-        :page-sizes="[10, 20, 50, 100]"
-        :total="pagination.total"
-        layout="total, sizes, prev, pager, next, jumper"
-        @size-change="handleSizeChange"
-        @current-change="handleCurrentChange"
-        class="pagination"
-      />
+      <div class="pagination-container">
+        <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="loadAdminUserList"
+          @current-change="loadAdminUserList"
+        />
+      </div>
     </el-card>
-
-    <!-- 添加/编辑对话框 -->
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
-      <el-form :model="formData" :rules="formRules" ref="formRef" label-width="100px">
+    
+    <!-- 注册/编辑对话框 -->
+    <el-dialog
+      v-model="dialogVisible"
+      :title="dialogTitle"
+      width="600px"
+      @close="resetForm"
+    >
+      <el-form
+        ref="formRef"
+        :model="formData"
+        :rules="formRules"
+        label-width="100px"
+      >
         <el-form-item label="用户名" prop="username">
-          <el-input v-model="formData.username" placeholder="请输入用户名" :disabled="isEdit" />
+          <el-input
+            v-model="formData.username"
+            placeholder="请输入用户名"
+            :disabled="isEdit"
+          />
+        </el-form-item>
+        
+        <el-form-item label="密码" :prop="isEdit ? '' : 'password'">
+          <el-input
+            v-model="formData.password"
+            type="password"
+            :placeholder="isEdit ? '留空则不修改密码' : '请输入密码(至少6位)'"
+            show-password
+          />
         </el-form-item>
-        <el-form-item label="昵称" prop="nickname">
-          <el-input v-model="formData.nickname" placeholder="请输入昵称" />
+        
+        <el-form-item label="真实姓名" prop="realName">
+          <el-input
+            v-model="formData.realName"
+            placeholder="请输入真实姓名"
+          />
         </el-form-item>
-        <el-form-item label="密码" prop="password" v-if="!isEdit">
-          <el-input v-model="formData.password" type="password" placeholder="请输入密码" show-password />
+        
+        <el-form-item label="手机号" prop="phone">
+          <el-input
+            v-model="formData.phone"
+            placeholder="请输入手机号"
+          />
         </el-form-item>
-        <el-form-item label="角色" prop="role">
-          <el-select v-model="formData.role" placeholder="请选择角色">
-            <el-option label="普通管理员" value="admin" />
-            <el-option label="超级管理员" value="super" />
+        
+        <el-form-item label="邮箱" prop="email">
+          <el-input
+            v-model="formData.email"
+            placeholder="请输入邮箱"
+          />
+        </el-form-item>
+        
+        <el-form-item label="角色" prop="roleIds">
+          <el-select
+            v-model="formData.roleIds"
+            multiple
+            placeholder="请选择角色"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="role in roleList"
+              :key="role.id"
+              :label="role.roleName"
+              :value="role.id"
+            />
           </el-select>
         </el-form-item>
+        
         <el-form-item label="状态" prop="status">
-          <el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
+          <el-radio-group v-model="formData.status">
+            <el-radio :label="1">启用</el-radio>
+            <el-radio :label="0">禁用</el-radio>
+          </el-radio-group>
         </el-form-item>
       </el-form>
+      
       <template #footer>
         <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
+        <el-button type="primary" @click="handleSubmit" :loading="submitting">
+          确定
+        </el-button>
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted } from 'vue'
+import { ref, onMounted, computed } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
-import { Plus } from '@element-plus/icons-vue'
+import { Plus, Refresh, Search, UserFilled } from '@element-plus/icons-vue'
+import { API_BASE_URL, API_ENDPOINTS } from '@/config/api'
+import { useUserStore } from '@/stores/user'
 
-// 搜索表单
-const searchForm = reactive({
-  username: '',
-  status: null
-})
+const userStore = useUserStore()
 
-// 表格数据
-const tableData = ref([])
+// 数据
 const loading = ref(false)
-
-// 分页
-const pagination = reactive({
-  page: 1,
-  pageSize: 10,
-  total: 0
-})
+const adminUserList = ref([])
+const searchQuery = ref('')
+const currentPage = ref(1)
+const pageSize = ref(10)
+const total = ref(0)
+const roleList = ref([])
 
 // 对话框
 const dialogVisible = ref(false)
-const dialogTitle = ref('添加管理员')
+const dialogTitle = ref('注册管理员')
 const isEdit = ref(false)
+const submitting = ref(false)
 const formRef = ref(null)
-const submitLoading = ref(false)
-
-// 表单数据
-const formData = reactive({
+const formData = ref({
   id: null,
   username: '',
-  nickname: '',
   password: '',
-  role: 'admin',
+  realName: '',
+  phone: '',
+  email: '',
+  roleIds: [],
   status: 1
 })
 
@@ -152,78 +268,84 @@ const formData = reactive({
 const formRules = {
   username: [
     { required: true, message: '请输入用户名', trigger: 'blur' },
-    { min: 3, max: 20, message: '用户名长度在3-20个字符', trigger: 'blur' }
-  ],
-  nickname: [
-    { required: true, message: '请输入昵称', trigger: 'blur' }
+    { min: 3, max: 20, message: '用户名长度在3到20个字符', trigger: 'blur' }
   ],
   password: [
     { required: true, message: '请输入密码', trigger: 'blur' },
-    { min: 6, max: 20, message: '密码长度在6-20个字符', trigger: 'blur' }
+    { min: 6, max: 20, message: '密码长度在6到20个字符', trigger: 'blur' }
+  ],
+  realName: [
+    { required: true, message: '请输入真实姓名', trigger: 'blur' }
+  ],
+  phone: [
+    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  ],
+  email: [
+    { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
   ],
-  role: [
+  roleIds: [
     { required: true, message: '请选择角色', trigger: 'change' }
   ]
 }
 
-// 获取列表数据
-const fetchData = async () => {
+// 加载管理员列表
+const loadAdminUserList = async () => {
   loading.value = true
   try {
-    // 模拟数据,实际应调用API
-    tableData.value = [
+    const response = await fetch(
+      `${API_BASE_URL}${API_ENDPOINTS.ADMIN_USER_LIST}?page=${currentPage.value}&pageSize=${pageSize.value}&keyword=${encodeURIComponent(searchQuery.value || '')}`,
       {
-        id: 1,
-        username: 'admin',
-        nickname: '超级管理员',
-        role: 'super',
-        status: 1,
-        createTime: '2024-01-01 00:00:00',
-        lastLoginTime: '2024-12-11 19:00:00'
-      },
-      {
-        id: 2,
-        username: 'manager',
-        nickname: '运营管理员',
-        role: 'admin',
-        status: 1,
-        createTime: '2024-06-01 10:00:00',
-        lastLoginTime: '2024-12-11 18:30:00'
+        method: 'GET',
+        headers: {
+          'Authorization': `Bearer ${userStore.token}`,
+          'Content-Type': 'application/json'
+        }
       }
-    ]
-    pagination.total = tableData.value.length
+    )
+    
+    const result = await response.json()
+    if (result.code === 200) {
+      adminUserList.value = result.data.list || []
+      total.value = result.data.total || 0
+    } else {
+      ElMessage.error(result.message || '加载失败')
+    }
   } catch (error) {
-    ElMessage.error('获取数据失败')
+    console.error('加载管理员列表失败:', error)
+    ElMessage.error('加载失败')
   } finally {
     loading.value = false
   }
 }
 
-// 搜索
-const handleSearch = () => {
-  pagination.page = 1
-  fetchData()
-}
-
-// 重置
-const handleReset = () => {
-  searchForm.username = ''
-  searchForm.status = null
-  handleSearch()
+// 加载角色列表
+const loadRoleList = async () => {
+  try {
+    const response = await fetch(
+      `${API_BASE_URL}${API_ENDPOINTS.ADMIN_USER_ROLES}`,
+      {
+        method: 'GET',
+        headers: {
+          'Authorization': `Bearer ${userStore.token}`,
+          'Content-Type': 'application/json'
+        }
+      }
+    )
+    
+    const result = await response.json()
+    if (result.code === 200) {
+      roleList.value = result.data || []
+    }
+  } catch (error) {
+    console.error('加载角色列表失败:', error)
+  }
 }
 
-// 添加
-const handleAdd = () => {
+// 创建
+const handleCreate = () => {
   isEdit.value = false
-  dialogTitle.value = '添加管理员'
-  Object.assign(formData, {
-    id: null,
-    username: '',
-    nickname: '',
-    password: '',
-    role: 'admin',
-    status: 1
-  })
+  dialogTitle.value = '注册管理员'
+  resetForm()
   dialogVisible.value = true
 }
 
@@ -231,100 +353,276 @@ const handleAdd = () => {
 const handleEdit = (row) => {
   isEdit.value = true
   dialogTitle.value = '编辑管理员'
-  Object.assign(formData, {
+  formData.value = {
     id: row.id,
     username: row.username,
-    nickname: row.nickname,
     password: '',
-    role: row.role,
+    realName: row.realName || '',
+    phone: row.phone || '',
+    email: row.email || '',
+    roleIds: row.roles ? row.roles.map(r => r.id) : [],
     status: row.status
-  })
-  dialogVisible.value = true
-}
-
-// 切换状态
-const handleToggleStatus = async (row) => {
-  const action = row.status === 1 ? '禁用' : '启用'
-  try {
-    await ElMessageBox.confirm(`确定要${action}该管理员吗?`, '提示', {
-      type: 'warning'
-    })
-    // 调用API
-    row.status = row.status === 1 ? 0 : 1
-    ElMessage.success(`${action}成功`)
-  } catch {
-    // 取消操作
   }
+  dialogVisible.value = true
 }
 
 // 删除
 const handleDelete = async (row) => {
   try {
-    await ElMessageBox.confirm('确定要删除该管理员吗?', '警告', {
-      type: 'warning'
-    })
-    // 调用API
-    ElMessage.success('删除成功')
-    fetchData()
-  } catch {
-    // 取消操作
+    await ElMessageBox.confirm(
+      `确定要删除管理员"${row.username}"吗?`,
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }
+    )
+    
+    const response = await fetch(
+      `${API_BASE_URL}${API_ENDPOINTS.ADMIN_USER_DELETE}/${row.id}`,
+      {
+        method: 'DELETE',
+        headers: {
+          'Authorization': `Bearer ${userStore.token}`,
+          'Content-Type': 'application/json'
+        }
+      }
+    )
+    
+    const result = await response.json()
+    if (result.code === 200) {
+      ElMessage.success('删除成功')
+      loadAdminUserList()
+    } else {
+      ElMessage.error(result.message || '删除失败')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('删除失败:', error)
+      ElMessage.error('删除失败')
+    }
   }
 }
 
 // 提交表单
 const handleSubmit = async () => {
   if (!formRef.value) return
+  
   await formRef.value.validate(async (valid) => {
-    if (valid) {
-      submitLoading.value = true
-      try {
-        // 调用API
-        ElMessage.success(isEdit.value ? '编辑成功' : '添加成功')
+    if (!valid) return
+    
+    submitting.value = true
+    try {
+      const url = isEdit.value
+        ? `${API_BASE_URL}${API_ENDPOINTS.ADMIN_USER_UPDATE}/${formData.value.id}`
+        : `${API_BASE_URL}${API_ENDPOINTS.ADMIN_USER_REGISTER}`
+      
+      const method = isEdit.value ? 'PUT' : 'POST'
+      
+      const body = { ...formData.value }
+      if (isEdit.value && !body.password) {
+        delete body.password
+      }
+      
+      const response = await fetch(url, {
+        method,
+        headers: {
+          'Authorization': `Bearer ${userStore.token}`,
+          'Content-Type': 'application/json'
+        },
+        body: JSON.stringify(body)
+      })
+      
+      const result = await response.json()
+      if (result.code === 200) {
+        ElMessage.success(isEdit.value ? '更新成功' : '注册成功')
         dialogVisible.value = false
-        fetchData()
-      } catch (error) {
-        ElMessage.error('操作失败')
-      } finally {
-        submitLoading.value = false
+        loadAdminUserList()
+      } else {
+        ElMessage.error(result.message || '操作失败')
       }
+    } catch (error) {
+      console.error('操作失败:', error)
+      ElMessage.error('操作失败')
+    } finally {
+      submitting.value = false
     }
   })
 }
 
-// 分页
-const handleSizeChange = (val) => {
-  pagination.pageSize = val
-  fetchData()
+// 重置表单
+const resetForm = () => {
+  formData.value = {
+    id: null,
+    username: '',
+    password: '',
+    realName: '',
+    phone: '',
+    email: '',
+    roleIds: [],
+    status: 1
+  }
+  if (formRef.value) {
+    formRef.value.resetFields()
+  }
+}
+
+// 判断是否是超级管理员
+const isSuperAdmin = (row) => {
+  return row.roles && row.roles.some(r => r.roleCode === 'SUPER_ADMIN')
 }
 
-const handleCurrentChange = (val) => {
-  pagination.page = val
-  fetchData()
+// 判断是否是当前用户
+const isCurrentUser = (row) => {
+  return row.id === userStore.userInfo?.id
+}
+
+// 禁用管理员
+const handleDisable = async (row) => {
+  try {
+    await ElMessageBox.confirm(
+      `确定要禁用管理员"${row.username}"吗?禁用后将无法登录系统。`,
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }
+    )
+    
+    const response = await fetch(
+      `${API_BASE_URL}${API_ENDPOINTS.ADMIN_USER_DISABLE}/${row.id}`,
+      {
+        method: 'PUT',
+        headers: {
+          'Authorization': `Bearer ${userStore.token}`,
+          'Content-Type': 'application/json'
+        }
+      }
+    )
+    
+    const result = await response.json()
+    if (result.code === 200) {
+      ElMessage.success('禁用成功')
+      loadAdminUserList()
+    } else {
+      ElMessage.error(result.message || '禁用失败')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('禁用失败:', error)
+      ElMessage.error('禁用失败')
+    }
+  }
 }
 
+// 启用管理员
+const handleEnable = async (row) => {
+  try {
+    await ElMessageBox.confirm(
+      `确定要启用管理员"${row.username}"吗?`,
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'info'
+      }
+    )
+    
+    const response = await fetch(
+      `${API_BASE_URL}${API_ENDPOINTS.ADMIN_USER_ENABLE}/${row.id}`,
+      {
+        method: 'PUT',
+        headers: {
+          'Authorization': `Bearer ${userStore.token}`,
+          'Content-Type': 'application/json'
+        }
+      }
+    )
+    
+    const result = await response.json()
+    if (result.code === 200) {
+      ElMessage.success('启用成功')
+      loadAdminUserList()
+    } else {
+      ElMessage.error(result.message || '启用失败')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('启用失败:', error)
+      ElMessage.error('启用失败')
+    }
+  }
+}
+
+// 格式化日期时间
+const formatDateTime = (dateTime) => {
+  if (!dateTime) return '-'
+  const date = new Date(dateTime)
+  return date.toLocaleString('zh-CN', {
+    year: 'numeric',
+    month: '2-digit',
+    day: '2-digit',
+    hour: '2-digit',
+    minute: '2-digit',
+    second: '2-digit'
+  })
+}
+
+// 初始化
 onMounted(() => {
-  fetchData()
+  loadAdminUserList()
+  loadRoleList()
 })
 </script>
 
 <style scoped>
-.admin-user-container {
+.admin-user-list-container {
   padding: 20px;
 }
 
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
+.page-title {
+  margin: 0 0 20px 0;
+  font-size: 24px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.toolbar-card {
+  margin-bottom: 20px;
 }
 
-.search-form {
+.table-card {
   margin-bottom: 20px;
 }
 
-.pagination {
+.custom-empty-state {
+  padding: 40px 0;
+  text-align: center;
+}
+
+.empty-icon {
+  font-size: 64px;
+  color: #c0c4cc;
+  margin-bottom: 16px;
+}
+
+.empty-text {
+  font-size: 14px;
+  color: #909399;
+  margin-bottom: 8px;
+}
+
+.empty-hint {
+  font-size: 12px;
+  color: #c0c4cc;
+}
+
+.pagination-container {
   margin-top: 20px;
   display: flex;
   justify-content: flex-end;
 }
 </style>
+

+ 431 - 239
marriageAdmin-vue/src/views/points-product/PointsProductList.vue

@@ -1,110 +1,237 @@
 <template>
   <div class="points-product-container">
-    <el-card class="box-card">
-      <template #header>
-        <div class="card-header">
-          <span>积分商品列表</span>
-          <el-button type="primary" @click="handleAdd">
-            <el-icon><Plus /></el-icon>
-            添加商品
-          </el-button>
-        </div>
-      </template>
-
-      <!-- 搜索栏 -->
-      <el-form :inline="true" :model="searchForm" class="search-form">
-        <el-form-item label="商品名称">
-          <el-input v-model="searchForm.name" placeholder="请输入商品名称" clearable />
-        </el-form-item>
-        <el-form-item label="状态">
-          <el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
-            <el-option label="上架" :value="1" />
-            <el-option label="下架" :value="0" />
-          </el-select>
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" @click="handleSearch">搜索</el-button>
-          <el-button @click="handleReset">重置</el-button>
-        </el-form-item>
-      </el-form>
-
-      <!-- 表格 -->
-      <el-table :data="tableData" v-loading="loading" stripe>
-        <el-table-column prop="id" label="ID" width="80" />
-        <el-table-column prop="image" label="商品图片" width="120">
+    <h2 class="page-title">积分商品管理</h2>
+    
+    <!-- 工具栏 -->
+    <el-card shadow="never" class="toolbar-card">
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-button type="primary" icon="Plus" @click="showCreateDialog">新增商品</el-button>
+          <el-button icon="Refresh" @click="loadList">刷新</el-button>
+        </el-col>
+        <el-col :span="12">
+          <el-space wrap style="float: right">
+            <el-select v-model="filters.category" placeholder="商品类型" clearable style="width: 120px" @change="loadList">
+              <el-option label="全部" :value="undefined" />
+              <el-option label="实物" :value="1" />
+              <el-option label="虚拟" :value="2" />
+            </el-select>
+            
+            <el-select v-model="filters.status" placeholder="状态" clearable style="width: 120px" @change="loadList">
+              <el-option label="全部" :value="undefined" />
+              <el-option label="上架" :value="1" />
+              <el-option label="下架" :value="0" />
+            </el-select>
+            
+            <el-input v-model="filters.keyword" placeholder="搜索商品名称" clearable style="width: 200px" @keyup.enter="loadList">
+              <template #append>
+                <el-button icon="Search" @click="loadList" />
+              </template>
+            </el-input>
+          </el-space>
+        </el-col>
+      </el-row>
+    </el-card>
+    
+    <!-- 商品列表 -->
+    <el-card shadow="never" class="table-card">
+      <el-table
+        v-loading="loading"
+        :data="list"
+        stripe
+        style="width: 100%"
+      >
+        <template #empty>
+          <el-empty description="暂无商品数据" />
+        </template>
+        
+        <el-table-column type="index" label="序号" width="60" />
+        
+        <el-table-column label="商品图片" width="120">
+          <template #default="{ row }">
+            <el-image
+              v-if="row.imageUrl"
+              :src="row.imageUrl"
+              fit="cover"
+              style="width: 80px; height: 80px; border-radius: 6px; cursor: pointer;"
+              :preview-src-list="[row.imageUrl]"
+            />
+            <span v-else style="color: #999;">暂无图片</span>
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="name" label="商品名称" min-width="150" show-overflow-tooltip />
+        
+        <el-table-column prop="description" label="商品描述" min-width="200" show-overflow-tooltip />
+        
+        <el-table-column prop="pointsPrice" label="积分价格" width="100" align="center">
+          <template #default="{ row }">
+            <span style="color: #E91E63; font-weight: bold;">{{ row.pointsPrice }}</span>
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="stock" label="库存" width="80" align="center">
+          <template #default="{ row }">
+            <el-tag :type="row.stock > 0 ? 'success' : 'danger'" size="small">
+              {{ row.stock }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="category" label="类型" width="100" align="center">
           <template #default="{ row }">
-            <el-image :src="row.image" style="width: 60px; height: 60px" fit="cover" />
+            <el-tag :type="row.category === 1 ? 'primary' : 'warning'" size="small">
+              {{ row.category === 1 ? '实物' : '虚拟' }}
+            </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="name" label="商品名称" width="200" />
-        <el-table-column prop="points" label="所需积分" width="120">
+        
+        <el-table-column prop="isRecommend" label="推荐" width="80" align="center">
           <template #default="{ row }">
-            <span style="color: #E6A23C; font-weight: bold;">{{ row.points }}</span>
+            <el-tag v-if="row.isRecommend === 1" type="danger" size="small">推荐</el-tag>
+            <span v-else style="color: #999;">-</span>
           </template>
         </el-table-column>
-        <el-table-column prop="stock" label="库存" width="100" />
-        <el-table-column prop="exchangeCount" label="已兑换" width="100" />
-        <el-table-column prop="status" label="状态" width="100">
+        
+        <el-table-column prop="status" label="状态" width="100" align="center">
           <template #default="{ row }">
-            <el-tag :type="row.status === 1 ? 'success' : 'info'">
+            <el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
               {{ row.status === 1 ? '上架' : '下架' }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="180" />
-        <el-table-column label="操作" fixed="right" width="200">
+        
+        <el-table-column prop="sortOrder" label="排序" width="80" align="center" />
+        
+        <el-table-column label="操作" width="220" fixed="right">
           <template #default="{ row }">
-            <el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
-            <el-button 
-              :type="row.status === 1 ? 'warning' : 'success'" 
-              size="small" 
-              @click="handleToggleStatus(row)"
-            >
-              {{ row.status === 1 ? '下架' : '上架' }}
-            </el-button>
-            <el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button>
+            <el-space>
+              <el-button type="primary" size="small" @click="showEditDialog(row)">
+                <el-icon><Edit /></el-icon>
+                编辑
+              </el-button>
+              <el-button
+                v-if="row.status === 0"
+                type="success"
+                size="small"
+                @click="handleOnShelf(row)"
+              >
+                上架
+              </el-button>
+              <el-button
+                v-if="row.status === 1"
+                type="warning"
+                size="small"
+                @click="handleOffShelf(row)"
+              >
+                下架
+              </el-button>
+              <el-button type="danger" size="small" @click="handleDelete(row)">
+                <el-icon><Delete /></el-icon>
+                删除
+              </el-button>
+            </el-space>
           </template>
         </el-table-column>
       </el-table>
-
+      
       <!-- 分页 -->
-      <el-pagination
-        v-model:current-page="pagination.page"
-        v-model:page-size="pagination.pageSize"
-        :page-sizes="[10, 20, 50, 100]"
-        :total="pagination.total"
-        layout="total, sizes, prev, pager, next, jumper"
-        @size-change="handleSizeChange"
-        @current-change="handleCurrentChange"
-        class="pagination"
-      />
+      <div class="pagination-container" v-if="list.length > 0">
+        <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
+          :total="total"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="loadList"
+          @current-change="loadList"
+        />
+      </div>
     </el-card>
-
-    <!-- 添加/编辑对话框 -->
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px">
-      <el-form :model="formData" :rules="formRules" ref="formRef" label-width="100px">
+    
+    <!-- 创建/编辑对话框 -->
+    <el-dialog
+      v-model="dialogVisible"
+      :title="isEdit ? '编辑商品' : '新增商品'"
+      width="700px"
+      @close="resetForm"
+    >
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
         <el-form-item label="商品名称" prop="name">
-          <el-input v-model="formData.name" placeholder="请输入商品名称" />
-        </el-form-item>
-        <el-form-item label="商品图片" prop="image">
-          <el-input v-model="formData.image" placeholder="请输入图片URL" />
-        </el-form-item>
-        <el-form-item label="所需积分" prop="points">
-          <el-input-number v-model="formData.points" :min="1" :max="999999" />
-        </el-form-item>
-        <el-form-item label="库存数量" prop="stock">
-          <el-input-number v-model="formData.stock" :min="0" :max="999999" />
+          <el-input v-model="form.name" placeholder="请输入商品名称" />
         </el-form-item>
+        
         <el-form-item label="商品描述" prop="description">
-          <el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入商品描述" />
+          <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入商品描述" />
         </el-form-item>
-        <el-form-item label="状态" prop="status">
-          <el-switch v-model="formData.status" :active-value="1" :inactive-value="0" active-text="上架" inactive-text="下架" />
+        
+        <el-form-item label="商品图片" prop="imageUrl">
+          <el-upload
+            class="image-uploader"
+            :action="uploadAction"
+            :headers="uploadHeaders"
+            :show-file-list="false"
+            :on-success="handleImageSuccess"
+            :before-upload="beforeImageUpload"
+            accept="image/*"
+          >
+            <el-image
+              v-if="form.imageUrl"
+              :src="form.imageUrl"
+              class="uploaded-image"
+              fit="cover"
+            />
+            <el-icon v-else class="uploader-icon"><Plus /></el-icon>
+          </el-upload>
+          <div class="upload-tip">支持 JPG、PNG 格式,大小不超过 10MB</div>
         </el-form-item>
+        
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="积分价格" prop="pointsPrice">
+              <el-input-number v-model="form.pointsPrice" :min="1" :precision="0" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="库存" prop="stock">
+              <el-input-number v-model="form.stock" :min="0" :precision="0" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="商品类型" prop="category">
+              <el-select v-model="form.category" placeholder="请选择商品类型" style="width: 100%">
+                <el-option label="实物" :value="1" />
+                <el-option label="虚拟" :value="2" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="排序" prop="sortOrder">
+              <el-input-number v-model="form.sortOrder" :min="0" :precision="0" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="是否推荐" prop="isRecommend">
+              <el-switch v-model="form.isRecommend" :active-value="1" :inactive-value="0" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="状态" prop="status">
+              <el-switch v-model="form.status" :active-value="1" :inactive-value="0" active-text="上架" inactive-text="下架" />
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
+      
       <template #footer>
         <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
+        <el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
       </template>
     </el-dialog>
   </div>
@@ -113,221 +240,286 @@
 <script setup>
 import { ref, reactive, onMounted } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
-import { Plus } from '@element-plus/icons-vue'
+import { Plus, Search, Refresh, Edit, Delete } from '@element-plus/icons-vue'
+import request from '@/utils/request'
 
-// 搜索表单
-const searchForm = reactive({
-  name: '',
-  status: null
-})
-
-// 表格数据
-const tableData = ref([])
 const loading = ref(false)
-
-// 分页
-const pagination = reactive({
-  page: 1,
-  pageSize: 10,
-  total: 0
-})
-
-// 对话框
 const dialogVisible = ref(false)
-const dialogTitle = ref('添加商品')
 const isEdit = ref(false)
-const formRef = ref(null)
 const submitLoading = ref(false)
+const formRef = ref(null)
+const currentPage = ref(1)
+const pageSize = ref(10)
+const total = ref(0)
+const list = ref([])
+
+const filters = reactive({
+  status: null,
+  category: null,
+  keyword: ''
+})
 
-// 表单数据
-const formData = reactive({
+const form = reactive({
   id: null,
   name: '',
-  image: '',
-  points: 100,
-  stock: 100,
   description: '',
-  status: 1
+  imageUrl: '',
+  pointsPrice: 0,
+  stock: 0,
+  category: 1,
+  isRecommend: 0,
+  status: 0,
+  sortOrder: 0
 })
 
-// 表单验证规则
-const formRules = {
-  name: [
-    { required: true, message: '请输入商品名称', trigger: 'blur' }
-  ],
-  points: [
-    { required: true, message: '请输入所需积分', trigger: 'blur' }
-  ],
-  stock: [
-    { required: true, message: '请输入库存数量', trigger: 'blur' }
-  ]
+const rules = {
+  name: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],
+  pointsPrice: [{ required: true, message: '请输入积分价格', trigger: 'blur' }],
+  category: [{ required: true, message: '请选择商品类型', trigger: 'change' }]
 }
 
-// 获取列表数据
-const fetchData = async () => {
+// 上传配置
+const uploadAction = ref('/admin/upload/image')
+const uploadHeaders = ref({
+  // 如果需要token,可以从localStorage获取
+  // Authorization: 'Bearer ' + localStorage.getItem('token')
+})
+
+const loadList = async () => {
   loading.value = true
   try {
-    // 模拟数据
-    tableData.value = [
-      {
-        id: 1,
-        name: '优质会员7天体验卡',
-        image: 'https://via.placeholder.com/100',
-        points: 500,
-        stock: 100,
-        exchangeCount: 50,
-        description: '7天VIP会员体验',
-        status: 1,
-        createTime: '2024-01-01 00:00:00'
-      },
-      {
-        id: 2,
-        name: '精美礼品盒',
-        image: 'https://via.placeholder.com/100',
-        points: 1000,
-        stock: 50,
-        exchangeCount: 20,
-        description: '精美礼品盒一份',
-        status: 1,
-        createTime: '2024-02-01 00:00:00'
-      },
-      {
-        id: 3,
-        name: '恋爱课程优惠券',
-        image: 'https://via.placeholder.com/100',
-        points: 200,
-        stock: 200,
-        exchangeCount: 80,
-        description: '恋爱课程8折优惠券',
-        status: 1,
-        createTime: '2024-03-01 00:00:00'
-      }
-    ]
-    pagination.total = tableData.value.length
+    const params = {
+      page: currentPage.value,
+      size: pageSize.value
+    }
+    
+    if (filters.status !== null && filters.status !== undefined) params.status = filters.status
+    if (filters.category !== null && filters.category !== undefined) params.category = filters.category
+    if (filters.keyword) params.keyword = filters.keyword
+    
+    const response = await request.get('/admin/points-product/list', { params })
+    
+    if (response.code === 200) {
+      list.value = response.data.records || []
+      total.value = response.data.total || 0
+    }
   } catch (error) {
-    ElMessage.error('获取数据失败')
+    console.error('加载失败:', error)
+    ElMessage.error('加载失败')
   } finally {
     loading.value = false
   }
 }
 
-// 搜索
-const handleSearch = () => {
-  pagination.page = 1
-  fetchData()
-}
-
-// 重置
-const handleReset = () => {
-  searchForm.name = ''
-  searchForm.status = null
-  handleSearch()
-}
-
-// 添加
-const handleAdd = () => {
+const showCreateDialog = () => {
   isEdit.value = false
-  dialogTitle.value = '添加商品'
-  Object.assign(formData, {
-    id: null,
-    name: '',
-    image: '',
-    points: 100,
-    stock: 100,
-    description: '',
-    status: 1
-  })
+  resetForm()
   dialogVisible.value = true
 }
 
-// 编辑
-const handleEdit = (row) => {
+const showEditDialog = (row) => {
   isEdit.value = true
-  dialogTitle.value = '编辑商品'
-  Object.assign(formData, { ...row })
+  Object.assign(form, {
+    id: row.id,
+    name: row.name,
+    description: row.description || '',
+    imageUrl: row.imageUrl || '',
+    pointsPrice: row.pointsPrice,
+    stock: row.stock || 0,
+    category: row.category,
+    isRecommend: row.isRecommend || 0,
+    status: row.status,
+    sortOrder: row.sortOrder || 0
+  })
   dialogVisible.value = true
 }
 
-// 切换状态
-const handleToggleStatus = async (row) => {
-  const action = row.status === 1 ? '下架' : '上架'
+const resetForm = () => {
+  if (formRef.value) {
+    formRef.value.resetFields()
+  }
+  form.id = null
+  form.name = ''
+  form.description = ''
+  form.imageUrl = ''
+  form.pointsPrice = 0
+  form.stock = 0
+  form.category = 1
+  form.isRecommend = 0
+  form.status = 0
+  form.sortOrder = 0
+}
+
+const handleSubmit = async () => {
+  if (!formRef.value) return
+  try {
+    await formRef.value.validate()
+    submitLoading.value = true
+    
+    const url = isEdit.value ? '/admin/points-product/update' : '/admin/points-product/create'
+    const method = isEdit.value ? 'put' : 'post'
+    
+    const response = await request[method](url, form)
+    if (response.code === 200) {
+      ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
+      dialogVisible.value = false
+      loadList()
+    } else {
+      ElMessage.error(response.msg || '操作失败')
+    }
+  } catch (error) {
+    console.error('提交失败:', error)
+    if (error !== 'cancel') {
+      ElMessage.error('操作失败')
+    }
+  } finally {
+    submitLoading.value = false
+  }
+}
+
+const handleOnShelf = async (row) => {
   try {
-    await ElMessageBox.confirm(`确定要${action}该商品吗?`, '提示', {
-      type: 'warning'
-    })
-    row.status = row.status === 1 ? 0 : 1
-    ElMessage.success(`${action}成功`)
-  } catch {
-    // 取消操作
+    const response = await request.put(`/admin/points-product/on-shelf/${row.id}`)
+    if (response.code === 200) {
+      ElMessage.success('上架成功')
+      loadList()
+    } else {
+      ElMessage.error(response.msg || '上架失败')
+    }
+  } catch (error) {
+    console.error('上架失败:', error)
+    ElMessage.error('上架失败')
+  }
+}
+
+const handleOffShelf = async (row) => {
+  try {
+    const response = await request.put(`/admin/points-product/off-shelf/${row.id}`)
+    if (response.code === 200) {
+      ElMessage.success('下架成功')
+      loadList()
+    } else {
+      ElMessage.error(response.msg || '下架失败')
+    }
+  } catch (error) {
+    console.error('下架失败:', error)
+    ElMessage.error('下架失败')
   }
 }
 
-// 删除
 const handleDelete = async (row) => {
   try {
-    await ElMessageBox.confirm('确定要删除该商品吗?', '警告', {
+    await ElMessageBox.confirm('确定要删除这个商品吗?', '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
       type: 'warning'
     })
-    ElMessage.success('删除成功')
-    fetchData()
-  } catch {
-    // 取消操作
+    
+    const response = await request.delete(`/admin/points-product/delete/${row.id}`)
+    if (response.code === 200) {
+      ElMessage.success('删除成功')
+      loadList()
+    } else {
+      ElMessage.error(response.msg || '删除失败')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('删除失败:', error)
+    }
   }
 }
 
-// 提交表单
-const handleSubmit = async () => {
-  if (!formRef.value) return
-  await formRef.value.validate(async (valid) => {
-    if (valid) {
-      submitLoading.value = true
-      try {
-        ElMessage.success(isEdit.value ? '编辑成功' : '添加成功')
-        dialogVisible.value = false
-        fetchData()
-      } catch (error) {
-        ElMessage.error('操作失败')
-      } finally {
-        submitLoading.value = false
-      }
-    }
-  })
-}
+// 图片上传相关
+const beforeImageUpload = (file) => {
+  const isImage = file.type.startsWith('image/')
+  const isLt10M = file.size / 1024 / 1024 < 10
 
-// 分页
-const handleSizeChange = (val) => {
-  pagination.pageSize = val
-  fetchData()
+  if (!isImage) {
+    ElMessage.error('只能上传图片文件!')
+    return false
+  }
+  if (!isLt10M) {
+    ElMessage.error('图片大小不能超过 10MB!')
+    return false
+  }
+  return true
 }
 
-const handleCurrentChange = (val) => {
-  pagination.page = val
-  fetchData()
+const handleImageSuccess = (response) => {
+  if (response.code === 200 && response.data && response.data.url) {
+    form.imageUrl = response.data.url
+    ElMessage.success('图片上传成功')
+  } else {
+    ElMessage.error('图片上传失败')
+  }
 }
 
-onMounted(() => {
-  fetchData()
-})
+onMounted(() => loadList())
 </script>
 
 <style scoped>
 .points-product-container {
-  padding: 20px;
+  padding: 0;
 }
 
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
+.page-title {
+  font-size: 24px;
+  font-weight: bold;
+  color: #333;
+  margin: 0 0 20px 0;
 }
 
-.search-form {
+.toolbar-card {
   margin-bottom: 20px;
 }
 
-.pagination {
-  margin-top: 20px;
+.table-card {
+  margin-bottom: 20px;
+}
+
+.pagination-container {
   display: flex;
-  justify-content: flex-end;
+  justify-content: center;
+  margin-top: 20px;
+  padding: 20px 0;
+}
+
+/* 图片上传样式 */
+.image-uploader {
+  :deep(.el-upload) {
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+    transition: all 0.3s;
+    width: 150px;
+    height: 150px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  
+  :deep(.el-upload:hover) {
+    border-color: #409eff;
+  }
+}
+
+.uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+}
+
+.uploaded-image {
+  width: 150px;
+  height: 150px;
+  display: block;
+}
+
+.upload-tip {
+  font-size: 12px;
+  color: #999;
+  margin-top: 8px;
 }
 </style>
+

+ 53 - 0
service/Essential/src/main/java/com/zhentao/exception/CustomErrorController.java

@@ -0,0 +1,53 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.springframework.boot.web.servlet.error.ErrorController;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 自定义错误控制器
+ * 处理Spring Boot默认的/error路径
+ */
+@RestController
+public class CustomErrorController implements ErrorController {
+
+    @RequestMapping("/error")
+    public ResponseEntity<Result<String>> handleError(HttpServletRequest request) {
+        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
+        String errorMessage = (String) request.getAttribute("javax.servlet.error.message");
+        Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
+
+        if (statusCode == null) {
+            statusCode = 500;
+        }
+
+        if (errorMessage == null || errorMessage.isEmpty()) {
+            if (statusCode == 404) {
+                errorMessage = "请求的资源不存在";
+            } else if (statusCode == 403) {
+                errorMessage = "拒绝访问";
+            } else if (statusCode == 401) {
+                errorMessage = "未授权,请先登录";
+            } else if (statusCode == 500) {
+                errorMessage = "服务器内部错误";
+            } else {
+                errorMessage = "未知错误";
+            }
+        }
+
+        if (exception != null && exception.getMessage() != null) {
+            errorMessage = exception.getMessage();
+        }
+
+        Result<String> result = Result.error(statusCode, errorMessage);
+        HttpStatus httpStatus = HttpStatus.valueOf(statusCode);
+        
+        return new ResponseEntity<>(result, httpStatus);
+    }
+}
+

+ 120 - 0
service/Essential/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java

@@ -0,0 +1,120 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 全局异常处理器
+ * 统一处理所有Controller抛出的异常
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+    /**
+     * 处理运行时异常
+     */
+    @ExceptionHandler(RuntimeException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
+        logger.error("运行时异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: " + e.getMessage());
+    }
+
+    /**
+     * 处理所有异常
+     */
+    @ExceptionHandler(Exception.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleException(Exception e, HttpServletRequest request) {
+        logger.error("系统异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统异常: " + e.getMessage());
+    }
+
+    /**
+     * 处理参数校验异常(@Valid)
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数校验失败: {}", errorMessage);
+        return Result.error(400, "参数校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理参数绑定异常
+     */
+    @ExceptionHandler(BindException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleBindException(BindException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数绑定失败: {}", errorMessage);
+        return Result.error(400, "参数绑定失败: " + errorMessage);
+    }
+
+    /**
+     * 处理约束校验异常
+     */
+    @ExceptionHandler(ConstraintViolationException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleConstraintViolationException(ConstraintViolationException e) {
+        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
+        String errorMessage = violations.stream()
+                .map(ConstraintViolation::getMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("约束校验失败: {}", errorMessage);
+        return Result.error(400, "约束校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理文件上传大小超限异常
+     */
+    @ExceptionHandler(MaxUploadSizeExceededException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
+        logger.warn("文件上传大小超限: {}", e.getMessage());
+        return Result.error(400, "文件大小超过限制,单个文件最大10MB,请压缩后重试");
+    }
+
+    /**
+     * 处理空指针异常
+     */
+    @ExceptionHandler(NullPointerException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
+        logger.error("空指针异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: 空指针异常");
+    }
+
+    /**
+     * 处理非法参数异常
+     */
+    @ExceptionHandler(IllegalArgumentException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleIllegalArgumentException(IllegalArgumentException e) {
+        logger.warn("非法参数: {}", e.getMessage());
+        return Result.error(400, "参数错误: " + e.getMessage());
+    }
+}
+

+ 26 - 0
service/admin/src/main/java/com/zhentao/config/MyBatisPlusConfig.java

@@ -0,0 +1,26 @@
+package com.zhentao.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * MyBatis-Plus配置类
+ */
+@Configuration
+public class MyBatisPlusConfig {
+
+    /**
+     * 分页插件
+     * 用于正确计算分页查询的总数
+     */
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+        return interceptor;
+    }
+}
+

+ 122 - 0
service/admin/src/main/java/com/zhentao/config/PermissionInterceptor.java

@@ -0,0 +1,122 @@
+package com.zhentao.config;
+
+import com.zhentao.entity.AdminUser;
+import com.zhentao.entity.Permission;
+import com.zhentao.mapper.PermissionMapper;
+import com.zhentao.service.AuthService;
+import com.zhentao.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 权限拦截器
+ * 根据Controller路径判断用户是否有权限访问
+ */
+@Component
+public class PermissionInterceptor implements HandlerInterceptor {
+    
+    @Autowired
+    private AuthService authService;
+    
+    @Autowired
+    private UserService userService;
+    
+    @Autowired
+    private PermissionMapper permissionMapper;
+    
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        // 只拦截Controller方法
+        if (!(handler instanceof HandlerMethod)) {
+            return true;
+        }
+        
+        // 获取请求路径和方法
+        String requestPath = request.getRequestURI();
+        String method = request.getMethod();
+        
+        // 排除登录接口
+        if (requestPath.contains("/admin/auth/login") || requestPath.contains("/admin/auth/logout")) {
+            return true;
+        }
+        
+        // 获取token
+        String token = request.getHeader("Authorization");
+        if (token == null || token.isEmpty()) {
+            response.setStatus(401);
+            response.setContentType("application/json;charset=UTF-8");
+            response.getWriter().write("{\"code\":401,\"message\":\"未授权,请先登录\"}");
+            return false;
+        }
+        
+        // 获取用户信息
+        AdminUser user = authService.getUserByToken(token);
+        if (user == null) {
+            response.setStatus(401);
+            response.setContentType("application/json;charset=UTF-8");
+            response.getWriter().write("{\"code\":401,\"message\":\"Token无效或已过期,请重新登录\"}");
+            return false;
+        }
+        
+        // 检查用户是否是超级管理员
+        boolean isSuperAdmin = userService.isSuperAdmin(user.getId());
+        
+        // 超级管理员拥有所有权限
+        if (isSuperAdmin) {
+            return true;
+        }
+        
+        // 查询用户的所有权限
+        List<Permission> permissions = permissionMapper.selectPermissionsByUserId(user.getId());
+        
+        // 检查是否有匹配的权限
+        boolean hasPermission = permissions.stream().anyMatch(permission -> {
+            String permissionPath = permission.getPath();
+            String permissionMethod = permission.getMethod();
+            
+            // 检查方法是否匹配
+            boolean methodMatch = "ALL".equals(permissionMethod) || method.equals(permissionMethod);
+            if (!methodMatch) {
+                return false;
+            }
+            
+            // 支持通配符匹配
+            if (permissionPath.contains("**")) {
+                // /admin/user/** 匹配 /admin/user/xxx/yyy
+                String prefix = permissionPath.substring(0, permissionPath.length() - 2);
+                if (requestPath.startsWith(prefix)) {
+                    return true;
+                }
+            } else if (permissionPath.contains("*")) {
+                // /admin/user/* 匹配 /admin/user/xxx (单层)
+                String pattern = permissionPath.replace("*", "[^/]+");
+                if (requestPath.matches(pattern)) {
+                    return true;
+                }
+            } else {
+                // 精确匹配
+                if (requestPath.equals(permissionPath)) {
+                    return true;
+                }
+            }
+            
+            return false;
+        });
+        
+        if (!hasPermission) {
+            response.setStatus(403);
+            response.setContentType("application/json;charset=UTF-8");
+            response.getWriter().write("{\"code\":403,\"message\":\"没有权限访问该资源\"}");
+            return false;
+        }
+        
+        return true;
+    }
+}
+

+ 16 - 0
service/admin/src/main/java/com/zhentao/config/WebMvcConfig.java

@@ -0,0 +1,16 @@
+package com.zhentao.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * Web MVC配置
+ * 注意:权限验证已迁移到Spring Security,不再使用PermissionInterceptor
+ */
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+    
+    // 权限验证已由Spring Security处理,不再需要拦截器
+    // 如需添加其他拦截器,可在此处配置
+}
+

+ 390 - 0
service/admin/src/main/java/com/zhentao/controller/AdminUserController.java

@@ -0,0 +1,390 @@
+package com.zhentao.controller;
+
+import com.zhentao.common.Result;
+import com.zhentao.entity.AdminUser;
+import com.zhentao.entity.Role;
+import com.zhentao.service.AuthService;
+import com.zhentao.service.RoleService;
+import com.zhentao.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 管理员用户管理控制器
+ */
+@RestController
+@RequestMapping("/admin/admin-user")
+@CrossOrigin(origins = "*")
+public class AdminUserController {
+    
+    @Autowired
+    private UserService userService;
+    
+    @Autowired
+    private AuthService authService;
+    
+    @Autowired
+    private RoleService roleService;
+    
+    /**
+     * 注册新管理员(仅超级管理员可用)
+     */
+    @PostMapping("/register")
+    public Result<Map<String, Object>> register(@RequestBody Map<String, Object> params,
+                                                  @RequestHeader(value = "Authorization", required = false) String token) {
+        try {
+            // 验证token
+            if (token == null || token.isEmpty()) {
+                return Result.error(401, "未授权,请先登录");
+            }
+            
+            AdminUser currentUser = authService.getUserByToken(token);
+            if (currentUser == null) {
+                return Result.error(401, "Token无效或已过期,请重新登录");
+            }
+            
+            // 检查当前用户是否是超级管理员
+            if (!userService.isSuperAdmin(currentUser.getId())) {
+                return Result.error(403, "只有超级管理员可以注册新管理员");
+            }
+            
+            // 获取参数
+            String username = (String) params.get("username");
+            String password = (String) params.get("password");
+            String realName = (String) params.get("realName");
+            String phone = (String) params.get("phone");
+            String email = (String) params.get("email");
+            Integer roleId = params.get("roleId") != null ? Integer.parseInt(params.get("roleId").toString()) : null;
+            
+            if (username == null || username.trim().isEmpty()) {
+                return Result.error("用户名不能为空");
+            }
+            if (password == null || password.trim().isEmpty()) {
+                return Result.error("密码不能为空");
+            }
+            if (password.length() < 6) {
+                return Result.error("密码长度不能少于6位");
+            }
+            
+            // 创建用户对象
+            AdminUser user = new AdminUser();
+            user.setUsername(username.trim());
+            user.setPassword(password);
+            user.setRealName(realName);
+            user.setPhone(phone);
+            user.setEmail(email);
+            
+            // 注册用户
+            AdminUser newUser = userService.register(user, roleId);
+            
+            Map<String, Object> data = new HashMap<>();
+            data.put("user", newUser);
+            data.put("message", "注册成功");
+            
+            return Result.success(data);
+        } catch (RuntimeException e) {
+            return Result.error(e.getMessage());
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("注册失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 管理员列表(分页)
+     */
+    @GetMapping("/list")
+    public Result<Map<String, Object>> list(
+            @RequestParam(defaultValue = "1") Integer page,
+            @RequestParam(defaultValue = "10") Integer pageSize,
+            @RequestParam(required = false) String keyword) {
+        try {
+            com.baomidou.mybatisplus.extension.plugins.pagination.Page<AdminUser> result = userService.getUserList(page, pageSize, keyword);
+            
+            // 为每个用户填充角色信息
+            List<Map<String, Object>> userList = result.getRecords().stream().map(user -> {
+                Map<String, Object> userMap = new HashMap<>();
+                userMap.put("id", user.getId());
+                userMap.put("username", user.getUsername());
+                userMap.put("realName", user.getRealName());
+                userMap.put("phone", user.getPhone());
+                userMap.put("email", user.getEmail());
+                userMap.put("status", user.getStatus());
+                userMap.put("createTime", user.getCreateTime());
+                userMap.put("updateTime", user.getUpdateTime());
+                userMap.put("lastLoginTime", user.getLastLoginTime());
+                
+                // 查询角色
+                List<Role> roles = userService.getUserRoles(user.getId());
+                userMap.put("roles", roles);
+                
+                return userMap;
+            }).collect(Collectors.toList());
+            
+            Map<String, Object> data = new HashMap<>();
+            data.put("list", userList);
+            data.put("total", result.getTotal());
+            data.put("page", page);
+            data.put("pageSize", pageSize);
+            
+            return Result.success(data);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("查询失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 获取管理员详情
+     */
+    @GetMapping("/detail/{userId}")
+    public Result<Map<String, Object>> detail(@PathVariable Integer userId) {
+        try {
+            AdminUser user = userService.getUserById(userId);
+            if (user == null) {
+                return Result.error("管理员不存在");
+            }
+            
+            Map<String, Object> data = new HashMap<>();
+            data.put("id", user.getId());
+            data.put("username", user.getUsername());
+            data.put("realName", user.getRealName());
+            data.put("phone", user.getPhone());
+            data.put("email", user.getEmail());
+            data.put("status", user.getStatus());
+            data.put("createTime", user.getCreateTime());
+            data.put("updateTime", user.getUpdateTime());
+            data.put("lastLoginTime", user.getLastLoginTime());
+            
+            // 查询角色
+            List<Role> roles = userService.getUserRoles(userId);
+            data.put("roles", roles);
+            
+            return Result.success(data);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("查询失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 更新管理员信息
+     */
+    @PutMapping("/update/{userId}")
+    public Result<String> update(@PathVariable Integer userId,
+                                 @RequestBody Map<String, Object> params,
+                                 @RequestHeader(value = "Authorization", required = false) String token) {
+        try {
+            // 验证token
+            if (token == null || token.isEmpty()) {
+                return Result.error(401, "未授权,请先登录");
+            }
+            
+            AdminUser currentUser = authService.getUserByToken(token);
+            if (currentUser == null) {
+                return Result.error(401, "Token无效或已过期,请重新登录");
+            }
+            
+            // 检查是否是超级管理员
+            if (!userService.isSuperAdmin(currentUser.getId())) {
+                return Result.error(403, "只有超级管理员可以修改管理员信息");
+            }
+            
+            AdminUser user = userService.getUserById(userId);
+            if (user == null) {
+                return Result.error("管理员不存在");
+            }
+            
+            // 更新字段
+            if (params.containsKey("realName")) {
+                user.setRealName((String) params.get("realName"));
+            }
+            if (params.containsKey("phone")) {
+                user.setPhone((String) params.get("phone"));
+            }
+            if (params.containsKey("email")) {
+                user.setEmail((String) params.get("email"));
+            }
+            if (params.containsKey("status")) {
+                user.setStatus(Integer.parseInt(params.get("status").toString()));
+            }
+            if (params.containsKey("password")) {
+                String password = (String) params.get("password");
+                if (password != null && !password.isEmpty()) {
+                    user.setPassword(password);
+                }
+            }
+            
+            // 更新角色
+            if (params.containsKey("roleIds")) {
+                @SuppressWarnings("unchecked")
+                List<Integer> roleIds = (List<Integer>) params.get("roleIds");
+                userService.assignRoles(userId, roleIds);
+            }
+            
+            boolean success = userService.updateUser(user);
+            if (success) {
+                return Result.success("更新成功");
+            } else {
+                return Result.error("更新失败");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("更新失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 删除管理员
+     */
+    @DeleteMapping("/delete/{userId}")
+    public Result<String> delete(@PathVariable Integer userId,
+                                 @RequestHeader(value = "Authorization", required = false) String token) {
+        try {
+            // 验证token
+            if (token == null || token.isEmpty()) {
+                return Result.error(401, "未授权,请先登录");
+            }
+            
+            AdminUser currentUser = authService.getUserByToken(token);
+            if (currentUser == null) {
+                return Result.error(401, "Token无效或已过期,请重新登录");
+            }
+            
+            // 检查是否是超级管理员
+            if (!userService.isSuperAdmin(currentUser.getId())) {
+                return Result.error(403, "只有超级管理员可以删除管理员");
+            }
+            
+            // 不能删除超级管理员
+            if (userService.isSuperAdmin(userId)) {
+                return Result.error("不能删除超级管理员");
+            }
+            
+            // 不能删除自己
+            if (userId.equals(currentUser.getId())) {
+                return Result.error("不能删除自己");
+            }
+            
+            boolean success = userService.deleteUser(userId);
+            if (success) {
+                return Result.success("删除成功");
+            } else {
+                return Result.error("删除失败");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("删除失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 禁用管理员
+     */
+    @PutMapping("/disable/{userId}")
+    public Result<String> disable(@PathVariable Integer userId,
+                                  @RequestHeader(value = "Authorization", required = false) String token) {
+        try {
+            // 验证token
+            if (token == null || token.isEmpty()) {
+                return Result.error(401, "未授权,请先登录");
+            }
+            
+            AdminUser currentUser = authService.getUserByToken(token);
+            if (currentUser == null) {
+                return Result.error(401, "Token无效或已过期,请重新登录");
+            }
+            
+            // 检查是否是超级管理员
+            if (!userService.isSuperAdmin(currentUser.getId())) {
+                return Result.error(403, "只有超级管理员可以禁用管理员");
+            }
+            
+            // 不能禁用超级管理员
+            if (userService.isSuperAdmin(userId)) {
+                return Result.error("不能禁用超级管理员");
+            }
+            
+            // 不能禁用自己
+            if (userId.equals(currentUser.getId())) {
+                return Result.error("不能禁用自己");
+            }
+            
+            AdminUser user = userService.getUserById(userId);
+            if (user == null) {
+                return Result.error("管理员不存在");
+            }
+            
+            user.setStatus(0); // 禁用
+            boolean success = userService.updateUser(user);
+            if (success) {
+                return Result.success("禁用成功");
+            } else {
+                return Result.error("禁用失败");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("禁用失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 启用管理员
+     */
+    @PutMapping("/enable/{userId}")
+    public Result<String> enable(@PathVariable Integer userId,
+                                  @RequestHeader(value = "Authorization", required = false) String token) {
+        try {
+            // 验证token
+            if (token == null || token.isEmpty()) {
+                return Result.error(401, "未授权,请先登录");
+            }
+            
+            AdminUser currentUser = authService.getUserByToken(token);
+            if (currentUser == null) {
+                return Result.error(401, "Token无效或已过期,请重新登录");
+            }
+            
+            // 检查是否是超级管理员
+            if (!userService.isSuperAdmin(currentUser.getId())) {
+                return Result.error(403, "只有超级管理员可以启用管理员");
+            }
+            
+            AdminUser user = userService.getUserById(userId);
+            if (user == null) {
+                return Result.error("管理员不存在");
+            }
+            
+            user.setStatus(1); // 启用
+            boolean success = userService.updateUser(user);
+            if (success) {
+                return Result.success("启用成功");
+            } else {
+                return Result.error("启用失败");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("启用失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 获取所有角色列表(用于注册和编辑时选择角色)
+     */
+    @GetMapping("/roles")
+    public Result<List<Role>> getRoles() {
+        try {
+            com.baomidou.mybatisplus.extension.plugins.pagination.Page<Role> rolePage = roleService.getRoleList(1, 100, null);
+            return Result.success(rolePage.getRecords());
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("获取角色列表失败:" + e.getMessage());
+        }
+    }
+}
+

+ 196 - 0
service/admin/src/main/java/com/zhentao/controller/PointsProductController.java

@@ -0,0 +1,196 @@
+package com.zhentao.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zhentao.common.Result;
+import com.zhentao.entity.PointsProduct;
+import com.zhentao.service.PointsProductService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * 积分商品管理控制器
+ */
+@Slf4j
+@RestController
+@RequestMapping("/admin/points-product")
+@CrossOrigin(origins = "*")
+public class PointsProductController {
+    
+    private final PointsProductService pointsProductService;
+    
+    public PointsProductController(PointsProductService pointsProductService) {
+        this.pointsProductService = pointsProductService;
+    }
+    
+    /**
+     * 分页查询积分商品列表
+     */
+    @GetMapping("/list")
+    public Result<Page<PointsProduct>> list(
+            @RequestParam(defaultValue = "1") Integer page,
+            @RequestParam(defaultValue = "10") Integer size,
+            @RequestParam(required = false) String keyword,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) Integer category) {
+        
+        log.info("查询积分商品列表: page={}, size={}, keyword={}, status={}, category={}", 
+                page, size, keyword, status, category);
+        
+        try {
+            Page<PointsProduct> result = pointsProductService.getProductList(page, size, keyword, status, category);
+            return Result.success(result);
+        } catch (Exception e) {
+            log.error("查询积分商品列表失败", e);
+            return Result.error("查询失败");
+        }
+    }
+    
+    /**
+     * 获取商品详情
+     */
+    @GetMapping("/detail/{id}")
+    public Result<PointsProduct> detail(@PathVariable Long id) {
+        log.info("查询积分商品详情: id={}", id);
+        
+        try {
+            PointsProduct product = pointsProductService.getProductById(id);
+            if (product == null) {
+                return Result.error("商品不存在");
+            }
+            return Result.success(product);
+        } catch (Exception e) {
+            log.error("查询积分商品详情失败", e);
+            return Result.error("查询失败");
+        }
+    }
+    
+    /**
+     * 创建商品
+     */
+    @PostMapping("/create")
+    public Result<String> create(@RequestBody PointsProduct product) {
+        log.info("创建积分商品: name={}", product.getName());
+        
+        try {
+            boolean success = pointsProductService.createProduct(product);
+            if (success) {
+                return Result.success("创建成功");
+            } else {
+                return Result.error("创建失败");
+            }
+        } catch (Exception e) {
+            log.error("创建积分商品失败", e);
+            return Result.error("创建失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 更新商品
+     */
+    @PutMapping("/update")
+    public Result<String> update(@RequestBody PointsProduct product) {
+        log.info("更新积分商品: id={}", product.getId());
+        
+        try {
+            if (product.getId() == null) {
+                return Result.error("商品ID不能为空");
+            }
+            
+            boolean success = pointsProductService.updateProduct(product);
+            if (success) {
+                return Result.success("更新成功");
+            } else {
+                return Result.error("更新失败");
+            }
+        } catch (Exception e) {
+            log.error("更新积分商品失败", e);
+            return Result.error("更新失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 删除商品
+     */
+    @DeleteMapping("/delete/{id}")
+    public Result<String> delete(@PathVariable Long id) {
+        log.info("删除积分商品: id={}", id);
+        
+        try {
+            boolean success = pointsProductService.deleteProduct(id);
+            if (success) {
+                return Result.success("删除成功");
+            } else {
+                return Result.error("删除失败");
+            }
+        } catch (Exception e) {
+            log.error("删除积分商品失败", e);
+            return Result.error("删除失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 上架商品
+     */
+    @PutMapping("/on-shelf/{id}")
+    public Result<String> onShelf(@PathVariable Long id) {
+        log.info("上架积分商品: id={}", id);
+        
+        try {
+            boolean success = pointsProductService.updateStatus(id, 1);
+            if (success) {
+                return Result.success("上架成功");
+            } else {
+                return Result.error("上架失败");
+            }
+        } catch (Exception e) {
+            log.error("上架积分商品失败", e);
+            return Result.error("上架失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 下架商品
+     */
+    @PutMapping("/off-shelf/{id}")
+    public Result<String> offShelf(@PathVariable Long id) {
+        log.info("下架积分商品: id={}", id);
+        
+        try {
+            boolean success = pointsProductService.updateStatus(id, 0);
+            if (success) {
+                return Result.success("下架成功");
+            } else {
+                return Result.error("下架失败");
+            }
+        } catch (Exception e) {
+            log.error("下架积分商品失败", e);
+            return Result.error("下架失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 更新商品状态(通用接口)
+     */
+    @PutMapping("/status")
+    public Result<String> updateStatus(@RequestBody Map<String, Object> params) {
+        Long id = Long.valueOf(params.get("id").toString());
+        Integer status = Integer.valueOf(params.get("status").toString());
+        
+        log.info("更新积分商品状态: id={}, status={}", id, status);
+        
+        try {
+            boolean success = pointsProductService.updateStatus(id, status);
+            if (success) {
+                return Result.success("状态更新成功");
+            } else {
+                return Result.error("状态更新失败");
+            }
+        } catch (Exception e) {
+            log.error("更新积分商品状态失败", e);
+            return Result.error("状态更新失败: " + e.getMessage());
+        }
+    }
+}
+

+ 136 - 0
service/admin/src/main/java/com/zhentao/entity/AdminPermission.java

@@ -0,0 +1,136 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 管理员权限表
+ * @TableName admin_permission
+ */
+@TableName(value ="admin_permission")
+@Data
+public class AdminPermission {
+    /**
+     * 权限ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 权限名称
+     */
+    private String permissionName;
+
+    /**
+     * 权限编码(唯一)
+     */
+    private String permissionCode;
+
+    /**
+     * 访问路径(Controller路径,如:/admin/user/list)
+     */
+    private String path;
+
+    /**
+     * HTTP方法:GET,POST,PUT,DELETE,ALL
+     */
+    private String method;
+
+    /**
+     * 权限描述
+     */
+    private String description;
+
+    /**
+     * 父权限ID(0表示顶级)
+     */
+    private Integer parentId;
+
+    /**
+     * 排序顺序
+     */
+    private Integer sortOrder;
+
+    /**
+     * 状态:0-禁用 1-启用
+     */
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        AdminPermission other = (AdminPermission) that;
+        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
+            && (this.getPermissionName() == null ? other.getPermissionName() == null : this.getPermissionName().equals(other.getPermissionName()))
+            && (this.getPermissionCode() == null ? other.getPermissionCode() == null : this.getPermissionCode().equals(other.getPermissionCode()))
+            && (this.getPath() == null ? other.getPath() == null : this.getPath().equals(other.getPath()))
+            && (this.getMethod() == null ? other.getMethod() == null : this.getMethod().equals(other.getMethod()))
+            && (this.getDescription() == null ? other.getDescription() == null : this.getDescription().equals(other.getDescription()))
+            && (this.getParentId() == null ? other.getParentId() == null : this.getParentId().equals(other.getParentId()))
+            && (this.getSortOrder() == null ? other.getSortOrder() == null : this.getSortOrder().equals(other.getSortOrder()))
+            && (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus()))
+            && (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime()))
+            && (this.getUpdateTime() == null ? other.getUpdateTime() == null : this.getUpdateTime().equals(other.getUpdateTime()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
+        result = prime * result + ((getPermissionName() == null) ? 0 : getPermissionName().hashCode());
+        result = prime * result + ((getPermissionCode() == null) ? 0 : getPermissionCode().hashCode());
+        result = prime * result + ((getPath() == null) ? 0 : getPath().hashCode());
+        result = prime * result + ((getMethod() == null) ? 0 : getMethod().hashCode());
+        result = prime * result + ((getDescription() == null) ? 0 : getDescription().hashCode());
+        result = prime * result + ((getParentId() == null) ? 0 : getParentId().hashCode());
+        result = prime * result + ((getSortOrder() == null) ? 0 : getSortOrder().hashCode());
+        result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode());
+        result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode());
+        result = prime * result + ((getUpdateTime() == null) ? 0 : getUpdateTime().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", permissionName=").append(permissionName);
+        sb.append(", permissionCode=").append(permissionCode);
+        sb.append(", path=").append(path);
+        sb.append(", method=").append(method);
+        sb.append(", description=").append(description);
+        sb.append(", parentId=").append(parentId);
+        sb.append(", sortOrder=").append(sortOrder);
+        sb.append(", status=").append(status);
+        sb.append(", createTime=").append(createTime);
+        sb.append(", updateTime=").append(updateTime);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 104 - 0
service/admin/src/main/java/com/zhentao/entity/AdminRole.java

@@ -0,0 +1,104 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 管理员角色表
+ * @TableName admin_role
+ */
+@TableName(value ="admin_role")
+@Data
+public class AdminRole {
+    /**
+     * 角色ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 角色名称
+     */
+    private String roleName;
+
+    /**
+     * 角色编码(唯一)
+     */
+    private String roleCode;
+
+    /**
+     * 角色描述
+     */
+    private String description;
+
+    /**
+     * 状态:0-禁用 1-启用
+     */
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        AdminRole other = (AdminRole) that;
+        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
+            && (this.getRoleName() == null ? other.getRoleName() == null : this.getRoleName().equals(other.getRoleName()))
+            && (this.getRoleCode() == null ? other.getRoleCode() == null : this.getRoleCode().equals(other.getRoleCode()))
+            && (this.getDescription() == null ? other.getDescription() == null : this.getDescription().equals(other.getDescription()))
+            && (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus()))
+            && (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime()))
+            && (this.getUpdateTime() == null ? other.getUpdateTime() == null : this.getUpdateTime().equals(other.getUpdateTime()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
+        result = prime * result + ((getRoleName() == null) ? 0 : getRoleName().hashCode());
+        result = prime * result + ((getRoleCode() == null) ? 0 : getRoleCode().hashCode());
+        result = prime * result + ((getDescription() == null) ? 0 : getDescription().hashCode());
+        result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode());
+        result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode());
+        result = prime * result + ((getUpdateTime() == null) ? 0 : getUpdateTime().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", roleName=").append(roleName);
+        sb.append(", roleCode=").append(roleCode);
+        sb.append(", description=").append(description);
+        sb.append(", status=").append(status);
+        sb.append(", createTime=").append(createTime);
+        sb.append(", updateTime=").append(updateTime);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 80 - 0
service/admin/src/main/java/com/zhentao/entity/AdminRolePermission.java

@@ -0,0 +1,80 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 角色权限关联表
+ * @TableName admin_role_permission
+ */
+@TableName(value ="admin_role_permission")
+@Data
+public class AdminRolePermission {
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 角色ID
+     */
+    private Integer roleId;
+
+    /**
+     * 权限ID
+     */
+    private Integer permissionId;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        AdminRolePermission other = (AdminRolePermission) that;
+        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
+            && (this.getRoleId() == null ? other.getRoleId() == null : this.getRoleId().equals(other.getRoleId()))
+            && (this.getPermissionId() == null ? other.getPermissionId() == null : this.getPermissionId().equals(other.getPermissionId()))
+            && (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
+        result = prime * result + ((getRoleId() == null) ? 0 : getRoleId().hashCode());
+        result = prime * result + ((getPermissionId() == null) ? 0 : getPermissionId().hashCode());
+        result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", roleId=").append(roleId);
+        sb.append(", permissionId=").append(permissionId);
+        sb.append(", createTime=").append(createTime);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 80 - 0
service/admin/src/main/java/com/zhentao/entity/AdminUserRole.java

@@ -0,0 +1,80 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 用户角色关联表
+ * @TableName admin_user_role
+ */
+@TableName(value ="admin_user_role")
+@Data
+public class AdminUserRole {
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 用户ID
+     */
+    private Integer userId;
+
+    /**
+     * 角色ID
+     */
+    private Integer roleId;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        AdminUserRole other = (AdminUserRole) that;
+        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
+            && (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
+            && (this.getRoleId() == null ? other.getRoleId() == null : this.getRoleId().equals(other.getRoleId()))
+            && (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
+        result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
+        result = prime * result + ((getRoleId() == null) ? 0 : getRoleId().hashCode());
+        result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", userId=").append(userId);
+        sb.append(", roleId=").append(roleId);
+        sb.append(", createTime=").append(createTime);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 76 - 0
service/admin/src/main/java/com/zhentao/entity/Permission.java

@@ -0,0 +1,76 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 权限实体
+ */
+@Data
+@TableName("admin_permission")
+public class Permission implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+    
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+    
+    /**
+     * 权限名称
+     */
+    private String permissionName;
+    
+    /**
+     * 权限编码(唯一)
+     */
+    private String permissionCode;
+    
+    /**
+     * 访问路径(Controller路径)
+     */
+    private String path;
+    
+    /**
+     * HTTP方法:GET,POST,PUT,DELETE,ALL
+     */
+    private String method;
+    
+    /**
+     * 权限描述
+     */
+    private String description;
+    
+    /**
+     * 父权限ID(0表示顶级)
+     */
+    private Integer parentId;
+    
+    /**
+     * 排序顺序
+     */
+    private Integer sortOrder;
+    
+    /**
+     * 状态:0-禁用 1-启用
+     */
+    private Integer status;
+    
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+    
+    /**
+     * 更新时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+}
+

+ 56 - 0
service/admin/src/main/java/com/zhentao/entity/Role.java

@@ -0,0 +1,56 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 角色实体
+ */
+@Data
+@TableName("admin_role")
+public class Role implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+    
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+    
+    /**
+     * 角色名称
+     */
+    private String roleName;
+    
+    /**
+     * 角色编码(唯一)
+     */
+    private String roleCode;
+    
+    /**
+     * 角色描述
+     */
+    private String description;
+    
+    /**
+     * 状态:0-禁用 1-启用
+     */
+    private Integer status;
+    
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+    
+    /**
+     * 更新时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+}
+

+ 40 - 0
service/admin/src/main/java/com/zhentao/entity/RolePermission.java

@@ -0,0 +1,40 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 角色权限关联实体
+ */
+@Data
+@TableName("admin_role_permission")
+public class RolePermission implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+    
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+    
+    /**
+     * 角色ID
+     */
+    private Integer roleId;
+    
+    /**
+     * 权限ID
+     */
+    private Integer permissionId;
+    
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+}
+

+ 40 - 0
service/admin/src/main/java/com/zhentao/entity/UserRole.java

@@ -0,0 +1,40 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 用户角色关联实体
+ */
+@Data
+@TableName("admin_user_role")
+public class UserRole implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+    
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+    
+    /**
+     * 用户ID
+     */
+    private Integer userId;
+    
+    /**
+     * 角色ID
+     */
+    private Integer roleId;
+    
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+}
+

+ 53 - 0
service/admin/src/main/java/com/zhentao/exception/CustomErrorController.java

@@ -0,0 +1,53 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.springframework.boot.web.servlet.error.ErrorController;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 自定义错误控制器
+ * 处理Spring Boot默认的/error路径
+ */
+@RestController
+public class CustomErrorController implements ErrorController {
+
+    @RequestMapping("/error")
+    public ResponseEntity<Result<String>> handleError(HttpServletRequest request) {
+        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
+        String errorMessage = (String) request.getAttribute("javax.servlet.error.message");
+        Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
+
+        if (statusCode == null) {
+            statusCode = 500;
+        }
+
+        if (errorMessage == null || errorMessage.isEmpty()) {
+            if (statusCode == 404) {
+                errorMessage = "请求的资源不存在";
+            } else if (statusCode == 403) {
+                errorMessage = "拒绝访问";
+            } else if (statusCode == 401) {
+                errorMessage = "未授权,请先登录";
+            } else if (statusCode == 500) {
+                errorMessage = "服务器内部错误";
+            } else {
+                errorMessage = "未知错误";
+            }
+        }
+
+        if (exception != null && exception.getMessage() != null) {
+            errorMessage = exception.getMessage();
+        }
+
+        Result<String> result = Result.error(statusCode, errorMessage);
+        HttpStatus httpStatus = HttpStatus.valueOf(statusCode);
+        
+        return new ResponseEntity<>(result, httpStatus);
+    }
+}
+

+ 103 - 0
service/admin/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java

@@ -0,0 +1,103 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.stream.Collectors;
+
+/**
+ * 全局异常处理器
+ * 统一处理所有Controller抛出的异常
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+    /**
+     * 处理运行时异常
+     */
+    @ExceptionHandler(RuntimeException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
+        logger.error("运行时异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: " + e.getMessage());
+    }
+
+    /**
+     * 处理所有异常
+     */
+    @ExceptionHandler(Exception.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleException(Exception e, HttpServletRequest request) {
+        logger.error("系统异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统异常: " + e.getMessage());
+    }
+
+    /**
+     * 处理参数校验异常(@Valid)
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数校验失败: {}", errorMessage);
+        return Result.error(400, "参数校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理参数绑定异常
+     */
+    @ExceptionHandler(BindException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleBindException(BindException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数绑定失败: {}", errorMessage);
+        return Result.error(400, "参数绑定失败: " + errorMessage);
+    }
+
+    /**
+     * 处理文件上传大小超限异常
+     */
+    @ExceptionHandler(MaxUploadSizeExceededException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
+        logger.warn("文件上传大小超限: {}", e.getMessage());
+        return Result.error(400, "文件大小超过限制,单个文件最大10MB,请压缩后重试");
+    }
+
+    /**
+     * 处理空指针异常
+     */
+    @ExceptionHandler(NullPointerException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
+        logger.error("空指针异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: 空指针异常");
+    }
+
+    /**
+     * 处理非法参数异常
+     */
+    @ExceptionHandler(IllegalArgumentException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleIllegalArgumentException(IllegalArgumentException e) {
+        logger.warn("非法参数: {}", e.getMessage());
+        return Result.error(400, "参数错误: " + e.getMessage());
+    }
+}
+

+ 18 - 0
service/admin/src/main/java/com/zhentao/mapper/AdminPermissionMapper.java

@@ -0,0 +1,18 @@
+package com.zhentao.mapper;
+
+import com.zhentao.entity.AdminPermission;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+* @author 联想
+* @description 针对表【admin_permission(管理员权限表)】的数据库操作Mapper
+* @createDate 2025-12-11 16:08:57
+* @Entity com.zhentao.entity.AdminPermission
+*/
+public interface AdminPermissionMapper extends BaseMapper<AdminPermission> {
+
+}
+
+
+
+

+ 18 - 0
service/admin/src/main/java/com/zhentao/mapper/AdminRoleMapper.java

@@ -0,0 +1,18 @@
+package com.zhentao.mapper;
+
+import com.zhentao.entity.AdminRole;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+* @author 联想
+* @description 针对表【admin_role(管理员角色表)】的数据库操作Mapper
+* @createDate 2025-12-11 16:08:57
+* @Entity com.zhentao.entity.AdminRole
+*/
+public interface AdminRoleMapper extends BaseMapper<AdminRole> {
+
+}
+
+
+
+

+ 18 - 0
service/admin/src/main/java/com/zhentao/mapper/AdminRolePermissionMapper.java

@@ -0,0 +1,18 @@
+package com.zhentao.mapper;
+
+import com.zhentao.entity.AdminRolePermission;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+* @author 联想
+* @description 针对表【admin_role_permission(角色权限关联表)】的数据库操作Mapper
+* @createDate 2025-12-11 16:08:57
+* @Entity com.zhentao.entity.AdminRolePermission
+*/
+public interface AdminRolePermissionMapper extends BaseMapper<AdminRolePermission> {
+
+}
+
+
+
+

+ 30 - 0
service/admin/src/main/java/com/zhentao/mapper/AdminUserRoleMapper.java

@@ -0,0 +1,30 @@
+package com.zhentao.mapper;
+
+import com.zhentao.entity.AdminUserRole;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+* @author 联想
+* @description 针对表【admin_user_role(用户角色关联表)】的数据库操作Mapper
+* @createDate 2025-12-11 16:08:57
+* @Entity com.zhentao.entity.AdminUserRole
+*/
+public interface AdminUserRoleMapper extends BaseMapper<AdminUserRole> {
+    
+    /**
+     * 根据用户ID查询角色ID列表
+     */
+    List<Integer> selectRoleIdsByUserId(@Param("userId") Integer userId);
+    
+    /**
+     * 根据用户ID删除所有角色关联
+     */
+    int deleteByUserId(@Param("userId") Integer userId);
+}
+
+
+
+

+ 31 - 0
service/admin/src/main/java/com/zhentao/mapper/PermissionMapper.java

@@ -0,0 +1,31 @@
+package com.zhentao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhentao.entity.Permission;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 权限Mapper
+ */
+@Mapper
+public interface PermissionMapper extends BaseMapper<Permission> {
+    
+    /**
+     * 根据角色ID查询权限列表
+     */
+    List<Permission> selectPermissionsByRoleId(@Param("roleId") Integer roleId);
+    
+    /**
+     * 根据用户ID查询权限列表
+     */
+    List<Permission> selectPermissionsByUserId(@Param("userId") Integer userId);
+    
+    /**
+     * 根据路径和方法查询权限
+     */
+    Permission selectByPathAndMethod(@Param("path") String path, @Param("method") String method);
+}
+

+ 13 - 0
service/admin/src/main/java/com/zhentao/mapper/RoleMapper.java

@@ -0,0 +1,13 @@
+package com.zhentao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhentao.entity.Role;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 角色Mapper
+ */
+@Mapper
+public interface RoleMapper extends BaseMapper<Role> {
+}
+

+ 26 - 0
service/admin/src/main/java/com/zhentao/mapper/RolePermissionMapper.java

@@ -0,0 +1,26 @@
+package com.zhentao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhentao.entity.RolePermission;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 角色权限关联Mapper
+ */
+@Mapper
+public interface RolePermissionMapper extends BaseMapper<RolePermission> {
+    
+    /**
+     * 根据角色ID查询权限ID列表
+     */
+    List<Integer> selectPermissionIdsByRoleId(@Param("roleId") Integer roleId);
+    
+    /**
+     * 根据角色ID删除所有权限关联
+     */
+    int deleteByRoleId(@Param("roleId") Integer roleId);
+}
+

+ 26 - 0
service/admin/src/main/java/com/zhentao/mapper/UserRoleMapper.java

@@ -0,0 +1,26 @@
+package com.zhentao.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhentao.entity.UserRole;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 用户角色关联Mapper
+ */
+@Mapper
+public interface UserRoleMapper extends BaseMapper<UserRole> {
+    
+    /**
+     * 根据用户ID查询角色ID列表
+     */
+    List<Integer> selectRoleIdsByUserId(@Param("userId") Integer userId);
+    
+    /**
+     * 根据用户ID删除所有角色关联
+     */
+    int deleteByUserId(@Param("userId") Integer userId);
+}
+

+ 83 - 0
service/admin/src/main/java/com/zhentao/security/AdminUserDetails.java

@@ -0,0 +1,83 @@
+package com.zhentao.security;
+
+import com.zhentao.entity.AdminUser;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Admin用户详情,实现Spring Security的UserDetails接口
+ */
+public class AdminUserDetails implements UserDetails {
+    
+    private final AdminUser adminUser;
+    private final List<GrantedAuthority> authorities;
+    private final boolean isSuperAdmin;
+    
+    public AdminUserDetails(AdminUser adminUser, List<String> permissions, boolean isSuperAdmin) {
+        this.adminUser = adminUser;
+        this.isSuperAdmin = isSuperAdmin;
+        
+        // 将权限字符串转换为GrantedAuthority
+        if (permissions != null && !permissions.isEmpty()) {
+            this.authorities = permissions.stream()
+                    .map(SimpleGrantedAuthority::new)
+                    .collect(Collectors.toList());
+        } else {
+            this.authorities = Collections.emptyList();
+        }
+    }
+    
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return authorities;
+    }
+    
+    @Override
+    public String getPassword() {
+        return adminUser.getPassword();
+    }
+    
+    @Override
+    public String getUsername() {
+        return adminUser.getUsername();
+    }
+    
+    @Override
+    public boolean isAccountNonExpired() {
+        return true;
+    }
+    
+    @Override
+    public boolean isAccountNonLocked() {
+        return adminUser.getStatus() != null && adminUser.getStatus() == 1;
+    }
+    
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return true;
+    }
+    
+    @Override
+    public boolean isEnabled() {
+        return adminUser.getStatus() != null && adminUser.getStatus() == 1;
+    }
+    
+    public AdminUser getAdminUser() {
+        return adminUser;
+    }
+    
+    public boolean isSuperAdmin() {
+        return isSuperAdmin;
+    }
+    
+    public Integer getUserId() {
+        return adminUser.getId();
+    }
+}
+

+ 188 - 0
service/admin/src/main/java/com/zhentao/security/AdminUserDetailsService.java

@@ -0,0 +1,188 @@
+package com.zhentao.security;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.zhentao.entity.AdminPermission;
+import com.zhentao.entity.AdminRole;
+import com.zhentao.entity.AdminRolePermission;
+import com.zhentao.entity.AdminUser;
+import com.zhentao.entity.AdminUserRole;
+import com.zhentao.mapper.AdminPermissionMapper;
+import com.zhentao.mapper.AdminRoleMapper;
+import com.zhentao.mapper.AdminRolePermissionMapper;
+import com.zhentao.mapper.AdminUserMapper;
+import com.zhentao.mapper.AdminUserRoleMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Admin用户详情服务,实现Spring Security的UserDetailsService
+ */
+@Service
+public class AdminUserDetailsService implements UserDetailsService {
+    
+    @Autowired
+    private AdminUserMapper adminUserMapper;
+    
+    @Autowired
+    private AdminUserRoleMapper adminUserRoleMapper;
+    
+    @Autowired
+    private AdminRoleMapper adminRoleMapper;
+    
+    @Autowired
+    private AdminRolePermissionMapper adminRolePermissionMapper;
+    
+    @Autowired
+    private AdminPermissionMapper adminPermissionMapper;
+    
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        // 根据用户名查询用户
+        QueryWrapper<AdminUser> userWrapper = new QueryWrapper<>();
+        userWrapper.eq("username", username);
+        AdminUser adminUser = adminUserMapper.selectOne(userWrapper);
+        
+        if (adminUser == null) {
+            throw new UsernameNotFoundException("用户不存在: " + username);
+        }
+        
+        // 查询用户角色
+        QueryWrapper<AdminUserRole> userRoleWrapper = new QueryWrapper<>();
+        userRoleWrapper.eq("user_id", adminUser.getId());
+        List<AdminUserRole> userRoles = adminUserRoleMapper.selectList(userRoleWrapper);
+        
+        // 判断是否是超级管理员
+        boolean isSuperAdmin = false;
+        List<Integer> roleIds = new ArrayList<>();
+        
+        for (AdminUserRole userRole : userRoles) {
+            roleIds.add(userRole.getRoleId());
+            AdminRole role = adminRoleMapper.selectById(userRole.getRoleId());
+            if (role != null && "超级管理员".equals(role.getRoleName())) {
+                isSuperAdmin = true;
+                break;
+            }
+        }
+        
+        // 查询权限
+        List<String> permissions = new ArrayList<>();
+        if (isSuperAdmin) {
+            // 超级管理员拥有所有权限,添加特殊权限标识
+            permissions.add("ROLE_SUPER_ADMIN");
+            permissions.add("*");
+        } else {
+            // 普通管理员查询具体权限
+            if (!roleIds.isEmpty()) {
+                QueryWrapper<AdminRolePermission> rolePermissionWrapper = new QueryWrapper<>();
+                rolePermissionWrapper.in("role_id", roleIds);
+                List<AdminRolePermission> rolePermissions = adminRolePermissionMapper.selectList(rolePermissionWrapper);
+                
+                Set<Integer> permissionIds = rolePermissions.stream()
+                        .map(AdminRolePermission::getPermissionId)
+                        .collect(Collectors.toSet());
+                
+                if (!permissionIds.isEmpty()) {
+                    QueryWrapper<AdminPermission> permissionWrapper = new QueryWrapper<>();
+                    permissionWrapper.in("id", permissionIds);
+                    permissionWrapper.eq("status", 1); // 只查询启用的权限
+                    List<AdminPermission> permissionList = adminPermissionMapper.selectList(permissionWrapper);
+                    
+                    for (AdminPermission permission : permissionList) {
+                        // 添加权限编码作为权限标识
+                        if (permission.getPermissionCode() != null) {
+                            permissions.add(permission.getPermissionCode());
+                        }
+                    }
+                }
+            }
+            
+            // 添加角色标识
+            for (Integer roleId : roleIds) {
+                AdminRole role = adminRoleMapper.selectById(roleId);
+                if (role != null && role.getStatus() == 1) {
+                    permissions.add("ROLE_" + role.getRoleCode());
+                }
+            }
+        }
+        
+        return new AdminUserDetails(adminUser, permissions, isSuperAdmin);
+    }
+    
+    /**
+     * 根据用户ID加载用户详情(用于Token认证)
+     */
+    public UserDetails loadUserById(Integer userId) {
+        AdminUser adminUser = adminUserMapper.selectById(userId);
+        
+        if (adminUser == null) {
+            throw new UsernameNotFoundException("用户不存在: " + userId);
+        }
+        
+        // 查询用户角色
+        QueryWrapper<AdminUserRole> userRoleWrapper = new QueryWrapper<>();
+        userRoleWrapper.eq("user_id", adminUser.getId());
+        List<AdminUserRole> userRoles = adminUserRoleMapper.selectList(userRoleWrapper);
+        
+        // 判断是否是超级管理员
+        boolean isSuperAdmin = false;
+        List<Integer> roleIds = new ArrayList<>();
+        
+        for (AdminUserRole userRole : userRoles) {
+            roleIds.add(userRole.getRoleId());
+            AdminRole role = adminRoleMapper.selectById(userRole.getRoleId());
+            if (role != null && "超级管理员".equals(role.getRoleName())) {
+                isSuperAdmin = true;
+                break;
+            }
+        }
+        
+        // 查询权限
+        List<String> permissions = new ArrayList<>();
+        if (isSuperAdmin) {
+            permissions.add("ROLE_SUPER_ADMIN");
+            permissions.add("*");
+        } else {
+            if (!roleIds.isEmpty()) {
+                QueryWrapper<AdminRolePermission> rolePermissionWrapper = new QueryWrapper<>();
+                rolePermissionWrapper.in("role_id", roleIds);
+                List<AdminRolePermission> rolePermissions = adminRolePermissionMapper.selectList(rolePermissionWrapper);
+                
+                Set<Integer> permissionIds = rolePermissions.stream()
+                        .map(AdminRolePermission::getPermissionId)
+                        .collect(Collectors.toSet());
+                
+                if (!permissionIds.isEmpty()) {
+                    QueryWrapper<AdminPermission> permissionWrapper = new QueryWrapper<>();
+                    permissionWrapper.in("id", permissionIds);
+                    permissionWrapper.eq("status", 1);
+                    List<AdminPermission> permissionList = adminPermissionMapper.selectList(permissionWrapper);
+                    
+                    for (AdminPermission permission : permissionList) {
+                        if (permission.getPermissionCode() != null) {
+                            permissions.add(permission.getPermissionCode());
+                        }
+                    }
+                }
+            }
+            
+            for (Integer roleId : roleIds) {
+                AdminRole role = adminRoleMapper.selectById(roleId);
+                if (role != null && role.getStatus() == 1) {
+                    permissions.add("ROLE_" + role.getRoleCode());
+                }
+            }
+        }
+        
+        return new AdminUserDetails(adminUser, permissions, isSuperAdmin);
+    }
+}
+

+ 81 - 0
service/admin/src/main/java/com/zhentao/security/JwtAuthenticationFilter.java

@@ -0,0 +1,81 @@
+package com.zhentao.security;
+
+import com.zhentao.service.AuthService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * JWT认证过滤器
+ * 从请求头中提取Token,验证并设置认证信息
+ */
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+    
+    @Autowired
+    private AuthService authService;
+    
+    @Autowired
+    private AdminUserDetailsService adminUserDetailsService;
+    
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
+                                    FilterChain filterChain) throws ServletException, IOException {
+        // 获取Token
+        String token = getTokenFromRequest(request);
+        
+        if (token != null) {
+            try {
+                // 从Token获取用户信息
+                com.zhentao.entity.AdminUser adminUser = authService.getUserByToken(token);
+                
+                if (adminUser != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+                    // 加载用户详情(包含权限信息)
+                    AdminUserDetails userDetails = (AdminUserDetails) adminUserDetailsService.loadUserById(adminUser.getId());
+                    
+                    // 创建认证对象
+                    UsernamePasswordAuthenticationToken authentication = 
+                            new UsernamePasswordAuthenticationToken(
+                                    userDetails,
+                                    null,
+                                    userDetails.getAuthorities()
+                            );
+                    
+                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+                    
+                    // 设置到Security上下文
+                    SecurityContextHolder.getContext().setAuthentication(authentication);
+                }
+            } catch (Exception e) {
+                logger.error("无法设置用户认证", e);
+            }
+        }
+        
+        filterChain.doFilter(request, response);
+    }
+    
+    /**
+     * 从请求中提取Token
+     */
+    private String getTokenFromRequest(HttpServletRequest request) {
+        String bearerToken = request.getHeader("Authorization");
+        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
+            return bearerToken.substring(7);
+        }
+        // 兼容不带Bearer前缀的Token
+        if (bearerToken != null && !bearerToken.isEmpty()) {
+            return bearerToken;
+        }
+        return null;
+    }
+}
+

+ 85 - 0
service/admin/src/main/java/com/zhentao/security/SecurityConfig.java

@@ -0,0 +1,85 @@
+package com.zhentao.security;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.Arrays;
+
+/**
+ * Spring Security 配置类
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class SecurityConfig {
+    
+    @Autowired
+    private JwtAuthenticationFilter jwtAuthenticationFilter;
+    
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+    
+    @Bean
+    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+        http
+            // 禁用CSRF(因为使用JWT)
+            .csrf().disable()
+            // 启用CORS
+            .cors().configurationSource(corsConfigurationSource())
+            .and()
+            // 设置Session为无状态(使用JWT)
+            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+            .and()
+            // 配置请求授权
+            .authorizeRequests(auth -> auth
+                // 允许登录接口匿名访问
+                .antMatchers("/admin/auth/login", "/admin/auth/logout").permitAll()
+                // 数据面板 - 仅超级管理员(检查是否有ROLE_SUPER_ADMIN权限或*权限)
+                .antMatchers("/admin/dashboard/**").hasAnyAuthority("ROLE_SUPER_ADMIN", "*")
+                // 管理员管理 - 仅超级管理员
+                .antMatchers("/admin/admin-user/**").hasAnyAuthority("ROLE_SUPER_ADMIN", "*")
+                // VIP套餐管理 - 仅超级管理员
+                .antMatchers("/admin/vip/**").hasAnyAuthority("ROLE_SUPER_ADMIN", "*")
+                // 其他管理接口需要认证
+                .antMatchers("/admin/**").authenticated()
+                // 其他请求允许访问
+                .anyRequest().permitAll()
+            )
+            // 添加JWT过滤器
+            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+        
+        return http.build();
+    }
+    
+    /**
+     * CORS配置
+     */
+    @Bean
+    public CorsConfigurationSource corsConfigurationSource() {
+        CorsConfiguration configuration = new CorsConfiguration();
+        configuration.setAllowedOriginPatterns(Arrays.asList("*"));
+        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
+        configuration.setAllowedHeaders(Arrays.asList("*"));
+        configuration.setAllowCredentials(true);
+        configuration.setMaxAge(3600L);
+        
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", configuration);
+        return source;
+    }
+}
+

+ 13 - 0
service/admin/src/main/java/com/zhentao/service/AdminPermissionService.java

@@ -0,0 +1,13 @@
+package com.zhentao.service;
+
+import com.zhentao.entity.AdminPermission;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author 联想
+* @description 针对表【admin_permission(管理员权限表)】的数据库操作Service
+* @createDate 2025-12-11 16:08:57
+*/
+public interface AdminPermissionService extends IService<AdminPermission> {
+
+}

+ 13 - 0
service/admin/src/main/java/com/zhentao/service/AdminRolePermissionService.java

@@ -0,0 +1,13 @@
+package com.zhentao.service;
+
+import com.zhentao.entity.AdminRolePermission;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author 联想
+* @description 针对表【admin_role_permission(角色权限关联表)】的数据库操作Service
+* @createDate 2025-12-11 16:08:57
+*/
+public interface AdminRolePermissionService extends IService<AdminRolePermission> {
+
+}

+ 13 - 0
service/admin/src/main/java/com/zhentao/service/AdminRoleService.java

@@ -0,0 +1,13 @@
+package com.zhentao.service;
+
+import com.zhentao.entity.AdminRole;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author 联想
+* @description 针对表【admin_role(管理员角色表)】的数据库操作Service
+* @createDate 2025-12-11 16:08:57
+*/
+public interface AdminRoleService extends IService<AdminRole> {
+
+}

+ 110 - 14
service/admin/src/main/java/com/zhentao/service/AdminSecurityService.java

@@ -1,30 +1,126 @@
 package com.zhentao.service;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.zhentao.entity.AdminPermission;
+import com.zhentao.entity.AdminRole;
+import com.zhentao.entity.AdminRolePermission;
+import com.zhentao.entity.AdminUserRole;
+import com.zhentao.mapper.AdminPermissionMapper;
+import com.zhentao.mapper.AdminRoleMapper;
+import com.zhentao.mapper.AdminRolePermissionMapper;
+import com.zhentao.mapper.AdminUserRoleMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
- * 管理员安全服务接口
+ * 管理员权限服务
  */
-public interface AdminSecurityService {
+@Service
+public class AdminSecurityService {
+    
+    @Autowired
+    private AdminUserRoleMapper adminUserRoleMapper;
+    
+    @Autowired
+    private AdminRoleMapper adminRoleMapper;
+    
+    @Autowired
+    private AdminRolePermissionMapper adminRolePermissionMapper;
+    
+    @Autowired
+    private AdminPermissionMapper adminPermissionMapper;
     
     /**
-     * 获取用户权限列表
-     * @param userId 用户ID
-     * @return 权限列表
+     * 检查用户是否是超级管理员
      */
-    List<String> getUserPermissions(Integer userId);
+    public boolean isSuperAdmin(Integer userId) {
+        QueryWrapper<AdminUserRole> wrapper = new QueryWrapper<>();
+        wrapper.eq("user_id", userId);
+        List<AdminUserRole> userRoles = adminUserRoleMapper.selectList(wrapper);
+        
+        for (AdminUserRole userRole : userRoles) {
+            AdminRole role = adminRoleMapper.selectById(userRole.getRoleId());
+            if (role != null && "超级管理员".equals(role.getRoleName())) {
+                return true;
+            }
+        }
+        return false;
+    }
     
     /**
-     * 获取用户角色列表
-     * @param userId 用户ID
-     * @return 角色列表
+     * 获取用户的所有权限编码列表
      */
-    List<String> getUserRoles(Integer userId);
+    public List<String> getUserPermissions(Integer userId) {
+        List<String> permissions = new ArrayList<>();
+        
+        // 检查是否是超级管理员
+        if (isSuperAdmin(userId)) {
+            permissions.add("*");
+            return permissions;
+        }
+        
+        // 查询用户角色
+        QueryWrapper<AdminUserRole> userRoleWrapper = new QueryWrapper<>();
+        userRoleWrapper.eq("user_id", userId);
+        List<AdminUserRole> userRoles = adminUserRoleMapper.selectList(userRoleWrapper);
+        
+        if (userRoles.isEmpty()) {
+            return permissions;
+        }
+        
+        List<Integer> roleIds = userRoles.stream()
+                .map(AdminUserRole::getRoleId)
+                .collect(Collectors.toList());
+        
+        // 查询角色权限
+        QueryWrapper<AdminRolePermission> rolePermissionWrapper = new QueryWrapper<>();
+        rolePermissionWrapper.in("role_id", roleIds);
+        List<AdminRolePermission> rolePermissions = adminRolePermissionMapper.selectList(rolePermissionWrapper);
+        
+        Set<Integer> permissionIds = rolePermissions.stream()
+                .map(AdminRolePermission::getPermissionId)
+                .collect(Collectors.toSet());
+        
+        if (!permissionIds.isEmpty()) {
+            QueryWrapper<AdminPermission> permissionWrapper = new QueryWrapper<>();
+            permissionWrapper.in("id", permissionIds);
+            permissionWrapper.eq("status", 1); // 只查询启用的权限
+            List<AdminPermission> permissionList = adminPermissionMapper.selectList(permissionWrapper);
+            
+            for (AdminPermission permission : permissionList) {
+                if (permission.getPermissionCode() != null) {
+                    permissions.add(permission.getPermissionCode());
+                }
+            }
+        }
+        
+        return permissions;
+    }
     
     /**
-     * 判断是否为超级管理员
-     * @param userId 用户ID
-     * @return 是否为超级管理员
+     * 获取用户的角色名称列表
      */
-    boolean isSuperAdmin(Integer userId);
+    public List<String> getUserRoles(Integer userId) {
+        List<String> roles = new ArrayList<>();
+        
+        QueryWrapper<AdminUserRole> wrapper = new QueryWrapper<>();
+        wrapper.eq("user_id", userId);
+        List<AdminUserRole> userRoles = adminUserRoleMapper.selectList(wrapper);
+        
+        for (AdminUserRole userRole : userRoles) {
+            AdminRole role = adminRoleMapper.selectById(userRole.getRoleId());
+            if (role != null && role.getStatus() == 1) {
+                roles.add(role.getRoleName());
+            }
+        }
+        
+        return roles;
+    }
 }
+

+ 13 - 0
service/admin/src/main/java/com/zhentao/service/AdminUserRoleService.java

@@ -0,0 +1,13 @@
+package com.zhentao.service;
+
+import com.zhentao.entity.AdminUserRole;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author 联想
+* @description 针对表【admin_user_role(用户角色关联表)】的数据库操作Service
+* @createDate 2025-12-11 16:08:57
+*/
+public interface AdminUserRoleService extends IService<AdminUserRole> {
+
+}

+ 13 - 0
service/admin/src/main/java/com/zhentao/service/AdminUserService.java

@@ -0,0 +1,13 @@
+package com.zhentao.service;
+
+import com.zhentao.entity.AdminUser;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author 联想
+* @description 针对表【admin_user(管理员用户表)】的数据库操作Service
+* @createDate 2025-12-11 16:08:57
+*/
+public interface AdminUserService extends IService<AdminUser> {
+
+}

+ 53 - 0
service/admin/src/main/java/com/zhentao/service/PermissionService.java

@@ -0,0 +1,53 @@
+package com.zhentao.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zhentao.entity.Permission;
+
+import java.util.List;
+
+/**
+ * 权限服务接口
+ */
+public interface PermissionService {
+    
+    /**
+     * 创建权限
+     */
+    Permission createPermission(Permission permission);
+    
+    /**
+     * 分页查询权限列表
+     */
+    Page<Permission> getPermissionList(Integer pageNum, Integer pageSize, String keyword);
+    
+    /**
+     * 根据ID查询权限
+     */
+    Permission getPermissionById(Integer permissionId);
+    
+    /**
+     * 更新权限信息
+     */
+    boolean updatePermission(Permission permission);
+    
+    /**
+     * 删除权限
+     */
+    boolean deletePermission(Integer permissionId);
+    
+    /**
+     * 根据用户ID查询权限列表
+     */
+    List<Permission> getUserPermissions(Integer userId);
+    
+    /**
+     * 检查用户是否有某个路径的访问权限
+     */
+    boolean hasPermission(Integer userId, String path, String method);
+    
+    /**
+     * 获取所有权限(树形结构)
+     */
+    List<Permission> getAllPermissionsTree();
+}
+

+ 43 - 0
service/admin/src/main/java/com/zhentao/service/PointsProductService.java

@@ -0,0 +1,43 @@
+package com.zhentao.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhentao.entity.PointsProduct;
+
+/**
+* @author 联想
+* @description 针对表【points_product】的数据库操作Service
+* @createDate 2025-12-11 10:49:42
+*/
+public interface PointsProductService extends IService<PointsProduct> {
+    
+    /**
+     * 分页查询积分商品列表
+     */
+    Page<PointsProduct> getProductList(Integer page, Integer size, String keyword, Integer status, Integer category);
+    
+    /**
+     * 根据ID获取商品详情
+     */
+    PointsProduct getProductById(Long id);
+    
+    /**
+     * 创建商品
+     */
+    boolean createProduct(PointsProduct product);
+    
+    /**
+     * 更新商品
+     */
+    boolean updateProduct(PointsProduct product);
+    
+    /**
+     * 删除商品
+     */
+    boolean deleteProduct(Long id);
+    
+    /**
+     * 更新商品状态(上架/下架)
+     */
+    boolean updateStatus(Long id, Integer status);
+}

+ 49 - 0
service/admin/src/main/java/com/zhentao/service/RoleService.java

@@ -0,0 +1,49 @@
+package com.zhentao.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zhentao.entity.Role;
+import com.zhentao.entity.Permission;
+
+import java.util.List;
+
+/**
+ * 角色服务接口
+ */
+public interface RoleService {
+    
+    /**
+     * 创建角色
+     */
+    Role createRole(Role role);
+    
+    /**
+     * 分页查询角色列表
+     */
+    Page<Role> getRoleList(Integer pageNum, Integer pageSize, String keyword);
+    
+    /**
+     * 根据ID查询角色
+     */
+    Role getRoleById(Integer roleId);
+    
+    /**
+     * 更新角色信息
+     */
+    boolean updateRole(Role role);
+    
+    /**
+     * 删除角色
+     */
+    boolean deleteRole(Integer roleId);
+    
+    /**
+     * 为角色分配权限
+     */
+    boolean assignPermissions(Integer roleId, List<Integer> permissionIds);
+    
+    /**
+     * 根据角色ID查询权限列表
+     */
+    List<Permission> getRolePermissions(Integer roleId);
+}
+

+ 59 - 0
service/admin/src/main/java/com/zhentao/service/UserService.java

@@ -0,0 +1,59 @@
+package com.zhentao.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zhentao.entity.AdminUser;
+import com.zhentao.entity.Role;
+
+import java.util.List;
+
+/**
+ * 用户服务接口
+ */
+public interface UserService {
+    
+    /**
+     * 注册新用户(仅超级管理员可用)
+     */
+    AdminUser register(AdminUser user, Integer roleId);
+    
+    /**
+     * 分页查询用户列表
+     */
+    Page<AdminUser> getUserList(Integer pageNum, Integer pageSize, String keyword);
+    
+    /**
+     * 根据ID查询用户
+     */
+    AdminUser getUserById(Integer userId);
+    
+    /**
+     * 更新用户信息
+     */
+    boolean updateUser(AdminUser user);
+    
+    /**
+     * 删除用户
+     */
+    boolean deleteUser(Integer userId);
+    
+    /**
+     * 根据用户ID查询角色列表
+     */
+    List<Role> getUserRoles(Integer userId);
+    
+    /**
+     * 为用户分配角色
+     */
+    boolean assignRoles(Integer userId, List<Integer> roleIds);
+    
+    /**
+     * 检查用户是否有某个角色
+     */
+    boolean hasRole(Integer userId, String roleCode);
+    
+    /**
+     * 检查用户是否是超级管理员
+     */
+    boolean isSuperAdmin(Integer userId);
+}
+

+ 22 - 0
service/admin/src/main/java/com/zhentao/service/impl/AdminPermissionServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.entity.AdminPermission;
+import com.zhentao.service.AdminPermissionService;
+import com.zhentao.mapper.AdminPermissionMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 联想
+* @description 针对表【admin_permission(管理员权限表)】的数据库操作Service实现
+* @createDate 2025-12-11 16:08:57
+*/
+@Service
+public class AdminPermissionServiceImpl extends ServiceImpl<AdminPermissionMapper, AdminPermission>
+    implements AdminPermissionService{
+
+}
+
+
+
+

+ 22 - 0
service/admin/src/main/java/com/zhentao/service/impl/AdminRolePermissionServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.entity.AdminRolePermission;
+import com.zhentao.service.AdminRolePermissionService;
+import com.zhentao.mapper.AdminRolePermissionMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 联想
+* @description 针对表【admin_role_permission(角色权限关联表)】的数据库操作Service实现
+* @createDate 2025-12-11 16:08:57
+*/
+@Service
+public class AdminRolePermissionServiceImpl extends ServiceImpl<AdminRolePermissionMapper, AdminRolePermission>
+    implements AdminRolePermissionService{
+
+}
+
+
+
+

+ 22 - 0
service/admin/src/main/java/com/zhentao/service/impl/AdminRoleServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.entity.AdminRole;
+import com.zhentao.service.AdminRoleService;
+import com.zhentao.mapper.AdminRoleMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 联想
+* @description 针对表【admin_role(管理员角色表)】的数据库操作Service实现
+* @createDate 2025-12-11 16:08:57
+*/
+@Service
+public class AdminRoleServiceImpl extends ServiceImpl<AdminRoleMapper, AdminRole>
+    implements AdminRoleService{
+
+}
+
+
+
+

+ 22 - 0
service/admin/src/main/java/com/zhentao/service/impl/AdminUserRoleServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.entity.AdminUserRole;
+import com.zhentao.service.AdminUserRoleService;
+import com.zhentao.mapper.AdminUserRoleMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 联想
+* @description 针对表【admin_user_role(用户角色关联表)】的数据库操作Service实现
+* @createDate 2025-12-11 16:08:57
+*/
+@Service
+public class AdminUserRoleServiceImpl extends ServiceImpl<AdminUserRoleMapper, AdminUserRole>
+    implements AdminUserRoleService{
+
+}
+
+
+
+

+ 22 - 0
service/admin/src/main/java/com/zhentao/service/impl/AdminUserServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.entity.AdminUser;
+import com.zhentao.service.AdminUserService;
+import com.zhentao.mapper.AdminUserMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 联想
+* @description 针对表【admin_user(管理员用户表)】的数据库操作Service实现
+* @createDate 2025-12-11 16:08:57
+*/
+@Service
+public class AdminUserServiceImpl extends ServiceImpl<AdminUserMapper, AdminUser>
+    implements AdminUserService{
+
+}
+
+
+
+

+ 165 - 0
service/admin/src/main/java/com/zhentao/service/impl/PermissionServiceImpl.java

@@ -0,0 +1,165 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zhentao.entity.Permission;
+import com.zhentao.mapper.PermissionMapper;
+import com.zhentao.service.PermissionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 权限服务实现
+ */
+@Service
+public class PermissionServiceImpl implements PermissionService {
+    
+    @Autowired
+    private PermissionMapper permissionMapper;
+    
+    @Override
+    public Permission createPermission(Permission permission) {
+        // 检查权限编码是否已存在
+        QueryWrapper<Permission> wrapper = new QueryWrapper<>();
+        wrapper.eq("permission_code", permission.getPermissionCode());
+        Permission existPermission = permissionMapper.selectOne(wrapper);
+        if (existPermission != null) {
+            throw new RuntimeException("权限编码已存在");
+        }
+        
+        permission.setStatus(1); // 默认启用
+        if (permission.getMethod() == null || permission.getMethod().isEmpty()) {
+            permission.setMethod("ALL");
+        }
+        permission.setCreateTime(LocalDateTime.now());
+        permission.setUpdateTime(LocalDateTime.now());
+        
+        permissionMapper.insert(permission);
+        return permission;
+    }
+    
+    @Override
+    public Page<Permission> getPermissionList(Integer pageNum, Integer pageSize, String keyword) {
+        Page<Permission> page = new Page<>(pageNum, pageSize);
+        QueryWrapper<Permission> wrapper = new QueryWrapper<>();
+        
+        if (StringUtils.hasText(keyword)) {
+            wrapper.and(w -> w.like("permission_name", keyword)
+                    .or().like("permission_code", keyword)
+                    .or().like("path", keyword));
+        }
+        
+        wrapper.orderByAsc("sort_order", "create_time");
+        
+        return permissionMapper.selectPage(page, wrapper);
+    }
+    
+    @Override
+    public Permission getPermissionById(Integer permissionId) {
+        return permissionMapper.selectById(permissionId);
+    }
+    
+    @Override
+    public boolean updatePermission(Permission permission) {
+        permission.setUpdateTime(LocalDateTime.now());
+        if (permission.getMethod() == null || permission.getMethod().isEmpty()) {
+            permission.setMethod("ALL");
+        }
+        return permissionMapper.updateById(permission) > 0;
+    }
+    
+    @Override
+    public boolean deletePermission(Integer permissionId) {
+        return permissionMapper.deleteById(permissionId) > 0;
+    }
+    
+    @Override
+    public List<Permission> getUserPermissions(Integer userId) {
+        return permissionMapper.selectPermissionsByUserId(userId);
+    }
+    
+    @Override
+    public boolean hasPermission(Integer userId, String path, String method) {
+        List<Permission> permissions = getUserPermissions(userId);
+        
+        for (Permission permission : permissions) {
+            String permissionPath = permission.getPath();
+            String permissionMethod = permission.getMethod();
+            
+            // 检查路径是否匹配(支持通配符)
+            if (pathMatches(permissionPath, path)) {
+                // 检查方法是否匹配
+                if ("ALL".equals(permissionMethod) || 
+                    permissionMethod.equalsIgnoreCase(method)) {
+                    return true;
+                }
+            }
+        }
+        
+        return false;
+    }
+    
+    /**
+     * 检查路径是否匹配(支持通配符)
+     */
+    private boolean pathMatches(String pattern, String path) {
+        // 完全匹配
+        if (pattern.equals(path)) {
+            return true;
+        }
+        
+        // 通配符匹配:/admin/user/** 匹配 /admin/user/list, /admin/user/add 等
+        if (pattern.endsWith("/**")) {
+            String prefix = pattern.substring(0, pattern.length() - 3);
+            return path.startsWith(prefix);
+        }
+        
+        // 通配符匹配:/admin/** 匹配所有 /admin/ 开头的路径
+        if (pattern.endsWith("**")) {
+            String prefix = pattern.substring(0, pattern.length() - 2);
+            return path.startsWith(prefix);
+        }
+        
+        return false;
+    }
+    
+    @Override
+    public List<Permission> getAllPermissionsTree() {
+        List<Permission> allPermissions = permissionMapper.selectList(
+            new QueryWrapper<Permission>().eq("status", 1)
+                .orderByAsc("sort_order", "create_time")
+        );
+        
+        // 构建树形结构
+        List<Permission> rootPermissions = allPermissions.stream()
+            .filter(p -> p.getParentId() == null || p.getParentId() == 0)
+            .collect(Collectors.toList());
+        
+        for (Permission root : rootPermissions) {
+            buildTree(root, allPermissions);
+        }
+        
+        return rootPermissions;
+    }
+    
+    /**
+     * 递归构建权限树
+     */
+    private void buildTree(Permission parent, List<Permission> allPermissions) {
+        List<Permission> children = allPermissions.stream()
+            .filter(p -> parent.getId().equals(p.getParentId()))
+            .collect(Collectors.toList());
+        
+        // 这里可以添加children字段到Permission实体,暂时简化处理
+        for (Permission child : children) {
+            buildTree(child, allPermissions);
+        }
+    }
+}
+

+ 123 - 0
service/admin/src/main/java/com/zhentao/service/impl/PointsProductServiceImpl.java

@@ -0,0 +1,123 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.entity.PointsProduct;
+import com.zhentao.service.PointsProductService;
+import com.zhentao.mapper.PointsProductMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.Date;
+
+/**
+* @author 联想
+* @description 针对表【points_product】的数据库操作Service实现
+* @createDate 2025-12-11 10:49:42
+*/
+@Slf4j
+@Service
+public class PointsProductServiceImpl extends ServiceImpl<PointsProductMapper, PointsProduct>
+    implements PointsProductService{
+    
+    @Override
+    public Page<PointsProduct> getProductList(Integer page, Integer size, String keyword, Integer status, Integer category) {
+        Page<PointsProduct> pageParam = new Page<>(page, size);
+        QueryWrapper<PointsProduct> wrapper = new QueryWrapper<>();
+        
+        // 关键词搜索(商品名称、描述)
+        if (StringUtils.hasText(keyword)) {
+            wrapper.and(w -> w.like("name", keyword).or().like("description", keyword));
+        }
+        
+        // 状态筛选(0-下架 1-上架)
+        if (status != null) {
+            wrapper.eq("status", status);
+        }
+        
+        // 分类筛选(1-实物 2-虚拟)
+        if (category != null) {
+            wrapper.eq("category", category);
+        }
+        
+        // 按排序字段和创建时间排序
+        wrapper.orderByAsc("sort_order").orderByDesc("create_time");
+        
+        return this.page(pageParam, wrapper);
+    }
+    
+    @Override
+    public PointsProduct getProductById(Long id) {
+        return this.getById(id);
+    }
+    
+    @Override
+    public boolean createProduct(PointsProduct product) {
+        try {
+            // 设置默认值
+            if (product.getStatus() == null) {
+                product.setStatus(0); // 默认下架
+            }
+            if (product.getIsRecommend() == null) {
+                product.setIsRecommend(0); // 默认不推荐
+            }
+            if (product.getSortOrder() == null) {
+                product.setSortOrder(0);
+            }
+            if (product.getStock() == null) {
+                product.setStock(0);
+            }
+            
+            Date now = new Date();
+            product.setCreateTime(now);
+            product.setUpdateTime(now);
+            
+            return this.save(product);
+        } catch (Exception e) {
+            log.error("创建积分商品失败", e);
+            return false;
+        }
+    }
+    
+    @Override
+    public boolean updateProduct(PointsProduct product) {
+        try {
+            product.setUpdateTime(new Date());
+            return this.updateById(product);
+        } catch (Exception e) {
+            log.error("更新积分商品失败", e);
+            return false;
+        }
+    }
+    
+    @Override
+    public boolean deleteProduct(Long id) {
+        try {
+            return this.removeById(id);
+        } catch (Exception e) {
+            log.error("删除积分商品失败", e);
+            return false;
+        }
+    }
+    
+    @Override
+    public boolean updateStatus(Long id, Integer status) {
+        try {
+            PointsProduct product = new PointsProduct();
+            product.setId(id);
+            product.setStatus(status);
+            product.setUpdateTime(new Date());
+            
+            return this.updateById(product);
+        } catch (Exception e) {
+            log.error("更新积分商品状态失败", e);
+            return false;
+        }
+    }
+}
+
+
+
+

+ 115 - 0
service/admin/src/main/java/com/zhentao/service/impl/RoleServiceImpl.java

@@ -0,0 +1,115 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zhentao.entity.Permission;
+import com.zhentao.entity.Role;
+import com.zhentao.entity.RolePermission;
+import com.zhentao.mapper.PermissionMapper;
+import com.zhentao.mapper.RoleMapper;
+import com.zhentao.mapper.RolePermissionMapper;
+import com.zhentao.service.RoleService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 角色服务实现
+ */
+@Service
+public class RoleServiceImpl implements RoleService {
+    
+    @Autowired
+    private RoleMapper roleMapper;
+    
+    @Autowired
+    private PermissionMapper permissionMapper;
+    
+    @Autowired
+    private RolePermissionMapper rolePermissionMapper;
+    
+    @Override
+    @Transactional
+    public Role createRole(Role role) {
+        // 检查角色编码是否已存在
+        QueryWrapper<Role> wrapper = new QueryWrapper<>();
+        wrapper.eq("role_code", role.getRoleCode());
+        Role existRole = roleMapper.selectOne(wrapper);
+        if (existRole != null) {
+            throw new RuntimeException("角色编码已存在");
+        }
+        
+        role.setStatus(1); // 默认启用
+        role.setCreateTime(LocalDateTime.now());
+        role.setUpdateTime(LocalDateTime.now());
+        
+        roleMapper.insert(role);
+        return role;
+    }
+    
+    @Override
+    public Page<Role> getRoleList(Integer pageNum, Integer pageSize, String keyword) {
+        Page<Role> page = new Page<>(pageNum, pageSize);
+        QueryWrapper<Role> wrapper = new QueryWrapper<>();
+        
+        if (StringUtils.hasText(keyword)) {
+            wrapper.and(w -> w.like("role_name", keyword)
+                    .or().like("role_code", keyword));
+        }
+        
+        wrapper.orderByDesc("create_time");
+        
+        return roleMapper.selectPage(page, wrapper);
+    }
+    
+    @Override
+    public Role getRoleById(Integer roleId) {
+        return roleMapper.selectById(roleId);
+    }
+    
+    @Override
+    public boolean updateRole(Role role) {
+        role.setUpdateTime(LocalDateTime.now());
+        return roleMapper.updateById(role) > 0;
+    }
+    
+    @Override
+    @Transactional
+    public boolean deleteRole(Integer roleId) {
+        // 删除角色权限关联
+        rolePermissionMapper.deleteByRoleId(roleId);
+        // 删除角色
+        return roleMapper.deleteById(roleId) > 0;
+    }
+    
+    @Override
+    @Transactional
+    public boolean assignPermissions(Integer roleId, List<Integer> permissionIds) {
+        // 删除原有权限
+        rolePermissionMapper.deleteByRoleId(roleId);
+        
+        // 分配新权限
+        if (permissionIds != null && !permissionIds.isEmpty()) {
+            LocalDateTime now = LocalDateTime.now();
+            for (Integer permissionId : permissionIds) {
+                RolePermission rolePermission = new RolePermission();
+                rolePermission.setRoleId(roleId);
+                rolePermission.setPermissionId(permissionId);
+                rolePermission.setCreateTime(now);
+                rolePermissionMapper.insert(rolePermission);
+            }
+        }
+        
+        return true;
+    }
+    
+    @Override
+    public List<Permission> getRolePermissions(Integer roleId) {
+        return permissionMapper.selectPermissionsByRoleId(roleId);
+    }
+}
+

+ 207 - 0
service/admin/src/main/java/com/zhentao/service/impl/UserServiceImpl.java

@@ -0,0 +1,207 @@
+package com.zhentao.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zhentao.entity.AdminUser;
+import com.zhentao.entity.AdminUserRole;
+import com.zhentao.entity.Role;
+import com.zhentao.mapper.AdminUserMapper;
+import com.zhentao.mapper.AdminUserRoleMapper;
+import com.zhentao.mapper.RoleMapper;
+import com.zhentao.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.DigestUtils;
+import org.springframework.util.StringUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * 用户服务实现
+ */
+@Service
+public class UserServiceImpl implements UserService {
+    
+    @Autowired
+    private AdminUserMapper adminUserMapper;
+    
+    @Autowired
+    private RoleMapper roleMapper;
+    
+    @Autowired
+    private AdminUserRoleMapper adminUserRoleMapper;
+    
+    @Override
+    @Transactional
+    public AdminUser register(AdminUser user, Integer roleId) {
+        // 检查用户名是否已存在
+        QueryWrapper<AdminUser> wrapper = new QueryWrapper<>();
+        wrapper.eq("username", user.getUsername());
+        AdminUser existUser = adminUserMapper.selectOne(wrapper);
+        if (existUser != null) {
+            throw new RuntimeException("用户名已存在");
+        }
+        
+        // 生成盐并加密密码:MD5(密码+盐)
+        String salt = generateSalt();
+        String encryptedPassword = encryptPassword(user.getPassword(), salt);
+        
+        // 设置用户信息
+        user.setPassword(encryptedPassword);
+        user.setSalt(salt);
+        user.setStatus(1); // 默认启用
+        user.setCreateTime(new Date());
+        user.setUpdateTime(new Date());
+        
+        // 保存用户
+        adminUserMapper.insert(user);
+        
+        // 分配角色:如果未指定角色,则分配默认的普通管理员角色
+        if (roleId == null) {
+            // 查询普通管理员角色ID(role_code = 'ADMIN')
+            QueryWrapper<Role> roleWrapper = new QueryWrapper<>();
+            roleWrapper.eq("role_code", "ADMIN");
+            Role defaultRole = roleMapper.selectOne(roleWrapper);
+            if (defaultRole != null) {
+                roleId = defaultRole.getId();
+            }
+        }
+        
+        // 创建用户角色关联
+        if (roleId != null) {
+            AdminUserRole adminUserRole = new AdminUserRole();
+            adminUserRole.setUserId(user.getId());
+            adminUserRole.setRoleId(roleId);
+            adminUserRole.setCreateTime(new Date());
+            adminUserRoleMapper.insert(adminUserRole);
+        }
+        
+        // 清除密码和盐
+        user.setPassword(null);
+        user.setSalt(null);
+        
+        return user;
+    }
+    
+    @Override
+    public Page<AdminUser> getUserList(Integer pageNum, Integer pageSize, String keyword) {
+        Page<AdminUser> page = new Page<>(pageNum, pageSize);
+        QueryWrapper<AdminUser> wrapper = new QueryWrapper<>();
+        
+        if (StringUtils.hasText(keyword)) {
+            wrapper.and(w -> w.like("username", keyword)
+                    .or().like("real_name", keyword)
+                    .or().like("phone", keyword));
+        }
+        
+        wrapper.orderByDesc("create_time");
+        
+        // 查询时排除密码和盐
+        Page<AdminUser> result = adminUserMapper.selectPage(page, wrapper);
+        result.getRecords().forEach(u -> {
+            u.setPassword(null);
+            u.setSalt(null);
+        });
+        
+        return result;
+    }
+    
+    @Override
+    public AdminUser getUserById(Integer userId) {
+        AdminUser user = adminUserMapper.selectById(userId);
+        if (user != null) {
+            user.setPassword(null);
+            user.setSalt(null);
+        }
+        return user;
+    }
+    
+    @Override
+    public boolean updateUser(AdminUser user) {
+        user.setUpdateTime(new Date());
+        // 如果更新了密码,需要重新加密:MD5(密码+盐)
+        if (StringUtils.hasText(user.getPassword())) {
+            String salt = generateSalt();
+            String encryptedPassword = encryptPassword(user.getPassword(), salt);
+            user.setPassword(encryptedPassword);
+            user.setSalt(salt);
+        } else {
+            // 不更新密码时,清除密码字段
+            user.setPassword(null);
+            user.setSalt(null);
+        }
+        return adminUserMapper.updateById(user) > 0;
+    }
+    
+    @Override
+    @Transactional
+    public boolean deleteUser(Integer userId) {
+        // 删除用户角色关联
+        adminUserRoleMapper.deleteByUserId(userId);
+        // 删除用户
+        return adminUserMapper.deleteById(userId) > 0;
+    }
+    
+    @Override
+    public List<Role> getUserRoles(Integer userId) {
+        List<Integer> roleIds = adminUserRoleMapper.selectRoleIdsByUserId(userId);
+        if (roleIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return roleMapper.selectBatchIds(roleIds);
+    }
+    
+    @Override
+    @Transactional
+    public boolean assignRoles(Integer userId, List<Integer> roleIds) {
+        // 删除原有角色
+        adminUserRoleMapper.deleteByUserId(userId);
+        
+        // 分配新角色
+        if (roleIds != null && !roleIds.isEmpty()) {
+            Date now = new Date();
+            for (Integer roleId : roleIds) {
+                AdminUserRole adminUserRole = new AdminUserRole();
+                adminUserRole.setUserId(userId);
+                adminUserRole.setRoleId(roleId);
+                adminUserRole.setCreateTime(now);
+                adminUserRoleMapper.insert(adminUserRole);
+            }
+        }
+        
+        return true;
+    }
+    
+    @Override
+    public boolean hasRole(Integer userId, String roleCode) {
+        List<Role> roles = getUserRoles(userId);
+        return roles.stream().anyMatch(r -> roleCode.equals(r.getRoleCode()));
+    }
+    
+    @Override
+    public boolean isSuperAdmin(Integer userId) {
+        return hasRole(userId, "SUPER_ADMIN");
+    }
+    
+    /**
+     * 生成随机盐
+     */
+    private String generateSalt() {
+        return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
+    }
+    
+    /**
+     * 密码加密:MD5(密码+盐)
+     */
+    private String encryptPassword(String password, String salt) {
+        String passwordWithSalt = password + salt;
+        return DigestUtils.md5DigestAsHex(passwordWithSalt.getBytes(StandardCharsets.UTF_8));
+    }
+}
+

+ 109 - 0
service/admin/src/main/java/com/zhentao/util/JwtUtil.java

@@ -0,0 +1,109 @@
+package com.zhentao.util;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * JWT工具类
+ * Token有效期:1小时
+ */
+@Component
+public class JwtUtil {
+    
+    @Value("${jwt.secret:change_me_secret_key_for_admin}")
+    private String secret;
+    
+    /**
+     * Token过期时间:1小时(3600秒)
+     */
+    private static final long EXPIRE_TIME = 60 * 60 * 1000; // 1小时
+    
+    /**
+     * 生成Token
+     * 
+     * @param userId 用户ID
+     * @return Token字符串
+     */
+    public String generateToken(Integer userId) {
+        Date now = new Date();
+        Date expireAt = new Date(now.getTime() + EXPIRE_TIME);
+        
+        Algorithm algorithm = Algorithm.HMAC256(secret);
+        
+        Map<String, Object> header = new HashMap<>();
+        header.put("typ", "JWT");
+        header.put("alg", "HS256");
+        
+        return JWT.create()
+                .withHeader(header)
+                .withClaim("userId", userId)
+                .withIssuedAt(now)
+                .withExpiresAt(expireAt)
+                .sign(algorithm);
+    }
+    
+    /**
+     * 验证Token并获取用户ID
+     * 
+     * @param token Token字符串
+     * @return 用户ID,如果Token无效则返回null
+     */
+    public Integer getUserIdFromToken(String token) {
+        try {
+            Algorithm algorithm = Algorithm.HMAC256(secret);
+            JWTVerifier verifier = JWT.require(algorithm).build();
+            DecodedJWT jwt = verifier.verify(token);
+            return jwt.getClaim("userId").asInt();
+        } catch (JWTVerificationException e) {
+            // Token无效或已过期
+            return null;
+        }
+    }
+    
+    /**
+     * 验证Token是否有效
+     * 
+     * @param token Token字符串
+     * @return 是否有效
+     */
+    public boolean validateToken(String token) {
+        try {
+            Algorithm algorithm = Algorithm.HMAC256(secret);
+            JWTVerifier verifier = JWT.require(algorithm).build();
+            verifier.verify(token);
+            return true;
+        } catch (JWTVerificationException e) {
+            return false;
+        }
+    }
+    
+    /**
+     * 检查Token是否即将过期(剩余时间少于5分钟)
+     * 
+     * @param token Token字符串
+     * @return 是否即将过期
+     */
+    public boolean isTokenExpiringSoon(String token) {
+        try {
+            DecodedJWT jwt = JWT.decode(token);
+            Date expiresAt = jwt.getExpiresAt();
+            if (expiresAt == null) {
+                return true;
+            }
+            long remainingTime = expiresAt.getTime() - System.currentTimeMillis();
+            return remainingTime < 5 * 60 * 1000; // 少于5分钟
+        } catch (Exception e) {
+            return true;
+        }
+    }
+}
+

+ 61 - 0
service/admin/src/main/java/com/zhentao/util/PasswordUtil.java

@@ -0,0 +1,61 @@
+package com.zhentao.util;
+
+import org.springframework.stereotype.Component;
+import org.springframework.util.DigestUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.util.Base64;
+
+/**
+ * 密码加密工具类
+ * 使用MD5(密码+盐)的方式加密
+ */
+@Component
+public class PasswordUtil {
+    
+    /**
+     * 生成随机盐(32位)
+     * 
+     * @return 盐字符串
+     */
+    public String generateSalt() {
+        SecureRandom random = new SecureRandom();
+        byte[] saltBytes = new byte[16];
+        random.nextBytes(saltBytes);
+        return Base64.getEncoder().encodeToString(saltBytes).substring(0, 32);
+    }
+    
+    /**
+     * 加密密码
+     * 加密方式:MD5(密码 + 盐)
+     * 
+     * @param password 原始密码
+     * @param salt 盐
+     * @return 加密后的密码
+     */
+    public String encryptPassword(String password, String salt) {
+        if (password == null || salt == null) {
+            throw new IllegalArgumentException("密码和盐不能为空");
+        }
+        String passwordWithSalt = password + salt;
+        return DigestUtils.md5DigestAsHex(passwordWithSalt.getBytes(StandardCharsets.UTF_8));
+    }
+    
+    /**
+     * 验证密码
+     * 
+     * @param inputPassword 用户输入的密码
+     * @param storedPassword 数据库中存储的加密密码
+     * @param salt 盐
+     * @return 是否匹配
+     */
+    public boolean verifyPassword(String inputPassword, String storedPassword, String salt) {
+        if (inputPassword == null || storedPassword == null || salt == null) {
+            return false;
+        }
+        String encryptedPassword = encryptPassword(inputPassword, salt);
+        return encryptedPassword.equals(storedPassword);
+    }
+}
+

+ 25 - 0
service/admin/src/main/resources/com/zhentao/mapper/AdminPermissionMapper.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhentao.mapper.AdminPermissionMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.entity.AdminPermission">
+            <id property="id" column="id" />
+            <result property="permissionName" column="permission_name" />
+            <result property="permissionCode" column="permission_code" />
+            <result property="path" column="path" />
+            <result property="method" column="method" />
+            <result property="description" column="description" />
+            <result property="parentId" column="parent_id" />
+            <result property="sortOrder" column="sort_order" />
+            <result property="status" column="status" />
+            <result property="createTime" column="create_time" />
+            <result property="updateTime" column="update_time" />
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,permission_name,permission_code,path,method,description,
+        parent_id,sort_order,status,create_time,update_time
+    </sql>
+</mapper>

+ 21 - 0
service/admin/src/main/resources/com/zhentao/mapper/AdminRoleMapper.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhentao.mapper.AdminRoleMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.entity.AdminRole">
+            <id property="id" column="id" />
+            <result property="roleName" column="role_name" />
+            <result property="roleCode" column="role_code" />
+            <result property="description" column="description" />
+            <result property="status" column="status" />
+            <result property="createTime" column="create_time" />
+            <result property="updateTime" column="update_time" />
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,role_name,role_code,description,status,create_time,
+        update_time
+    </sql>
+</mapper>

+ 17 - 0
service/admin/src/main/resources/com/zhentao/mapper/AdminRolePermissionMapper.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhentao.mapper.AdminRolePermissionMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.entity.AdminRolePermission">
+            <id property="id" column="id" />
+            <result property="roleId" column="role_id" />
+            <result property="permissionId" column="permission_id" />
+            <result property="createTime" column="create_time" />
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,role_id,permission_id,create_time
+    </sql>
+</mapper>

+ 25 - 0
service/admin/src/main/resources/com/zhentao/mapper/AdminUserMapper.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhentao.mapper.AdminUserMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.entity.AdminUser">
+            <id property="id" column="id" />
+            <result property="username" column="username" />
+            <result property="password" column="password" />
+            <result property="salt" column="salt" />
+            <result property="realName" column="real_name" />
+            <result property="phone" column="phone" />
+            <result property="email" column="email" />
+            <result property="status" column="status" />
+            <result property="createTime" column="create_time" />
+            <result property="updateTime" column="update_time" />
+            <result property="lastLoginTime" column="last_login_time" />
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,username,password,salt,real_name,phone,
+        email,status,create_time,update_time,last_login_time
+    </sql>
+</mapper>

+ 30 - 0
service/admin/src/main/resources/com/zhentao/mapper/AdminUserRoleMapper.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhentao.mapper.AdminUserRoleMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.entity.AdminUserRole">
+            <id property="id" column="id" />
+            <result property="userId" column="user_id" />
+            <result property="roleId" column="role_id" />
+            <result property="createTime" column="create_time" />
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,user_id,role_id,create_time
+    </sql>
+    
+    <!-- 根据用户ID查询角色ID列表 -->
+    <select id="selectRoleIdsByUserId" resultType="java.lang.Integer">
+        SELECT role_id
+        FROM admin_user_role
+        WHERE user_id = #{userId}
+    </select>
+    
+    <!-- 根据用户ID删除所有角色关联 -->
+    <delete id="deleteByUserId">
+        DELETE FROM admin_user_role
+        WHERE user_id = #{userId}
+    </delete>
+</mapper>

+ 27 - 0
service/admin/src/main/resources/com/zhentao/mapper/PointsProductMapper.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhentao.mapper.PointsProductMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.entity.PointsProduct">
+            <id property="id" column="id" />
+            <result property="name" column="name" />
+            <result property="description" column="description" />
+            <result property="imageUrl" column="image_url" />
+            <result property="pointsPrice" column="points_price" />
+            <result property="stock" column="stock" />
+            <result property="category" column="category" />
+            <result property="isRecommend" column="is_recommend" />
+            <result property="status" column="status" />
+            <result property="sortOrder" column="sort_order" />
+            <result property="createTime" column="create_time" />
+            <result property="updateTime" column="update_time" />
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,name,description,image_url,points_price,stock,
+        category,is_recommend,status,sort_order,create_time,
+        update_time
+    </sql>
+</mapper>

+ 87 - 0
service/admin/src/main/resources/db/schema.sql

@@ -0,0 +1,87 @@
+-- 用户表(扩展admin_user表,添加盐字段)
+ALTER TABLE `admin_user` 
+ADD COLUMN `salt` VARCHAR(32) NULL COMMENT '密码盐' AFTER `password`;
+
+-- 角色表
+CREATE TABLE IF NOT EXISTS `admin_role` (
+  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
+  `role_name` VARCHAR(50) NOT NULL COMMENT '角色名称',
+  `role_code` VARCHAR(50) NOT NULL COMMENT '角色编码(唯一)',
+  `description` VARCHAR(255) NULL COMMENT '角色描述',
+  `status` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '状态:0-禁用 1-启用',
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_role_code` (`role_code`),
+  KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员角色表';
+
+-- 权限表
+CREATE TABLE IF NOT EXISTS `admin_permission` (
+  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '权限ID',
+  `permission_name` VARCHAR(100) NOT NULL COMMENT '权限名称',
+  `permission_code` VARCHAR(100) NOT NULL COMMENT '权限编码(唯一)',
+  `path` VARCHAR(255) NOT NULL COMMENT '访问路径(Controller路径,如:/admin/user/list)',
+  `method` VARCHAR(10) NULL DEFAULT 'ALL' COMMENT 'HTTP方法:GET,POST,PUT,DELETE,ALL',
+  `description` VARCHAR(255) NULL COMMENT '权限描述',
+  `parent_id` INT(11) NULL DEFAULT 0 COMMENT '父权限ID(0表示顶级)',
+  `sort_order` INT(11) NULL DEFAULT 0 COMMENT '排序顺序',
+  `status` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '状态:0-禁用 1-启用',
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_permission_code` (`permission_code`),
+  KEY `idx_path` (`path`),
+  KEY `idx_status` (`status`),
+  KEY `idx_parent_id` (`parent_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员权限表';
+
+-- 用户角色关联表
+CREATE TABLE IF NOT EXISTS `admin_user_role` (
+  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` INT(11) NOT NULL COMMENT '用户ID',
+  `role_id` INT(11) NOT NULL COMMENT '角色ID',
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_user_role` (`user_id`, `role_id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_role_id` (`role_id`),
+  CONSTRAINT `fk_user_role_user` FOREIGN KEY (`user_id`) REFERENCES `admin_user` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `fk_user_role_role` FOREIGN KEY (`role_id`) REFERENCES `admin_role` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';
+
+-- 角色权限关联表
+CREATE TABLE IF NOT EXISTS `admin_role_permission` (
+  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `role_id` INT(11) NOT NULL COMMENT '角色ID',
+  `permission_id` INT(11) NOT NULL COMMENT '权限ID',
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_role_permission` (`role_id`, `permission_id`),
+  KEY `idx_role_id` (`role_id`),
+  KEY `idx_permission_id` (`permission_id`),
+  CONSTRAINT `fk_role_permission_role` FOREIGN KEY (`role_id`) REFERENCES `admin_role` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `fk_role_permission_permission` FOREIGN KEY (`permission_id`) REFERENCES `admin_permission` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';
+
+-- 初始化超级管理员角色
+INSERT INTO `admin_role` (`role_name`, `role_code`, `description`, `status`) 
+VALUES ('超级管理员', 'SUPER_ADMIN', '拥有所有权限的超级管理员', 1)
+ON DUPLICATE KEY UPDATE `role_name` = '超级管理员';
+
+-- 初始化普通管理员角色
+INSERT INTO `admin_role` (`role_name`, `role_code`, `description`, `status`) 
+VALUES ('普通管理员', 'ADMIN', '普通管理员', 1)
+ON DUPLICATE KEY UPDATE `role_name` = '普通管理员';
+
+-- 初始化常用权限(示例)
+INSERT INTO `admin_permission` (`permission_name`, `permission_code`, `path`, `method`, `description`, `parent_id`, `sort_order`, `status`) VALUES
+('用户管理', 'USER_MANAGE', '/admin/user/**', 'ALL', '用户管理相关权限', 0, 1, 1),
+('用户列表', 'USER_LIST', '/admin/user/list', 'GET', '查看用户列表', 1, 1, 1),
+('用户注册', 'USER_REGISTER', '/admin/user/register', 'POST', '注册新用户', 1, 2, 1),
+('用户编辑', 'USER_EDIT', '/admin/user/edit/**', 'PUT', '编辑用户信息', 1, 3, 1),
+('用户删除', 'USER_DELETE', '/admin/user/delete/**', 'DELETE', '删除用户', 1, 4, 1),
+('角色管理', 'ROLE_MANAGE', '/admin/role/**', 'ALL', '角色管理相关权限', 0, 2, 1),
+('权限管理', 'PERMISSION_MANAGE', '/admin/permission/**', 'ALL', '权限管理相关权限', 0, 3, 1)
+ON DUPLICATE KEY UPDATE `permission_name` = VALUES(`permission_name`);
+

+ 37 - 0
service/admin/src/main/resources/mapper/PermissionMapper.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhentao.mapper.PermissionMapper">
+    
+    <!-- 根据角色ID查询权限列表 -->
+    <select id="selectPermissionsByRoleId" resultType="com.zhentao.entity.Permission">
+        SELECT p.*
+        FROM admin_permission p
+        INNER JOIN admin_role_permission rp ON p.id = rp.permission_id
+        WHERE rp.role_id = #{roleId}
+        AND p.status = 1
+        ORDER BY p.sort_order, p.create_time
+    </select>
+    
+    <!-- 根据用户ID查询权限列表 -->
+    <select id="selectPermissionsByUserId" resultType="com.zhentao.entity.Permission">
+        SELECT DISTINCT p.*
+        FROM admin_permission p
+        INNER JOIN admin_role_permission rp ON p.id = rp.permission_id
+        INNER JOIN admin_user_role ur ON rp.role_id = ur.role_id
+        WHERE ur.user_id = #{userId}
+        AND p.status = 1
+        ORDER BY p.sort_order, p.create_time
+    </select>
+    
+    <!-- 根据路径和方法查询权限 -->
+    <select id="selectByPathAndMethod" resultType="com.zhentao.entity.Permission">
+        SELECT *
+        FROM admin_permission
+        WHERE path = #{path}
+        AND (method = #{method} OR method = 'ALL')
+        AND status = 1
+        LIMIT 1
+    </select>
+    
+</mapper>
+

+ 19 - 0
service/admin/src/main/resources/mapper/RolePermissionMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhentao.mapper.RolePermissionMapper">
+    
+    <!-- 根据角色ID查询权限ID列表 -->
+    <select id="selectPermissionIdsByRoleId" resultType="java.lang.Integer">
+        SELECT permission_id
+        FROM admin_role_permission
+        WHERE role_id = #{roleId}
+    </select>
+    
+    <!-- 根据角色ID删除所有权限关联 -->
+    <delete id="deleteByRoleId">
+        DELETE FROM admin_role_permission
+        WHERE role_id = #{roleId}
+    </delete>
+    
+</mapper>
+

+ 19 - 0
service/admin/src/main/resources/mapper/UserRoleMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zhentao.mapper.UserRoleMapper">
+    
+    <!-- 根据用户ID查询角色ID列表 -->
+    <select id="selectRoleIdsByUserId" resultType="java.lang.Integer">
+        SELECT role_id
+        FROM admin_user_role
+        WHERE user_id = #{userId}
+    </select>
+    
+    <!-- 根据用户ID删除所有角色关联 -->
+    <delete id="deleteByUserId">
+        DELETE FROM admin_user_role
+        WHERE user_id = #{userId}
+    </delete>
+    
+</mapper>
+

+ 53 - 0
service/dynamic/src/main/java/com/zhentao/exception/CustomErrorController.java

@@ -0,0 +1,53 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.springframework.boot.web.servlet.error.ErrorController;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 自定义错误控制器
+ * 处理Spring Boot默认的/error路径
+ */
+@RestController
+public class CustomErrorController implements ErrorController {
+
+    @RequestMapping("/error")
+    public ResponseEntity<Result<String>> handleError(HttpServletRequest request) {
+        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
+        String errorMessage = (String) request.getAttribute("javax.servlet.error.message");
+        Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
+
+        if (statusCode == null) {
+            statusCode = 500;
+        }
+
+        if (errorMessage == null || errorMessage.isEmpty()) {
+            if (statusCode == 404) {
+                errorMessage = "请求的资源不存在";
+            } else if (statusCode == 403) {
+                errorMessage = "拒绝访问";
+            } else if (statusCode == 401) {
+                errorMessage = "未授权,请先登录";
+            } else if (statusCode == 500) {
+                errorMessage = "服务器内部错误";
+            } else {
+                errorMessage = "未知错误";
+            }
+        }
+
+        if (exception != null && exception.getMessage() != null) {
+            errorMessage = exception.getMessage();
+        }
+
+        Result<String> result = Result.error(statusCode, errorMessage);
+        HttpStatus httpStatus = HttpStatus.valueOf(statusCode);
+        
+        return new ResponseEntity<>(result, httpStatus);
+    }
+}
+

+ 120 - 0
service/dynamic/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java

@@ -0,0 +1,120 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 全局异常处理器
+ * 统一处理所有Controller抛出的异常
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+    /**
+     * 处理运行时异常
+     */
+    @ExceptionHandler(RuntimeException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
+        logger.error("运行时异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: " + e.getMessage());
+    }
+
+    /**
+     * 处理所有异常
+     */
+    @ExceptionHandler(Exception.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleException(Exception e, HttpServletRequest request) {
+        logger.error("系统异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统异常: " + e.getMessage());
+    }
+
+    /**
+     * 处理参数校验异常(@Valid)
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数校验失败: {}", errorMessage);
+        return Result.error(400, "参数校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理参数绑定异常
+     */
+    @ExceptionHandler(BindException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleBindException(BindException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数绑定失败: {}", errorMessage);
+        return Result.error(400, "参数绑定失败: " + errorMessage);
+    }
+
+    /**
+     * 处理约束校验异常
+     */
+    @ExceptionHandler(ConstraintViolationException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleConstraintViolationException(ConstraintViolationException e) {
+        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
+        String errorMessage = violations.stream()
+                .map(ConstraintViolation::getMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("约束校验失败: {}", errorMessage);
+        return Result.error(400, "约束校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理文件上传大小超限异常
+     */
+    @ExceptionHandler(MaxUploadSizeExceededException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
+        logger.warn("文件上传大小超限: {}", e.getMessage());
+        return Result.error(400, "文件大小超过限制,单个文件最大10MB,请压缩后重试");
+    }
+
+    /**
+     * 处理空指针异常
+     */
+    @ExceptionHandler(NullPointerException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
+        logger.error("空指针异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: 空指针异常");
+    }
+
+    /**
+     * 处理非法参数异常
+     */
+    @ExceptionHandler(IllegalArgumentException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleIllegalArgumentException(IllegalArgumentException e) {
+        logger.warn("非法参数: {}", e.getMessage());
+        return Result.error(400, "参数错误: " + e.getMessage());
+    }
+}
+

+ 26 - 0
service/homePage/src/main/java/com/zhentao/config/MyBatisPlusConfig.java

@@ -0,0 +1,26 @@
+package com.zhentao.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * MyBatis-Plus配置类
+ */
+@Configuration
+public class MyBatisPlusConfig {
+
+    /**
+     * 分页插件
+     * 用于正确计算分页查询的总数和限制每页数据量
+     */
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+        return interceptor;
+    }
+}
+

+ 53 - 0
service/homePage/src/main/java/com/zhentao/exception/CustomErrorController.java

@@ -0,0 +1,53 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.springframework.boot.web.servlet.error.ErrorController;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 自定义错误控制器
+ * 处理Spring Boot默认的/error路径
+ */
+@RestController
+public class CustomErrorController implements ErrorController {
+
+    @RequestMapping("/error")
+    public ResponseEntity<Result<String>> handleError(HttpServletRequest request) {
+        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
+        String errorMessage = (String) request.getAttribute("javax.servlet.error.message");
+        Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
+
+        if (statusCode == null) {
+            statusCode = 500;
+        }
+
+        if (errorMessage == null || errorMessage.isEmpty()) {
+            if (statusCode == 404) {
+                errorMessage = "请求的资源不存在";
+            } else if (statusCode == 403) {
+                errorMessage = "拒绝访问";
+            } else if (statusCode == 401) {
+                errorMessage = "未授权,请先登录";
+            } else if (statusCode == 500) {
+                errorMessage = "服务器内部错误";
+            } else {
+                errorMessage = "未知错误";
+            }
+        }
+
+        if (exception != null && exception.getMessage() != null) {
+            errorMessage = exception.getMessage();
+        }
+
+        Result<String> result = Result.error(statusCode, errorMessage);
+        HttpStatus httpStatus = HttpStatus.valueOf(statusCode);
+        
+        return new ResponseEntity<>(result, httpStatus);
+    }
+}
+

+ 120 - 0
service/homePage/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java

@@ -0,0 +1,120 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 全局异常处理器
+ * 统一处理所有Controller抛出的异常
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+    /**
+     * 处理运行时异常
+     */
+    @ExceptionHandler(RuntimeException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
+        logger.error("运行时异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: " + e.getMessage());
+    }
+
+    /**
+     * 处理所有异常
+     */
+    @ExceptionHandler(Exception.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleException(Exception e, HttpServletRequest request) {
+        logger.error("系统异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统异常: " + e.getMessage());
+    }
+
+    /**
+     * 处理参数校验异常(@Valid)
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数校验失败: {}", errorMessage);
+        return Result.error(400, "参数校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理参数绑定异常
+     */
+    @ExceptionHandler(BindException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleBindException(BindException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数绑定失败: {}", errorMessage);
+        return Result.error(400, "参数绑定失败: " + errorMessage);
+    }
+
+    /**
+     * 处理约束校验异常
+     */
+    @ExceptionHandler(ConstraintViolationException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleConstraintViolationException(ConstraintViolationException e) {
+        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
+        String errorMessage = violations.stream()
+                .map(ConstraintViolation::getMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("约束校验失败: {}", errorMessage);
+        return Result.error(400, "约束校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理文件上传大小超限异常
+     */
+    @ExceptionHandler(MaxUploadSizeExceededException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
+        logger.warn("文件上传大小超限: {}", e.getMessage());
+        return Result.error(400, "文件大小超过限制,单个文件最大10MB,请压缩后重试");
+    }
+
+    /**
+     * 处理空指针异常
+     */
+    @ExceptionHandler(NullPointerException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
+        logger.error("空指针异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: 空指针异常");
+    }
+
+    /**
+     * 处理非法参数异常
+     */
+    @ExceptionHandler(IllegalArgumentException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleIllegalArgumentException(IllegalArgumentException e) {
+        logger.warn("非法参数: {}", e.getMessage());
+        return Result.error(400, "参数错误: " + e.getMessage());
+    }
+}
+

+ 92 - 0
service/randomMatch/src/main/java/com/zhentao/common/Result.java

@@ -0,0 +1,92 @@
+package com.zhentao.common;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 统一响应结果类
+ */
+@Data
+public class Result<T> implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+    
+    /**
+     * 状态码
+     */
+    private Integer code;
+    
+    /**
+     * 消息
+     */
+    private String message;
+    
+    /**
+     * 数据
+     */
+    private T data;
+    
+    /**
+     * 成功
+     */
+    public static <T> Result<T> success() {
+        Result<T> result = new Result<>();
+        result.setCode(200);
+        result.setMessage("操作成功");
+        return result;
+    }
+    
+    /**
+     * 成功(带数据)
+     */
+    public static <T> Result<T> success(T data) {
+        Result<T> result = new Result<>();
+        result.setCode(200);
+        result.setMessage("操作成功");
+        result.setData(data);
+        return result;
+    }
+    
+    /**
+     * 成功(自定义消息和数据)
+     */
+    public static <T> Result<T> success(String message, T data) {
+        Result<T> result = new Result<>();
+        result.setCode(200);
+        result.setMessage(message);
+        result.setData(data);
+        return result;
+    }
+    
+    /**
+     * 失败
+     */
+    public static <T> Result<T> error() {
+        Result<T> result = new Result<>();
+        result.setCode(500);
+        result.setMessage("操作失败");
+        return result;
+    }
+    
+    /**
+     * 失败(自定义消息)
+     */
+    public static <T> Result<T> error(String message) {
+        Result<T> result = new Result<>();
+        result.setCode(500);
+        result.setMessage(message);
+        return result;
+    }
+    
+    /**
+     * 失败(自定义状态码和消息)
+     */
+    public static <T> Result<T> error(Integer code, String message) {
+        Result<T> result = new Result<>();
+        result.setCode(code);
+        result.setMessage(message);
+        return result;
+    }
+}
+

+ 53 - 0
service/randomMatch/src/main/java/com/zhentao/exception/CustomErrorController.java

@@ -0,0 +1,53 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.springframework.boot.web.servlet.error.ErrorController;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 自定义错误控制器
+ * 处理Spring Boot默认的/error路径
+ */
+@RestController
+public class CustomErrorController implements ErrorController {
+
+    @RequestMapping("/error")
+    public ResponseEntity<Result<String>> handleError(HttpServletRequest request) {
+        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
+        String errorMessage = (String) request.getAttribute("javax.servlet.error.message");
+        Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
+
+        if (statusCode == null) {
+            statusCode = 500;
+        }
+
+        if (errorMessage == null || errorMessage.isEmpty()) {
+            if (statusCode == 404) {
+                errorMessage = "请求的资源不存在";
+            } else if (statusCode == 403) {
+                errorMessage = "拒绝访问";
+            } else if (statusCode == 401) {
+                errorMessage = "未授权,请先登录";
+            } else if (statusCode == 500) {
+                errorMessage = "服务器内部错误";
+            } else {
+                errorMessage = "未知错误";
+            }
+        }
+
+        if (exception != null && exception.getMessage() != null) {
+            errorMessage = exception.getMessage();
+        }
+
+        Result<String> result = Result.error(statusCode, errorMessage);
+        HttpStatus httpStatus = HttpStatus.valueOf(statusCode);
+        
+        return new ResponseEntity<>(result, httpStatus);
+    }
+}
+

+ 103 - 0
service/randomMatch/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java

@@ -0,0 +1,103 @@
+package com.zhentao.exception;
+
+import com.zhentao.common.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.stream.Collectors;
+
+/**
+ * 全局异常处理器
+ * 统一处理所有Controller抛出的异常
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+    /**
+     * 处理运行时异常
+     */
+    @ExceptionHandler(RuntimeException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
+        logger.error("运行时异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: " + e.getMessage());
+    }
+
+    /**
+     * 处理所有异常
+     */
+    @ExceptionHandler(Exception.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleException(Exception e, HttpServletRequest request) {
+        logger.error("系统异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统异常: " + e.getMessage());
+    }
+
+    /**
+     * 处理参数校验异常(@Valid)
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数校验失败: {}", errorMessage);
+        return Result.error(400, "参数校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理参数绑定异常
+     */
+    @ExceptionHandler(BindException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleBindException(BindException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数绑定失败: {}", errorMessage);
+        return Result.error(400, "参数绑定失败: " + errorMessage);
+    }
+
+    /**
+     * 处理文件上传大小超限异常
+     */
+    @ExceptionHandler(MaxUploadSizeExceededException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
+        logger.warn("文件上传大小超限: {}", e.getMessage());
+        return Result.error(400, "文件大小超过限制,单个文件最大10MB,请压缩后重试");
+    }
+
+    /**
+     * 处理空指针异常
+     */
+    @ExceptionHandler(NullPointerException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result<String> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
+        logger.error("空指针异常: {}", e.getMessage(), e);
+        return Result.error(500, "系统错误: 空指针异常");
+    }
+
+    /**
+     * 处理非法参数异常
+     */
+    @ExceptionHandler(IllegalArgumentException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result<String> handleIllegalArgumentException(IllegalArgumentException e) {
+        logger.warn("非法参数: {}", e.getMessage());
+        return Result.error(400, "参数错误: " + e.getMessage());
+    }
+}
+

+ 53 - 0
service/websocket/src/main/java/com/zhentao/exception/CustomErrorController.java

@@ -0,0 +1,53 @@
+package com.zhentao.exception;
+
+import com.zhentao.vo.ResultVo;
+import org.springframework.boot.web.servlet.error.ErrorController;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 自定义错误控制器
+ * 处理Spring Boot默认的/error路径
+ */
+@RestController
+public class CustomErrorController implements ErrorController {
+
+    @RequestMapping("/error")
+    public ResponseEntity<ResultVo<Object>> handleError(HttpServletRequest request) {
+        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
+        String errorMessage = (String) request.getAttribute("javax.servlet.error.message");
+        Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
+
+        if (statusCode == null) {
+            statusCode = 500;
+        }
+
+        if (errorMessage == null || errorMessage.isEmpty()) {
+            if (statusCode == 404) {
+                errorMessage = "请求的资源不存在";
+            } else if (statusCode == 403) {
+                errorMessage = "拒绝访问";
+            } else if (statusCode == 401) {
+                errorMessage = "未授权,请先登录";
+            } else if (statusCode == 500) {
+                errorMessage = "服务器内部错误";
+            } else {
+                errorMessage = "未知错误";
+            }
+        }
+
+        if (exception != null && exception.getMessage() != null) {
+            errorMessage = exception.getMessage();
+        }
+
+        ResultVo<Object> result = ResultVo.fail(statusCode, errorMessage);
+        HttpStatus httpStatus = HttpStatus.valueOf(statusCode);
+        
+        return new ResponseEntity<>(result, httpStatus);
+    }
+}
+

+ 120 - 0
service/websocket/src/main/java/com/zhentao/exception/GlobalExceptionHandler.java

@@ -0,0 +1,120 @@
+package com.zhentao.exception;
+
+import com.zhentao.vo.ResultVo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 全局异常处理器
+ * 统一处理所有Controller抛出的异常
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+    /**
+     * 处理运行时异常
+     */
+    @ExceptionHandler(RuntimeException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public ResultVo<Object> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
+        logger.error("运行时异常: {}", e.getMessage(), e);
+        return ResultVo.fail(500, "系统错误: " + e.getMessage());
+    }
+
+    /**
+     * 处理所有异常
+     */
+    @ExceptionHandler(Exception.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public ResultVo<Object> handleException(Exception e, HttpServletRequest request) {
+        logger.error("系统异常: {}", e.getMessage(), e);
+        return ResultVo.fail(500, "系统异常: " + e.getMessage());
+    }
+
+    /**
+     * 处理参数校验异常(@Valid)
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public ResultVo<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数校验失败: {}", errorMessage);
+        return ResultVo.fail(400, "参数校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理参数绑定异常
+     */
+    @ExceptionHandler(BindException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public ResultVo<Object> handleBindException(BindException e) {
+        String errorMessage = e.getBindingResult().getFieldErrors().stream()
+                .map(FieldError::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("参数绑定失败: {}", errorMessage);
+        return ResultVo.fail(400, "参数绑定失败: " + errorMessage);
+    }
+
+    /**
+     * 处理约束校验异常
+     */
+    @ExceptionHandler(ConstraintViolationException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public ResultVo<Object> handleConstraintViolationException(ConstraintViolationException e) {
+        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
+        String errorMessage = violations.stream()
+                .map(ConstraintViolation::getMessage)
+                .collect(Collectors.joining(", "));
+        logger.warn("约束校验失败: {}", errorMessage);
+        return ResultVo.fail(400, "约束校验失败: " + errorMessage);
+    }
+
+    /**
+     * 处理文件上传大小超限异常
+     */
+    @ExceptionHandler(MaxUploadSizeExceededException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public ResultVo<Object> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
+        logger.warn("文件上传大小超限: {}", e.getMessage());
+        return ResultVo.fail(400, "文件大小超过限制,单个文件最大10MB,请压缩后重试");
+    }
+
+    /**
+     * 处理空指针异常
+     */
+    @ExceptionHandler(NullPointerException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public ResultVo<Object> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
+        logger.error("空指针异常: {}", e.getMessage(), e);
+        return ResultVo.fail(500, "系统错误: 空指针异常");
+    }
+
+    /**
+     * 处理非法参数异常
+     */
+    @ExceptionHandler(IllegalArgumentException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public ResultVo<Object> handleIllegalArgumentException(IllegalArgumentException e) {
+        logger.warn("非法参数: {}", e.getMessage());
+        return ResultVo.fail(400, "参数错误: " + e.getMessage());
+    }
+}
+