Просмотр исходного кода

✨ feat(admin): 新增银龄智库管理后台功能

- 创建银龄智库列表页面和管理表单组件
- 在管理菜单中添加"银龄智库管理"菜单项
- 配置银龄智库路由和API客户端
- 简化银龄智库实体结构,移除复杂关联字段
- 添加银龄智库CRUD接口和数据库实体

📦 build(api): 集成银龄智库API路由

- 注册银龄智库API路由到主应用
- 创建通用的CRUD路由配置
- 配置搜索字段和权限中间件

📝 docs: 添加银龄智库管理后台文档

- 创建实施指南、实施计划和快速部署检查清单
yourname 7 месяцев назад
Родитель
Сommit
508b8727fe

+ 645 - 0
silver-knowledge-admin-code-guide.md

@@ -0,0 +1,645 @@
+# 银龄智库管理后台代码实施指南
+
+## 1. 创建管理页面组件
+
+### 1.1 SilverKnowledges.tsx 列表页面
+
+**文件路径**: `src/client/admin/pages/SilverKnowledges.tsx`
+
+```tsx
+import React, { useState, useRef } from 'react';
+import { Card, Button, Space, Tag, Modal, message, Switch, Popconfirm } from 'antd';
+import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, StarOutlined } from '@ant-design/icons';
+import type { ProColumns, ActionType } from '@ant-design/pro-table';
+import ProTable from '@ant-design/pro-table';
+import { useNavigate } from 'react-router';
+import { useAuth } from '../hooks/AuthProvider';
+import type { InferResponseType } from 'hono/client';
+import { silverUsersClient } from '@/client/api';
+import dayjs from 'dayjs';
+
+type SilverKnowledge = InferResponseType<typeof silverUsersClient.knowledges.$get, 200>['data'][0];
+
+const SilverKnowledges: React.FC = () => {
+  const navigate = useNavigate();
+  const { user } = useAuth();
+  const actionRef = useRef<ActionType>();
+  const [loading, setLoading] = useState(false);
+
+  const handleAdd = () => {
+    navigate('/admin/silver-knowledges/new');
+  };
+
+  const handleEdit = (record: SilverKnowledge) => {
+    navigate(`/admin/silver-knowledges/${record.id}/edit`);
+  };
+
+  const handleDelete = async (id: number) => {
+    try {
+      await silverUsersClient.knowledges[':id'].$delete({
+        param: { id: id.toString() }
+      });
+      message.success('删除成功');
+      actionRef.current?.reload();
+    } catch (error) {
+      message.error('删除失败');
+    }
+  };
+
+  const handleToggleStatus = async (record: SilverKnowledge, newStatus: number) => {
+    try {
+      await silverUsersClient.knowledges[':id'].$put({
+        param: { id: record.id.toString() },
+        json: { status: newStatus }
+      });
+      message.success('状态更新成功');
+      actionRef.current?.reload();
+    } catch (error) {
+      message.error('状态更新失败');
+    }
+  };
+
+  const handleToggleFeatured = async (record: SilverKnowledge, isFeatured: boolean) => {
+    try {
+      await silverUsersClient.knowledges[':id'].$put({
+        param: { id: record.id.toString() },
+        json: { isFeatured: isFeatured ? 1 : 0 }
+      });
+      message.success(isFeatured ? '已设为推荐' : '已取消推荐');
+      actionRef.current?.reload();
+    } catch (error) {
+      message.error('推荐设置失败');
+    }
+  };
+
+  const columns: ProColumns<SilverKnowledge>[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      width: 80,
+      hideInSearch: true,
+    },
+    {
+      title: '标题',
+      dataIndex: 'title',
+      ellipsis: true,
+      width: 200,
+    },
+    {
+      title: '分类',
+      dataIndex: 'category',
+      render: (_, record) => record.category?.name || '-',
+      width: 100,
+    },
+    {
+      title: '类型',
+      dataIndex: 'type',
+      valueEnum: {
+        1: { text: '文章', status: 'Processing' },
+        2: { text: '视频', status: 'Success' },
+        3: { text: '文档', status: 'Default' },
+        4: { text: '课程', status: 'Warning' },
+        5: { text: '经验分享', status: 'Success' },
+        6: { text: '案例分享', status: 'Processing' },
+        7: { text: '研究报告', status: 'Error' },
+      },
+      width: 100,
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      render: (_, record) => {
+        const statusMap = {
+          0: <Tag color="default">草稿</Tag>,
+          1: <Tag color="success">已发布</Tag>,
+          2: <Tag color="warning">已隐藏</Tag>,
+          3: <Tag color="error">已删除</Tag>,
+          4: <Tag color="processing">审核中</Tag>,
+        };
+        return statusMap[record.status] || '-';
+      },
+      valueEnum: {
+        0: { text: '草稿', status: 'Default' },
+        1: { text: '已发布', status: 'Success' },
+        2: { text: '已隐藏', status: 'Warning' },
+        3: { text: '已删除', status: 'Error' },
+        4: { text: '审核中', status: 'Processing' },
+      },
+      width: 100,
+    },
+    {
+      title: '作者',
+      dataIndex: ['user', 'nickname'],
+      width: 100,
+      hideInSearch: true,
+    },
+    {
+      title: '浏览',
+      dataIndex: 'viewCount',
+      width: 80,
+      hideInSearch: true,
+    },
+    {
+      title: '点赞',
+      dataIndex: 'likeCount',
+      width: 80,
+      hideInSearch: true,
+    },
+    {
+      title: '收藏',
+      dataIndex: 'favoriteCount',
+      width: 80,
+      hideInSearch: true,
+    },
+    {
+      title: '推荐',
+      dataIndex: 'isFeatured',
+      render: (_, record) => (
+        <Switch
+          checked={record.isFeatured === 1}
+          onChange={(checked) => handleToggleFeatured(record, checked)}
+          checkedChildren="是"
+          unCheckedChildren="否"
+        />
+      ),
+      width: 80,
+      hideInSearch: true,
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'createdAt',
+      render: (_, record) => dayjs(record.createdAt).format('YYYY-MM-DD HH:mm'),
+      width: 150,
+      hideInSearch: true,
+    },
+    {
+      title: '操作',
+      valueType: 'option',
+      width: 200,
+      render: (_, record) => (
+        <Space>
+          <Button
+            type="link"
+            size="small"
+            icon={<EyeOutlined />}
+            onClick={() => window.open(`/knowledge/${record.id}`, '_blank')}
+          >
+            查看
+          </Button>
+          <Button
+            type="link"
+            size="small"
+            icon={<EditOutlined />}
+            onClick={() => handleEdit(record)}
+          >
+            编辑
+          </Button>
+          <Popconfirm
+            title="确定要删除吗?"
+            onConfirm={() => handleDelete(record.id)}
+            okText="确定"
+            cancelText="取消"
+          >
+            <Button danger type="link" size="small" icon={<DeleteOutlined />}>
+              删除
+            </Button>
+          </Popconfirm>
+        </Space>
+      ),
+    },
+  ];
+
+  return (
+    <Card>
+      <ProTable<SilverKnowledge>
+        headerTitle="银龄智库管理"
+        actionRef={actionRef}
+        rowKey="id"
+        search={{
+          labelWidth: 120,
+        }}
+        toolBarRender={() => [
+          <Button key="add" type="primary" onClick={handleAdd} icon={<PlusOutlined />}>
+            新建知识
+          </Button>,
+        ]}
+        request={async (params) => {
+          const response = await silverUsersClient.knowledges.$get({
+            query: {
+              page: params.current || 1,
+              pageSize: params.pageSize || 10,
+              keyword: params.title,
+              ...params,
+            }
+          });
+          const data = await response.json();
+          return {
+            data: data.data,
+            success: response.ok,
+            total: data.pagination.total,
+          };
+        }}
+        columns={columns}
+        pagination={{
+          showSizeChanger: true,
+          showQuickJumper: true,
+        }}
+      />
+    </Card>
+  );
+};
+
+export default SilverKnowledges;
+```
+
+### 1.2 SilverKnowledgeForm.tsx 表单页面
+
+**文件路径**: `src/client/admin/pages/SilverKnowledgeForm.tsx`
+
+```tsx
+import React, { useEffect, useState } from 'react';
+import { Card, Form, Input, Select, Button, Space, Upload, message, Row, Col } from 'antd';
+import { InboxOutlined, ArrowLeftOutlined } from '@ant-design/icons';
+import { useNavigate, useParams } from 'react-router';
+import ReactQuill from 'react-quill';
+import 'react-quill/dist/quill.snow.css';
+import { useAuth } from '../hooks/AuthProvider';
+import { silverUsersClient } from '@/client/api';
+import type { InferResponseType, InferRequestType } from 'hono/client';
+import dayjs from 'dayjs';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { Dragger } = Upload;
+
+type SilverKnowledge = InferResponseType<typeof silverUsersClient.knowledges[':id'].$get, 200>;
+type CreateKnowledge = InferRequestType<typeof silverUsersClient.knowledges.$post>['json'];
+type UpdateKnowledge = InferRequestType<typeof silverUsersClient.knowledges[':id'].$put>['json'];
+
+const knowledgeTypes = [
+  { value: 1, label: '文章' },
+  { value: 2, label: '视频' },
+  { value: 3, label: '文档' },
+  { value: 4, label: '课程' },
+  { value: 5, label: '经验分享' },
+  { value: 6, label: '案例分享' },
+  { value: 7, label: '研究报告' },
+];
+
+const knowledgeStatus = [
+  { value: 0, label: '草稿' },
+  { value: 1, label: '已发布' },
+  { value: 2, label: '已隐藏' },
+  { value: 4, label: '审核中' },
+];
+
+const SilverKnowledgeForm: React.FC = () => {
+  const navigate = useNavigate();
+  const { id } = useParams();
+  const { user } = useAuth();
+  const [form] = Form.useForm();
+  const [loading, setLoading] = useState(false);
+  const [categories, setCategories] = useState<any[]>([]);
+
+  useEffect(() => {
+    fetchCategories();
+    if (id) {
+      fetchKnowledge();
+    }
+  }, [id]);
+
+  const fetchCategories = async () => {
+    try {
+      const response = await silverUsersClient['knowledge-categories'].$get();
+      const data = await response.json();
+      setCategories(data.data || []);
+    } catch (error) {
+      console.error('获取分类失败:', error);
+    }
+  };
+
+  const fetchKnowledge = async () => {
+    if (!id) return;
+    try {
+      const response = await silverUsersClient.knowledges[':id'].$get({
+        param: { id }
+      });
+      const data = await response.json();
+      form.setFieldsValue({
+        ...data,
+        tags: data.tags ? JSON.parse(data.tags) : [],
+        attachments: data.attachments ? JSON.parse(data.attachments) : [],
+      });
+    } catch (error) {
+      message.error('获取知识详情失败');
+    }
+  };
+
+  const handleSubmit = async (values: any) => {
+    setLoading(true);
+    try {
+      const submitData = {
+        ...values,
+        userId: user?.id,
+        tags: values.tags ? JSON.stringify(values.tags) : undefined,
+        attachments: values.attachments ? JSON.stringify(values.attachments) : undefined,
+      };
+
+      if (id) {
+        await silverUsersClient.knowledges[':id'].$put({
+          param: { id },
+          json: submitData
+        });
+        message.success('更新成功');
+      } else {
+        await silverUsersClient.knowledges.$post({
+          json: submitData
+        });
+        message.success('创建成功');
+      }
+      navigate('/admin/silver-knowledges');
+    } catch (error) {
+      message.error(id ? '更新失败' : '创建失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleBack = () => {
+    navigate('/admin/silver-knowledges');
+  };
+
+  const uploadProps = {
+    name: 'file',
+    multiple: true,
+    action: '/api/v1/files/upload-policy',
+    onChange(info: any) {
+      const { status } = info.file;
+      if (status === 'done') {
+        message.success(`${info.file.name} 上传成功`);
+      } else if (status === 'error') {
+        message.error(`${info.file.name} 上传失败`);
+      }
+    },
+  };
+
+  return (
+    <Card
+      title={id ? '编辑知识' : '新建知识'}
+      extra={
+        <Button icon={<ArrowLeftOutlined />} onClick={handleBack}>
+          返回列表
+        </Button>
+      }
+    >
+      <Form
+        form={form}
+        layout="vertical"
+        onFinish={handleSubmit}
+        initialValues={{
+          type: 1,
+          status: 0,
+          isFeatured: 0,
+          sortOrder: 0,
+        }}
+      >
+        <Row gutter={24}>
+          <Col span={16}>
+            <Form.Item
+              name="title"
+              label="标题"
+              rules={[{ required: true, message: '请输入标题' }]}
+            >
+              <Input placeholder="请输入知识标题" maxLength={255} />
+            </Form.Item>
+
+            <Form.Item
+              name="content"
+              label="内容"
+              rules={[{ required: true, message: '请输入内容' }]}
+            >
+              <ReactQuill
+                theme="snow"
+                style={{ height: 300 }}
+                placeholder="请输入知识内容"
+              />
+            </Form.Item>
+
+            <Form.Item
+              name="summary"
+              label="摘要"
+            >
+              <TextArea
+                rows={4}
+                placeholder="请输入知识摘要(选填)"
+                maxLength={500}
+                showCount
+              />
+            </Form.Item>
+          </Col>
+
+          <Col span={8}>
+            <Form.Item
+              name="categoryId"
+              label="分类"
+            >
+              <Select placeholder="请选择分类" allowClear>
+                {categories.map(category => (
+                  <Option key={category.id} value={category.id}>
+                    {category.name}
+                  </Option>
+                ))}
+              </Select>
+            </Form.Item>
+
+            <Form.Item
+              name="type"
+              label="类型"
+              rules={[{ required: true, message: '请选择类型' }]}
+            >
+              <Select placeholder="请选择类型">
+                {knowledgeTypes.map(type => (
+                  <Option key={type.value} value={type.value}>
+                    {type.label}
+                  </Option>
+                ))}
+              </Select>
+            </Form.Item>
+
+            <Form.Item
+              name="status"
+              label="状态"
+              rules={[{ required: true, message: '请选择状态' }]}
+            >
+              <Select placeholder="请选择状态">
+                {knowledgeStatus.map(status => (
+                  <Option key={status.value} value={status.value}>
+                    {status.label}
+                  </Option>
+                ))}
+              </Select>
+            </Form.Item>
+
+            <Form.Item
+              name="tags"
+              label="标签"
+            >
+              <Select
+                mode="tags"
+                placeholder="请输入标签"
+                tokenSeparators={[',']}
+              />
+            </Form.Item>
+
+            <Form.Item
+              name="keywords"
+              label="关键词"
+            >
+              <TextArea
+                rows={3}
+                placeholder="请输入搜索关键词,用逗号分隔"
+              />
+            </Form.Item>
+
+            <Form.Item
+              name="author"
+              label="原作者"
+            >
+              <Input placeholder="请输入原作者" maxLength={100} />
+            </Form.Item>
+
+            <Form.Item
+              name="source"
+              label="知识来源"
+            >
+              <Input placeholder="请输入知识来源" maxLength={255} />
+            </Form.Item>
+
+            <Form.Item
+              name="coverImage"
+              label="封面图片"
+            >
+              <Input placeholder="请输入封面图片URL" />
+            </Form.Item>
+
+            <Form.Item
+              name="attachments"
+              label="附件"
+            >
+              <Dragger {...uploadProps}>
+                <p className="ant-upload-drag-icon">
+                  <InboxOutlined />
+                </p>
+                <p className="ant-upload-text">点击或拖拽文件到此处上传</p>
+                <p className="ant-upload-hint">支持单个或批量上传</p>
+              </Dragger>
+            </Form.Item>
+          </Col>
+        </Row>
+
+        <Form.Item>
+          <Space>
+            <Button type="primary" htmlType="submit" loading={loading}>
+              {id ? '更新' : '创建'}
+            </Button>
+            <Button onClick={handleBack}>取消</Button>
+          </Space>
+        </Form.Item>
+      </Form>
+    </Card>
+  );
+};
+
+export default SilverKnowledgeForm;
+```
+
+## 2. 更新菜单配置
+
+### 2.1 修改 menu.tsx
+
+**添加导入**:
+```typescript
+import { BookOutlined } from '@ant-design/icons';
+```
+
+**添加菜单项**:
+```typescript
+{
+  key: 'silver-knowledges',
+  label: '银龄智库发布',
+  icon: <BookOutlined />,
+  path: '/admin/silver-knowledges',
+  permission: 'silver-knowledge:manage'
+}
+```
+
+## 3. 更新路由配置
+
+### 3.1 修改 routes.tsx
+
+**添加导入**:
+```typescript
+import { SilverKnowledgesPage } from './pages/SilverKnowledges';
+import { SilverKnowledgeFormPage } from './pages/SilverKnowledgeForm';
+```
+
+**添加路由**:
+```typescript
+{
+  path: 'silver-knowledges',
+  element: <SilverKnowledgesPage />,
+  errorElement: <ErrorPage />
+},
+{
+  path: 'silver-knowledges/new',
+  element: <SilverKnowledgeFormPage />,
+  errorElement: <ErrorPage />
+},
+{
+  path: 'silver-knowledges/:id/edit',
+  element: <SilverKnowledgeFormPage />,
+  errorElement: <ErrorPage />
+}
+```
+
+## 4. 客户端API配置
+
+### 4.1 修改 api.ts
+
+**添加类型定义**:
+```typescript
+import type { SilverKnowledgeRoutes } from '@/server/api/silver-users/knowledges';
+
+export const silverUsersClient = hc<SilverKnowledgeRoutes>('/api/v1', {
+  fetch: axiosFetch,
+}).api.v1['silver-users'];
+```
+
+## 5. 依赖安装
+
+```bash
+# 安装富文本编辑器
+npm install react-quill @types/react-quill
+
+# 安装日期处理
+npm install dayjs
+```
+
+## 6. 使用说明
+
+### 6.1 访问路径
+- 列表页面: `/admin/silver-knowledges`
+- 新建页面: `/admin/silver-knowledges/new`
+- 编辑页面: `/admin/silver-knowledges/:id/edit`
+
+### 6.2 功能特点
+- 完整的CRUD操作
+- 富文本编辑器支持
+- 图片和附件上传
+- 状态管理
+- 推荐设置
+- 搜索和筛选
+- 批量操作
+
+### 6.3 权限控制
+需要用户具有 `silver-knowledge:manage` 权限才能访问

