新增邀请码页面,新增添加好友功能
Change-Id: Ifa0a5c355ab3693eecfe919de06fa9bef8171695
diff --git a/react-ui/src/pages/Invite/data.d.ts b/react-ui/src/pages/Invite/data.d.ts
new file mode 100644
index 0000000..2a07e7a
--- /dev/null
+++ b/react-ui/src/pages/Invite/data.d.ts
@@ -0,0 +1,12 @@
+// data.ts 建议的类型定义
+export interface GetInviteCodeResponse {
+ code: number;
+ msg: string;
+ data: string; // 邀请码字符串
+}
+
+export interface GetUserByInviteCodeResponse {
+ code: number;
+ msg: string;
+ data: number; // 用户ID
+}
\ No newline at end of file
diff --git a/react-ui/src/pages/Invite/index.tsx b/react-ui/src/pages/Invite/index.tsx
new file mode 100644
index 0000000..56989ff
--- /dev/null
+++ b/react-ui/src/pages/Invite/index.tsx
@@ -0,0 +1,300 @@
+import React, { useState, useEffect } from 'react';
+import { useModel } from 'umi';
+import { Card, Input, Button, Space, Typography, Row, Col, message } from 'antd';
+import { CopyOutlined, UserAddOutlined, GiftOutlined, ShareAltOutlined } from '@ant-design/icons';
+import { getUserInviteCode, getUserByInviteCode } from './service';
+
+const { Title, Text, Paragraph } = Typography;
+
+const InvitePage: React.FC = () => {
+ const [myInviteCode, setMyInviteCode] = useState<string>('');
+ const [inputInviteCode, setInputInviteCode] = useState<string>('');
+ const [loading, setLoading] = useState<boolean>(false);
+ const [submitting, setSubmitting] = useState<boolean>(false);
+
+ // 获取当前用户信息
+ const { initialState } = useModel('@@initialState');
+ const userId = initialState?.currentUser?.userId || '';
+
+ // 获取我的邀请码
+ const fetchMyInviteCode = async () => {
+ if (!userId) {
+ message.error('用户信息获取失败');
+ return;
+ }
+
+ setLoading(true);
+ try {
+ const response = await getUserInviteCode();
+ if (response.code === 200) {
+ // 根据后端返回的数据结构调整
+ setMyInviteCode(response.msg);
+ } else {
+ message.error(response.msg || '获取邀请码失败');
+ }
+ } catch (error) {
+ message.error('获取邀请码失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 复制邀请码
+ const copyInviteCode = () => {
+ if (myInviteCode) {
+ navigator.clipboard.writeText(myInviteCode).then(() => {
+ message.success('邀请码已复制到剪贴板');
+ }).catch(() => {
+ message.error('复制失败,请手动复制');
+ });
+ }
+ };
+
+ // 查询邀请码对应的用户
+ const handleSubmitInviteCode = async () => {
+ if (!inputInviteCode.trim()) {
+ message.warning('请输入邀请码');
+ return;
+ }
+
+ setSubmitting(true);
+ try {
+ const response = await getUserByInviteCode(inputInviteCode.trim());
+
+ if (response.code === 200) {
+ message.success(`邀请码有效,对应用户ID: ${response.data}`);
+ setInputInviteCode('');
+ // 这里可以根据业务需求进行后续操作,比如关注用户等
+ } else {
+ message.error(response.msg || '邀请码无效');
+ }
+ } catch (error) {
+ message.error('查询邀请码失败');
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchMyInviteCode();
+ }, []);
+
+ return (
+ <div
+ style={{
+ padding: '32px 24px',
+ maxWidth: '800px',
+ margin: '0 auto',
+ minHeight: '100vh',
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ }}
+ >
+ {/* 头部标题区域 */}
+ <div style={{ textAlign: 'center', marginBottom: '40px' }}>
+ <GiftOutlined
+ style={{
+ fontSize: '48px',
+ color: '#fff',
+ marginBottom: '16px',
+ filter: 'drop-shadow(0 4px 8px rgba(0,0,0,0.2))'
+ }}
+ />
+ <Title
+ level={1}
+ style={{
+ color: '#fff',
+ margin: 0,
+ fontSize: '32px',
+ fontWeight: 600,
+ textShadow: '0 2px 4px rgba(0,0,0,0.3)'
+ }}
+ >
+ 邀请码管理
+ </Title>
+ <Paragraph
+ style={{
+ color: 'rgba(255,255,255,0.8)',
+ fontSize: '16px',
+ margin: '8px 0 0 0'
+ }}
+ >
+ 邀请好友,共享美好体验
+ </Paragraph>
+ </div>
+
+ <Row gutter={[24, 24]}>
+ {/* 我的邀请码 */}
+ <Col xs={24} lg={12}>
+ <Card
+ title={
+ <Space>
+ <ShareAltOutlined style={{ color: '#1890ff' }} />
+ <span style={{ color: '#1890ff', fontWeight: 600 }}>我的邀请码</span>
+ </Space>
+ }
+ style={{
+ height: '100%',
+ borderRadius: '16px',
+ boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
+ border: 'none',
+ background: 'rgba(255,255,255,0.95)',
+ backdropFilter: 'blur(10px)'
+ }}
+ bodyStyle={{ padding: '24px' }}
+ >
+ <Space direction="vertical" style={{ width: '100%' }} size="large">
+ <div style={{
+ padding: '20px',
+ background: 'linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%)',
+ borderRadius: '12px',
+ textAlign: 'center'
+ }}>
+ <Text
+ type="secondary"
+ style={{
+ fontSize: '14px',
+ display: 'block',
+ marginBottom: '16px'
+ }}
+ >
+ 分享下方邀请码给好友,邀请他们加入
+ </Text>
+ <div style={{
+ background: '#fff',
+ padding: '16px',
+ borderRadius: '8px',
+ border: '2px dashed #d9d9d9',
+ marginBottom: '16px'
+ }}>
+ <Text
+ copyable={false}
+ style={{
+ fontSize: '24px',
+ fontWeight: 'bold',
+ color: '#1890ff',
+ fontFamily: 'Monaco, monospace',
+ letterSpacing: '2px'
+ }}
+ >
+ {loading ? '加载中...' : (myInviteCode || '暂无邀请码')}
+ </Text>
+ </div>
+ <Button
+ type="primary"
+ size="large"
+ icon={<CopyOutlined />}
+ onClick={copyInviteCode}
+ disabled={!myInviteCode}
+ style={{
+ borderRadius: '8px',
+ height: '44px',
+ fontSize: '16px',
+ fontWeight: 500,
+ boxShadow: '0 4px 12px rgba(24, 144, 255, 0.3)'
+ }}
+ block
+ >
+ 复制邀请码
+ </Button>
+ </div>
+ </Space>
+ </Card>
+ </Col>
+
+ {/* 输入邀请码 */}
+ <Col xs={24} lg={12}>
+ <Card
+ title={
+ <Space>
+ <UserAddOutlined style={{ color: '#52c41a' }} />
+ <span style={{ color: '#52c41a', fontWeight: 600 }}>查询邀请码</span>
+ </Space>
+ }
+ style={{
+ height: '100%',
+ borderRadius: '16px',
+ boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
+ border: 'none',
+ background: 'rgba(255,255,255,0.95)',
+ backdropFilter: 'blur(10px)'
+ }}
+ bodyStyle={{ padding: '24px' }}
+ >
+ <Space direction="vertical" style={{ width: '100%' }} size="large">
+ <div style={{
+ padding: '20px',
+ background: 'linear-gradient(135deg, #f6ffed 0%, #e6f7ff 100%)',
+ borderRadius: '12px'
+ }}>
+ <Text
+ type="secondary"
+ style={{
+ fontSize: '14px',
+ display: 'block',
+ marginBottom: '20px',
+ textAlign: 'center'
+ }}
+ >
+ 输入邀请码
+ </Text>
+ <Space direction="vertical" style={{ width: '100%' }} size="middle">
+ <Input
+ value={inputInviteCode}
+ onChange={(e) => setInputInviteCode(e.target.value)}
+ placeholder="请输入邀请码"
+ size="large"
+ style={{
+ borderRadius: '8px',
+ fontSize: '16px',
+ height: '48px',
+ textAlign: 'center',
+ fontFamily: 'Monaco, monospace',
+ letterSpacing: '1px'
+ }}
+ onPressEnter={handleSubmitInviteCode}
+ />
+ <Button
+ type="primary"
+ size="large"
+ icon={<UserAddOutlined />}
+ onClick={handleSubmitInviteCode}
+ loading={submitting}
+ disabled={!inputInviteCode.trim()}
+ style={{
+ borderRadius: '8px',
+ height: '44px',
+ fontSize: '16px',
+ fontWeight: 500,
+ background: '#52c41a',
+ borderColor: '#52c41a',
+ boxShadow: '0 4px 12px rgba(82, 196, 26, 0.3)'
+ }}
+ block
+ >
+ 邀请码
+ </Button>
+ </Space>
+ </div>
+ </Space>
+ </Card>
+ </Col>
+ </Row>
+
+ {/* 底部装饰 */}
+ <div style={{
+ textAlign: 'center',
+ marginTop: '40px',
+ padding: '20px',
+ background: 'rgba(255,255,255,0.1)',
+ borderRadius: '12px',
+ backdropFilter: 'blur(10px)'
+ }}>
+ <Text style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px' }}>
+ 通过邀请码连接更多朋友,一起享受精彩体验
+ </Text>
+ </div>
+ </div>
+ );
+};
+
+export default InvitePage;
\ No newline at end of file
diff --git a/react-ui/src/pages/Invite/service.ts b/react-ui/src/pages/Invite/service.ts
new file mode 100644
index 0000000..e6cf9af
--- /dev/null
+++ b/react-ui/src/pages/Invite/service.ts
@@ -0,0 +1,19 @@
+import { request } from '@umijs/max';
+import type {
+ GetInviteCodeResponse,
+ GetUserByInviteCodeResponse
+} from './data';
+
+// 获取用户邀请码
+export async function getUserInviteCode(): Promise<GetInviteCodeResponse> {
+ return request('/api/system/user/invite/code', {
+ method: 'GET', // 修改为 GET 请求
+ });
+}
+
+// 根据邀请码获取用户信息
+export async function getUserByInviteCode(code: string): Promise<GetUserByInviteCodeResponse> {
+ return request(`/api/system/user/invite/user/${code}`, {
+ method: 'GET', // 修改为 GET 请求,并使用路径参数
+ });
+}
\ No newline at end of file
diff --git a/react-ui/src/pages/Message/index.tsx b/react-ui/src/pages/Message/index.tsx
index 04f9881..9ae9467 100644
--- a/react-ui/src/pages/Message/index.tsx
+++ b/react-ui/src/pages/Message/index.tsx
@@ -1,8 +1,10 @@
import React, { useState, useEffect } from 'react';
-import { Card, List, Avatar, Input, Button, Row, Col, message } from 'antd';
+import { Card, List, Avatar, Input, Button, Row, Col, message, Modal, Form } from 'antd';
+import { UserAddOutlined, SearchOutlined } from '@ant-design/icons';
import { SysUserMessage, ChatContact } from './data.d';
-import { getChatContactList, getChatHistory, sendMessage, getUserInfo } from './service';
+import { getChatContactList, getChatHistory, sendMessage, getUserInfo, addFriend } from './service';
import './index.less';
+import { useModel } from 'umi';
const MessagePage: React.FC = () => {
const [chatContacts, setChatContacts] = useState<ChatContact[]>([]);
@@ -11,6 +13,12 @@
const [chatMessages, setChatMessages] = useState<SysUserMessage[]>([]);
const [loading, setLoading] = useState(false);
const [sending, setSending] = useState(false);
+ const { initialState } = useModel('@@initialState');
+ const ccuserId = initialState?.currentUser?.userId || '';
+ // 添加好友相关状态
+ const [addFriendVisible, setAddFriendVisible] = useState(false);
+ const [addFriendLoading, setAddFriendLoading] = useState(false);
+ const [form] = Form.useForm();
// 获取聊天对象列表
const fetchChatContacts = async () => {
@@ -36,7 +44,7 @@
setLoading(true);
const response = await getChatHistory({
userId,
- currentUserId: 1, // 假设当前用户ID为1,实际应该从用户状态获取
+ currentUserId: Number(ccuserId), // 假设当前用户ID为1,实际应该从用户状态获取
pageSize: 100 // 获取最近100条消息
});
@@ -110,6 +118,26 @@
}
};
+ // 添加好友
+ const handleAddFriend = async (values: { username: string }) => {
+ try {
+ setAddFriendLoading(true);
+ await addFriend({ userId: Number(ccuserId), authorUsername: values.username });
+ message.success('好友添加成功');
+
+ // 重新获取聊天对象列表
+ await fetchChatContacts();
+
+ // 关闭弹窗并重置表单
+ setAddFriendVisible(false);
+ form.resetFields();
+ } catch (error) {
+ message.error('添加好友失败,请检查用户名是否正确');
+ } finally {
+ setAddFriendLoading(false);
+ }
+ };
+
// 选择聊天对象
const handleSelectUser = (userId: number) => {
setSelectedUserId(userId);
@@ -137,7 +165,6 @@
// 获取用户名
const getUserName = (userId: number) => {
const contact = chatContacts.find(c => c.userId === userId);
-
return contact?.nickName || `用户${userId}`;
};
@@ -159,7 +186,19 @@
{/* 左侧聊天对象列表 */}
<Col span={8}>
<Card
- title="聊天列表"
+ title={
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+ <span>聊天列表</span>
+ <Button
+ type="primary"
+ icon={<UserAddOutlined />}
+ size="small"
+ onClick={() => setAddFriendVisible(true)}
+ >
+ 添加好友
+ </Button>
+ </div>
+ }
bordered={false}
style={{ height: '100%' }}
loading={loading && !selectedUserId}
@@ -180,11 +219,11 @@
className={selectedUserId === contact.userId ? 'selected' : ''}
>
<List.Item.Meta
- // avatar={
- // <Avatar size="large">
- // {contact.nickName.charAt(0)}
- // </Avatar>
- // }
+ avatar={
+ <Avatar size="large">
+ {contact.nickName.charAt(0).toUpperCase()}
+ </Avatar>
+ }
title={
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{
@@ -355,6 +394,59 @@
</div>
</Col>
</Row>
+
+ {/* 添加好友弹窗 */}
+ <Modal
+ title="添加好友"
+ open={addFriendVisible}
+ onCancel={() => {
+ setAddFriendVisible(false);
+ form.resetFields();
+ }}
+ footer={null}
+ width={400}
+ >
+ <Form
+ form={form}
+ layout="vertical"
+ onFinish={handleAddFriend}
+ style={{ marginTop: '20px' }}
+ >
+ <Form.Item
+ label="用户名"
+ name="username"
+ rules={[
+ { required: true, message: '请输入用户名' },
+ { min: 2, message: '用户名至少2个字符' },
+ { max: 20, message: '用户名最多20个字符' }
+ ]}
+ >
+ <Input
+ placeholder="请输入要添加的用户名"
+ prefix={<SearchOutlined />}
+ size="large"
+ />
+ </Form.Item>
+ <Form.Item style={{ marginBottom: 0, textAlign: 'right' }}>
+ <Button
+ onClick={() => {
+ setAddFriendVisible(false);
+ form.resetFields();
+ }}
+ style={{ marginRight: '8px' }}
+ >
+ 取消
+ </Button>
+ <Button
+ type="primary"
+ htmlType="submit"
+ loading={addFriendLoading}
+ >
+ 添加
+ </Button>
+ </Form.Item>
+ </Form>
+ </Modal>
</div>
);
};
diff --git a/react-ui/src/pages/Message/service.ts b/react-ui/src/pages/Message/service.ts
index c5bc356..4eb3840 100644
--- a/react-ui/src/pages/Message/service.ts
+++ b/react-ui/src/pages/Message/service.ts
@@ -112,6 +112,20 @@
}
}
+// 添加好友
+export async function addFriend(params: { userId: number, authorUsername: string }) {
+ try {
+ return await request('api/system/user/follow', {
+ method: 'post',
+ data: params,
+ });
+ } catch (error) {
+ // 发送消息失败时记录错误但不返回默认数据,让组件处理错误
+ console.error('发送消息接口异常:', error);
+ throw error; // 重新抛出错误,让调用方处理
+ }
+};
+
/** 获取用户信息(用于聊天对象显示) */
export async function getUserInfo(userId: number) {
// 默认用户信息
diff --git a/react-ui/src/pages/Reward/components/UpdateForm.tsx b/react-ui/src/pages/Reward/components/UpdateForm.tsx
index 526b946..a7d9444 100644
--- a/react-ui/src/pages/Reward/components/UpdateForm.tsx
+++ b/react-ui/src/pages/Reward/components/UpdateForm.tsx
@@ -85,8 +85,8 @@
</Form.Item>
<Form.Item
name="amount"
- label="悬赏金额"
- rules={[{ required: true, message: '请输入悬赏金额!' }]}
+ label="悬赏积分"
+ rules={[{ required: true, message: '请输入悬赏积分!' }]}
>
<InputNumber
style={{ width: '100%' }}
diff --git a/react-ui/src/pages/Reward/index.tsx b/react-ui/src/pages/Reward/index.tsx
index e55cfc1..42a9bac 100644
--- a/react-ui/src/pages/Reward/index.tsx
+++ b/react-ui/src/pages/Reward/index.tsx
@@ -1,4 +1,4 @@
-import { ExclamationCircleOutlined, PlusOutlined, DeleteOutlined, UploadOutlined } from '@ant-design/icons';
+import { ExclamationCircleOutlined, PlusOutlined, DeleteOutlined, UploadOutlined, TrophyOutlined } from '@ant-design/icons';
import { Button, message, Modal, Switch, Upload, Form } from 'antd';
import React, { useRef, useState, useEffect } from 'react';
import { FormattedMessage, useIntl } from 'umi';
@@ -223,10 +223,21 @@
valueType: 'text',
},
{
- title: '悬赏金额',
+ title: (
+ <span>
+ <TrophyOutlined style={{ marginRight: 4, color: '#faad14' }} />
+ 悬赏积分
+ </span>
+ ),
dataIndex: 'amount',
- valueType: 'money',
+ valueType: 'digit',
hideInSearch: true,
+ render: (_, record) => (
+ <span style={{ color: '#faad14', fontWeight: 'bold' }}>
+ <TrophyOutlined style={{ marginRight: 4 }} />
+ {record.amount}
+ </span>
+ ),
},
// {
// title: '悬赏状态',
@@ -283,6 +294,7 @@
size="small"
key="accept"
style={{ color: '#52c41a' }}
+ icon={<TrophyOutlined />}
onClick={() => {
setCurrentAcceptReward(record);
setAcceptModalVisible(true);
@@ -468,7 +480,12 @@
{/* 接悬赏模态框 */}
<Modal
- title={`接悬赏 - ${currentAcceptReward?.title || ''}`}
+ title={
+ <span>
+ <TrophyOutlined style={{ color: '#faad14', marginRight: 8 }} />
+ 接悬赏 - {currentAcceptReward?.title || ''}
+ </span>
+ }
open={acceptModalVisible}
onOk={handleAcceptSubmit}
onCancel={() => {
@@ -482,7 +499,13 @@
>
<div style={{ marginBottom: 16 }}>
<p><strong>悬赏标题:</strong>{currentAcceptReward?.title}</p>
- <p><strong>悬赏金额:</strong>¥{currentAcceptReward?.amount}</p>
+ <p>
+ <strong>悬赏积分:</strong>
+ <span style={{ color: '#faad14', fontWeight: 'bold' }}>
+ <TrophyOutlined style={{ marginRight: 4 }} />
+ {currentAcceptReward?.amount}
+ </span>
+ </p>
<p><strong>备注:</strong>{currentAcceptReward?.remark || '无'}</p>
</div>
diff --git a/react-ui/src/pages/Torrent/Comments/index.tsx b/react-ui/src/pages/Torrent/Comments/index.tsx
index 05c5165..3dd3c68 100644
--- a/react-ui/src/pages/Torrent/Comments/index.tsx
+++ b/react-ui/src/pages/Torrent/Comments/index.tsx
@@ -5,7 +5,6 @@
import { Layout } from 'antd';
import { listComments, addComment } from './service';
import { responseSysTorrentComment, SysTorrentComment } from './data';
-
const { Content } = Layout;
const { TextArea } = Input;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserFollowController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserFollowController.java
index 17d7ca1..2e791c1 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserFollowController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserFollowController.java
@@ -1,4 +1,3 @@
-// 作者关注控制器
package com.ruoyi.web.controller.system;
import com.ruoyi.common.core.controller.BaseController;
@@ -19,14 +18,24 @@
@PostMapping
public AjaxResult follow(@RequestBody SysUserFollow follow) {
follow.setUserId(getUserId());
- return toAjax(followService.followAuthor(follow));
+ int result = followService.followAuthor(follow);
+ if (result == -1) {
+ return AjaxResult.error("作者不存在");
+ } else if (result == 0) {
+ return AjaxResult.error("已关注该作者");
+ }
+ return toAjax(result);
}
@PreAuthorize("@ss.hasPermi('system:user:follow:remove')")
@DeleteMapping
public AjaxResult unfollow(@RequestBody SysUserFollow follow) {
follow.setUserId(getUserId());
- return toAjax(followService.unfollowAuthor(follow));
+ int result = followService.unfollowAuthor(follow);
+ if (result == -1) {
+ return AjaxResult.error("作者不存在");
+ }
+ return toAjax(result);
}
@GetMapping("/list")
@@ -34,11 +43,12 @@
return AjaxResult.success(followService.getFollowList(getUserId()));
}
- @GetMapping("/isFollowing/{authorId}")
- public AjaxResult isFollowing(@PathVariable Long authorId) {
+ @PreAuthorize("@ss.hasPermi('system:user:follow:query')")
+ @GetMapping("/isFollowing/{authorUsername}")
+ public AjaxResult isFollowing(@PathVariable String authorUsername) {
SysUserFollow follow = new SysUserFollow();
follow.setUserId(getUserId());
- follow.setAuthorId(authorId);
+ follow.setAuthorUsername(authorUsername);
return AjaxResult.success(followService.isFollowing(follow));
}
}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserInviteController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserInviteController.java
new file mode 100644
index 0000000..eb22abd
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserInviteController.java
@@ -0,0 +1,36 @@
+package com.ruoyi.web.controller.system;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.system.domain.SysUserInvite;
+import com.ruoyi.system.service.ISysUserInviteService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/system/user/invite")
+public class SysUserInviteController extends BaseController {
+ @Autowired
+ private ISysUserInviteService inviteService;
+
+ @PreAuthorize("@ss.hasPermi('system:user:invite:query')")
+ @GetMapping("/code")
+ public AjaxResult getInviteCode() {
+ SysUserInvite invite = inviteService.getInviteCode(getUserId());
+ if (invite == null) {
+ return AjaxResult.error("未找到邀请码");
+ }
+ return AjaxResult.success(invite.getCode());
+ }
+
+ @PreAuthorize("@ss.hasPermi('system:user:invite:query')")
+ @GetMapping("/user/{code}")
+ public AjaxResult getUserByInviteCode(@PathVariable String code) {
+ SysUserInvite invite = inviteService.getUserByInviteCode(code);
+ if (invite == null) {
+ return AjaxResult.error("邀请码不存在");
+ }
+ return AjaxResult.success(invite.getUserId());
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserFollow.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserFollow.java
index a5878b1..4831e79 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserFollow.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserFollow.java
@@ -1,4 +1,3 @@
-// 作者关注
package com.ruoyi.system.domain;
import com.ruoyi.common.annotation.Excel;
@@ -20,4 +19,7 @@
@Excel(name = "作者ID")
@NotNull(message = "作者ID不能为空")
private Long authorId;
+
+ // 用于接收前端传入的作者用户名
+ private String authorUsername;
}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserInvite.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserInvite.java
new file mode 100644
index 0000000..3432587
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserInvite.java
@@ -0,0 +1,15 @@
+package com.ruoyi.system.domain;
+
+import com.ruoyi.common.core.domain.BaseEntity;
+import lombok.Data;
+
+@Data
+public class SysUserInvite extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ private Long codeId;
+
+ private String code;
+
+ private Long userId;
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserFollowMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserFollowMapper.java
index 77bca7e..521f26b 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserFollowMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserFollowMapper.java
@@ -1,4 +1,3 @@
-// 作者关注 Mapper
package com.ruoyi.system.mapper;
import com.ruoyi.system.domain.SysUserFollow;
@@ -9,4 +8,5 @@
int deleteFollow(SysUserFollow follow);
List<SysUserFollow> selectFollowListByUserId(Long userId);
SysUserFollow selectFollow(SysUserFollow follow);
+ Long selectUserIdByUsername(String username);
}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserInviteMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserInviteMapper.java
new file mode 100644
index 0000000..b0239ca
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserInviteMapper.java
@@ -0,0 +1,8 @@
+package com.ruoyi.system.mapper;
+
+import com.ruoyi.system.domain.SysUserInvite;
+
+public interface SysUserInviteMapper {
+ SysUserInvite selectInviteByUserId(Long userId);
+ SysUserInvite selectInviteByCode(String code);
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserInviteService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserInviteService.java
new file mode 100644
index 0000000..d5b04f4
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserInviteService.java
@@ -0,0 +1,8 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.system.domain.SysUserInvite;
+
+public interface ISysUserInviteService {
+ SysUserInvite getInviteCode(Long userId);
+ SysUserInvite getUserByInviteCode(String code);
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserFollowServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserFollowServiceImpl.java
index 9cec124..a12f985 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserFollowServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserFollowServiceImpl.java
@@ -1,9 +1,10 @@
-// 作者关注服务实现
package com.ruoyi.system.service.impl;
import com.ruoyi.system.domain.SysUserFollow;
+import com.ruoyi.system.domain.SysUserMessage;
import com.ruoyi.system.mapper.SysUserFollowMapper;
import com.ruoyi.system.service.ISysUserFollowService;
+import com.ruoyi.system.service.ISysUserMessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@@ -13,17 +14,46 @@
@Autowired
private SysUserFollowMapper followMapper;
+ @Autowired
+ private ISysUserMessageService messageService;
+
@Override
public int followAuthor(SysUserFollow follow) {
+ // 根据 authorUsername 查询 authorId
+ Long authorId = followMapper.selectUserIdByUsername(follow.getAuthorUsername());
+ if (authorId == null) {
+ return -1; // 作者不存在
+ }
+ follow.setAuthorId(authorId);
+
+ // 检查是否已关注
if (followMapper.selectFollow(follow) != null) {
return 0; // 已经关注
}
- return followMapper.insertFollow(follow);
+
+ // 插入关注记录
+ int result = followMapper.insertFollow(follow);
+ if (result > 0) {
+ // 发送 "hello" 消息
+ SysUserMessage message = new SysUserMessage();
+ message.setSenderId(follow.getUserId());
+ message.setReceiverId(authorId);
+ message.setContent("hello");
+ messageService.sendMessage(message);
+ }
+ return result;
}
@Override
public int unfollowAuthor(SysUserFollow follow) {
- return followMapper.deleteFollow(follow);
+ // 根据 authorUsername 查询 authorId
+ Long authorId = followMapper.selectUserIdByUsername(follow.getAuthorUsername());
+ if (authorId == null) {
+ return -1; // 作者不存在
+ }
+ follow.setAuthorId(authorId);
+
+ return followMapper.deleteFollow(follow); // 返回删除的行数(0 或 1)
}
@Override
@@ -33,6 +63,13 @@
@Override
public boolean isFollowing(SysUserFollow follow) {
+ // 根据 authorUsername 查询 authorId
+ Long authorId = followMapper.selectUserIdByUsername(follow.getAuthorUsername());
+ if (authorId == null) {
+ return false; // 作者不存在
+ }
+ follow.setAuthorId(authorId);
+
return followMapper.selectFollow(follow) != null;
}
}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserInviteServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserInviteServiceImpl.java
new file mode 100644
index 0000000..e499a5a
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserInviteServiceImpl.java
@@ -0,0 +1,23 @@
+package com.ruoyi.system.service.impl;
+
+import com.ruoyi.system.domain.SysUserInvite;
+import com.ruoyi.system.mapper.SysUserInviteMapper;
+import com.ruoyi.system.service.ISysUserInviteService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SysUserInviteServiceImpl implements ISysUserInviteService {
+ @Autowired
+ private SysUserInviteMapper inviteMapper;
+
+ @Override
+ public SysUserInvite getInviteCode(Long userId) {
+ return inviteMapper.selectInviteByUserId(userId);
+ }
+
+ @Override
+ public SysUserInvite getUserByInviteCode(String code) {
+ return inviteMapper.selectInviteByCode(code);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserFollowMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserFollowMapper.xml
index 6fb9169..bea8888 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysUserFollowMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysUserFollowMapper.xml
@@ -28,4 +28,10 @@
from sys_user_follow
where user_id = #{userId} and author_id = #{authorId}
</select>
+
+ <select id="selectUserIdByUsername" resultType="java.lang.Long">
+ select user_id
+ from sys_user
+ where user_name = #{username}
+ </select>
</mapper>
\ No newline at end of file
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserInviteMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserInviteMapper.xml
new file mode 100644
index 0000000..82da470
--- /dev/null
+++ b/ruoyi-system/src/main/resources/mapper/system/SysUserInviteMapper.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysUserInviteMapper">
+ <resultMap id="SysUserInviteMap" type="com.ruoyi.system.domain.SysUserInvite">
+ <id property="codeId" column="code_id"/>
+ <result property="code" column="code"/>
+ <result property="userId" column="user_id"/>
+ </resultMap>
+
+ <select id="selectInviteByUserId" resultMap="SysUserInviteMap">
+ select code_id, code, user_id
+ from invite_codes
+ where user_id = #{userId}
+ </select>
+
+ <select id="selectInviteByCode" resultMap="SysUserInviteMap">
+ select code_id, code, user_id
+ from invite_codes
+ where code = #{code}
+ </select>
+</mapper>
\ No newline at end of file