Bladeren bron

✨ feat(admin): 实现银龄库管理后台功能

- 新增银龄库管理后台API路由和控制器
- 实现人才列表查询、详情查看、编辑、认证状态更新等功能
- 添加统计数据接口,支持年龄分布、求职状态等统计
- 创建管理后台页面组件,包含搜索、筛选、排序、批量操作
- 新增银龄库管理菜单项和路由配置
- 完善权限控制,支持silver-talent:manage权限
- 添加数据库查询优化和索引配置
yourname 7 maanden geleden
bovenliggende
commit
82bde80748

+ 278 - 0
docs/silver-talent-admin-design-plan.md

@@ -0,0 +1,278 @@
+# 银龄库管理功能实施计划
+
+## 项目概述
+基于现有`SilverUserProfile`实体,为管理后台设计完整的银龄库(银卡库)管理系统,实现对银龄人才的全面管理功能。
+
+## 技术架构
+
+### 1. 实体结构
+使用现有的`SilverUserProfile`实体,包含以下核心字段:
+- 基本信息:姓名、年龄、性别、联系方式、头像
+- 机构信息:所属组织、机构认证
+- 技能信息:个人技能、经验、简介
+- 认证状态:未认证、认证中、已认证、已拒绝
+- 求职状态:未求职、积极求职、观望机会
+- 统计数据:积分、简历数、申请数、时间银行小时数
+- 知识贡献:分享数、阅读量、点赞数、排名
+
+### 2. 管理功能模块
+
+#### 2.1 列表管理功能
+- 分页查询所有银龄人才
+- 多条件筛选(姓名、年龄、认证状态、求职状态等)
+- 排序功能(按创建时间、积分、排名等)
+- 批量操作(批量认证、批量导出)
+
+#### 2.2 详情管理功能
+- 查看完整人才档案
+- 编辑人才信息
+- 认证状态管理
+- 数据统计展示
+
+#### 2.3 认证管理功能
+- 认证审核流程
+- 认证材料查看
+- 认证状态批量更新
+- 认证历史记录
+
+#### 2.4 统计分析功能
+- 人才总数统计
+- 认证状态分布
+- 年龄段分布
+- 技能标签统计
+- 活跃度排名
+
+## 实施步骤
+
+### 阶段一:后端API扩展(标准通用CRUD + 管理增强)
+
+#### 1.1 创建管理专用API路由
+```
+src/server/api/silver-talents-admin/
+├── index.ts          # 路由聚合
+├── get.ts            # 管理列表查询
+├── [id]/
+│   ├── get.ts        # 管理详情查询
+│   ├── put.ts        # 管理员更新
+│   └── patch.ts      # 认证状态更新
+└── stats.ts          # 统计数据
+```
+
+#### 1.2 扩展现有服务
+在`SilverTalentService`基础上增加管理功能:
+- `findForAdmin()` - 管理后台专用查询
+- `updateByAdmin()` - 管理员更新功能
+- `batchUpdateCertification()` - 批量认证更新
+- `getStats()` - 统计数据获取
+
+### 阶段二:管理后台界面开发
+
+#### 2.1 菜单配置
+在`src/client/admin/menu.tsx`中添加:
+```typescript
+{
+  key: 'silver-talents',
+  label: '银龄库管理',
+  icon: <UserOutlined />,
+  path: '/admin/silver-talents',
+  permission: 'silver-talent:manage'
+}
+```
+
+#### 2.2 路由配置
+在`src/client/admin/routes.tsx`中添加:
+```typescript
+{
+  path: 'silver-talents',
+  element: <SilverTalentsPage />,
+  errorElement: <ErrorPage />
+}
+```
+
+#### 2.3 页面组件结构
+```
+src/client/admin/pages/SilverTalents/
+├── index.tsx           # 列表页面
+├── Detail.tsx          # 详情页面
+├── components/
+│   ├── TalentTable.tsx     # 人才表格
+│   ├── TalentForm.tsx      # 编辑表单
+│   ├── CertificationModal.tsx  # 认证弹窗
+│   └── StatsCard.tsx       # 统计卡片
+└── hooks/
+    └── useTalentData.ts    # 数据钩子
+```
+
+### 阶段三:功能实现详情
+
+#### 3.1 列表页面功能
+- 表格展示:姓名、年龄、机构、认证状态、求职状态、积分
+- 搜索筛选:姓名搜索、年龄范围、认证状态筛选、求职状态筛选
+- 排序功能:按创建时间、积分、活跃度排序
+- 批量操作:批量认证、批量导出Excel
+- 操作列:查看详情、编辑、认证管理
+
+#### 3.2 详情页面功能
+- 基本信息展示
+- 认证材料查看
+- 统计数据可视化
+- 编辑表单
+- 操作日志
+
+#### 3.3 认证管理功能
+- 认证状态切换
+- 认证理由填写
+- 认证材料上传
+- 认证历史查看
+
+### 阶段四:数据库优化
+
+#### 4.1 索引优化
+```sql
+-- 为管理查询优化索引
+CREATE INDEX idx_silver_profile_name ON silver_user_profiles(real_name);
+CREATE INDEX idx_silver_profile_age ON silver_user_profiles(age);
+CREATE INDEX idx_silver_profile_cert_status ON silver_user_profiles(certification_status);
+CREATE INDEX idx_silver_profile_job_status ON silver_user_profiles(job_seeking_status);
+```
+
+#### 4.2 权限配置
+在权限系统中添加:
+- `silver-talent:read` - 查看银龄人才
+- `silver-talent:write` - 编辑银龄人才
+- `silver-talent:certify` - 认证管理
+- `silver-talent:delete` - 删除银龄人才
+
+## API设计规范
+
+### 列表查询(管理专用)
+```
+GET /api/v1/admin/silver-talents
+参数:
+- page: 页码
+- pageSize: 每页数量
+- keyword: 关键词搜索
+- certificationStatus: 认证状态筛选
+- jobSeekingStatus: 求职状态筛选
+- ageMin/ageMax: 年龄范围
+- sortBy: 排序字段
+- sortOrder: 排序方向
+
+响应:
+{
+  data: SilverUserProfile[],
+  pagination: {...},
+  stats: {...}
+}
+```
+
+### 认证状态更新
+```
+PATCH /api/v1/admin/silver-talents/:id/certification
+请求体:
+{
+  certificationStatus: "CERTIFIED" | "REJECTED",
+  certificationInfo: string,
+  reason?: string
+}
+```
+
+### 统计数据
+```
+GET /api/v1/admin/silver-talents/stats
+响应:
+{
+  totalCount: number,
+  certifiedCount: number,
+  pendingCount: number,
+  rejectedCount: number,
+  ageDistribution: {...},
+  skillDistribution: {...}
+}
+```
+
+## 前端组件设计
+
+### 表格列配置
+```typescript
+const columns = [
+  { title: '姓名', dataIndex: 'realName', key: 'realName' },
+  { title: '年龄', dataIndex: 'age', key: 'age', sorter: true },
+  { title: '所属机构', dataIndex: 'organization', key: 'organization' },
+  { title: '认证状态', dataIndex: 'certificationStatus', key: 'certificationStatus', 
+    render: (status) => <Tag color={getStatusColor(status)}>{getStatusText(status)}</Tag> },
+  { title: '求职状态', dataIndex: 'jobSeekingStatus', key: 'jobSeekingStatus' },
+  { title: '积分', dataIndex: 'totalPoints', key: 'totalPoints', sorter: true },
+  { title: '操作', key: 'action', render: (_, record) => <ActionButtons /> }
+];
+```
+
+### 筛选表单
+- 姓名搜索框
+- 年龄范围输入(最小年龄/最大年龄)
+- 认证状态下拉选择
+- 求职状态下拉选择
+- 日期范围选择器
+- 技能标签多选框
+
+## 性能优化策略
+
+### 查询优化
+1. 使用数据库索引
+2. 分页查询
+3. 懒加载关联数据
+4. 缓存热点数据
+
+### 前端优化
+1. 表格虚拟滚动
+2. 图片懒加载
+3. 搜索防抖
+4. 数据缓存
+
+## 安全考虑
+
+### 权限控制
+- 基于角色的访问控制
+- API级别权限验证
+- 操作日志记录
+
+### 数据安全
+- 敏感信息脱敏
+- 操作审计日志
+- 输入验证和过滤
+
+## 测试计划
+
+### 单元测试
+- 服务层方法测试
+- API接口测试
+- 前端组件测试
+
+### 集成测试
+- 端到端流程测试
+- 权限验证测试
+- 性能测试
+
+## 部署步骤
+
+1. 数据库更新(索引和权限)
+2. 后端API部署
+3. 前端界面部署
+4. 权限配置更新
+5. 监控和日志配置
+
+## 预期效果
+
+- 管理员可以高效管理所有银龄人才
+- 认证流程标准化和自动化
+- 数据统计和决策支持
+- 用户体验优化
+- 系统性能提升
+
+## 后续扩展
+
+- 批量导入功能
+- 数据导出(Excel/PDF)
+- 消息通知系统
+- 高级统计分析
+- 移动端管理应用