+ 154 - 0
silver-knowledge-admin-implementation-plan.md

@@ -0,0 +1,154 @@
+# 银龄智库管理后台实施计划
+
+## 项目概述
+在管理后台增加"银龄智库发布"菜单,实现银龄智库的发布和管理功能。
+
+## 当前状态分析
+- ✅ 银龄智库实体(SilverKnowledge)已存在,包含完整字段定义
+- ✅ 通用CRUD API已存在 (`/api/v1/silver-users/knowledges`)
+- ❌ 管理后台缺少对应的页面和菜单
+
+## 实施步骤
+
+### 1. 创建管理页面组件
+**文件位置**: `src/client/admin/pages/SilverKnowledges.tsx`
+
+```typescript
+// 主要功能:
+// - 银龄智库列表展示(分页、搜索、筛选)
+// - 状态管理(草稿、已发布、已隐藏、审核中)
+// - 批量操作(删除、发布、隐藏)
+// - 推荐设置
+```
+
+### 2. 创建发布/编辑表单组件
+**文件位置**: `src/client/admin/pages/SilverKnowledgeForm.tsx`
+
+```typescript
+// 表单字段:
+// - 标题(必填)
+// - 内容(必填)
+// - 摘要
+// - 分类选择
+// - 知识类型(文章、视频、文档等)
+// - 标签
+// - 封面图片
+// - 附件上传
+// - 关键词
+// - 原作者
+// - 知识来源
+```
+
+### 3. 更新菜单配置
+**文件位置**: `src/client/admin/menu.tsx`
+
+**修改内容**:
+- 在菜单数组中添加新的"银龄智库发布"菜单项
+- 使用合适的图标(BookOutlined)
+- 设置权限控制
+
+### 4. 更新路由配置
+**文件位置**: `src/client/admin/routes.tsx`
+
+**修改内容**:
+- 导入新的页面组件
+- 添加路由配置:
+  - `/admin/silver-knowledges` - 列表页面
+  - `/admin/silver-knowledges/new` - 新建页面
+  - `/admin/silver-knowledges/:id/edit` - 编辑页面
+
+### 5. 客户端API配置
+**文件位置**: `src/client/api.ts`
+
+**需要添加**:
+- 银龄智库客户端API定义
+- 类型推断支持
+
+## 详细实施计划
+
+### 阶段1:创建页面组件
+1. **SilverKnowledges.tsx** - 列表管理页面
+   - 使用Ant Design ProTable
+   - 支持搜索、筛选、分页
+   - 状态标签显示
+   - 操作按钮(编辑、删除、发布、隐藏、推荐)
+
+2. **SilverKnowledgeForm.tsx** - 表单页面
+   - 使用Ant Design Form
+   - 支持富文本编辑器
+   - 图片上传组件
+   - 分类选择下拉框
+   - 标签输入组件
+
+### 阶段2:菜单和路由
+1. 在`menu.tsx`中添加:
+```typescript
+{
+  key: 'silver-knowledges',
+  label: '银龄智库发布',
+  icon: <BookOutlined />,
+  path: '/admin/silver-knowledges',
+  permission: 'silver-knowledge:manage'
+}
+```
+
+2. 在`routes.tsx`中添加:
+```typescript
+{
+  path: 'silver-knowledges',
+  element: <SilverKnowledgesPage />,
+  errorElement: <ErrorPage />
+}
+```
+
+### 阶段3:功能实现
+1. **列表功能**:
+   - 分页查询
+   - 关键词搜索(标题、内容、摘要)
+   - 状态筛选
+   - 分类筛选
+   - 时间范围筛选
+   - 排序(创建时间、浏览量、点赞数)
+
+2. **表单功能**:
+   - 新建知识
+   - 编辑知识
+   - 草稿保存
+   - 预览功能
+   - 图片/附件上传
+
+3. **状态管理**:
+   - 发布/草稿切换
+   - 推荐设置
+   - 隐藏/显示
+
+## 技术栈
+- React 18 + TypeScript
+- Ant Design 5.x
+- React Router 6.x
+- Hono Client (API调用)
+
+## 权限设计
+- `silver-knowledge:manage` - 管理权限
+- `silver-knowledge:create` - 创建权限
+- `silver-knowledge:edit` - 编辑权限
+- `silver-knowledge:delete` - 删除权限
+
+## UI/UX设计
+- 响应式设计
+- 卡片式布局
+- 状态标签色彩区分
+- 批量操作支持
+- 搜索筛选面板
+
+## 测试计划
+1. 功能测试:CRUD操作
+2. 权限测试:不同角色访问
+3. 性能测试:大数据量加载
+4. 兼容性测试:不同浏览器
+
+## 预计完成时间
+- 页面组件开发:2-3小时
+- 菜单路由配置:30分钟
+- 功能联调测试:1小时
+- 总计:4-5小时

