我是人,我做了悬赏中心,可以进行悬赏哦

Change-Id: I845de799a633be286a6fadee2b7aa533238c3652
diff --git a/src/locales/zh-CN/system/menu.ts b/src/locales/zh-CN/system/menu.ts
index 8a01e58..b06ec93 100644
--- a/src/locales/zh-CN/system/menu.ts
+++ b/src/locales/zh-CN/system/menu.ts
@@ -19,4 +19,7 @@
 	'system.menu.update_by': '更新者',
 	'system.menu.update_time': '更新时间',
 	'system.menu.remark': '备注',
+	'system.menu.bounty': '悬赏管理',        // 父级菜单名称
+  	'system.menu.bounty.publish': '发布悬赏',  // 子菜单1
+  	'system.menu.bounty.reply': '回复悬赏'
 };
diff --git a/src/pages/Bounty/BountyManage.tsx b/src/pages/Bounty/BountyManage.tsx
new file mode 100644
index 0000000..609e6c6
--- /dev/null
+++ b/src/pages/Bounty/BountyManage.tsx
@@ -0,0 +1,70 @@
+import React, { useState, useEffect } from 'react';
+import { Table, Button, message, Space } from 'antd';
+import axios from 'axios'; // 新增 axios 导入
+
+const BountyManage: React.FC = () => {
+  const [dataSource, setDataSource] = useState<API.Bounty[]>([]);
+
+  // 加载悬赏列表(修改请求方式)
+  useEffect(() => {
+    const loadBountyList = async () => {
+      try {
+        const res = await axios.get('/api/bounties', { params: { status: [0, 1, 2].join(',') } }); // 替换 get 为 axios.get
+        if (res.data.code === 200) {
+          setDataSource(res.data.data.records || []);
+        }
+      } catch (err) {
+        message.error('加载悬赏列表失败');
+      }
+    };
+    loadBountyList();
+  }, []);
+
+  // 操作列(修改请求方式)
+  const handleClose = async (id: number) => {
+    try {
+      //这个接口哪来的??
+      const res = await axios.post('/api/bounties/close', { id }); // 替换 post 为 axios.post
+      if (res.data.code === 200) {
+        message.success('悬赏已关闭');
+        setDataSource(dataSource.map(item => item.id === id ? { ...item, status: 2 } : item));
+      }
+    } catch (err) {
+      message.error('关闭失败');
+    }
+  };
+
+  const columns = [
+    { title: '我是manage标题', dataIndex: 'title' },
+    { title: '奖励', dataIndex: 'reward' },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      // 显式声明 status 为 number 类型
+      render: (status: number) => status === 0 ? '进行中' : status === 1 ? '已完成' : '已关闭'
+    },
+    {
+      title: '操作',
+      // 显式声明 record 为 API.Bounty 类型(需确保 API.Bounty 已在 types.d.ts 中定义)
+      render: (value: unknown, record: API.Bounty) => (
+        <Space>
+          <Button type="link" onClick={() => handleClose(record.id)}>关闭</Button>
+        </Space>
+      )
+    }
+  ];
+
+  return (
+    <div className="page-container">
+      <h2>悬赏管理</h2>
+      <Table
+        dataSource={dataSource}
+        columns={columns}
+        rowKey="id"
+        pagination={{ pageSize: 10 }}
+      />
+    </div>
+  );
+};
+
+export default BountyManage;
diff --git a/src/pages/Bounty/BountyPublish.tsx b/src/pages/Bounty/BountyPublish.tsx
new file mode 100644
index 0000000..b2d121b
--- /dev/null
+++ b/src/pages/Bounty/BountyPublish.tsx
@@ -0,0 +1,116 @@
+import React from 'react';
+import { Form, Input, InputNumber, DatePicker, Button, message } from 'antd';
+import axios from 'axios';
+import { publishBounty } from '@/services/bounty/bounty'; // 新增 axios 导入
+
+interface BountyPublishProps {
+  onSuccess?: () => void; // 提交成功回调
+  onCancel?: () => void;  // 取消操作回调
+}
+
+
+
+const BountyPublish: React.FC<BountyPublishProps> = ({ onSuccess, onCancel }) => {
+  const [form] = Form.useForm();
+
+  // ✅ 替换 axios 请求为服务方法调用
+  const handleSubmit = async (values: {
+    title: string;
+    description: string;
+    reward: number;
+    deadline: string;
+  }) => {
+    try {
+      // 使用服务层方法
+      const res = await publishBounty(values);
+
+      if (res) {
+        //message.success('悬赏发布成功!');
+        form.resetFields();
+        onSuccess?.();
+      } else {
+        message.error(res.msg || '发布失败,请重试');
+      }
+    } catch (err) {
+      console.error('发布失败:', err);
+      message.error('网络请求失败,请检查网络');
+    }
+  };
+
+
+  return (
+    <div className="page-container">
+      <h2>发布新悬赏</h2>
+      <Form
+        form={form}
+        layout="vertical"
+        onFinish={handleSubmit}
+        requiredMark="optional"
+        style={{ maxWidth: 600 }}
+      >
+        {/* 标题 */}
+        <Form.Item
+          name="title"
+          label="悬赏标题"
+          rules={[{ required: true, message: '请输入悬赏标题' }]}
+        >
+          <Input placeholder="请输入悬赏标题" />
+        </Form.Item>
+
+        {/* 描述 */}
+        <Form.Item
+          name="description"
+          label="悬赏描述"
+          rules={[{ required: true, message: '请输入悬赏描述' }]}
+        >
+          <Input.TextArea rows={4} placeholder="请输入悬赏描述" />
+        </Form.Item>
+
+        {/* 奖励 */}
+        <Form.Item
+          name="reward"
+          label="悬赏奖励"
+          rules={[
+            { required: true, message: '请输入奖励数值' },
+            { type: 'number', min: 1, message: '奖励必须大于0' },
+          ]}
+        >
+          <InputNumber min={1} placeholder="请输入奖励数值" style={{ width: '100%' }} />
+        </Form.Item>
+
+        {/* 截止时间 */}
+        <Form.Item
+          name="deadline"
+          label="截止时间"
+          rules={[{ required: true, message: '请选择截止时间' }]}
+        >
+          <DatePicker
+            showTime
+            format="YYYY-MM-DD HH:mm:ss"
+            placeholder="选择截止时间"
+            style={{ width: '100%' }}
+          />
+        </Form.Item>
+
+        {/* 提交按钮 */}
+        <Form.Item>
+          <Button type="primary" htmlType="submit">
+            发布悬赏
+          </Button>
+          <Button
+            type="default"
+            onClick={() => {
+              form.resetFields();
+              onCancel?.(); // 🔥 取消时触发回调
+            }}
+            style={{ marginLeft: 16 }}
+          >
+            重置
+          </Button>
+        </Form.Item>
+      </Form>
+    </div>
+  );
+};
+
+export default BountyPublish;
diff --git a/src/pages/Bounty/BountyReply.tsx b/src/pages/Bounty/BountyReply.tsx
new file mode 100644
index 0000000..c347686
--- /dev/null
+++ b/src/pages/Bounty/BountyReply.tsx
@@ -0,0 +1,154 @@
+import React, { useState, useEffect } from 'react';
+import { Form, Input, Select, Button, Upload, message } from 'antd';
+import axios from 'axios'; // 新增 axios 导入
+import { submitBountyReply, uploadBountyAttachment } from '@/services/bounty/bounty'; // ✅ 新增导入上传接口
+const { Option } = Select;
+interface BountyReplyProps {
+  bountyId: number;    // 需要回复的悬赏ID
+  onSuccess?: () => void; // 提交成功回调
+  onCancel?: () => void;  // 取消操作回调
+}
+
+const BountyReply: React.FC<BountyReplyProps> = ({
+                                                   bountyId,
+                                                   onSuccess,
+                                                   onCancel
+                                                 }) => {
+  const [form] = Form.useForm();
+  const [bountyList, setBountyList] = useState<{ id: number; title: string }[]>([]);
+  const [uploading, setUploading] = useState(false); // ✅ 新增上传状态
+  // 加载可回复的悬赏列表(修改请求方式)
+  useEffect(() => {
+    const loadBountyList = async () => {
+      try {
+        const res = await axios.get('/api/bounties', { params: { status: 0 } }); // 替换 get 为 axios.get
+        if (res.data.code === 200) {
+          setBountyList(res.data.data.records || []);
+        }
+      } catch (err) {
+        message.error('加载悬赏列表失败');
+      }
+    };
+    loadBountyList();
+  }, []);
+
+  // 附件上传逻辑(修改请求方式)
+  const handleUpload = async (file: File) => {
+    try {
+      setUploading(true);
+      // 1. 上传文件获取路径
+      const filePath = await uploadBountyAttachment(file);
+      // 2. 保存文件对象到表单(用于后续提交)
+      form.setFieldsValue({ attachment: filePath, file }); // ✅ 同时保存路径和文件对象
+      return { url: filePath };
+    } catch (err) {
+      message.error('上传失败');
+      return { error: new Error('上传失败') };
+    } finally {
+      setUploading(false);
+    }
+  };
+
+  // 提交回复逻辑(修改请求方式)
+  // ✅ 替换 axios 请求为服务方法调用
+  const handleSubmit = async (values: { content: string; attachment?: string }) => {
+    try {
+      // 从表单获取文件对象(需在 handleUpload 中保存)
+      const file = form.getFieldValue('file'); // ✅ 获取文件对象
+
+      const res = await submitBountyReply({
+        bountyId,
+        ...values,
+        file, // ✅ 传递文件对象
+      });
+
+      if (res?.code === 200) {
+        message.success('提交成功');
+        form.resetFields();
+        onSuccess?.();
+      } else {
+        throw new Error('接口异常: ' + JSON.stringify(res));
+      }
+    } catch (err) {
+      console.error('提交失败:', err);
+      message.error('提交失败,请重试');
+    }
+  };
+
+  // @ts-ignore
+  // @ts-ignore
+  // @ts-ignore
+  // @ts-ignore
+  // @ts-ignore
+  // @ts-ignore
+  return (
+    <div className="page-container">
+      <h2>回复悬赏</h2>
+      <Form
+        form={form}
+        layout="vertical"
+        onFinish={handleSubmit}
+        requiredMark="optional"
+        style={{ maxWidth: 600 }}
+      >
+        {/* 选择悬赏(默认选中传入的bountyId) */}
+        <Form.Item
+          name="bountyId"
+          label="选择悬赏"
+          rules={[{ required: true, message: '请选择要回复的悬赏' }]}
+          initialValue={bountyId}
+        >
+          <Select disabled={!!bountyId} placeholder="请选择要回复的悬赏">
+            {bountyList.map((item) => (
+              <Option key={item.id} value={item.id}>
+                {item.title}
+              </Option>
+            ))}
+          </Select>
+        </Form.Item>
+
+        {/* 回复内容(保持原有逻辑) */}
+        <Form.Item
+          name="content"
+          label="回复内容"
+          rules={[{ required: true, message: '请输入回复内容' }]}
+        >
+          <Input.TextArea rows={4} placeholder="请输入回复内容" />
+        </Form.Item>
+
+        {/* 附件上传(保持原有逻辑) */}
+        <Form.Item name="attachment" label="附件上传">
+          <Upload
+            customRequest={({ file, onSuccess, onError }) => {
+              handleUpload(file as File); // 显式断言 file 为 File 类型
+            }} // 使用服务层上传
+            maxCount={1}
+            accept=".doc,.docx,.pdf,.zip"
+            showUploadList={false} // 隐藏默认上传列表
+          >
+            <Button loading={uploading}>上传附件</Button>
+          </Upload>
+        </Form.Item>
+
+        {/* 提交按钮 */}
+        <Form.Item>
+          <Button type="primary" htmlType="submit">
+            提交回复
+          </Button>
+          <Button
+            type="default"
+            onClick={() => {
+              form.resetFields();
+              onCancel?.(); // 🔥 取消时触发回调
+            }}
+            style={{ marginLeft: 16 }}
+          >
+            取消
+          </Button>
+        </Form.Item>
+      </Form>
+    </div>
+  );
+};
+
+export default BountyReply;
diff --git a/src/pages/Bounty/Detail.tsx b/src/pages/Bounty/Detail.tsx
new file mode 100644
index 0000000..41a25df
--- /dev/null
+++ b/src/pages/Bounty/Detail.tsx
@@ -0,0 +1,231 @@
+import React, { useState, useEffect } from 'react';
+import { Button, Descriptions, List, message, Tag } from 'antd';
+import axios from 'axios'; // 新增 axios 导入
+import { useParams } from 'umi';
+
+import { getBountyDetail, getProfile } from '@/services/bounty/bounty';
+import { downloadAttachment,adoptSubmission } from '@/services/bounty/bounty';
+
+
+
+const BountyDetail: React.FC = () => {
+  const [bounty, setBounty] = useState<API.BountyDetail>({} as API.BountyDetail);
+  const [loading, setLoading] = useState(false); // 新增加载状态
+  const [currentUserId, setCurrentUserId] = useState<number | null>(null);
+
+  console.log('当前用户id:',currentUserId);
+  console.log('悬赏发布用户id:',bounty.creator_id);
+
+  const handleAdoptSubmission = async (submissionId: number, currentStatus: number) => {
+    console.log('【采纳请求】', {
+      submissionId,
+      currentStatus,
+      bounty: bounty,
+      currentUserId
+    });
+
+    if (currentStatus === 1) return;
+
+    try {
+      const res = await adoptSubmission(submissionId);
+      console.log('【采纳响应】', {
+        status: res.status,
+        data: res.data,
+        headers: res.headers
+      });
+
+      if (res.code === 200) {
+        message.success('采纳成功');
+        setBounty(prev => {
+          console.log('【状态更新】', {
+            old: prev.submissions,
+            new: prev.submissions?.map(sub =>
+              sub.id === submissionId ? { ...sub, status: 1 } : sub
+            )
+          });
+
+          return {
+            ...prev,
+            submissions: prev.submissions?.map(sub =>
+              sub.id === submissionId ? { ...sub, status: 1 } : sub
+            )
+          };
+        });
+      } else {
+        message.error(`采纳失败: ${res.msg}`);
+      }
+    } catch (error: any) {
+      console.error('【采纳错误】', {
+        error: error,
+        response: error.response?.data,
+        status: error.response?.status
+      });
+
+      message.error(`网络异常: ${error.message}`);
+    }
+  };
+
+  const handleDownload = async (attachmentPath: string, submissionUserId: number) => {
+    try {
+
+      // ✅ 新增权限校验
+      if (!currentUserId || (currentUserId !== bounty.creator_id && currentUserId !== submissionUserId)) {
+        message.error('无权查看此附件');
+        return;
+      }
+
+      const blob = await downloadAttachment(attachmentPath);
+      const url = window.URL.createObjectURL(blob);
+      const a = document.createElement('a');
+      a.href = url;
+      a.download = attachmentPath.split('/').pop() || 'file';
+      document.body.appendChild(a);
+      a.click();
+      a.remove();
+      window.URL.revokeObjectURL(url);
+    } catch (err) {
+      message.error('下载附件失败,请重试');
+      console.error('下载附件失败:', err);
+    }
+  };
+
+
+
+  useEffect(() => {
+    const fetchProfile = async () => {
+      try {
+        const res = await getProfile(); // 调用后端接口
+        if (res && res.code === 200) {
+          setCurrentUserId(res.data.userId); // 从接口获取 userId
+        } else {
+          message.error('获取用户信息失败');
+        }
+      } catch (err) {
+        console.error('获取用户信息失败:', err);
+      }
+    };
+    fetchProfile();
+  }, []);
+
+
+  const { id } = useParams<{ id: string }>();
+
+  // 修改加载方法(适配统一请求)✅
+  useEffect(() => {
+    if (!id) return;
+
+    const loadBountyDetail = async () => {
+      try {
+        setLoading(true);
+        const res = await getBountyDetail(id);
+        console.log('【详情响应】原始数据:', res); // 👈 关键日志
+
+        if (res && res.code === 200) {
+          setBounty(res.data);
+          // 👇 新增:检查 submissions 数据结构
+          console.log('【submissions 数据】:', res.data.submissions);
+        } else {
+          throw new Error('响应结构异常');
+        }
+      } catch (err) {
+        console.error('【详情请求】错误:', err);
+      } finally {
+        setLoading(false);
+      }
+    };
+
+    loadBountyDetail();
+  }, [id]);
+
+  return (
+    <div className="page-container">
+      <h2>悬赏详情</h2>
+
+      {/* 基础信息 */}
+      <Descriptions title="悬赏信息" bordered>
+        <Descriptions.Item label="标题">{bounty.title}</Descriptions.Item>
+        <Descriptions.Item label="发布者ID">{bounty.creator_id}</Descriptions.Item> // ✅ 新增字段
+        <Descriptions.Item label="奖励">{bounty.reward}</Descriptions.Item>
+        <Descriptions.Item label="状态">
+          {bounty.status === 0 ? '进行中' : bounty.status === 1 ? '已完成' : '已关闭'}
+        </Descriptions.Item>
+        <Descriptions.Item label="截止时间">{bounty.deadline}</Descriptions.Item>
+        <Descriptions.Item label="描述" span={3}>
+          {bounty.description}
+        </Descriptions.Item>
+      </Descriptions>
+
+      {/* 回复列表 */}
+      {bounty.submissions && (
+        <div style={{ marginTop: 24 }}>
+          <h3>回复列表</h3>
+          <List
+            dataSource={bounty.submissions}
+            renderItem={(item) => (
+              <List.Item>
+                <List.Item.Meta
+                  title={`回复人ID:${item.userId}`}
+                  description={
+                    <>
+                      {item.content}
+                      {/* 状态标签 */}
+                      <span style={{ marginLeft: 16 }}>
+          {item.status === 1 ? (
+            <Tag color="green">已采纳</Tag>
+          ) : (
+            <Tag color="red">未被采纳</Tag>
+          )}
+        </span>
+                    </>
+                  }
+                />
+
+                {/* 发布者操作按钮 */}
+                {currentUserId === bounty.creator_id && (
+                  <Button
+                    type="primary"
+                    size="small"
+                    onClick={() => handleAdoptSubmission(item.id, item.status)}
+                    disabled={item.status === 1}
+                  >
+                    {item.status === 1 ? '已采纳' : '采纳'}
+                  </Button>
+                )}
+
+                {/* 附件下载 */}
+                {item.attachment && currentUserId === bounty.creator_id && (
+                  <a onClick={(e) => handleDownload(item.attachment, item.userId)} style={{ marginLeft: 8 }}>
+                    查看附件
+                  </a>
+                )}
+              </List.Item>
+            )}
+          />
+        </div>
+      )}
+    </div>
+  );
+};
+
+// 定义类型(建议单独放在src/types.d.ts中)
+declare namespace API {
+  export interface BountyDetail {
+    id: number;
+    title: string;
+    creator_id: number; // ✅ 新增:发布者ID
+    description: string;
+    reward: number;
+    deadline: string;
+    status: number;
+    submissions: Array<{
+      id: number;
+      userId: number; // ✅ 替换 id 为 userId
+      username: string;
+      content: string;
+      attachment: string;
+      status: number;
+    }>;
+  }
+}
+
+export default BountyDetail;
diff --git a/src/pages/Bounty/List.tsx b/src/pages/Bounty/List.tsx
new file mode 100644
index 0000000..ca789c5
--- /dev/null
+++ b/src/pages/Bounty/List.tsx
@@ -0,0 +1,183 @@
+import React, { useState, useEffect } from 'react';
+import { Table, Button, message, Space, Modal } from 'antd';
+import axios from 'axios'; // 新增 axios 导入
+import { useNavigate } from 'umi';
+
+import { getBountyList } from '@/services/bounty/bounty';
+import BountyPublish from './BountyPublish';
+import BountyReply from './BountyReply';
+const BountyList: React.FC = () => {
+  const [dataSource, setDataSource] = useState<API.Bounty[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [publishVisible, setPublishVisible] = useState(false);
+  const [replyVisible, setReplyVisible] = useState(false);
+  const [selectedId, setSelectedId] = useState<number | null>(null);
+
+  const navigate = useNavigate();
+
+  // 加载悬赏列表(修改请求方式)
+
+    const loadBountyList = async () => {
+      try {
+        setLoading(true);
+        const res = await getBountyList();
+
+        // 添加详细日志输出
+        console.log('接口原始响应:', res);
+
+        // 修改数据处理逻辑以适配实际数据结构
+        if (res && Array.isArray(res)) {
+          // 当接口直接返回数组时
+          setDataSource(res);
+        } else if (res && res.code === 200) {
+          // 当接口返回标准分页结构时
+          setDataSource(res.data.records || res.data || []);
+        } else {
+          throw new Error('接口返回异常结构');
+        }
+      } catch (err) {
+        console.error('加载数据异常:', err);
+        console.error('完整错误:', err);
+
+        // 添加网络错误处理
+        // @ts-ignore
+        if (err?.response?.status === 401) {
+          message.error('认证失效,请重新登录');
+          navigate('/login');
+        } else {
+          message.error('加载悬赏列表失败');
+        }
+      } finally {
+        setLoading(false);
+      }
+    };
+
+  useEffect(() => {
+    loadBountyList();
+  }, []);
+
+  // ✅ 发布成功回调(普通函数,无需useEffect)
+  const handlePublishSuccess = () => {
+    setPublishVisible(false);
+    loadBountyList(); // 重新加载数据
+    message.success('悬赏发布成功');
+  };
+
+  // ✅ 回复成功回调(普通函数,无需useEffect)
+  const handleReplySuccess = () => {
+    setReplyVisible(false);
+    loadBountyList(); // 重新加载数据
+    message.success('回复提交成功');
+  };
+
+
+  // 跳转详情页
+  const goDetail = (id: number) => {
+    navigate(`/bounty/detail/${id}`);
+  };
+
+  // 表格列配置
+  const columns = [
+    {
+      title: '标题',
+      dataIndex: 'title',
+      width: 200,
+      ellipsis: true,
+    },
+    {
+      title: '发布者ID',
+      dataIndex: 'creator_id', // ✅ 新增列
+      width: 100,
+      align: 'center' as const,
+    },
+    {
+      title: '奖励',
+      dataIndex: 'reward',
+      width: 100,
+      align: 'center' as const,
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      width: 100,
+      render: (status: number) => (
+        status === 0 ? '未回复' : status === 1 ? '已回复' : '已关闭'
+      )
+    },
+    {
+      title: '操作',
+      width: 160,
+      render: (value: unknown, record: API.Bounty) => (
+        <Space>
+          <Button
+            type="link"
+            onClick={() => navigate(`/bounty/detail/${record.id}`)}
+          >
+            查看详情
+          </Button>
+          <Button
+            type="link"
+            onClick={() => {
+              setSelectedId(record.id);
+              setReplyVisible(true);
+            }}
+          >
+            回复悬赏
+          </Button>
+        </Space>
+      )
+    }
+  ];
+
+  return (
+    <div className="page-container">
+      {/* 顶部操作区 */}
+      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
+        <h2>悬赏列表</h2>
+        {/* 发布按钮 */}
+        <Button
+          type="primary"
+          onClick={() => setPublishVisible(true)}
+        >
+          发布悬赏
+        </Button>
+      </div>
+
+      {/* 悬赏列表 */}
+      <Table
+        dataSource={dataSource}
+        columns={columns}
+        rowKey="id"
+        pagination={{ pageSize: 10 }}
+        loading={loading}
+      />
+
+      {/* 发布悬赏模态框 */}
+      <Modal
+        title="发布新悬赏"
+        open={publishVisible}
+        onCancel={() => setPublishVisible(false)}
+        footer={null}
+      >
+        <BountyPublish onSuccess={handlePublishSuccess} />
+      </Modal>
+
+      {/* 回复悬赏模态框 */}
+      <Modal
+        title="回复悬赏"
+        open={replyVisible}
+        onCancel={() => setReplyVisible(false)}
+        footer={null}
+      >
+        {selectedId && (
+          <BountyReply
+            bountyId={selectedId}
+            onSuccess={handleReplySuccess}
+          />
+        )}
+      </Modal>
+    </div>
+  );
+};
+
+export default BountyList;
diff --git a/src/services/bounty/bounty.ts b/src/services/bounty/bounty.ts
new file mode 100644
index 0000000..9c1ef34
--- /dev/null
+++ b/src/services/bounty/bounty.ts
@@ -0,0 +1,120 @@
+// src/services/bounty.ts
+import { request } from '@umijs/max';
+
+// 查询悬赏列表
+export async function getBountyList(params?: Record<string, any>) {
+  return request('/api/bounties', {
+    method: 'GET',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+    params,
+  });
+}
+
+export async function getBountyDetail(id: number | string) {
+  return request(`/api/bounties/${id}`, {
+    method: 'GET',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+  }).then(data => ({ code: 200, data })); // ✅ 自动包装成标准结构
+}
+
+// 新增:发布悬赏
+export async function publishBounty(params: {
+  title: string;
+  description: string;
+  reward: number;
+  deadline: string;
+}) {
+  return request('/api/bounties/publish', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+    data: {
+      ...params,
+      status: 0
+    },
+  });
+}
+
+// 新增:上传附件接口
+export async function uploadBountyAttachment(file: File) {
+  const formData = new FormData();
+  formData.append('file', file);
+
+  return request('/api/bounty-submissions/upload', {
+    method: 'POST',
+    data: formData,
+    // ✅ 移除手动设置 Content-Type
+    // headers: {
+    //   'Content-Type': 'multipart/form-data', // ❌ 浏览器会自动设置 boundary
+    // },
+  });
+}
+
+// 修改:提交悬赏回复(不再处理文件)
+// src/services/bounty/bounty.ts
+// src/services/bounty/bounty.ts
+// src/services/bounty/bounty.ts
+export async function submitBountyReply(params: {
+  bountyId: number;
+  content: string;
+  attachment?: string;
+  file?: File;
+}) {
+  console.log('【提交请求】/api/bounty-submissions 参数:', params);
+
+  const formData = new FormData();
+  formData.append('submission', new Blob([JSON.stringify(params)], { type: 'application/json' }));
+  if (params.file) {
+    formData.append('file', params.file);
+  }
+
+  return request('/api/bounty-submissions', {
+    method: 'POST',
+    data: formData,
+  }).then(res => {
+    console.log('【接口响应】/api/bounty-submissions:', res);
+    // ✅ 确保返回统一结构
+    return {
+      code: res.code || (res ? 200 : 500),
+      data: res.data || res,
+      msg: res.message || '操作成功',
+    };
+  });
+}
+
+
+export async function downloadAttachment(attachmentPath: string): Promise<Blob> {
+  // ✅ 提取文件名
+  const filename = attachmentPath.split('/').pop() || 'file';
+
+  return request(`/api/bounty-submissions/download`, {
+    method: 'GET',
+    params: {
+      filename, // ✅ 只传递文件名
+    },
+    responseType: 'blob',
+  });
+}
+
+export async function getProfile() {
+  return request('/api/system/user/profile', {
+    method: 'GET',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+  });
+}
+
+export async function adoptSubmission(submissionId: number) {
+  return request(`/api/bounty-submissions/${submissionId}/adopt`, {
+    method: 'PUT',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+  });
+}
diff --git a/src/services/post/index.ts b/src/services/post/index.ts
index 3452095..13e5bae 100644
--- a/src/services/post/index.ts
+++ b/src/services/post/index.ts
@@ -1,3 +1,4 @@
+// @ts-ignore
 import { request } from '@umijs/max';
 
 export interface PostListParams {
@@ -26,7 +27,7 @@
 
 // 获取帖子列表
 export async function getPostList(params: PostListParams) {
-    console.log('getPostList', params);    
+    console.log('getPostList', params);
   return request<API.TableDataInfo>('/api/post-center/list', {
     method: 'GET',
     params,
@@ -138,7 +139,7 @@
     method: 'POST',
     data: file,
   });
-} 
+}
 
 // 删除图片
 export async function deleteImage(filename: string): Promise<API.AjaxResult> {
@@ -259,4 +260,4 @@
     method: 'PUT',
     data: { action, postId, reason },
   });
-} 
\ No newline at end of file
+}
diff --git a/src/types.d.ts b/src/types.d.ts
new file mode 100644
index 0000000..d65e0e6
--- /dev/null
+++ b/src/types.d.ts
@@ -0,0 +1,14 @@
+//如果需要定义 API 类型(如接口返回的用户信息、列表数据等),推荐:
+
+declare namespace API {
+  // 补充 Bounty 类型定义(根据实际接口返回字段调整)
+  export interface Bounty {
+    id: number;
+    title: string;
+    description: string;
+    reward: number;
+    deadline: string;
+    status: number; // 0-进行中,1-已完成,2-已关闭
+    creator_id: number;
+  }
+}