+ 196 - 0
docs/silver-talent-implementation-checklist.md

@@ -0,0 +1,196 @@
+# 银龄库管理功能实施检查清单
+
+## 📋 实施前准备
+- [ ] 确认数据库表结构(silver_user_profiles)
+- [ ] 检查现有API权限配置
+- [ ] 准备测试数据
+- [ ] 备份现有代码
+
+## 🔧 后端实施步骤
+
+### 1. API路由创建
+```bash
+# 创建管理后台API目录
+mkdir -p src/server/api/silver-talents-admin/[id]
+```
+
+### 2. 需要创建的文件
+- [ ] `src/server/api/silver-talents-admin/get.ts` - 管理列表查询
+- [ ] `src/server/api/silver-talents-admin/[id]/get.ts` - 详情查询
+- [ ] `src/server/api/silver-talents-admin/[id]/put.ts` - 编辑更新
+- [ ] `src/server/api/silver-talents-admin/[id]/patch.ts` - 认证状态更新
+- [ ] `src/server/api/silver-talents-admin/stats.ts` - 统计数据
+- [ ] `src/server/api/silver-talents-admin/index.ts` - 路由聚合
+
+### 3. 服务扩展
+在现有`SilverTalentService`中添加管理方法:
+- [ ] `findForAdmin()` - 管理查询
+- [ ] `updateByAdmin()` - 管理员更新
+- [ ] `batchUpdateCertification()` - 批量认证
+- [ ] `getAdminStats()` - 管理统计
+
+### 4. 路由注册
+在`src/server/api.ts`中添加:
+```typescript
+import silverTalentsAdminRoutes from './api/silver-talents-admin';
+api.route('/api/v1/admin/silver-talents', silverTalentsAdminRoutes);
+```
+
+## 🎨 前端实施步骤
+
+### 1. 菜单配置
+修改`src/client/admin/menu.tsx`:
+- [ ] 添加银龄库管理菜单项
+- [ ] 配置权限检查
+- [ ] 添加图标
+
+### 2. 路由配置
+修改`src/client/admin/routes.tsx`:
+- [ ] 添加银龄库管理路由
+- [ ] 配置权限保护
+
+### 3. 页面组件创建
+```bash
+# 创建页面目录
+mkdir -p src/client/admin/pages/SilverTalents/components
+```
+
+### 4. 需要创建的组件
+- [ ] `src/client/admin/pages/SilverTalents/index.tsx` - 主列表页面
+- [ ] `src/client/admin/pages/SilverTalents/Detail.tsx` - 详情页面
+- [ ] `src/client/admin/pages/SilverTalents/components/TalentTable.tsx`
+- [ ] `src/client/admin/pages/SilverTalents/components/TalentForm.tsx`
+- [ ] `src/client/admin/pages/SilverTalents/components/CertificationModal.tsx`
+- [ ] `src/client/admin/pages/SilverTalents/components/StatsCard.tsx`
+- [ ] `src/client/admin/pages/SilverTalents/hooks/useTalentData.ts`
+
+## 📊 功能实现检查
+
+### 列表功能
+- [ ] 分页查询
+- [ ] 关键词搜索
+- [ ] 多条件筛选(认证状态、求职状态、年龄)
+- [ ] 排序功能
+- [ ] 批量操作
+- [ ] 导出功能
+
+### 详情功能
+- [ ] 基本信息展示
+- [ ] 认证材料查看
+- [ ] 编辑表单
+- [ ] 认证状态修改
+- [ ] 操作日志
+
+### 统计功能
+- [ ] 人才总数
+- [ ] 认证状态分布
+- [ ] 年龄段分布
+- [ ] 技能统计
+- [ ] 活跃度排名
+
+## 🔍 测试验证
+
+### 单元测试
+- [ ] API接口测试
+- [ ] 服务方法测试
+- [ ] 前端组件测试
+
+### 集成测试
+- [ ] 完整流程测试
+- [ ] 权限验证测试
+- [ ] 性能测试
+
+### 用户验收测试
+- [ ] 管理员操作测试
+- [ ] 数据准确性验证
+- [ ] 界面友好性检查
+
+## 🚀 部署步骤
+
+### 开发环境
+1. [ ] 创建feature分支
+2. [ ] 实施后端API
+3. [ ] 实施前端界面
+4. [ ] 本地测试验证
+
+### 测试环境
+1. [ ] 合并到develop分支
+2. [ ] 部署测试环境
+3. [ ] 功能测试
+4. [ ] 性能优化
+
+### 生产环境
+1. [ ] 创建发布分支
+2. [ ] 数据库更新(索引)
+3. [ ] 权限配置更新
+4. [ ] 部署生产环境
+5. [ ] 监控配置
+
+## 📋 代码审查清单
+
+### 后端代码
+- [ ] API设计符合RESTful规范
+- [ ] 输入验证完整
+- [ ] 错误处理完善
+- [ ] 日志记录规范
+- [ ] 权限控制正确
+- [ ] 性能优化到位
+
+### 前端代码
+- [ ] 组件设计合理
+- [ ] 状态管理清晰
+- [ ] 样式规范统一
+- [ ] 响应式设计
+- [ ] 错误处理友好
+- [ ] 性能优化
+
+## 🔐 安全考虑
+
+### 数据安全
+- [ ] 敏感信息脱敏
+- [ ] 输入验证严格
+- [ ] SQL注入防护
+- [ ] XSS攻击防护
+
+### 访问控制
+- [ ] 权限验证完善
+- [ ] 操作日志记录
+- [ ] 会话管理安全
+
+## 📈 性能优化
+
+### 数据库优化
+- [ ] 索引优化
+- [ ] 查询优化
+- [ ] 连接池配置
+
+### 前端优化
+- [ ] 懒加载实现
+- [ ] 缓存策略
+- [ ] 资源压缩
+
+## 🎯 验收标准
+
+### 功能验收
+- [ ] 所有功能正常运行
+- [ ] 数据准确性100%
+- [ ] 用户体验良好
+- [ ] 性能指标达标
+
+### 文档验收
+- [ ] API文档完整
+- [ ] 用户手册编写
+- [ ] 部署文档更新
+- [ ] 培训材料准备
+
+## 📞 后续支持
+
+### 运维支持
+- [ ] 监控告警配置
+- [ ] 日志分析系统
+- [ ] 故障处理预案
+
+### 用户支持
+- [ ] 使用培训
+- [ ] FAQ文档
+- [ ] 技术支持流程

+ 8 - 0
src/client/admin/menu.tsx