+ 142 - 0
silver-knowledge-quick-setup.md

@@ -0,0 +1,142 @@
+# 银龄智库管理后台快速部署检查清单
+
+## 部署步骤(按顺序执行)
+
+### ✅ 步骤1:创建页面组件
+**执行命令:**
+```bash
+# 创建页面文件
+touch src/client/admin/pages/SilverKnowledges.tsx
+touch src/client/admin/pages/SilverKnowledgeForm.tsx
+```
+
+**复制代码:**
+- 将 `SilverKnowledges.tsx` 代码复制到对应文件
+- 将 `SilverKnowledgeForm.tsx` 代码复制到对应文件
+
+### ✅ 步骤2:更新菜单配置
+**文件:** `src/client/admin/menu.tsx`
+
+**修改内容:**
+1. 添加图标导入:
+```typescript
+import { BookOutlined } from '@ant-design/icons';
+```
+
+2. 在 `menuItems` 数组中添加新菜单项(建议放在银龄库管理后面):
+```typescript
+{
+  key: 'silver-knowledges',
+  label: '银龄智库发布',
+  icon: <BookOutlined />,
+  path: '/admin/silver-knowledges',
+  permission: 'silver-knowledge:manage'
+}
+```
+
+### ✅ 步骤3:更新路由配置
+**文件:** `src/client/admin/routes.tsx`
+
+**修改内容:**
+1. 添加页面导入:
+```typescript
+import { SilverKnowledgesPage } from './pages/SilverKnowledges';
+import { SilverKnowledgeFormPage } from './pages/SilverKnowledgeForm';
+```
+
+2. 在 `children` 数组中添加路由(建议放在 silver-jobs 后面):
+```typescript
+{
+  path: 'silver-knowledges',
+  element: <SilverKnowledgesPage />,
+  errorElement: <ErrorPage />
+},
+{
+  path: 'silver-knowledges/new',
+  element: <SilverKnowledgeFormPage />,
+  errorElement: <ErrorPage />
+},
+{
+  path: 'silver-knowledges/:id/edit',
+  element: <SilverKnowledgeFormPage />,
+  errorElement: <ErrorPage />
+}
+```
+
+### ✅ 步骤4:安装依赖包
+```bash
+npm install react-quill @types/react-quill dayjs
+```
+
+### ✅ 步骤5:启动开发服务器
+```bash
+npm run dev
+```
+
+## 验证清单
+
+### 功能验证
+- [ ] 管理后台左侧菜单显示"银龄智库发布"
+- [ ] 点击菜单进入银龄智库列表页面
+- [ ] 列表页面显示所有银龄智库内容
+- [ ] 点击"新建知识"按钮进入创建页面
+- [ ] 表单支持富文本编辑
+- [ ] 支持图片和附件上传
+- [ ] 支持状态管理(草稿/发布/隐藏)
+- [ ] 支持推荐设置
+- [ ] 支持编辑和删除操作
+- [ ] 支持搜索和筛选功能
+
+### 权限验证
+- [ ] 需要登录才能访问
+- [ ] 需要 `silver-knowledge:manage` 权限
+- [ ] 不同权限用户显示对应功能
+
+### 数据验证
+- [ ] 列表正确显示银龄智库数据
+- [ ] 创建新知识成功保存
+- [ ] 编辑现有知识成功更新
+- [ ] 删除操作正确执行
+
+## 常见问题解决
+
+### Q1: 富文本编辑器样式问题
+**解决:** 确保在 `src/client/admin/index.tsx` 中引入:
+```typescript
+import 'react-quill/dist/quill.snow.css';
+```
+
+### Q2: 图片上传失败
+**解决:** 检查 MinIO 配置和文件上传权限
+
+### Q3: 权限不足
+**解决:** 在数据库中添加对应权限:
+```sql
+INSERT INTO permissions (name, description) VALUES 
+('silver-knowledge:manage', '管理银龄智库');
+```
+
+## 测试账号
+使用管理员账号登录后可直接访问:
+- URL: `http://localhost:5173/admin/silver-knowledges`
+- 菜单:管理后台 → 银龄智库发布
+
+## 完成标志
+- [ ] 左侧菜单显示"银龄智库发布"
+- [ ] 可以正常访问 `/admin/silver-knowledges`
+- [ ] 可以创建新的银龄智库内容
+- [ ] 可以编辑现有内容
+- [ ] 可以删除内容
+- [ ] 支持搜索和筛选
+
+## 后续优化建议
+1. 添加批量操作功能
+2. 添加导出功能
+3. 添加统计分析面板
+4. 优化移动端体验
+5. 添加审核流程
+
+## 技术支持
+如有问题,请参考:
+- 实施指南:`silver-knowledge-admin-code-guide.md`
+- 完整计划:`silver-knowledge-admin-implementation-plan.md`

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

@@ -12,6 +12,7 @@ import {
   CarryOutOutlined,
   CarryOutOutlined,
   AuditOutlined,
   AuditOutlined,
   PictureOutlined,
   PictureOutlined,
+  BookOutlined,
 } from '@ant-design/icons';
 } from '@ant-design/icons';
 
 
 export interface MenuItem {
 export interface MenuItem {
@@ -125,6 +126,13 @@ export const useMenu = () => {
       path: '/admin/home-icons',
       path: '/admin/home-icons',
       permission: 'home-icon:manage'
       permission: 'home-icon:manage'
     },
     },
+    {
+      key: 'silver-knowledges',
+      label: '银龄智库管理',
+      icon: <BookOutlined />,
+      path: '/admin/silver-knowledges',
+      permission: 'silver-knowledge:manage'
+    },
   ];
   ];
 
 
   // 用户菜单项
   // 用户菜单项