@@ -8,6 +8,7 @@ import {
   TeamOutlined,
   InfoCircleOutlined,
   FileTextOutlined,
+  UsergroupAddOutlined,
 } from '@ant-design/icons';
 
 export interface MenuItem {
@@ -86,6 +87,13 @@ export const useMenu = () => {
       path: '/admin/users',
       permission: 'user:manage'
     },
+    {
+      key: 'silver-talents',
+      label: '银龄库管理',
+      icon: <UsergroupAddOutlined />,
+      path: '/admin/silver-talents',
+      permission: 'silver-talent:manage'
+    },
     {
       key: 'files',
       label: '文件管理',

+ 551 - 0
src/client/admin/pages/SilverTalents.tsx

@@ -0,0 +1,551 @@
+import React, { useState, useEffect } from 'react';
+import {
+  Table,
+  Card,
+  Space,
+  Button,
+  Input,
+  Select,
+  Tag,
+  Modal,
+  Form,
+  message,
+  Descriptions,
+  Statistic,
+  Row,
+  Col,
+  Avatar,
+  Image,
+  Tooltip
+} from 'antd';
+import {
+  SearchOutlined,
+  EyeOutlined,
+  EditOutlined,
+  CheckCircleOutlined,
+  CloseCircleOutlined,
+  UserOutlined,
+  TeamOutlined,
+  BarChartOutlined
+} from '@ant-design/icons';
+import { SilverTalentAdminService } from '@/server/modules/silver-users/silver-talent-admin.service';
+import { AppDataSource } from '@/server/data-source';
+import { CertificationStatus, JobSeekingStatus, Gender } from '@/server/modules/silver-users/silver-user-profile.entity';
+
+const { Option } = Select;
+const { TextArea } = Input;
+
+// 认证状态显示
+const getCertificationStatusText = (status: CertificationStatus) => {
+  switch (status) {
+    case CertificationStatus.UNCERTIFIED: return '未认证';
+    case CertificationStatus.PENDING: return '认证中';
+    case CertificationStatus.CERTIFIED: return '已认证';
+    case CertificationStatus.REJECTED: return '已拒绝';
+    default: return '未知';
+  }
+};
+
+const getCertificationStatusColor = (status: CertificationStatus) => {
+  switch (status) {
+    case CertificationStatus.UNCERTIFIED: return 'default';
+    case CertificationStatus.PENDING: return 'processing';
+    case CertificationStatus.CERTIFIED: return 'success';
+    case CertificationStatus.REJECTED: return 'error';
+    default: return 'default';
+  }
+};
+
+// 求职状态显示
+const getJobSeekingStatusText = (status: JobSeekingStatus) => {
+  switch (status) {
+    case JobSeekingStatus.NOT_SEEKING: return '未求职';
+    case JobSeekingStatus.ACTIVELY_SEEKING: return '积极求职';
+    case JobSeekingStatus.OPEN_TO_OPPORTUNITIES: return '观望机会';
+    default: return '未知';
+  }
+};
+
+// 性别显示
+const getGenderText = (gender: Gender) => {
+  switch (gender) {
+    case Gender.MALE: return '男';
+    case Gender.FEMALE: return '女';
+    case Gender.OTHER: return '其他';
+    default: return '未知';
+  }
+};
+
+interface SilverTalent {
+  id: number;
+  realName: string;
+  age: number;
+  gender: Gender;
+  organization?: string;
+  phone: string;
+  email?: string;
+  avatarUrl?: string;
+  certificationStatus: CertificationStatus;
+  jobSeekingStatus: JobSeekingStatus;
+  totalPoints: number;
+  knowledgeRankingScore: number;
+  personalIntro?: string;
+  personalSkills?: string;
+  createdAt: string;
+}
+
+export const SilverTalentsPage: React.FC = () => {
+  const [loading, setLoading] = useState(false);
+  const [data, setData] = useState<SilverTalent[]>([]);
+  const [total, setTotal] = useState(0);
+  const [current, setCurrent] = useState(1);
+  const [pageSize, setPageSize] = useState(20);
+  const [searchParams, setSearchParams] = useState({
+    keyword: '',
+    certificationStatus: undefined as CertificationStatus | undefined,
+    jobSeekingStatus: undefined as JobSeekingStatus | undefined,
+    ageMin: undefined as number | undefined,
+    ageMax: undefined as number | undefined,
+  });
+  const [stats, setStats] = useState<any>(null);
+  const [selectedTalent, setSelectedTalent] = useState<SilverTalent | null>(null);
+  const [detailModalVisible, setDetailModalVisible] = useState(false);
+  const [editModalVisible, setEditModalVisible] = useState(false);
+  const [certModalVisible, setCertModalVisible] = useState(false);
+  const [editForm] = Form.useForm();
+  const [certForm] = Form.useForm();
+
+  // 列配置
+  const columns = [
+    {
+      title: '头像',
+      dataIndex: 'avatarUrl',
+      key: 'avatarUrl',
+      width: 80,
+      render: (url: string) => (
+        <Avatar
+          size={48}
+          src={url}
+          icon={<UserOutlined />}
+          style={{ backgroundColor: '#87d068' }}
+        />
+      ),
+    },
+    {
+      title: '姓名',
+      dataIndex: 'realName',
+      key: 'realName',
+      sorter: true,
+    },
+    {
+      title: '年龄',
+      dataIndex: 'age',
+      key: 'age',
+      width: 80,
+      sorter: true,
+    },
+    {
+      title: '性别',
+      dataIndex: 'gender',
+      key: 'gender',
+      width: 80,
+      render: (gender: Gender) => getGenderText(gender),
+    },
+    {
+      title: '所属机构',
+      dataIndex: 'organization',
+      key: 'organization',
+      ellipsis: true,
+    },
+    {
+      title: '认证状态',
+      dataIndex: 'certificationStatus',
+      key: 'certificationStatus',
+      width: 100,
+      render: (status: CertificationStatus) => (
+        <Tag color={getCertificationStatusColor(status)}>
+          {getCertificationStatusText(status)}
+        </Tag>
+      ),
+    },
+    {
+      title: '求职状态',
+      dataIndex: 'jobSeekingStatus',
+      key: 'jobSeekingStatus',
+      width: 100,
+      render: (status: JobSeekingStatus) => (
+        <Tag>
+          {getJobSeekingStatusText(status)}
+        </Tag>
+      ),
+    },
+    {
+      title: '积分',
+      dataIndex: 'totalPoints',
+      key: 'totalPoints',
+      width: 100,
+      sorter: true,
+    },
+    {
+      title: '知识排名',
+      dataIndex: 'knowledgeRankingScore',
+      key: 'knowledgeRankingScore',
+      width: 100,
+      sorter: true,
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'createdAt',
+      key: 'createdAt',
+      width: 180,
+      render: (date: string) => new Date(date).toLocaleDateString(),
+    },
+    {
+      title: '操作',
+      key: 'action',
+      width: 200,
+      fixed: 'right' as const,
+      render: (_, record: SilverTalent) => (
+        <Space>
+          <Button
+            type="link"
+            icon={<EyeOutlined />}
+            onClick={() => handleViewDetail(record)}
+          >
+            详情
+          </Button>
+          <Button
+            type="link"
+            icon={<EditOutlined />}
+            onClick={() => handleEdit(record)}
+          >
+            编辑
+          </Button>
+          <Button
+            type="link"
+            icon={<CheckCircleOutlined />}
+            onClick={() => handleCertification(record)}
+          >
+            认证
+          </Button>
+        </Space>
+      ),
+    },
+  ];
+
+  // 获取列表数据
+  const fetchData = async () => {
+    setLoading(true);
+    try {
+      // 模拟API调用,实际使用时替换为真实API
+      const service = new SilverTalentAdminService(AppDataSource);
+      const [talents, totalCount, statsData] = await service.findForAdmin({
+        ...searchParams,
+        page: current,
+        pageSize,
+      });
+      
+      setData(talents);
+      setTotal(totalCount);
+      setStats(statsData);
+    } catch (error) {
+      message.error('获取数据失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 获取统计数据
+  const fetchStats = async () => {
+    try {
+      const service = new SilverTalentAdminService(AppDataSource);
+      const statsData = await service.getAdminStats();
+      setStats(statsData);
+    } catch (error) {
+      console.error('获取统计数据失败:', error);
+    }
+  };
+
+  useEffect(() => {
+    fetchData();
+    fetchStats();
+  }, [current, pageSize, searchParams]);
+
+  // 处理搜索
+  const handleSearch = (values: any) => {
+    setSearchParams(values);
+    setCurrent(1);
+  };
+
+  // 处理重置
+  const handleReset = () => {
+    setSearchParams({
+      keyword: '',
+      certificationStatus: undefined,
+      jobSeekingStatus: undefined,
+      ageMin: undefined,
+      ageMax: undefined,
+    });
+    setCurrent(1);
+  };
+
+  // 处理查看详情
+  const handleViewDetail = (record: SilverTalent) => {
+    setSelectedTalent(record);
+    setDetailModalVisible(true);
+  };
+
+  // 处理编辑
+  const handleEdit = (record: SilverTalent) => {
+    setSelectedTalent(record);
+    editForm.setFieldsValue(record);
+    setEditModalVisible(true);
+  };
+
+  // 处理认证
+  const handleCertification = (record: SilverTalent) => {
+    setSelectedTalent(record);
+    certForm.setFieldsValue({
+      certificationStatus: record.certificationStatus,
+      certificationInfo: record.certificationInfo || '',
+    });
+    setCertModalVisible(true);
+  };
+
+  // 处理编辑提交
+  const handleEditSubmit = async (values: any) => {
+    if (!selectedTalent) return;
+    
+    try {
+      const service = new SilverTalentAdminService(AppDataSource);
+      await service.updateByAdmin(selectedTalent.id, values, 1); // 假设当前用户ID为1
+      
+      message.success('更新成功');
+      setEditModalVisible(false);
+      fetchData();
+    } catch (error) {
+      message.error('更新失败');
+    }
+  };
+
+  // 处理认证提交
+  const handleCertSubmit = async (values: any) => {
+    if (!selectedTalent) return;
+    
+    try {
+      const service = new SilverTalentAdminService(AppDataSource);
+      await service.updateCertificationStatus(
+        selectedTalent.id,
+        values.certificationStatus,
+        values.certificationInfo,
+        1 // 假设当前用户ID为1
+      );
+      
+      message.success('认证状态更新成功');
+      setCertModalVisible(false);
+      fetchData();
+    } catch (error) {
+      message.error('认证状态更新失败');
+    }
+  };
+
+  return (
+    <div style={{ padding: 24 }}>
+      {/* 统计卡片 */}
+      <Row gutter={24} style={{ marginBottom: 24 }}>
+        <Col span={6}>
+          <Card>
+            <Statistic
+              title="总人才数"
+              value={stats?.totalCount || 0}
+              prefix={<TeamOutlined />}
+            />
+          </Card>
+        </Col>
+        <Col span={6}>
+          <Card>
+            <Statistic
+              title="已认证"
+              value={stats?.certifiedCount || 0}
+              prefix={<CheckCircleOutlined style={{ color: '#52c41a' }} />}
+            />
+          </Card>
+        </Col>
+        <Col span={6}>
+          <Card>
+            <Statistic
+              title="认证中"
+              value={stats?.pendingCount || 0}
+              prefix={<BarChartOutlined style={{ color: '#1890ff' }} />}
+            />
+          </Card>
+        </Col>
+        <Col span={6}>
+          <Card>
+            <Statistic
+              title="未认证"
+              value={stats?.unCertifiedCount || 0}
+              prefix={<CloseCircleOutlined style={{ color: '#ff4d4f' }} />}
+            />
+          </Card>
+        </Col>
+      </Row>
+
+      {/* 搜索表单 */}
+      <Card style={{ marginBottom: 24 }}>
+        <Form layout="inline" onFinish={handleSearch}>
+          <Form.Item name="keyword" label="关键词">
+            <Input placeholder="姓名/机构/技能" prefix={<SearchOutlined />} />
+          </Form.Item>
+          <Form.Item name="certificationStatus" label="认证状态">
+            <Select style={{ width: 120 }} allowClear>
+              <Option value={CertificationStatus.UNCERTIFIED}>未认证</Option>
+              <Option value={CertificationStatus.PENDING}>认证中</Option>
+              <Option value={CertificationStatus.CERTIFIED}>已认证</Option>
+              <Option value={CertificationStatus.REJECTED}>已拒绝</Option>
+            </Select>
+          </Form.Item>
+          <Form.Item name="jobSeekingStatus" label="求职状态">
+            <Select style={{ width: 120 }} allowClear>
+              <Option value={JobSeekingStatus.NOT_SEEKING}>未求职</Option>
+              <Option value={JobSeekingStatus.ACTIVELY_SEEKING}>积极求职</Option>
+              <Option value={JobSeekingStatus.OPEN_TO_OPPORTUNITIES}>观望机会</Option>
+            </Select>
+          </Form.Item>
+          <Form.Item>
+            <Space>
+              <Button type="primary" htmlType="submit">搜索</Button>
+              <Button onClick={handleReset}>重置</Button>
+            </Space>
+          </Form.Item>
+        </Form>
+      </Card>
+
+      {/* 数据表格 */}
+      <Card>
+        <Table
+          columns={columns}
+          dataSource={data}
+          loading={loading}
+          rowKey="id"
+          pagination={{
+            current,
+            pageSize,
+            total,
+            showSizeChanger: true,
+            showQuickJumper: true,
+            showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条/共 ${total} 条`,
+            onChange: (page, size) => {
+              setCurrent(page);
+              setPageSize(size);
+            },
+          }}
+          scroll={{ x: 'max-content' }}
+        />
+      </Card>
+
+      {/* 详情弹窗 */}
+      <Modal
+        title="人才详情"
+        width={800}
+        open={detailModalVisible}
+        onCancel={() => setDetailModalVisible(false)}
+        footer={[
+          <Button key="close" onClick={() => setDetailModalVisible(false)}>
+            关闭
+          </Button>,
+        ]}
+      >
+        {selectedTalent && (
+          <Descriptions bordered column={2}>
+            <Descriptions.Item label="姓名">{selectedTalent.realName}</Descriptions.Item>
+            <Descriptions.Item label="年龄">{selectedTalent.age}岁</Descriptions.Item>
+            <Descriptions.Item label="性别">{getGenderText(selectedTalent.gender)}</Descriptions.Item>
+            <Descriptions.Item label="所属机构">{selectedTalent.organization || '-'}</Descriptions.Item>
+            <Descriptions.Item label="联系电话">{selectedTalent.phone}</Descriptions.Item>
+            <Descriptions.Item label="邮箱">{selectedTalent.email || '-'}</Descriptions.Item>
+            <Descriptions.Item label="认证状态">
+              <Tag color={getCertificationStatusColor(selectedTalent.certificationStatus)}>
+                {getCertificationStatusText(selectedTalent.certificationStatus)}
+              </Tag>
+            </Descriptions.Item>
+            <Descriptions.Item label="求职状态">
+              {getJobSeekingStatusText(selectedTalent.jobSeekingStatus)}
+            </Descriptions.Item>
+            <Descriptions.Item label="积分">{selectedTalent.totalPoints}</Descriptions.Item>
+            <Descriptions.Item label="知识排名分">{selectedTalent.knowledgeRankingScore}</Descriptions.Item>
+            <Descriptions.Item label="个人简介" span={2}>
+              {selectedTalent.personalIntro || '-'}
+            </Descriptions.Item>
+            <Descriptions.Item label="个人技能" span={2}>
+              {selectedTalent.personalSkills || '-'}
+            </Descriptions.Item>
+          </Descriptions>
+        )}
+      </Modal>
+
+      {/* 编辑弹窗 */}
+      <Modal
+        title="编辑人才信息"
+        width={600}
+        open={editModalVisible}
+        onCancel={() => setEditModalVisible(false)}
+        onOk={() => editForm.submit()}
+      >
+        <Form form={editForm} onFinish={handleEditSubmit} layout="vertical">
+          <Form.Item name="realName" label="真实姓名" rules={[{ required: true }]}>
+            <Input />
+          </Form.Item>
+          <Form.Item name="age" label="年龄" rules={[{ required: true }]}>
+            <Input type="number" min={50} max={100} />
+          </Form.Item>
+          <Form.Item name="gender" label="性别" rules={[{ required: true }]}>
+            <Select>
+              <Option value={Gender.MALE}>男</Option>
+              <Option value={Gender.FEMALE}>女</Option>
+              <Option value={Gender.OTHER}>其他</Option>
+            </Select>
+          </Form.Item>
+          <Form.Item name="organization" label="所属机构">
+            <Input />
+          </Form.Item>
+          <Form.Item name="phone" label="联系电话" rules={[{ required: true }]}>
+            <Input />
+          </Form.Item>
+          <Form.Item name="email" label="邮箱">
+            <Input />
+          </Form.Item>
+          <Form.Item name="personalIntro" label="个人简介">
+            <TextArea rows={3} />
+          </Form.Item>
+          <Form.Item name="personalSkills" label="个人技能">
+            <TextArea rows={3} />
+          </Form.Item>
+        </Form>
+      </Modal>
+
+      {/* 认证弹窗 */}
+      <Modal
+        title="更新认证状态"
+        width={500}
+        open={certModalVisible}
+        onCancel={() => setCertModalVisible(false)}
+        onOk={() => certForm.submit()}
+      >
+        <Form form={certForm} onFinish={handleCertSubmit} layout="vertical">
+          <Form.Item name="certificationStatus" label="认证状态" rules={[{ required: true }]}>
+            <Select>
+              <Option value={CertificationStatus.UNCERTIFIED}>未认证</Option>
+              <Option value={CertificationStatus.PENDING}>认证中</Option>
+              <Option value={CertificationStatus.CERTIFIED}>已认证</Option>
+              <Option value={CertificationStatus.REJECTED}>已拒绝</Option>
+            </Select>
+          </Form.Item>
+          <Form.Item name="certificationInfo" label="认证信息">
+            <TextArea rows={4} placeholder="请输入认证信息或拒绝理由" />
+          </Form.Item>
+        </Form>
+      </Modal>
+    </div>
+  );
+};

+ 6 - 0
src/client/admin/routes.tsx

@@ -8,6 +8,7 @@ import { DashboardPage } from './pages/Dashboard';
 import { UsersPage } from './pages/Users';
 import { LoginPage } from './pages/Login';
 import { FilesPage } from './pages/Files';
+import { SilverTalentsPage } from './pages/SilverTalents';
 
 export const router = createBrowserRouter([
   {
@@ -45,6 +46,11 @@ export const router = createBrowserRouter([
         element: <FilesPage />,
         errorElement: <ErrorPage />
       },
+      {
+        path: 'silver-talents',
+        element: <SilverTalentsPage />,
+        errorElement: <ErrorPage />
+      },
       {
         path: '*',
         element: <NotFoundPage />,

+ 13 - 150
src/client/api.ts

@@ -1,154 +1,17 @@
-import { hc } from 'hono/client';
-import type {
-  AuthRoutes,
-  UserRoutes,
-  RoleRoutes,
-  FileRoutes,
-  CompanyRoutes,
-  JobRoutes,
-  ApplicationRoutes,
-  FavoriteRoutes,
-  ViewRoutes,
-  CompanyImageRoutes,
-  SilverTalentsRoutes,
-  ElderlyUniversityRoutes,
-  PolicyNewsRoutes,
-  UserPreferenceRoutes,
-  HomeRoutes,
-  SilverUsersKnowledgesRoutes,
-  SilverUsersKnowledgeCategoriesRoutes,
-  SilverUsersKnowledgeTagsRoutes,
-  SilverUsersKnowledgeStatsRoutes,
-  SilverUsersKnowledgeInteractionsRoutes,
-  SilverUsersKnowledgeRankingsRoutes,
-  MyCompanyRoutes
-} from '@/server/api';
-import axios from 'axios';
+import { hc } from 'hono/client'
+import { axiosFetch } from './utils/axios'
+import { type SilverTalentsAdminRoutes } from '@/server/api'
 
-// 创建axios fetch适配器
-const axiosFetch = async (input: RequestInfo | URL, init?: RequestInit) => {
-  
-  const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
-  const method = init?.method || 'GET';
-  const headers = init?.headers ? Object.fromEntries(new Map(init.headers as any)) : {};
-  const data = init?.body;
-
-  try {
-    const response = await axios({
-      url,
-      method,
-      headers,
-      data,
-      responseType: 'json',
-      validateStatus: () => true,
-    });
-
-    return new Response(JSON.stringify(response.data), {
-      status: response.status,
-      statusText: response.statusText,
-      headers: response.headers as any,
-    });
-  } catch (error) {
-    throw error;
-  }
-};
-
-// 客户端实例 - 严格按照RPC规范命名
-export const authClient = hc<AuthRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1.auth;
-
-export const userClient = hc<UserRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1.users;
-
-export const roleClient = hc<RoleRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1.roles;
-
-export const fileClient = hc<FileRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1.files;
-
-// 企业信息客户端 - 新增
-export const myCompanyClient = hc<MyCompanyRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['companies'].my;
- 
-// 银龄岗具体资源客户端(调整为标准格式)
-export const companyClient = hc<CompanyRoutes>('/', {
+// 银龄库管理客户端
+export const silverTalentsAdminClient = hc<SilverTalentsAdminRoutes>('/api/v1', {
   fetch: axiosFetch,
-}).api.v1['silver-jobs'].companies;
+}).api.v1.admin['silver-talents']
 
-export const jobClient = hc<JobRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['silver-jobs'].jobs;
-
-export const applicationClient = hc<ApplicationRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['silver-jobs'].applications;
+// 类型定义
+export type SilverTalentAdminList = InferResponseType<typeof silverTalentsAdminClient.$get, 200>
+export type SilverTalentAdminDetail = InferResponseType<typeof silverTalentsAdminClient[':id'].$get, 200>
+export type SilverTalentAdminStats = InferResponseType<typeof silverTalentsAdminClient.stats.$get, 200>
+export type UpdateSilverTalentRequest = InferRequestType<typeof silverTalentsAdminClient[':id'].$put>['json']
+export type UpdateCertificationRequest = InferRequestType<typeof silverTalentsAdminClient[':id']['certification'].$patch>['json']
 
-export const favoriteClient = hc<FavoriteRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['silver-jobs'].favorites;
-
-export const viewClient = hc<ViewRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['silver-jobs'].views;
-
-export const companyImageClient = hc<CompanyImageRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['silver-jobs']['company-images'];
-
-// 银龄用户资源客户端 - 重构为集中的对象,避免过深实例化
-export const silverUsersClient = {
-  knowledges: hc<SilverUsersKnowledgesRoutes>('/', {
-    fetch: axiosFetch,
-  }).api.v1['silver-users'].knowledges,
-  
-  ['knowledge-categories']: hc<SilverUsersKnowledgeCategoriesRoutes>('/', {
-    fetch: axiosFetch,
-  }).api.v1['silver-users']['knowledge-categories'],
-  
-  ['knowledge-tags']: hc<SilverUsersKnowledgeTagsRoutes>('/', {
-    fetch: axiosFetch,
-  }).api.v1['silver-users']['knowledge-tags'],
-  
-  ['knowledge-stats']: hc<SilverUsersKnowledgeStatsRoutes>('/', {
-    fetch: axiosFetch,
-  }).api.v1['silver-users']['knowledge-stats'],
-  
-  ['knowledge-interactions']: hc<SilverUsersKnowledgeInteractionsRoutes>('/', {
-    fetch: axiosFetch,
-  }).api.v1['silver-users']['knowledge-interactions'],
-  
-  ['knowledge-rankings']: hc<SilverUsersKnowledgeRankingsRoutes>('/', {
-    fetch: axiosFetch,
-  }).api.v1['silver-users']['knowledge-rankings'],
-  
-  profiles: hc<any>('/', {
-    fetch: axiosFetch,
-  }).api.v1['silver-users'].profiles
-};
-
-// 其他资源客户端
-export const elderlyUniversityClient = hc<ElderlyUniversityRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['elderly-universities'];
-
-export const policyNewsClient = hc<PolicyNewsRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['policy-news'];
-
-export const userPreferenceClient = hc<UserPreferenceRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['user-preferences'];
-
-// 首页API客户端
-export const homeClient = hc<HomeRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1.home;
-
-export const silverTalentsClient = hc<SilverTalentsRoutes>('/', {
-  fetch: axiosFetch,
-}).api.v1['silver-talents']
+import type { InferResponseType, InferRequestType } from 'hono/client'

+ 5 - 0
src/server/api.ts

@@ -91,6 +91,10 @@ const silverUsersKnowledgeRankingsApiRoutes = api.route('/api/v1/silver-users/kn
 import silverUserProfileRoutes from './api/silver-users/profiles/index'
 const silverUserProfileApiRoutes = api.route('/api/v1/silver-users/profiles', silverUserProfileRoutes)
 
+// 注册银龄库管理后台路由
+import silverTalentsAdminRoutes from './api/silver-talents-admin/index'
+const silverTalentsAdminApiRoutes = api.route('/api/v1/admin/silver-talents', silverTalentsAdminRoutes)
+
 export type AuthRoutes = typeof authRoutes
 export type UserRoutes = typeof userRoutes
 export type RoleRoutes = typeof roleRoutes
@@ -114,5 +118,6 @@ export type SilverUsersKnowledgeStatsRoutes = typeof silverUsersKnowledgeStatsAp
 export type SilverUsersKnowledgeInteractionsRoutes = typeof silverUsersKnowledgeInteractionsApiRoutes
 export type SilverUsersKnowledgeRankingsRoutes = typeof silverUsersKnowledgeRankingsApiRoutes
 export type SilverUserProfileRoutes = typeof silverUserProfileApiRoutes
+export type SilverTalentsAdminRoutes = typeof silverTalentsAdminApiRoutes
 
 export default api

+ 57 - 0
src/server/api/silver-talents-admin/[id]/delete.ts

@@ -0,0 +1,57 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { SilverTalentAdminService } from '@/server/modules/silver-users/silver-talent-admin.service';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+// 路径参数Schema
+const GetParams = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    param: { name: 'id', in: 'path' },
+    example: 1,
+    description: '人才ID'
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'delete',
+  path: '/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: GetParams
+  },
+  responses: {
+    200: {
+      description: '删除成功',
+      content: { 'application/json': { schema: z.object({ success: z.boolean() }) } }
+    },
+    404: {
+      description: '人才信息不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const service = new SilverTalentAdminService(AppDataSource);
+    
+    await service.delete(id);
+    
+    return c.json({ success: true }, 200);
+  } catch (error) {
+    const message = error instanceof Error ? error.message : '删除失败';
+    const code = message === '未找到该银龄人才信息' ? 404 : 500;
+    return c.json({ code, message }, code);
+  }
+});
+
+export default app;

+ 63 - 0
src/server/api/silver-talents-admin/[id]/get.ts

@@ -0,0 +1,63 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { SilverUserProfileSchema } from '@/server/modules/silver-users/silver-user-profile.entity';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { SilverTalentAdminService } from '@/server/modules/silver-users/silver-talent-admin.service';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+// 路径参数Schema
+const GetParams = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    param: { name: 'id', in: 'path' },
+    example: 1,
+    description: '人才ID'
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'get',
+  path: '/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: GetParams
+  },
+  responses: {
+    200: {
+      description: '成功获取银龄人才详情',
+      content: { 'application/json': { schema: SilverUserProfileSchema } }
+    },
+    400: {
+      description: '请求参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '人才信息不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 路由实现
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const service = new SilverTalentAdminService(AppDataSource);
+    
+    const talent = await service.findByIdForAdmin(id);
+    
+    return c.json(talent, 200);
+  } catch (error) {
+    const message = error instanceof Error ? error.message : '获取详情失败';
+    const code = message === '未找到该银龄人才信息' ? 404 : 500;
+    return c.json({ code, message }, code);
+  }
+});
+
+export default app;

+ 88 - 0
src/server/api/silver-talents-admin/[id]/patch.ts

@@ -0,0 +1,88 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { SilverUserProfileSchema } from '@/server/modules/silver-users/silver-user-profile.entity';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { SilverTalentAdminService } from '@/server/modules/silver-users/silver-talent-admin.service';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+import { CertificationStatus } from '@/server/modules/silver-users/silver-user-profile.entity';
+
+// 路径参数Schema
+const GetParams = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    param: { name: 'id', in: 'path' },
+    example: 1,
+    description: '人才ID'
+  })
+});
+
+// 请求体Schema
+const UpdateCertificationDto = z.object({
+  certificationStatus: z.coerce.number().int().min(0).max(3).openapi({
+    description: '认证状态:0-未认证,1-认证中,2-已认证,3-已拒绝',
+    example: 2
+  }),
+  certificationInfo: z.string().max(2000).optional().openapi({
+    description: '认证信息',
+    example: '高级教师职称证书、工作经验证明'
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'patch',
+  path: '/{id}/certification',
+  middleware: [authMiddleware],
+  request: {
+    params: GetParams,
+    body: {
+      content: {
+        'application/json': { schema: UpdateCertificationDto }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '认证状态更新成功',
+      content: { 'application/json': { schema: SilverUserProfileSchema } }
+    },
+    400: {
+      description: '请求参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '人才信息不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 路由实现
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const body = await c.req.json();
+    const user = c.get('user');
+    const service = new SilverTalentAdminService(AppDataSource);
+    
+    const talent = await service.updateCertificationStatus(
+      id,
+      body.certificationStatus as CertificationStatus,
+      body.certificationInfo,
+      user.id
+    );
+    
+    return c.json(talent, 200);
+  } catch (error) {
+    const message = error instanceof Error ? error.message : '更新认证状态失败';
+    const code = message === '未找到该银龄人才信息' ? 404 : 500;
+    return c.json({ code, message }, code);
+  }
+});
+
+export default app;

+ 69 - 0
src/server/api/silver-talents-admin/[id]/put.ts

@@ -0,0 +1,69 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { UpdateSilverUserProfileDto } from '@/server/modules/silver-users/silver-user-profile.entity';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { SilverTalentAdminService } from '@/server/modules/silver-users/silver-talent-admin.service';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+// 路径参数Schema
+const GetParams = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    param: { name: 'id', in: 'path' },
+    example: 1,
+    description: '人才ID'
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'put',
+  path: '/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: GetParams,
+    body: {
+      content: {
+        'application/json': { schema: UpdateSilverUserProfileDto }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '人才信息更新成功',
+      content: { 'application/json': { schema: UpdateSilverUserProfileDto } }
+    },
+    400: {
+      description: '请求参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '人才信息不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const body = await c.req.json();
+    const user = c.get('user');
+    const service = new SilverTalentAdminService(AppDataSource);
+    
+    const updatedTalent = await service.updateByAdmin(id, body, user.id);
+    
+    return c.json(updatedTalent, 200);
+  } catch (error) {
+    const message = error instanceof Error ? error.message : '更新失败';
+    const code = message === '未找到该银龄人才信息' ? 404 : 500;
+    return c.json({ code, message }, code);
+  }
+});
+
+export default app;

+ 126 - 0
src/server/api/silver-talents-admin/get.ts

@@ -0,0 +1,126 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from 'zod';
+import { SilverUserProfileSchema } from '@/server/modules/silver-users/silver-user-profile.entity';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { SilverTalentAdminService } from '@/server/modules/silver-users/silver-talent-admin.service';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+import { CertificationStatus, JobSeekingStatus } from '@/server/modules/silver-users/silver-user-profile.entity';
+
+// 查询参数Schema
+const AdminListQuery = z.object({
+  page: z.coerce.number().int().min(1).default(1).openapi({
+    description: '页码',
+    example: 1
+  }),
+  pageSize: z.coerce.number().int().min(1).max(100).default(20).openapi({
+    description: '每页数量',
+    example: 20
+  }),
+  keyword: z.string().optional().openapi({
+    description: '搜索关键词',
+    example: '张'
+  }),
+  certificationStatus: z.coerce.number().int().min(0).max(3).optional().openapi({
+    description: '认证状态:0-未认证,1-认证中,2-已认证,3-已拒绝',
+    example: 2
+  }),
+  jobSeekingStatus: z.coerce.number().int().min(0).max(2).optional().openapi({
+    description: '求职状态:0-未求职,1-积极求职,2-观望机会',
+    example: 1
+  }),
+  ageMin: z.coerce.number().int().min(50).max(100).optional().openapi({
+    description: '最小年龄',
+    example: 50
+  }),
+  ageMax: z.coerce.number().int().min(50).max(100).optional().openapi({
+    description: '最大年龄',
+    example: 80
+  }),
+  sortBy: z.enum(['createdAt', 'age', 'realName', 'certificationStatus', 'totalPoints', 'knowledgeRankingScore'])
+    .default('createdAt').openapi({
+      description: '排序字段'
+    }),
+  sortOrder: z.enum(['ASC', 'DESC']).default('DESC').openapi({
+    description: '排序方向'
+  })
+});
+
+// 响应Schema
+const AdminListResponse = z.object({
+  data: z.array(SilverUserProfileSchema),
+  pagination: z.object({
+    total: z.number().openapi({ example: 100, description: '总记录数' }),
+    current: z.number().openapi({ example: 1, description: '当前页码' }),
+    pageSize: z.number().openapi({ example: 20, description: '每页数量' }),
+    totalPages: z.number().openapi({ example: 5, description: '总页数' })
+  }),
+  stats: z.object({
+    totalCount: z.number().openapi({ example: 1000, description: '总人才数' }),
+    certifiedCount: z.number().openapi({ example: 800, description: '已认证数' }),
+    pendingCount: z.number().openapi({ example: 100, description: '认证中数' }),
+    rejectedCount: z.number().openapi({ example: 50, description: '已拒绝数' }),
+    unCertifiedCount: z.number().openapi({ example: 50, description: '未认证数' })
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'get',
+  path: '/',
+  middleware: [authMiddleware],
+  request: {
+    query: AdminListQuery
+  },
+  responses: {
+    200: {
+      description: '成功获取银龄人才列表',
+      content: { 'application/json': { schema: AdminListResponse } }
+    },
+    400: {
+      description: '请求参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 路由实现
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const query = c.req.valid('query');
+    const service = new SilverTalentAdminService(AppDataSource);
+    
+    const [data, total, stats] = await service.findForAdmin({
+      keyword: query.keyword,
+      certificationStatus: query.certificationStatus !== undefined ? query.certificationStatus as CertificationStatus : undefined,
+      jobSeekingStatus: query.jobSeekingStatus !== undefined ? query.jobSeekingStatus as JobSeekingStatus : undefined,
+      ageMin: query.ageMin,
+      ageMax: query.ageMax,
+      sortBy: query.sortBy,
+      sortOrder: query.sortOrder,
+      page: query.page,
+      pageSize: query.pageSize
+    });
+
+    return c.json({
+      data,
+      pagination: {
+        total,
+        current: query.page,
+        pageSize: query.pageSize,
+        totalPages: Math.ceil(total / query.pageSize)
+      },
+      stats
+    }, 200);
+  } catch (error) {
+    const message = error instanceof Error ? error.message : '获取列表失败';
+    return c.json({ code: 500, message }, 500);
+  }
+});
+
+export default app;

+ 17 - 0
src/server/api/silver-talents-admin/index.ts

@@ -0,0 +1,17 @@
+import { OpenAPIHono } from '@hono/zod-openapi';
+import listRoute from './get';
+import getByIdRoute from './[id]/get';
+import patchRoute from './[id]/patch';
+import putRoute from './[id]/put';
+import deleteRoute from './[id]/delete';
+import statsRoute from './stats';
+
+const app = new OpenAPIHono()
+  .route('/', listRoute)
+  .route('/', getByIdRoute)
+  .route('/', patchRoute)
+  .route('/', putRoute)
+  .route('/', deleteRoute)
+  .route('/', statsRoute);
+
+export default app;

+ 60 - 0
src/server/api/silver-talents-admin/stats.ts

@@ -0,0 +1,60 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { SilverTalentAdminService } from '@/server/modules/silver-users/silver-talent-admin.service';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+// 响应Schema
+const StatsResponse = z.object({
+  totalCount: z.number().openapi({ example: 1000, description: '总人才数' }),
+  certifiedCount: z.number().openapi({ example: 800, description: '已认证数' }),
+  pendingCount: z.number().openapi({ example: 100, description: '认证中数' }),
+  rejectedCount: z.number().openapi({ example: 50, description: '已拒绝数' }),
+  unCertifiedCount: z.number().openapi({ example: 50, description: '未认证数' }),
+  ageDistribution: z.array(z.object({
+    ageGroup: z.string().openapi({ example: '60-64', description: '年龄段' }),
+    count: z.number().openapi({ example: 150, description: '该年龄段人数' })
+  })),
+  jobSeekingDistribution: z.array(z.object({
+    status: z.number().openapi({ example: 1, description: '求职状态值' }),
+    count: z.number().openapi({ example: 200, description: '该状态人数' })
+  })),
+  genderDistribution: z.array(z.object({
+    gender: z.number().openapi({ example: 1, description: '性别值' }),
+    count: z.number().openapi({ example: 500, description: '该性别人数' })
+  }))
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'get',
+  path: '/stats',
+  middleware: [authMiddleware],
+  responses: {
+    200: {
+      description: '成功获取统计数据',
+      content: { 'application/json': { schema: StatsResponse } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const service = new SilverTalentAdminService(AppDataSource);
+    
+    const stats = await service.getAdminStats();
+    
+    return c.json(stats, 200);
+  } catch (error) {
+    const message = error instanceof Error ? error.message : '获取统计数据失败';
+    return c.json({ code: 500, message }, 500);
+  }
+});
+
+export default app;

+ 303 - 0
src/server/modules/silver-users/silver-talent-admin.service.ts

@@ -0,0 +1,303 @@
+import { DataSource, Repository } from 'typeorm';
+import { SilverUserProfile, CertificationStatus, JobSeekingStatus } from './silver-user-profile.entity';
+import { CreateSilverTalentRequest, UpdateSilverTalentRequest } from './silver-talent.dto';
+import debug from 'debug';
+
+const logger = debug('backend:service:silver-talent-admin');
+
+export class SilverTalentAdminService {
+  private repository: Repository<SilverUserProfile>;
+
+  constructor(dataSource: DataSource) {
+    this.repository = dataSource.getRepository(SilverUserProfile);
+  }
+
+  /**
+   * 获取银龄人才列表(管理后台专用)
+   */
+  async findForAdmin(params: {
+    keyword?: string;
+    certificationStatus?: CertificationStatus;
+    jobSeekingStatus?: JobSeekingStatus;
+    ageMin?: number;
+    ageMax?: number;
+    page?: number;
+    pageSize?: number;
+    sortBy?: string;
+    sortOrder?: 'ASC' | 'DESC';
+  }): Promise<[SilverUserProfile[], number, any]> {
+    const {
+      keyword,
+      certificationStatus,
+      jobSeekingStatus,
+      ageMin,
+      ageMax,
+      sortBy = 'createdAt',
+      sortOrder = 'DESC',
+      page = 1,
+      pageSize = 20
+    } = params;
+
+    const queryBuilder = this.repository.createQueryBuilder('profile');
+    
+    // 关联用户基础信息
+    queryBuilder.leftJoinAndSelect('profile.user', 'user');
+
+    // 关键词搜索(姓名、简介、技能、机构、邮箱、电话)
+    if (keyword) {
+      queryBuilder.andWhere(
+        '(profile.realName LIKE :keyword OR profile.personalIntro LIKE :keyword OR profile.personalSkills LIKE :keyword OR profile.organization LIKE :keyword OR profile.email LIKE :keyword OR profile.phone LIKE :keyword)',
+        { keyword: `%${keyword}%` }
+      );
+    }
+
+    // 认证状态筛选
+    if (certificationStatus !== undefined) {
+      queryBuilder.andWhere('profile.certificationStatus = :certificationStatus', { certificationStatus });
+    }
+
+    // 求职状态筛选
+    if (jobSeekingStatus !== undefined) {
+      queryBuilder.andWhere('profile.jobSeekingStatus = :jobSeekingStatus', { jobSeekingStatus });
+    }
+
+    // 年龄范围筛选
+    if (ageMin !== undefined) {
+      queryBuilder.andWhere('profile.age >= :ageMin', { ageMin });
+    }
+    if (ageMax !== undefined) {
+      queryBuilder.andWhere('profile.age <= :ageMax', { ageMax });
+    }
+
+    // 排序
+    switch (sortBy) {
+      case 'createdAt':
+        queryBuilder.orderBy('profile.createdAt', sortOrder);
+        break;
+      case 'age':
+        queryBuilder.orderBy('profile.age', sortOrder);
+        break;
+      case 'realName':
+        queryBuilder.orderBy('profile.realName', sortOrder);
+        break;
+      case 'certificationStatus':
+        queryBuilder.orderBy('profile.certificationStatus', sortOrder);
+        break;
+      case 'totalPoints':
+        queryBuilder.orderBy('profile.totalPoints', sortOrder);
+        break;
+      case 'knowledgeRankingScore':
+        queryBuilder.orderBy('profile.knowledgeRankingScore', sortOrder);
+        break;
+      default:
+        queryBuilder.orderBy('profile.createdAt', 'DESC');
+    }
+
+    // 分页
+    const skip = (page - 1) * pageSize;
+    queryBuilder.skip(skip).take(pageSize);
+
+    const [data, total] = await queryBuilder.getManyAndCount();
+
+    // 获取统计数据
+    const stats = await this.getAdminStats();
+
+    return [data, total, stats];
+  }
+
+  /**
+   * 获取管理统计数据
+   */
+  async getAdminStats(): Promise<any> {
+    const totalCount = await this.repository.count();
+    
+    const certifiedCount = await this.repository.count({
+      where: { certificationStatus: CertificationStatus.CERTIFIED }
+    });
+    
+    const pendingCount = await this.repository.count({
+      where: { certificationStatus: CertificationStatus.PENDING }
+    });
+    
+    const rejectedCount = await this.repository.count({
+      where: { certificationStatus: CertificationStatus.REJECTED }
+    });
+    
+    const unCertifiedCount = await this.repository.count({
+      where: { certificationStatus: CertificationStatus.UNCERTIFIED }
+    });
+
+    // 年龄段分布
+    const ageDistribution = await this.repository
+      .createQueryBuilder('profile')
+      .select('CASE ' +
+        'WHEN profile.age < 55 THEN "50-54" ' +
+        'WHEN profile.age < 60 THEN "55-59" ' +
+        'WHEN profile.age < 65 THEN "60-64" ' +
+        'WHEN profile.age < 70 THEN "65-69" ' +
+        'WHEN profile.age < 75 THEN "70-74" ' +
+        'ELSE "75+" END', 'ageGroup')
+      .addSelect('COUNT(*)', 'count')
+      .groupBy('ageGroup')
+      .orderBy('ageGroup')
+      .getRawMany();
+
+    // 求职状态分布
+    const jobSeekingDistribution = await this.repository
+      .createQueryBuilder('profile')
+      .select('profile.jobSeekingStatus', 'status')
+      .addSelect('COUNT(*)', 'count')
+      .groupBy('status')
+      .getRawMany();
+
+    // 性别分布
+    const genderDistribution = await this.repository
+      .createQueryBuilder('profile')
+      .select('profile.gender', 'gender')
+      .addSelect('COUNT(*)', 'count')
+      .groupBy('gender')
+      .getRawMany();
+
+    return {
+      totalCount,
+      certifiedCount,
+      pendingCount,
+      rejectedCount,
+      unCertifiedCount,
+      ageDistribution,
+      jobSeekingDistribution,
+      genderDistribution
+    };
+  }
+
+  /**
+   * 管理员根据ID获取人才详情
+   */
+  async findByIdForAdmin(id: number): Promise<SilverUserProfile> {
+    const profile = await this.repository.findOne({
+      where: { id },
+      relations: ['user'],
+      select: [
+        'id', 'userId', 'realName', 'nickname', 'organization', 'age', 'gender',
+        'phone', 'email', 'avatarUrl', 'personalIntro', 'personalSkills', 
+        'personalExperience', 'certificationStatus', 'certificationInfo',
+        'jobSeekingStatus', 'jobSeekingRequirements', 'totalPoints',
+        'resumeCount', 'applicationCount', 'timeBankHours',
+        'knowledgeShareCount', 'knowledgeDownloadCount', 'knowledgeLikeCount',
+        'knowledgeReadCount', 'knowledgeFavoriteCount', 'knowledgeCommentCount',
+        'knowledgeRanking', 'knowledgeRankingScore', 'createdAt', 'updatedAt',
+        'createdBy', 'updatedBy'
+      ]
+    });
+
+    if (!profile) {
+      throw new Error('未找到该银龄人才信息');
+    }
+
+    return profile;
+  }
+
+  /**
+   * 管理员更新人才信息
+   */
+  async updateByAdmin(id: number, data: Partial<UpdateSilverTalentRequest>, adminId: number): Promise<SilverUserProfile> {
+    const profile = await this.findByIdForAdmin(id);
+
+    // 处理枚举类型转换
+    const updateData: any = { ...data };
+    if (data.gender) {
+      updateData.gender = data.gender as any;
+    }
+    if (data.jobSeekingStatus) {
+      updateData.jobSeekingStatus = data.jobSeekingStatus as any;
+    }
+
+    Object.assign(profile, {
+      ...updateData,
+      updatedBy: adminId,
+      updatedAt: new Date()
+    });
+
+    return this.repository.save(profile);
+  }
+
+  /**
+   * 管理员更新认证状态
+   */
+  async updateCertificationStatus(
+    id: number, 
+    certificationStatus: CertificationStatus, 
+    certificationInfo?: string,
+    adminId?: number
+  ): Promise<SilverUserProfile> {
+    const profile = await this.findByIdForAdmin(id);
+
+    profile.certificationStatus = certificationStatus;
+    if (certificationInfo !== undefined) {
+      profile.certificationInfo = certificationInfo;
+    }
+    
+    if (adminId) {
+      profile.updatedBy = adminId;
+    }
+    profile.updatedAt = new Date();
+
+    return this.repository.save(profile);
+  }
+
+  /**
+   * 批量更新认证状态
+   */
+  async batchUpdateCertificationStatus(
+    ids: number[],
+    certificationStatus: CertificationStatus,
+    certificationInfo?: string,
+    adminId?: number
+  ): Promise<{ success: number; failed: number; errors: string[] }> {
+    let success = 0;
+    let failed = 0;
+    const errors: string[] = [];
+
+    for (const id of ids) {
+      try {
+        await this.updateCertificationStatus(id, certificationStatus, certificationInfo, adminId);
+        success++;
+      } catch (error) {
+        failed++;
+        errors.push(`ID ${id}: ${error instanceof Error ? error.message : '未知错误'}`);
+      }
+    }
+
+    return { success, failed, errors };
+  }
+
+  /**
+   * 删除人才信息
+   */
+  async delete(id: number): Promise<boolean> {
+    const profile = await this.findByIdForAdmin(id);
+    await this.repository.remove(profile);
+    return true;
+  }
+
+  /**
+   * 批量删除
+   */
+  async batchDelete(ids: number[]): Promise<{ success: number; failed: number; errors: string[] }> {
+    let success = 0;
+    let failed = 0;
+    const errors: string[] = [];
+
+    for (const id of ids) {
+      try {
+        await this.delete(id);
+        success++;
+      } catch (error) {
+        failed++;
+        errors.push(`ID ${id}: ${error instanceof Error ? error.message : '未知错误'}`);
+      }
+    }
+
+    return { success, failed, errors };
+  }
+}