+ 327 - 0
src/client/admin/pages/SilverKnowledges.tsx

@@ -0,0 +1,327 @@
+import React, { useState, useEffect } from 'react';
+import { Card, Table, Button, Modal, Form, Input, Select, message, Space, Tag, Popconfirm } from 'antd';
+import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
+import type { ColumnsType } from 'antd/es/table';
+import type { InferResponseType, InferRequestType } from 'hono/client';
+import { silverKnowledgeClient } from '@/client/api';
+
+const { TextArea } = Input;
+const { Option } = Select;
+
+type SilverKnowledge = InferResponseType<typeof silverKnowledgeClient.$get, 200>['data'][0];
+type CreateKnowledgeRequest = InferRequestType<typeof silverKnowledgeClient.$post>['json'];
+type UpdateKnowledgeRequest = InferRequestType<typeof silverKnowledgeClient[':id']['$put']>['json'];
+
+const SilverKnowledges: React.FC = () => {
+  const [data, setData] = useState<SilverKnowledge[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [modalVisible, setModalVisible] = useState(false);
+  const [modalType, setModalType] = useState<'create' | 'edit'>('create');
+  const [currentRecord, setCurrentRecord] = useState<SilverKnowledge | null>(null);
+  const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
+  const [form] = Form.useForm();
+  const [searchText, setSearchText] = useState('');
+
+  const fetchData = async (page = 1, pageSize = 10) => {
+    setLoading(true);
+    try {
+      const response = await silverKnowledgeClient.$get({
+        query: { page, pageSize, keyword: searchText || undefined }
+      });
+      const result = await response.json();
+      setData(result.data);
+      setPagination({
+        current: page,
+        pageSize,
+        total: result.pagination.total
+      });
+    } catch (error) {
+      message.error('获取数据失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    fetchData();
+  }, []);
+
+  const handleCreate = () => {
+    setModalType('create');
+    setCurrentRecord(null);
+    form.resetFields();
+    setModalVisible(true);
+  };
+
+  const handleEdit = (record: SilverKnowledge) => {
+    setModalType('edit');
+    setCurrentRecord(record);
+    form.setFieldsValue({
+      ...record,
+      tags: record.tags || undefined
+    });
+    setModalVisible(true);
+  };
+
+  const handleDelete = async (id: number) => {
+    try {
+      await silverKnowledgeClient[':id']['$delete']({
+        param: { id: id.toString() }
+      });
+      message.success('删除成功');
+      fetchData(pagination.current, pagination.pageSize);
+    } catch (error) {
+      message.error('删除失败');
+    }
+  };
+
+  const handleSubmit = async () => {
+    try {
+      const values = await form.validateFields();
+      
+      if (modalType === 'create') {
+        await silverKnowledgeClient.$post({
+          json: values as CreateKnowledgeRequest
+        });
+        message.success('创建成功');
+      } else {
+        await silverKnowledgeClient[':id']['$put']({
+          param: { id: currentRecord!.id.toString() },
+          json: values as UpdateKnowledgeRequest
+        });
+        message.success('更新成功');
+      }
+      
+      setModalVisible(false);
+      fetchData(pagination.current, pagination.pageSize);
+    } catch (error) {
+      message.error(modalType === 'create' ? '创建失败' : '更新失败');
+    }
+  };
+
+  const handleTableChange = (pagination: any) => {
+    fetchData(pagination.current, pagination.pageSize);
+  };
+
+  const handleSearch = () => {
+    fetchData(1, pagination.pageSize);
+  };
+
+  const columns: ColumnsType<SilverKnowledge> = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      key: 'id',
+      width: 80,
+    },
+    {
+      title: '标题',
+      dataIndex: 'title',
+      key: 'title',
+      ellipsis: true,
+    },
+    {
+      title: '分类',
+      dataIndex: 'category',
+      key: 'category',
+      width: 120,
+    },
+    {
+      title: '作者',
+      dataIndex: 'author',
+      key: 'author',
+      width: 120,
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      key: 'status',
+      width: 100,
+      render: (status: number) => (
+        <Tag color={status === 1 ? 'green' : 'orange'}>
+          {status === 1 ? '已发布' : '草稿'}
+        </Tag>
+      ),
+    },
+    {
+      title: '浏览量',
+      dataIndex: 'viewCount',
+      key: 'viewCount',
+      width: 100,
+    },
+    {
+      title: '点赞数',
+      dataIndex: 'likeCount',
+      key: 'likeCount',
+      width: 100,
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'createdAt',
+      key: 'createdAt',
+      width: 180,
+      render: (text: string) => new Date(text).toLocaleString(),
+    },
+    {
+      title: '操作',
+      key: 'action',
+      width: 200,
+      fixed: 'right',
+      render: (_, record) => (
+        <Space size="middle">
+          <Button
+            type="link"
+            icon={<EyeOutlined />}
+            onClick={() => window.open(`/silver-wisdom/${record.id}`, '_blank')}
+          >
+            查看
+          </Button>
+          <Button
+            type="link"
+            icon={<EditOutlined />}
+            onClick={() => handleEdit(record)}
+          >
+            编辑
+          </Button>
+          <Popconfirm
+            title="确定要删除这条知识吗?"
+            onConfirm={() => handleDelete(record.id)}
+            okText="确定"
+            cancelText="取消"
+          >
+            <Button type="link" danger icon={<DeleteOutlined />}>
+              删除
+            </Button>
+          </Popconfirm>
+        </Space>
+      ),
+    },
+  ];
+
+  return (
+    <div className="p-6">
+      <Card
+        title={
+          <div className="flex justify-between items-center">
+            <span>银龄智库管理</span>
+            <Button
+              type="primary"
+              icon={<PlusOutlined />}
+              onClick={handleCreate}
+            >
+              新增知识
+            </Button>
+          </div>
+        }
+        className="shadow-sm"
+      >
+        <div className="mb-4 flex gap-4">
+          <Input.Search
+            placeholder="搜索标题、内容或作者"
+            value={searchText}
+            onChange={(e) => setSearchText(e.target.value)}
+            onSearch={handleSearch}
+            style={{ width: 300 }}
+            allowClear
+          />
+        </div>
+        
+        <Table
+          columns={columns}
+          dataSource={data}
+          rowKey="id"
+          loading={loading}
+          pagination={{
+            ...pagination,
+            showSizeChanger: true,
+            showQuickJumper: true,
+            showTotal: (total) => `共 ${total} 条记录`,
+          }}
+          onChange={handleTableChange}
+          scroll={{ x: 1200 }}
+        />
+      </Card>
+
+      <Modal
+        title={modalType === 'create' ? '新增银龄知识' : '编辑银龄知识'}
+        open={modalVisible}
+        onOk={handleSubmit}
+        onCancel={() => setModalVisible(false)}
+        width={800}
+        destroyOnClose
+      >
+        <Form
+          form={form}
+          layout="vertical"
+          initialValues={{ status: 1 }}
+        >
+          <Form.Item
+            label="知识标题"
+            name="title"
+            rules={[{ required: true, message: '请输入知识标题' }]}
+          >
+            <Input placeholder="请输入知识标题" maxLength={255} />
+          </Form.Item>
+
+          <Form.Item
+            label="知识内容"
+            name="content"
+            rules={[{ required: true, message: '请输入知识内容' }]}
+          >
+            <TextArea
+              rows={6}
+              placeholder="请输入知识内容"
+              maxLength={5000}
+              showCount
+            />
+          </Form.Item>
+
+          <Form.Item
+            label="知识分类"
+            name="category"
+            rules={[{ required: true, message: '请选择知识分类' }]}
+          >
+            <Select placeholder="请选择知识分类">
+              <Option value="健康养生">健康养生</Option>
+              <Option value="生活技巧">生活技巧</Option>
+              <Option value="心理情感">心理情感</Option>
+              <Option value="法律常识">法律常识</Option>
+              <Option value="金融理财">金融理财</Option>
+              <Option value="科技应用">科技应用</Option>
+              <Option value="教育培训">教育培训</Option>
+              <Option value="兴趣爱好">兴趣爱好</Option>
+            </Select>
+          </Form.Item>
+
+          <Form.Item
+            label="作者"
+            name="author"
+            rules={[{ required: true, message: '请输入作者姓名' }]}
+          >
+            <Input placeholder="请输入作者姓名" maxLength={100} />
+          </Form.Item>
+
+          <Form.Item
+            label="标签"
+            name="tags"
+          >
+            <Input placeholder="请输入标签,用逗号分隔" />
+          </Form.Item>
+
+          <Form.Item
+            label="状态"
+            name="status"
+            rules={[{ required: true, message: '请选择状态' }]}
+          >
+            <Select>
+              <Option value={1}>发布</Option>
+              <Option value={0}>草稿</Option>
+            </Select>
+          </Form.Item>
+        </Form>
+      </Modal>
+    </div>
+  );
+};
+
+export default SilverKnowledges;

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

@@ -12,6 +12,7 @@ import { SilverTalentsPage } from './pages/SilverTalents';
 import { SilverJobsPage } from './pages/SilverJobs';
 import { SilverJobsPage } from './pages/SilverJobs';
 import CompanyCertificationPage from './pages/CompanyCertification';
 import CompanyCertificationPage from './pages/CompanyCertification';
 import HomeIconsPage from './pages/HomeIcons';
 import HomeIconsPage from './pages/HomeIcons';
+import SilverKnowledgesPage from './pages/SilverKnowledges';
 
 
 export const router = createBrowserRouter([
 export const router = createBrowserRouter([
   {
   {
@@ -69,6 +70,11 @@ export const router = createBrowserRouter([
         element: <HomeIconsPage />,
         element: <HomeIconsPage />,
         errorElement: <ErrorPage />
         errorElement: <ErrorPage />
       },
       },
+      {
+        path: 'silver-knowledges',
+        element: <SilverKnowledgesPage />,
+        errorElement: <ErrorPage />
+      },
       {
       {
         path: '*',
         path: '*',
         element: <NotFoundPage />,
         element: <NotFoundPage />,

+ 8 - 2
src/client/api.ts

@@ -26,7 +26,8 @@ import type {
   SilverJobRoutes,
   SilverJobRoutes,
   CompanyCertificationRoutes,
   CompanyCertificationRoutes,
   SilverCompaniesRoutes,
   SilverCompaniesRoutes,
-  HomeIconRoutes
+  HomeIconRoutes,
+  SilverKnowledgeRoutes
 } from '@/server/api';
 } from '@/server/api';
 import { axiosFetch } from './utils/axios'
 import { axiosFetch } from './utils/axios'
 
 
@@ -154,4 +155,9 @@ export const silverJobClient = hc<SilverJobRoutes>('/', {
 // 首页图标管理客户端
 // 首页图标管理客户端
 export const homeIconClient = hc<HomeIconRoutes>('/', {
 export const homeIconClient = hc<HomeIconRoutes>('/', {
   fetch: axiosFetch,
   fetch: axiosFetch,
-}).api.v1['home-icons']
+}).api.v1['home-icons']
+
+// 银龄智库管理客户端
+export const silverKnowledgeClient = hc<SilverKnowledgeRoutes>('/', {
+  fetch: axiosFetch,
+}).api.v1['silver-knowledges']

+ 5 - 0
src/server/api.ts

@@ -114,6 +114,10 @@ const silverJobApiRoutes = api.route('/api/v1/silver-jobs', silverJobRoutes)
 // 注册首页图标管理路由
 // 注册首页图标管理路由
 const homeIconApiRoutes = api.route('/api/v1/home-icons', homeIconRoutes)
 const homeIconApiRoutes = api.route('/api/v1/home-icons', homeIconRoutes)
 
 
+// 注册银龄智库管理后台路由
+import silverKnowledgeRoutes from './api/silver-knowledges/index'
+const silverKnowledgeApiRoutes = api.route('/api/v1/silver-knowledges', silverKnowledgeRoutes)
+
 export type AuthRoutes = typeof authRoutes
 export type AuthRoutes = typeof authRoutes
 export type CompanyCertificationRoutes = typeof companyCertificationApiRoutes
 export type CompanyCertificationRoutes = typeof companyCertificationApiRoutes
 export type UserRoutes = typeof userRoutes
 export type UserRoutes = typeof userRoutes
@@ -142,5 +146,6 @@ export type SilverTalentsAdminRoutes = typeof silverTalentsAdminApiRoutes
 export type SilverJobRoutes = typeof silverJobApiRoutes
 export type SilverJobRoutes = typeof silverJobApiRoutes
 export type SilverCompaniesRoutes = typeof silverCompaniesApiRoutes
 export type SilverCompaniesRoutes = typeof silverCompaniesApiRoutes
 export type HomeIconRoutes = typeof homeIconApiRoutes
 export type HomeIconRoutes = typeof homeIconApiRoutes
+export type SilverKnowledgeRoutes = typeof silverKnowledgeApiRoutes
 
 
 export default api
 export default api

+ 21 - 0
src/server/api/silver-knowledges/index.ts

@@ -0,0 +1,21 @@
+import { createCrudRoutes } from '@/server/utils/generic-crud.routes';
+import { SilverKnowledge } from '@/server/modules/silver-users/silver-knowledge.entity';
+import { SilverKnowledgeSchema, CreateSilverKnowledgeDto, UpdateSilverKnowledgeDto } from '@/server/modules/silver-users/silver-knowledge.entity';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const silverKnowledgeRoutes = createCrudRoutes({
+  entity: SilverKnowledge,
+  createSchema: CreateSilverKnowledgeDto,
+  updateSchema: UpdateSilverKnowledgeDto,
+  getSchema: SilverKnowledgeSchema,
+  listSchema: SilverKnowledgeSchema,
+  searchFields: ['title', 'content', 'author', 'category'],
+  relations: ['user'],
+  middleware: [authMiddleware],
+  userTracking: {
+    createdByField: 'userId',
+    updatedByField: 'userId'
+  }
+});
+
+export default silverKnowledgeRoutes;

+ 80 - 0
src/server/modules/silver-users/knowledge.entity.ts

@@ -0,0 +1,80 @@
+import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
+import { z } from '@hono/zod-openapi';
+import { User } from './user.entity';
+
+@Entity('silver_knowledges')
+export class SilverKnowledge {
+  @PrimaryGeneratedColumn({ unsigned: true })
+  id!: number;
+
+  @Column({ name: 'title', type: 'varchar', length: 255 })
+  title!: string;
+
+  @Column({ name: 'content', type: 'text' })
+  content!: string;
+
+  @Column({ name: 'category', type: 'varchar', length: 50 })
+  category!: string;
+
+  @Column({ name: 'tags', type: 'text', nullable: true })
+  tags!: string | null;
+
+  @Column({ name: 'author', type: 'varchar', length: 100 })
+  author!: string;
+
+  @Column({ name: 'status', type: 'tinyint', default: 1 })
+  status!: number; // 1: 发布, 0: 草稿
+
+  @Column({ name: 'view_count', type: 'int', default: 0 })
+  viewCount!: number;
+
+  @Column({ name: 'like_count', type: 'int', default: 0 })
+  likeCount!: number;
+
+  @Column({ name: 'user_id', type: 'int', unsigned: true })
+  userId!: number;
+
+  @ManyToOne(() => User)
+  @JoinColumn({ name: 'user_id' })
+  user!: User;
+
+  @CreateDateColumn({ name: 'created_at' })
+  createdAt!: Date;
+
+  @UpdateDateColumn({ name: 'updated_at' })
+  updatedAt!: Date;
+}
+
+// Zod Schema定义
+export const SilverKnowledgeSchema = z.object({
+  id: z.number().int().positive().openapi({ description: '知识ID' }),
+  title: z.string().max(255).openapi({ description: '知识标题', example: '健康生活小贴士' }),
+  content: z.string().openapi({ description: '知识内容', example: '今日分享几个健康生活的小技巧...' }),
+  category: z.string().max(50).openapi({ description: '知识分类', example: '健康养生' }),
+  tags: z.string().nullable().openapi({ description: '标签,逗号分隔', example: '健康,养生,老年人' }),
+  author: z.string().max(100).openapi({ description: '作者', example: '张医生' }),
+  status: z.number().int().min(0).max(1).openapi({ description: '状态(0-草稿,1-发布)', example: 1 }),
+  viewCount: z.number().int().nonnegative().openapi({ description: '浏览次数', example: 100 }),
+  likeCount: z.number().int().nonnegative().openapi({ description: '点赞次数', example: 25 }),
+  userId: z.number().int().positive().openapi({ description: '用户ID', example: 1 }),
+  createdAt: z.date().openapi({ description: '创建时间', example: '2024-01-01T00:00:00Z' }),
+  updatedAt: z.date().openapi({ description: '更新时间', example: '2024-01-01T00:00:00Z' })
+});
+
+export const CreateSilverKnowledgeDto = z.object({
+  title: z.string().max(255).openapi({ description: '知识标题', example: '健康生活小贴士' }),
+  content: z.string().openapi({ description: '知识内容', example: '今日分享几个健康生活的小技巧...' }),
+  category: z.string().max(50).openapi({ description: '知识分类', example: '健康养生' }),
+  tags: z.string().nullable().optional().openapi({ description: '标签,逗号分隔', example: '健康,养生,老年人' }),
+  author: z.string().max(100).openapi({ description: '作者', example: '张医生' }),
+  status: z.coerce.number().int().min(0).max(1).default(1).openapi({ description: '状态(0-草稿,1-发布)', example: 1 })
+});
+
+export const UpdateSilverKnowledgeDto = z.object({
+  title: z.string().max(255).optional().openapi({ description: '知识标题', example: '健康生活小贴士' }),
+  content: z.string().optional().openapi({ description: '知识内容', example: '今日分享几个健康生活的小技巧...' }),
+  category: z.string().max(50).optional().openapi({ description: '知识分类', example: '健康养生' }),
+  tags: z.string().nullable().optional().openapi({ description: '标签,逗号分隔', example: '健康,养生,老年人' }),
+  author: z.string().max(100).optional().openapi({ description: '作者', example: '张医生' }),
+  status: z.coerce.number().int().min(0).max(1).optional().openapi({ description: '状态(0-草稿,1-发布)', example: 1 })
+});

+ 40 - 355
src/server/modules/silver-users/silver-knowledge.entity.ts

@@ -1,395 +1,80 @@
-import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn, OneToMany } from 'typeorm';
-import { UserEntity } from '../users/user.entity';
-import { SilverKnowledgeCategory, SilverKnowledgeCategorySchema } from './silver-knowledge-category.entity';
-import { SilverKnowledgeInteraction } from './silver-knowledge-interaction.entity';
-import { SilverKnowledgeTagRelation } from './silver-knowledge-tag-relation.entity';
+import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
 import { z } from '@hono/zod-openapi';
 import { z } from '@hono/zod-openapi';
-
-export enum KnowledgeType {
-  ARTICLE = 1,      // 文章
-  VIDEO = 2,        // 视频
-  DOCUMENT = 3,     // 文档
-  COURSE = 4,       // 课程
-  EXPERIENCE = 5,   // 经验分享
-  CASE = 6,         // 案例分享
-  RESEARCH = 7      // 研究报告
-}
-
-export enum KnowledgeStatus {
-  DRAFT = 0,        // 草稿
-  PUBLISHED = 1,    // 已发布
-  HIDDEN = 2,       // 已隐藏
-  DELETED = 3,      // 已删除
-  REVIEWING = 4     // 审核中
-}
+import { UserEntity } from '../users/user.entity';
 
 
 @Entity('silver_knowledges')
 @Entity('silver_knowledges')
 export class SilverKnowledge {
 export class SilverKnowledge {
   @PrimaryGeneratedColumn({ unsigned: true })
   @PrimaryGeneratedColumn({ unsigned: true })
   id!: number;
   id!: number;
 
 
-  @Column({ name: 'user_id', type: 'int', unsigned: true })
-  userId!: number;
-
-  @ManyToOne(() => UserEntity, user => user.silverKnowledges)
-  @JoinColumn({ name: 'user_id' })
-  user!: UserEntity;
-
-  @Column({ name: 'category_id', type: 'int', unsigned: true, nullable: true })
-  categoryId!: number | null;
-
-  @ManyToOne(() => SilverKnowledgeCategory, category => category.silverKnowledges)
-  @JoinColumn({ name: 'category_id' })
-  category!: SilverKnowledgeCategory | null;
-
   @Column({ name: 'title', type: 'varchar', length: 255 })
   @Column({ name: 'title', type: 'varchar', length: 255 })
   title!: string;
   title!: string;
 
 
   @Column({ name: 'content', type: 'text' })
   @Column({ name: 'content', type: 'text' })
   content!: string;
   content!: string;
 
 
-  @Column({ name: 'summary', type: 'text', nullable: true })
-  summary!: string | null;
-
-  @Column({ name: 'type', type: 'tinyint', unsigned: true })
-  type!: KnowledgeType;
+  @Column({ name: 'category', type: 'varchar', length: 50 })
+  category!: string;
 
 
   @Column({ name: 'tags', type: 'text', nullable: true })
   @Column({ name: 'tags', type: 'text', nullable: true })
-  tags!: string | null; // JSON array of tags
-
-  @Column({ name: 'cover_image', type: 'varchar', length: 500, nullable: true })
-  coverImage!: string | null;
+  tags!: string | null;
 
 
-  @Column({ name: 'attachments', type: 'text', nullable: true })
-  attachments!: string | null; // JSON array of attachment URLs
+  @Column({ name: 'author', type: 'varchar', length: 100 })
+  author!: string;
 
 
-  @Column({ name: 'status', type: 'tinyint', default: KnowledgeStatus.DRAFT })
-  status!: KnowledgeStatus;
+  @Column({ name: 'status', type: 'tinyint', default: 1 })
+  status!: number; // 1: 发布, 0: 草稿
 
 
-  // 统计字段 - 单个知识统计
-  @Column({ name: 'view_count', type: 'int', unsigned: true, default: 0 })
+  @Column({ name: 'view_count', type: 'int', default: 0 })
   viewCount!: number;
   viewCount!: number;
 
 
-  @Column({ name: 'download_count', type: 'int', unsigned: true, default: 0 })
-  downloadCount!: number;
-
-  @Column({ name: 'like_count', type: 'int', unsigned: true, default: 0 })
+  @Column({ name: 'like_count', type: 'int', default: 0 })
   likeCount!: number;
   likeCount!: number;
 
 
-  @Column({ name: 'comment_count', type: 'int', unsigned: true, default: 0 })
-  commentCount!: number;
-
-  @Column({ name: 'favorite_count', type: 'int', unsigned: true, default: 0 })
-  favoriteCount!: number;
-
-  @Column({ name: 'share_count', type: 'int', unsigned: true, default: 0 })
-  shareCount!: number;
-
-  @Column({ name: 'read_count', type: 'int', unsigned: true, default: 0 })
-  readCount!: number; // 更准确的阅读统计
-
-  // 推荐和排序
-  @Column({ name: 'is_featured', type: 'tinyint', default: 0 })
-  isFeatured!: number;
-
-  @Column({ name: 'featured_at', type: 'timestamp', nullable: true })
-  featuredAt!: Date | null;
-
-  @Column({ name: 'sort_order', type: 'int', default: 0 })
-  sortOrder!: number;
-
-  // 元数据
-  @Column({ name: 'keywords', type: 'text', nullable: true })
-  keywords!: string | null; // 用于搜索优化的关键词
-
-  @Column({ name: 'source', type: 'varchar', length: 255, nullable: true })
-  source!: string | null; // 知识来源
-
-  @Column({ name: 'author', type: 'varchar', length: 100, nullable: true })
-  author!: string | null; // 原作者
+  @Column({ name: 'user_id', type: 'int', unsigned: true })
+  userId!: number;
 
 
-  @Column({ name: 'duration', type: 'int', unsigned: true, nullable: true })
-  duration!: number | null; // 视频时长(秒)或阅读时长(分钟)
+  @ManyToOne(() => UserEntity)
+  @JoinColumn({ name: 'user_id' })
+  user!: UserEntity;
 
 
-  // 时间戳
   @CreateDateColumn({ name: 'created_at' })
   @CreateDateColumn({ name: 'created_at' })
   createdAt!: Date;
   createdAt!: Date;
 
 
   @UpdateDateColumn({ name: 'updated_at' })
   @UpdateDateColumn({ name: 'updated_at' })
   updatedAt!: Date;
   updatedAt!: Date;
-
-  @Column({ name: 'published_at', type: 'timestamp', nullable: true })
-  publishedAt!: Date | null;
-
-  // 用户跟踪
-  @Column({ name: 'created_by', type: 'int', nullable: true })
-  createdBy!: number | null;
-
-  @Column({ name: 'updated_by', type: 'int', nullable: true })
-  updatedBy!: number | null;
-
-  @OneToMany(() => SilverKnowledgeInteraction, interaction => interaction.knowledge)
-  silverKnowledgeInteractions!: SilverKnowledgeInteraction[];
-
-  @OneToMany(() => SilverKnowledgeTagRelation, tagRelation => tagRelation.knowledge)
-  silverKnowledgeTagRelations!: SilverKnowledgeTagRelation[];
 }
 }
 
 
 // Zod Schema定义
 // Zod Schema定义
 export const SilverKnowledgeSchema = z.object({
 export const SilverKnowledgeSchema = z.object({
-  id: z.number().int().positive().openapi({
-    description: '知识ID',
-    example: 1
-  }),
-  userId: z.number().int().positive().openapi({
-    description: '发布用户ID',
-    example: 1
-  }),
-  user: z.object({
-    id: z.number().int().positive(),
-    username: z.string(),
-    nickname: z.string().nullable(),
-    name: z.string().nullable(),
-    avatar: z.string().nullable()
-  }).optional().openapi({
-    description: '发布用户',
-    example: {
-      id: 1,
-      username: 'user123',
-      nickname: '张三'
-    }
-  }),
-  categoryId: z.number().int().positive().optional().nullable().openapi({
-    description: '分类ID',
-    example: 5
-  }),
-  category: SilverKnowledgeCategorySchema.optional().nullable().openapi({
-    description: '知识分类',
-    example: {
-      id: 5,
-      name: '健康养生'
-    }
-  }),
-  title: z.string().max(255).openapi({
-    description: '知识标题',
-    example: '老年人健康饮食指南'
-  }),
-  content: z.string().openapi({
-    description: '知识内容',
-    example: '老年人健康饮食需要注意以下几点...'
-  }),
-  summary: z.string().optional().openapi({
-    description: '知识摘要',
-    example: '本指南详细介绍了老年人健康饮食的科学方法...'
-  }),
-  type: z.coerce.number().int().min(1).max(7).openapi({
-    description: '知识类型 1:文章 2:视频 3:文档 4:课程 5:经验分享 6:案例分享 7:研究报告',
-    example: 1
-  }),
-  tags: z.string().optional().nullable().openapi({
-    description: '标签(JSON数组)',
-    example: '["健康养生", "老年人", "饮食"]'
-  }),
-  coverImage: z.string().max(500).url().optional().nullable().openapi({
-    description: '封面图片URL',
-    example: 'https://example.com/cover.jpg'
-  }),
-  attachments: z.string().optional().nullable().openapi({
-    description: '附件URL(JSON数组)',
-    example: '["https://example.com/attach1.pdf"]'
-  }),
-  status: z.coerce.number().int().min(0).max(4).default(0).openapi({
-    description: '状态 0:草稿 1:已发布 2:已隐藏 3:已删除 4:审核中',
-    example: 1
-  }),
-  viewCount: z.number().int().min(0).default(0).openapi({
-    description: '浏览次数',
-    example: 1234
-  }),
-  downloadCount: z.number().int().min(0).default(0).openapi({
-    description: '下载次数',
-    example: 567
-  }),
-  likeCount: z.number().int().min(0).default(0).openapi({
-    description: '点赞次数',
-    example: 234
-  }),
-  commentCount: z.number().int().min(0).default(0).openapi({
-    description: '评论次数',
-    example: 89
-  }),
-  favoriteCount: z.number().int().min(0).default(0).openapi({
-    description: '收藏次数',
-    example: 156
-  }),
-  shareCount: z.number().int().min(0).default(0).openapi({
-    description: '分享次数',
-    example: 78
-  }),
-  readCount: z.number().int().min(0).default(0).openapi({
-    description: '阅读次数',
-    example: 2345
-  }),
-  isFeatured: z.coerce.number().int().min(0).max(1).default(0).openapi({
-    description: '是否推荐 0:否 1:是',
-    example: 1
-  }),
-  featuredAt: z.date().optional().nullable().openapi({
-    description: '推荐时间',
-    example: '2024-01-01T00:00:00Z'
-  }),
-  sortOrder: z.number().int().default(0).openapi({
-    description: '排序顺序',
-    example: 1
-  }),
-  keywords: z.string().optional().nullable().openapi({
-    description: '搜索关键词',
-    example: '健康,养生,老年人'
-  }),
-  source: z.string().max(255).optional().nullable().openapi({
-    description: '知识来源',
-    example: '中国老年学会'
-  }),
-  author: z.string().max(100).optional().nullable().openapi({
-    description: '原作者',
-    example: '张医生'
-  }),
-  duration: z.number().int().positive().optional().nullable().openapi({
-    description: '时长(秒/分钟)',
-    example: 15
-  }),
-  createdAt: z.date().openapi({
-    description: '创建时间',
-    example: '2024-01-01T00:00:00Z'
-  }),
-  updatedAt: z.date().openapi({
-    description: '更新时间',
-    example: '2024-01-01T00:00:00Z'
-  }),
-  publishedAt: z.date().optional().nullable().openapi({
-    description: '发布时间',
-    example: '2024-01-01T00:00:00Z'
-  })
+  id: z.number().int().positive().openapi({ description: '知识ID' }),
+  title: z.string().max(255).openapi({ description: '知识标题', example: '健康生活小贴士' }),
+  content: z.string().openapi({ description: '知识内容', example: '今日分享几个健康生活的小技巧...' }),
+  category: z.string().max(50).openapi({ description: '知识分类', example: '健康养生' }),
+  tags: z.string().nullable().openapi({ description: '标签,逗号分隔', example: '健康,养生,老年人' }),
+  author: z.string().max(100).openapi({ description: '作者', example: '张医生' }),
+  status: z.number().int().min(0).max(1).openapi({ description: '状态(0-草稿,1-发布)', example: 1 }),
+  viewCount: z.number().int().nonnegative().openapi({ description: '浏览次数', example: 100 }),
+  likeCount: z.number().int().nonnegative().openapi({ description: '点赞次数', example: 25 }),
+  userId: z.number().int().positive().openapi({ description: '用户ID', example: 1 }),
+  createdAt: z.date().openapi({ description: '创建时间', example: '2024-01-01T00:00:00Z' }),
+  updatedAt: z.date().openapi({ description: '更新时间', example: '2024-01-01T00:00:00Z' })
 });
 });
 
 
 export const CreateSilverKnowledgeDto = z.object({
 export const CreateSilverKnowledgeDto = z.object({
-  userId: z.coerce.number().int().positive().openapi({
-    description: '发布用户ID',
-    example: 1
-  }),
-  categoryId: z.number().int().positive().optional().openapi({
-    description: '分类ID',
-    example: 5
-  }),
-  title: z.string().max(255).openapi({
-    description: '知识标题',
-    example: '老年人健康饮食指南'
-  }),
-  content: z.string().openapi({
-    description: '知识内容',
-    example: '老年人健康饮食需要注意以下几点...'
-  }),
-  summary: z.string().optional().openapi({
-    description: '知识摘要',
-    example: '本指南详细介绍了老年人健康饮食的科学方法...'
-  }),
-  type: z.coerce.number().int().min(1).max(7).openapi({
-    description: '知识类型',
-    example: 1
-  }),
-  tags: z.string().optional().openapi({
-    description: '标签(JSON数组)',
-    example: '["健康养生", "老年人", "饮食"]'
-  }),
-  coverImage: z.string().max(500).url().optional().openapi({
-    description: '封面图片URL',
-    example: 'https://example.com/cover.jpg'
-  }),
-  attachments: z.string().optional().openapi({
-    description: '附件URL(JSON数组)',
-    example: '["https://example.com/attach1.pdf"]'
-  }),
-  keywords: z.string().optional().openapi({
-    description: '搜索关键词',
-    example: '健康,养生,老年人'
-  }),
-  source: z.string().max(255).optional().openapi({
-    description: '知识来源',
-    example: '中国老年学会'
-  }),
-  author: z.string().max(100).optional().openapi({
-    description: '原作者',
-    example: '张医生'
-  }),
-  duration: z.number().int().positive().optional().openapi({
-    description: '时长(秒/分钟)',
-    example: 15
-  })
+  title: z.string().max(255).openapi({ description: '知识标题', example: '健康生活小贴士' }),
+  content: z.string().openapi({ description: '知识内容', example: '今日分享几个健康生活的小技巧...' }),
+  category: z.string().max(50).openapi({ description: '知识分类', example: '健康养生' }),
+  tags: z.string().nullable().optional().openapi({ description: '标签,逗号分隔', example: '健康,养生,老年人' }),
+  author: z.string().max(100).openapi({ description: '作者', example: '张医生' }),
+  status: z.coerce.number().int().min(0).max(1).default(1).openapi({ description: '状态(0-草稿,1-发布)', example: 1 })
 });
 });
 
 
 export const UpdateSilverKnowledgeDto = z.object({
 export const UpdateSilverKnowledgeDto = z.object({
-  categoryId: z.number().int().positive().optional().openapi({
-    description: '分类ID',
-    example: 5
-  }),
-  title: z.string().max(255).optional().openapi({
-    description: '知识标题',
-    example: '老年人健康饮食指南'
-  }),
-  content: z.string().optional().openapi({
-    description: '知识内容',
-    example: '老年人健康饮食需要注意以下几点...'
-  }),
-  summary: z.string().optional().openapi({
-    description: '知识摘要',
-    example: '本指南详细介绍了老年人健康饮食的科学方法...'
-  }),
-  type: z.coerce.number().int().min(1).max(7).optional().openapi({
-    description: '知识类型',
-    example: 1
-  }),
-  tags: z.string().optional().openapi({
-    description: '标签(JSON数组)',
-    example: '["健康养生", "老年人", "饮食"]'
-  }),
-  coverImage: z.string().max(500).url().optional().openapi({
-    description: '封面图片URL',
-    example: 'https://example.com/cover.jpg'
-  }),
-  attachments: z.string().optional().openapi({
-    description: '附件URL(JSON数组)',
-    example: '["https://example.com/attach1.pdf"]'
-  }),
-  status: z.coerce.number().int().min(0).max(4).optional().openapi({
-    description: '状态',
-    example: 1
-  }),
-  keywords: z.string().optional().openapi({
-    description: '搜索关键词',
-    example: '健康,养生,老年人'
-  }),
-  source: z.string().max(255).optional().openapi({
-    description: '知识来源',
-    example: '中国老年学会'
-  }),
-  author: z.string().max(100).optional().openapi({
-    description: '原作者',
-    example: '张医生'
-  }),
-  duration: z.number().int().positive().optional().openapi({
-    description: '时长(秒/分钟)',
-    example: 15
-  }),
-  isFeatured: z.coerce.number().int().min(0).max(1).optional().openapi({
-    description: '是否推荐',
-    example: 1
-  }),
-  featuredAt: z.date().optional().nullable().openapi({
-    description: '推荐时间',
-    example: '2024-01-01T00:00:00Z'
-  }),
-  sortOrder: z.number().int().optional().openapi({
-    description: '排序顺序',
-    example: 1
-  })
+  title: z.string().max(255).optional().openapi({ description: '知识标题', example: '健康生活小贴士' }),
+  content: z.string().optional().openapi({ description: '知识内容', example: '今日分享几个健康生活的小技巧...' }),
+  category: z.string().max(50).optional().openapi({ description: '知识分类', example: '健康养生' }),
+  tags: z.string().nullable().optional().openapi({ description: '标签,逗号分隔', example: '健康,养生,老年人' }),
+  author: z.string().max(100).optional().openapi({ description: '作者', example: '张医生' }),
+  status: z.coerce.number().int().min(0).max(1).optional().openapi({ description: '状态(0-草稿,1-发布)', example: 1 })
 });
 });

+ 0 - 321
src/server/modules/silver-users/silver-knowledge.service.ts

@@ -1,330 +1,9 @@
 import { GenericCrudService } from '@/server/utils/generic-crud.service';
 import { GenericCrudService } from '@/server/utils/generic-crud.service';
 import { DataSource } from 'typeorm';
 import { DataSource } from 'typeorm';
 import { SilverKnowledge } from './silver-knowledge.entity';
 import { SilverKnowledge } from './silver-knowledge.entity';
-import { SilverKnowledgeStats } from './silver-knowledge-stats.entity';
-import { SilverKnowledgeInteraction } from './silver-knowledge-interaction.entity';
-import { SilverKnowledgeCategory } from './silver-knowledge-category.entity';
-import { SilverKnowledgeTag } from './silver-knowledge-tag.entity';
-import { SilverKnowledgeTagRelation } from './silver-knowledge-tag-relation.entity';
-import { InteractionType } from './silver-knowledge-interaction.entity';
 
 
 export class SilverKnowledgeService extends GenericCrudService<SilverKnowledge> {
 export class SilverKnowledgeService extends GenericCrudService<SilverKnowledge> {
   constructor(dataSource: DataSource) {
   constructor(dataSource: DataSource) {
     super(dataSource, SilverKnowledge);
     super(dataSource, SilverKnowledge);
   }
   }
-
-  /**
-   * 获取用户的知识库统计信息
-   */
-  async getUserStats(userId: number): Promise<SilverKnowledgeStats | null> {
-    const statsRepository = this.dataSource.getRepository(SilverKnowledgeStats);
-    return statsRepository.findOne({ where: { userId } });
-  }
-
-  /**
-   * 获取知识排行榜
-   */
-  async getKnowledgeRanking(limit: number = 10): Promise<any[]> {
-    return this.dataSource.query(`
-      SELECT 
-        k.id,
-        k.title,
-        k.cover_image as coverImage,
-        k.download_count as downloadCount,
-        k.read_count as readCount,
-        k.like_count as likeCount,
-        k.favorite_count as favoriteCount,
-        k.created_at as createdAt,
-        p.real_name as authorName,
-        p.avatar_url as authorAvatar
-      FROM silver_knowledges k
-      JOIN silver_user_profiles p ON k.user_id = p.user_id
-      WHERE k.status = 1
-      ORDER BY k.download_count DESC, k.read_count DESC
-      LIMIT ?
-    `, [limit]);
-  }
-
-  /**
-   * 获取用户排行榜
-   */
-  async getUserRanking(limit: number = 10): Promise<any[]> {
-    return this.dataSource.query(`
-      SELECT 
-        p.user_id as userId,
-        p.real_name as realName,
-        p.avatar_url as avatarUrl,
-        s.total_knowledges as totalKnowledges,
-        s.total_downloads as totalDownloads,
-        s.total_reads as totalReads,
-        s.total_favorites as totalFavorites,
-        s.ranking_score as rankingScore,
-        s.download_rank as downloadRank,
-        s.read_rank as readRank,
-        s.favorite_rank as favoriteRank
-      FROM silver_knowledge_stats s
-      JOIN silver_user_profiles p ON s.user_id = p.user_id
-      WHERE s.total_knowledges > 0
-      ORDER BY s.ranking_score DESC
-      LIMIT ?
-    `, [limit]);
-  }
-
-  /**
-   * 记录用户交互
-   */
-  async recordInteraction(userId: number, knowledgeId: number, type: InteractionType, content?: string): Promise<void> {
-    const interactionRepository = this.dataSource.getRepository(SilverKnowledgeInteraction);
-    
-    await interactionRepository.save({
-      userId,
-      knowledgeId,
-      type,
-      content: content || null
-    });
-
-    // 更新知识统计
-    const knowledge = await this.getById(knowledgeId);
-    if (knowledge) {
-      const fieldMap = {
-        [InteractionType.VIEW]: 'viewCount',
-        [InteractionType.DOWNLOAD]: 'downloadCount',
-        [InteractionType.LIKE]: 'likeCount',
-        [InteractionType.COMMENT]: 'commentCount',
-        [InteractionType.FAVORITE]: 'favoriteCount',
-        [InteractionType.SHARE]: 'shareCount'
-      };
-
-      const field = fieldMap[type];
-      if (field) {
-        await this.repository.increment({ id: knowledgeId }, field, 1);
-      }
-    }
-  }
-
-  /**
-   * 获取知识详情,包含作者信息和统计
-   */
-  async getKnowledgeDetail(id: number): Promise<any> {
-    const [result] = await this.dataSource.query(`
-      SELECT 
-        k.*,
-        p.real_name as authorName,
-        p.avatar_url as authorAvatar,
-        p.organization as authorOrganization,
-        c.name as categoryName,
-        c.icon as categoryIcon
-      FROM silver_knowledges k
-      LEFT JOIN silver_user_profiles p ON k.user_id = p.user_id
-      LEFT JOIN silver_knowledge_categories c ON k.category_id = c.id
-      WHERE k.id = ?
-    `, [id]);
-    
-    return result;
-  }
-
-  /**
-   * 获取知识列表,支持分页、搜索和筛选
-   */
-  async getKnowledgeList(options: {
-    page?: number;
-    pageSize?: number;
-    keyword?: string;
-    categoryId?: number;
-    type?: number;
-    userId?: number;
-    status?: number;
-    isFeatured?: boolean;
-    sortBy?: string;
-    sortOrder?: 'ASC' | 'DESC';
-  }): Promise<[SilverKnowledge[], number]> {
-    const {
-      page = 1,
-      pageSize = 10,
-      keyword,
-      categoryId,
-      type,
-      userId,
-      status = 1, // 默认只显示已发布
-      isFeatured,
-      sortBy = 'createdAt',
-      sortOrder = 'DESC'
-    } = options;
-
-    const queryBuilder = this.repository.createQueryBuilder('k')
-      .leftJoinAndSelect('k.category', 'c')
-      .leftJoinAndSelect('k.userProfile', 'p');
-
-    // 关键词搜索
-    if (keyword) {
-      queryBuilder.andWhere(
-        '(k.title LIKE :keyword OR k.content LIKE :keyword OR k.keywords LIKE :keyword)',
-        { keyword: `%${keyword}%` }
-      );
-    }
-
-    // 分类筛选
-    if (categoryId) {
-      queryBuilder.andWhere('k.categoryId = :categoryId', { categoryId });
-    }
-
-    // 类型筛选
-    if (type) {
-      queryBuilder.andWhere('k.type = :type', { type });
-    }
-
-    // 用户筛选
-    if (userId) {
-      queryBuilder.andWhere('k.userId = :userId', { userId });
-    }
-
-    // 状态筛选
-    if (status !== undefined) {
-      queryBuilder.andWhere('k.status = :status', { status });
-    }
-
-    // 推荐筛选
-    if (isFeatured) {
-      queryBuilder.andWhere('k.isFeatured = 1');
-    }
-
-    // 排序
-    queryBuilder.orderBy(`k.${sortBy}`, sortOrder);
-
-    // 分页
-    queryBuilder.skip((page - 1) * pageSize).take(pageSize);
-
-    return queryBuilder.getManyAndCount();
-  }
-
-  /**
-   * 获取热门知识
-   */
-  async getHotKnowledges(limit: number = 10): Promise<SilverKnowledge[]> {
-    return this.repository.find({
-      where: { status: 1 },
-      order: {
-        downloadCount: 'DESC',
-        readCount: 'DESC',
-        likeCount: 'DESC'
-      },
-      take: limit
-    });
-  }
-
-  /**
-   * 获取推荐知识
-   */
-  async getFeaturedKnowledges(limit: number = 10): Promise<SilverKnowledge[]> {
-    return this.repository.find({
-      where: { status: 1, isFeatured: 1 },
-      order: {
-        featuredAt: 'DESC',
-        createdAt: 'DESC'
-      },
-      take: limit
-    });
-  }
-}
-
-export class SilverKnowledgeCategoryService extends GenericCrudService<SilverKnowledgeCategory> {
-  constructor(dataSource: DataSource) {
-    super(dataSource, SilverKnowledgeCategory);
-  }
-
-  /**
-   * 获取分类树
-   */
-  async getCategoryTree(): Promise<SilverKnowledgeCategory[]> {
-    const categories = await this.repository.find({
-      where: { isActive: 1 },
-      order: { sortOrder: 'ASC', createdAt: 'DESC' }
-    });
-
-    const buildTree = (parentId: number | null): any[] => {
-      return categories
-        .filter(c => c.parentId === parentId)
-        .map(c => ({
-          ...c,
-          children: buildTree(c.id)
-        }));
-    };
-
-    return buildTree(null);
-  }
-
-  /**
-   * 获取分类及其知识数量
-   */
-  async getCategoriesWithCount(): Promise<any[]> {
-    return this.dataSource.query(`
-      SELECT 
-        c.*,
-        COUNT(k.id) as knowledgeCount
-      FROM silver_knowledge_categories c
-      LEFT JOIN silver_knowledges k ON c.id = k.category_id AND k.status = 1
-      WHERE c.is_active = 1
-      GROUP BY c.id
-      ORDER BY c.sort_order, c.created_at
-    `);
-  }
-}
-
-export class SilverKnowledgeTagService extends GenericCrudService<SilverKnowledgeTag> {
-  constructor(dataSource: DataSource) {
-    super(dataSource, SilverKnowledgeTag);
-  }
-
-  /**
-   * 获取热门标签
-   */
-  async getHotTags(limit: number = 20): Promise<SilverKnowledgeTag[]> {
-    return this.repository.find({
-      where: { isActive: 1 },
-      order: { usageCount: 'DESC', createdAt: 'DESC' },
-      take: limit
-    });
-  }
-}
-
-export class SilverKnowledgeStatsService extends GenericCrudService<SilverKnowledgeStats> {
-  constructor(dataSource: DataSource) {
-    super(dataSource, SilverKnowledgeStats);
-  }
-
-  /**
-   * 计算并更新用户排名
-   */
-  async calculateUserRankings(): Promise<void> {
-    await this.dataSource.query(`
-      UPDATE silver_knowledge_stats s
-      SET s.ranking_score = (
-        s.total_downloads * 0.4 + 
-        s.total_reads * 0.3 + 
-        s.total_favorites * 0.3
-      ),
-      s.last_calculated = NOW();
-    `);
-
-    await this.dataSource.query(`
-      SET @rank := 0;
-      UPDATE silver_knowledge_stats s
-      JOIN (
-        SELECT id, @rank := @rank + 1 as new_rank
-        FROM silver_knowledge_stats
-        WHERE total_knowledges > 0
-        ORDER BY ranking_score DESC
-      ) r ON s.id = r.id
-      SET s.download_rank = r.new_rank,
-          s.read_rank = r.new_rank,
-          s.favorite_rank = r.new_rank;
-    `);
-
-    await this.dataSource.query(`
-      UPDATE silver_user_profiles p
-      JOIN silver_knowledge_stats s ON p.user_id = s.user_id
-      SET p.knowledge_ranking = s.download_rank,
-          p.knowledge_ranking_score = s.ranking_score;
-    `);
-  }
 }
 }