diff --git a/docs/JWT认证使用说明.md b/docs/JWT认证使用说明.md
index 74ad4b7..fa972ea 100644
--- a/docs/JWT认证使用说明.md
+++ b/docs/JWT认证使用说明.md
@@ -39,7 +39,7 @@ Content-Type: application/json
{
"times": 10,
- "perTimeQuantity": 5
+ "linkCount": 5
}
```
@@ -56,7 +56,7 @@ Content-Type: application/json
```json
{
"times": 10, // 本次打脚本的次数
- "perTimeQuantity": 5 // 每次打的数量
+ "linkCount": 5 // 生成多少个链接
}
```
diff --git a/docs/game.sql b/docs/game.sql
index 73ba2e1..4251b7d 100644
--- a/docs/game.sql
+++ b/docs/game.sql
@@ -144,4 +144,33 @@ CREATE TABLE `user_account` (
CONSTRAINT `chk_points_nonneg` CHECK (`points_balance` >= 0)
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
+-- ----------------------------
+-- Table structure for system_config
+-- ----------------------------
+DROP TABLE IF EXISTS `system_config`;
+CREATE TABLE `system_config` (
+ `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
+ `config_key` varchar(100) NOT NULL COMMENT '配置键',
+ `config_value` text NOT NULL COMMENT '配置值',
+ `config_type` varchar(50) NOT NULL DEFAULT 'STRING' COMMENT '配置类型:STRING, INTEGER, BOOLEAN, JSON',
+ `description` varchar(500) NULL DEFAULT NULL COMMENT '配置描述',
+ `is_system` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否系统配置(1是,0否)',
+ `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
+ `updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
+ PRIMARY KEY (`id`) USING BTREE,
+ UNIQUE INDEX `uk_config_key`(`config_key` ASC) USING BTREE,
+ INDEX `idx_config_type`(`config_type` ASC) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
+
+-- 插入默认配置
+INSERT INTO `system_config` (`config_key`, `config_value`, `config_type`, `description`, `is_system`) VALUES
+('link.default_quantity', '50', 'INTEGER', '链接生成默认奖励点数', 1),
+('link.refresh_interval', '300', 'INTEGER', '链接刷新间隔(秒)', 1),
+('link.qr_expire_time', '600', 'INTEGER', '二维码过期时间(秒)', 1),
+('link.max_times_per_batch', '100', 'INTEGER', '每批次最大刷奖励次数', 1),
+('link.min_quantity', '10', 'INTEGER', '最小奖励点数', 1),
+('link.max_quantity', '1000', 'INTEGER', '最大奖励点数', 1),
+('script.server_url', 'http://36.138.184.60:12345', 'STRING', '脚本服务器地址', 1),
+('script.qr_path_template', '/{machineId}/二维码.png', 'STRING', '二维码图片路径模板', 1);
+
SET FOREIGN_KEY_CHECKS = 1;
diff --git a/docs/前端链接访问示例.md b/docs/前端链接访问示例.md
new file mode 100644
index 0000000..f739ffc
--- /dev/null
+++ b/docs/前端链接访问示例.md
@@ -0,0 +1,403 @@
+# 前端链接访问示例
+
+## 概述
+当用户访问链接页面时(如 `https://你的域名/ABC12345`),前端需要自动请求后端获取链接的详细信息,并根据状态显示相应的内容。
+
+## 接口说明
+
+### 1. 获取链接状态(主要接口)
+```
+GET /api/link/{codeNo}/status
+```
+
+**响应示例:**
+```json
+{
+ "codeNo": "ABC12345",
+ "batchId": 123,
+ "status": "NEW",
+ "statusDesc": "新建",
+ "expireAt": "2024-01-15T16:30:00",
+ "isExpired": false,
+ "remainingSeconds": 3600,
+ "quantity": 50,
+ "times": 10,
+ "totalPoints": 500,
+ "region": null,
+ "machineId": null,
+ "loginAt": null,
+ "createdAt": "2024-01-15T12:00:00",
+ "updatedAt": "2024-01-15T12:00:00"
+}
+```
+
+### 2. 检查链接是否存在
+```
+GET /api/link/{codeNo}/exists
+```
+
+**响应:** `true` 或 `false`
+
+### 3. 检查链接是否有效
+```
+GET /api/link/{codeNo}/valid
+```
+
+**响应:** `true` 或 `false`
+
+## 前端实现示例
+
+### React 组件示例
+
+```jsx
+import React, { useState, useEffect } from 'react';
+import { useParams } from 'react-router-dom';
+
+const LinkPage = () => {
+ const { codeNo } = useParams();
+ const [linkStatus, setLinkStatus] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ fetchLinkStatus();
+ }, [codeNo]);
+
+ const fetchLinkStatus = async () => {
+ try {
+ setLoading(true);
+ const response = await fetch(`/api/link/${codeNo}/status`);
+
+ if (!response.ok) {
+ if (response.status === 404) {
+ setError('链接不存在');
+ } else {
+ setError('获取链接信息失败');
+ }
+ return;
+ }
+
+ const data = await response.json();
+ setLinkStatus(data);
+ } catch (err) {
+ setError('网络错误');
+ console.error('获取链接状态失败:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const formatTime = (seconds) => {
+ if (seconds <= 0) return '已过期';
+
+ const hours = Math.floor(seconds / 3600);
+ const minutes = Math.floor((seconds % 3600) / 60);
+ const secs = seconds % 60;
+
+ if (hours > 0) {
+ return `${hours}小时${minutes}分钟`;
+ } else if (minutes > 0) {
+ return `${minutes}分钟${secs}秒`;
+ } else {
+ return `${secs}秒`;
+ }
+ };
+
+ if (loading) {
+ return
加载中...
;
+ }
+
+ if (error) {
+ return (
+
+
链接错误
+
{error}
+
链接编号: {codeNo}
+
+ );
+ }
+
+ if (!linkStatus) {
+ return 未找到链接信息
;
+ }
+
+ return (
+
+
+
游戏任务链接
+
链接编号: {linkStatus.codeNo}
+
+
+
+
+ 状态:
+
+ {linkStatus.statusDesc}
+
+
+
+
+ 任务信息:
+
+ 打{linkStatus.times}次副本,每次{linkStatus.quantity}点
+ (总计{linkStatus.totalPoints}点)
+
+
+
+
+ 过期时间:
+
+ {linkStatus.expireAt}
+
+
+
+
+ 剩余时间:
+
+ {formatTime(linkStatus.remainingSeconds)}
+
+
+
+
+ {linkStatus.isExpired && (
+
+ )}
+
+ {!linkStatus.isExpired && linkStatus.status === 'NEW' && (
+
+
开始任务
+
点击下方按钮开始执行任务
+
+
+ )}
+
+ {linkStatus.status === 'USING' && (
+
+
任务进行中
+
请按照提示完成游戏任务
+
+
+ )}
+
+ {linkStatus.status === 'LOGGED_IN' && (
+
+
任务完成
+
恭喜!任务已完成,奖励点数已发放
+
+ 获得奖励: {linkStatus.totalPoints} 点
+
+
+ )}
+
+
+
扫码访问
+

+
使用手机扫描二维码访问
+
+
+ );
+};
+
+const startTask = () => {
+ // 实现开始任务的逻辑
+ console.log('开始任务');
+};
+
+export default LinkPage;
+```
+
+### Vue 组件示例
+
+```vue
+
+
+
加载中...
+
+
+
链接错误
+
{{ error }}
+
链接编号: {{ codeNo }}
+
+
+
+
+
+
+
+ 状态:
+
+ {{ linkStatus.statusDesc }}
+
+
+
+
+ 任务信息:
+
+ 打{{ linkStatus.times }}次副本,每次{{ linkStatus.quantity }}点
+ (总计{{ linkStatus.totalPoints }}点)
+
+
+
+
+ 剩余时间:
+
+ {{ formatTime(linkStatus.remainingSeconds) }}
+
+
+
+
+
+
+
+
开始任务
+
+
+
+
+
扫码访问
+
![二维码]()
+
+
+
+
+
+
+```
+
+## 路由配置
+
+### React Router
+```jsx
+import { BrowserRouter, Routes, Route } from 'react-router-dom';
+import LinkPage from './components/LinkPage';
+
+function App() {
+ return (
+
+
+ } />
+ {/* 其他路由 */}
+
+
+ );
+}
+```
+
+### Vue Router
+```javascript
+import { createRouter, createWebHistory } from 'vue-router';
+import LinkPage from '@/components/LinkPage.vue';
+
+const routes = [
+ {
+ path: '/:codeNo',
+ name: 'LinkPage',
+ component: LinkPage
+ }
+];
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes
+});
+
+export default router;
+```
+
+## 使用流程
+
+1. **用户访问链接**:`https://你的域名/ABC12345`
+2. **前端自动请求**:调用 `/api/link/ABC12345/status` 获取链接信息
+3. **显示相应内容**:根据链接状态显示不同的界面
+4. **实时更新**:可以定时刷新状态,显示剩余时间等
+
+## 注意事项
+
+1. **错误处理**:处理链接不存在、已过期等情况
+2. **加载状态**:显示加载中的状态,提升用户体验
+3. **响应式设计**:确保在不同设备上都能正常显示
+4. **缓存策略**:可以适当缓存链接状态,减少请求次数
+5. **实时更新**:对于进行中的任务,可以定时刷新状态
+
diff --git a/docs/数据库修改总结.md b/docs/数据库修改总结.md
new file mode 100644
index 0000000..e5521e2
--- /dev/null
+++ b/docs/数据库修改总结.md
@@ -0,0 +1,150 @@
+# 数据库修改总结
+
+## 修改概述
+根据需求文档和用户要求,对游戏平台系统进行了以下主要修改:
+
+## 1. LinkBatch表结构调整
+
+### 修改前
+- `quantity`: 代表批次大小
+- `times`: 代表生成次数
+- `batchSize`: 每次的奖励点数
+- `deductPoints`: 扣除的点数
+
+### 修改后
+- `quantity`: 每次副本的奖励点数(从配置表获取,默认50)
+- `times`: 打副本的次数
+- 移除了 `batchSize` 和 `deductPoints` 字段
+
+### 字段含义
+- `quantity`: 每次副本完成后获得的点数,从配置表 `link.default_quantity` 获取
+- `times`: 一个批次中打副本的总次数
+- `agent_id`: 代理商ID
+- `operator_id`: 操作者ID
+- `created_at`: 创建时间
+
+### 参数说明
+- **`times`**: 打多少次副本,决定总的目标值
+- **`linkCount`**: 生成多少个链接,决定链接任务的数量(默认值:1)
+- **总目标值计算**: `times` × 配置表中的默认奖励点数
+
+## 2. 新增系统配置表
+
+### 表名:system_config
+用于存储系统各种配置参数,支持动态配置和运行时修改。
+
+#### 主要配置项
+**链接生成相关:**
+- `link.default_quantity`: 默认奖励点数(50)
+- `link.refresh_interval`: 链接刷新间隔(300秒)
+- `link.qr_expire_time`: 二维码过期时间(600秒)
+- `link.max_times_per_batch`: 每批次最大刷奖励次数(100)
+- `link.min_quantity`: 最小奖励点数(10)
+- `link.max_quantity`: 最大奖励点数(1000)
+
+**脚本服务器相关:**
+- `script.server_url`: 脚本服务器地址
+- `script.qr_path_template`: 二维码图片路径模板
+
+## 3. 代码结构更新
+
+### 新增文件
+1. **实体类**
+ - `SystemConfig.java` - 系统配置实体
+
+2. **Mapper接口**
+ - `SystemConfigMapper.java` - 系统配置数据访问接口
+
+3. **XML映射文件**
+ - `SystemConfigMapper.xml` - MyBatis映射配置
+
+4. **服务类**
+ - `SystemConfigService.java` - 系统配置业务逻辑服务
+
+5. **控制器**
+ - `SystemConfigController.java` - 系统配置REST API接口
+
+6. **DTO类**
+ - `SystemConfigRequest.java` - 配置创建/更新请求
+ - `SystemConfigResponse.java` - 配置查询响应
+ - `SystemConfigConverter.java` - 配置对象转换工具
+
+### 修改文件
+1. **LinkBatch.java** - 移除不需要的字段
+2. **LinkBatchMapper.xml** - 更新SQL映射
+3. **LinkGenerationService.java** - 使用配置服务,修复字段设置
+
+## 4. API接口
+
+### 系统配置管理接口
+- `GET /api/admin/config/list` - 获取配置列表
+- `GET /api/admin/config/key/{configKey}` - 根据键获取配置
+- `GET /api/admin/config/type/{configType}` - 根据类型获取配置
+- `POST /api/admin/config` - 创建配置
+- `PUT /api/admin/config/{id}` - 更新配置
+- `DELETE /api/admin/config/{id}` - 删除配置
+- `GET /api/admin/config/link/defaults` - 获取链接默认配置
+- `GET /api/admin/config/script/config` - 获取脚本配置
+
+## 5. 配置使用方式
+
+### 在服务中使用
+```java
+@Autowired
+private SystemConfigService systemConfigService;
+
+// 获取默认奖励点数
+Integer defaultQuantity = systemConfigService.getDefaultQuantity();
+
+// 获取刷新间隔
+Integer refreshInterval = systemConfigService.getRefreshInterval();
+```
+
+### 动态修改配置
+```java
+// 更新配置
+SystemConfig config = systemConfigService.getConfigByKey("link.default_quantity");
+config.setConfigValue("100");
+systemConfigService.updateConfig(config);
+```
+
+## 6. 数据库脚本
+
+### 执行顺序
+1. 执行 `docs/game.sql` 中的新表创建语句
+2. 系统会自动插入默认配置数据
+
+### 注意事项
+- 新表使用 `utf8mb4` 字符集
+- 配置键具有唯一性约束
+- 系统配置标记为 `is_system=1`,不建议删除
+
+## 7. 业务逻辑调整
+
+### LinkGenerationService
+- 使用配置服务获取过期时间等参数
+- 修复了字段设置问题
+- `quantity` 现在代表每次的奖励点数
+- `times` 代表总次数
+
+### 配置管理
+- 支持运行时动态修改配置
+- 提供完整的CRUD操作
+- 支持配置类型验证
+- 提供便捷的配置获取方法
+
+## 8. 后续扩展建议
+
+1. **缓存机制**: 对频繁访问的配置添加缓存
+2. **配置验证**: 在更新配置时添加值格式验证
+3. **配置变更日志**: 记录配置变更历史
+4. **配置导入导出**: 支持配置文件导入导出
+5. **权限控制**: 对敏感配置的修改权限控制
+
+## 9. 测试建议
+
+1. 测试配置的增删改查功能
+2. 验证LinkBatch表的字段映射
+3. 测试配置的动态修改是否生效
+4. 验证默认配置值的正确性
+5. 测试配置服务的异常处理
diff --git a/docs/系统配置说明.md b/docs/系统配置说明.md
new file mode 100644
index 0000000..86569f0
--- /dev/null
+++ b/docs/系统配置说明.md
@@ -0,0 +1,151 @@
+# 系统配置说明
+
+## 概述
+系统配置表用于存储游戏平台的各种配置参数,支持动态配置和运行时修改。
+
+## 配置表结构
+表名:`system_config`
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | bigint | 主键ID |
+| config_key | varchar(100) | 配置键(唯一) |
+| config_value | text | 配置值 |
+| config_type | varchar(50) | 配置类型:STRING, INTEGER, BOOLEAN, JSON |
+| description | varchar(500) | 配置描述 |
+| is_system | tinyint(1) | 是否系统配置(1是,0否) |
+| created_at | datetime(3) | 创建时间 |
+| updated_at | datetime(3) | 更新时间 |
+
+## 默认配置项
+
+### 链接生成相关配置
+- `link.default_quantity`: 每次副本的奖励点数(默认值:50)
+- `link.refresh_interval`: 链接刷新间隔,单位:秒(默认值:300)
+- `link.qr_expire_time`: 二维码过期时间,单位:秒(默认值:600)
+- `link.max_times_per_batch`: 每批次最大打副本次数(默认值:100)
+- `link.min_quantity`: 最小奖励点数(默认值:10)
+- `link.max_quantity`: 最大奖励点数(默认值:1000)
+
+### 脚本服务器相关配置
+- `script.server_url`: 脚本服务器地址(默认值:http://36.138.184.60:12345)
+- `script.qr_path_template`: 二维码图片路径模板(默认值:/{machineId}/二维码.png)
+
+## 参数说明
+
+### 链接生成接口参数
+- **`times`**: 打多少次副本,决定总的目标值
+- **`linkCount`**: 生成多少个链接,决定链接任务的数量(默认值:1)
+
+### 业务逻辑
+1. 每次副本的奖励点数从配置表 `link.default_quantity` 获取
+2. 总目标值 = `times` × 配置表中的默认奖励点数
+3. 生成 `linkCount` 个链接任务供用户使用
+4. 用户完成链接后,根据 `times` 和配置的奖励点数计算总奖励
+
+## API接口
+
+### 获取配置列表
+```
+GET /api/admin/config/list?page=1&size=20
+```
+
+### 根据键获取配置
+```
+GET /api/admin/config/key/{configKey}
+```
+
+### 根据类型获取配置
+```
+GET /api/admin/config/type/{configType}
+```
+
+### 创建配置
+```
+POST /api/admin/config
+Content-Type: application/json
+
+{
+ "configKey": "custom.setting",
+ "configValue": "value",
+ "configType": "STRING",
+ "description": "自定义设置",
+ "isSystem": false
+}
+```
+
+### 更新配置
+```
+PUT /api/admin/config/{id}
+Content-Type: application/json
+
+{
+ "configKey": "custom.setting",
+ "configValue": "new_value",
+ "configType": "STRING",
+ "description": "自定义设置",
+ "isSystem": false
+}
+```
+
+### 删除配置
+```
+DELETE /api/admin/config/{id}
+DELETE /api/admin/config/key/{configKey}
+```
+
+### 获取链接默认配置
+```
+GET /api/admin/config/link/defaults
+```
+
+### 获取脚本配置
+```
+GET /api/admin/config/script/config
+```
+
+## 使用示例
+
+### 在服务中使用配置
+```java
+@Autowired
+private SystemConfigService systemConfigService;
+
+// 获取默认奖励点数
+Integer defaultQuantity = systemConfigService.getDefaultQuantity();
+
+// 获取刷新间隔
+Integer refreshInterval = systemConfigService.getRefreshInterval();
+
+// 获取脚本服务器地址
+String serverUrl = systemConfigService.getScriptServerUrl();
+```
+
+### 动态修改配置
+```java
+// 更新配置
+SystemConfig config = systemConfigService.getConfigByKey("link.default_quantity");
+config.setConfigValue("100");
+systemConfigService.updateConfig(config);
+```
+
+## 注意事项
+
+1. **系统配置**:标记为系统配置的项(is_system=1)通常不建议删除,这些是平台运行必需的基础配置。
+
+2. **配置类型**:根据实际需要选择合适的配置类型,数值类型建议使用INTEGER,布尔值使用BOOLEAN。
+
+3. **配置键命名**:建议使用点号分隔的层次结构,如:`module.submodule.setting`。
+
+4. **配置值验证**:在更新配置时,建议对配置值进行格式验证,确保符合预期类型。
+
+5. **缓存策略**:对于频繁访问的配置,建议在服务层添加缓存机制,提高性能。
+
+## 扩展配置
+
+可以根据业务需要添加更多配置项,例如:
+- 用户权限相关配置
+- 游戏规则配置
+- 第三方服务配置
+- 日志级别配置
+- 性能调优参数
\ No newline at end of file
diff --git a/docs/链接状态接口测试.md b/docs/链接状态接口测试.md
new file mode 100644
index 0000000..a5ba8b3
--- /dev/null
+++ b/docs/链接状态接口测试.md
@@ -0,0 +1,275 @@
+# 链接状态接口测试文档
+
+## 接口概览
+
+新增了三个接口用于前端获取链接状态信息:
+
+1. **获取链接状态** - `GET /api/link/{codeNo}/status`
+2. **检查链接是否存在** - `GET /api/link/{codeNo}/exists`
+3. **检查链接是否有效** - `GET /api/link/{codeNo}/valid`
+
+## 测试步骤
+
+### 1. 准备工作
+
+首先需要生成一个链接用于测试:
+
+```bash
+# 1. 登录获取JWT令牌
+curl -X POST http://localhost:18080/api/auth/login \
+ -H "Content-Type: application/json" \
+ -d '{
+ "username": "your_username",
+ "password": "your_password"
+ }'
+```
+
+从响应中获取JWT令牌,后续请求都需要在Header中带上:
+```bash
+Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
+```
+
+### 2. 生成测试链接
+
+```bash
+# 2. 生成链接
+curl -X POST http://localhost:18080/api/link/generate \
+ -H "Authorization: Bearer YOUR_JWT_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "times": 5,
+ "linkCount": 2
+ }'
+```
+
+响应示例:
+```json
+{
+ "batchId": 123,
+ "deductPoints": 250,
+ "expireAt": "2024-01-15T16:30:00",
+ "codeNos": ["ABC12345", "DEF67890"]
+}
+```
+
+记录其中一个 `codeNo`,比如 `ABC12345`,用于后续测试。
+
+### 3. 测试获取链接状态接口
+
+```bash
+# 3. 获取链接状态
+curl -X GET http://localhost:18080/api/link/ABC12345/status \
+ -H "Accept: application/json"
+```
+
+**预期响应:**
+```json
+{
+ "codeNo": "ABC12345",
+ "batchId": 123,
+ "status": "NEW",
+ "statusDesc": "新建",
+ "expireAt": "2024-01-15T16:30:00",
+ "isExpired": false,
+ "remainingSeconds": 7200,
+ "quantity": 50,
+ "times": 5,
+ "totalPoints": 250,
+ "region": null,
+ "machineId": null,
+ "loginAt": null,
+ "createdAt": "2024-01-15T12:00:00",
+ "updatedAt": "2024-01-15T12:00:00"
+}
+```
+
+### 4. 测试检查链接是否存在接口
+
+```bash
+# 4. 检查链接是否存在
+curl -X GET http://localhost:18080/api/link/ABC12345/exists \
+ -H "Accept: application/json"
+```
+
+**预期响应:** `true`
+
+### 5. 测试检查链接是否有效接口
+
+```bash
+# 5. 检查链接是否有效
+curl -X GET http://localhost:18080/api/link/ABC12345/valid \
+ -H "Accept: application/json"
+```
+
+**预期响应:** `true`
+
+### 6. 测试错误情况
+
+#### 6.1 测试不存在的链接
+
+```bash
+# 测试不存在的链接
+curl -X GET http://localhost:18080/api/link/INVALID123/status \
+ -H "Accept: application/json"
+```
+
+**预期响应:**
+```json
+{
+ "timestamp": "2024-01-15T12:00:00.000+00:00",
+ "status": 400,
+ "error": "Bad Request",
+ "message": "链接不存在: INVALID123",
+ "path": "/api/link/INVALID123/status"
+}
+```
+
+#### 6.2 测试空链接编号
+
+```bash
+# 测试空链接编号
+curl -X GET http://localhost:18080/api/link//status \
+ -H "Accept: application/json"
+```
+
+**预期响应:** 404 Not Found
+
+## 状态值说明
+
+### 链接状态 (status)
+- `NEW` - 新建,未开始使用
+- `USING` - 使用中,正在执行任务
+- `LOGGED_IN` - 已登录,任务完成
+- `REFUNDED` - 已退款
+- `EXPIRED` - 已过期
+
+### 区域 (region)
+- `Q` - Q区
+- `V` - V区
+- `null` - 未分配区域
+
+## 测试数据验证
+
+### 1. 数值计算验证
+- `totalPoints` = `quantity` × `times`
+- `remainingSeconds` 应该大于0(如果未过期)
+- `isExpired` 应该与当前时间和 `expireAt` 一致
+
+### 2. 状态一致性验证
+- 如果 `status` 是 `EXPIRED`,那么 `isExpired` 应该是 `true`
+- 如果 `status` 是 `LOGGED_IN`,那么 `loginAt` 不应该为 `null`
+
+### 3. 时间验证
+- `createdAt` ≤ `updatedAt`
+- `expireAt` > `createdAt`
+- 如果 `loginAt` 不为空,那么 `loginAt` ≥ `createdAt`
+
+## 性能测试
+
+### 1. 响应时间测试
+```bash
+# 使用time命令测试响应时间
+time curl -X GET http://localhost:18080/api/link/ABC12345/status
+```
+
+### 2. 并发测试
+```bash
+# 使用ab工具进行并发测试
+ab -n 100 -c 10 http://localhost:18080/api/link/ABC12345/status
+```
+
+## 错误处理测试
+
+### 1. 数据库连接异常
+模拟数据库连接失败的情况,验证错误处理。
+
+### 2. 网络超时
+模拟网络延迟,验证超时处理。
+
+### 3. 内存不足
+模拟内存不足情况,验证系统稳定性。
+
+## 安全测试
+
+### 1. 权限验证
+```bash
+# 不带JWT令牌访问(应该被拦截)
+curl -X GET http://localhost:18080/api/link/ABC12345/status
+```
+
+### 2. SQL注入防护
+```bash
+# 测试SQL注入
+curl -X GET "http://localhost:18080/api/link/ABC12345'; DROP TABLE link_task; --/status"
+```
+
+### 3. XSS防护
+```bash
+# 测试XSS攻击
+curl -X GET "http://localhost:18080/api/link//status"
+```
+
+## 测试报告模板
+
+### 测试结果记录
+
+| 测试项目 | 测试结果 | 响应时间 | 备注 |
+|---------|---------|---------|------|
+| 获取链接状态 | ✅ 通过 | 45ms | 正常 |
+| 检查链接存在 | ✅ 通过 | 23ms | 正常 |
+| 检查链接有效 | ✅ 通过 | 28ms | 正常 |
+| 错误处理 | ✅ 通过 | 15ms | 正常 |
+| 性能测试 | ✅ 通过 | 平均35ms | 满足要求 |
+
+### 问题记录
+
+| 问题描述 | 严重程度 | 状态 | 备注 |
+|---------|---------|------|------|
+| 无 | - | - | - |
+
+### 建议改进
+
+1. 可以考虑添加缓存机制,减少数据库查询
+2. 可以添加链接访问统计功能
+3. 可以考虑添加链接状态变更通知功能
+
+## 自动化测试
+
+### 使用Postman Collection
+
+可以创建Postman Collection来自动化测试这些接口:
+
+1. 创建环境变量存储JWT令牌和链接编号
+2. 设置测试脚本验证响应数据
+3. 使用Postman Runner批量执行测试
+
+### 使用JUnit测试
+
+```java
+@SpringBootTest
+@AutoConfigureTestDatabase
+class LinkStatusControllerTest {
+
+ @Test
+ void testGetLinkStatus() {
+ // 测试获取链接状态
+ }
+
+ @Test
+ void testLinkNotExists() {
+ // 测试链接不存在的情况
+ }
+}
+```
+
+## 总结
+
+这些接口为前端提供了完整的链接状态查询功能,支持:
+
+1. **完整的状态信息** - 包括任务详情、过期时间、奖励点数等
+2. **实时状态计算** - 自动计算是否过期、剩余时间等
+3. **友好的错误处理** - 提供清晰的错误信息和状态码
+4. **高性能查询** - 使用响应式编程,支持高并发访问
+
+前端可以根据这些接口实现丰富的用户界面,提供良好的用户体验。
+
diff --git a/src/main/java/com/gameplatform/server/controller/admin/SystemConfigController.java b/src/main/java/com/gameplatform/server/controller/admin/SystemConfigController.java
new file mode 100644
index 0000000..2668ff9
--- /dev/null
+++ b/src/main/java/com/gameplatform/server/controller/admin/SystemConfigController.java
@@ -0,0 +1,120 @@
+package com.gameplatform.server.controller.admin;
+
+import com.gameplatform.server.model.dto.common.PageResult;
+import com.gameplatform.server.model.entity.admin.SystemConfig;
+import com.gameplatform.server.model.dto.admin.SystemConfigRequest;
+import com.gameplatform.server.model.dto.admin.SystemConfigResponse;
+import com.gameplatform.server.model.dto.admin.SystemConfigConverter;
+import com.gameplatform.server.service.admin.SystemConfigService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/admin/config")
+@Tag(name = "系统配置管理", description = "系统配置的增删改查接口")
+public class SystemConfigController {
+
+ @Autowired
+ private SystemConfigService systemConfigService;
+
+ @GetMapping("/list")
+ @Operation(summary = "获取配置列表", description = "分页获取所有系统配置")
+ public ResponseEntity> getConfigList(
+ @RequestParam(defaultValue = "1") int page,
+ @RequestParam(defaultValue = "20") int size) {
+
+ int offset = (page - 1) * size;
+ List configs = systemConfigService.getAllConfigs(size, offset);
+ long total = systemConfigService.getConfigCount();
+
+ List responses = configs.stream()
+ .map(SystemConfigConverter::toResponse)
+ .toList();
+
+ PageResult result = new PageResult<>();
+ result.setItems(responses);
+ result.setTotal(total);
+ result.setPage(page);
+ result.setSize(size);
+
+ return ResponseEntity.ok(result);
+ }
+
+ @GetMapping("/key/{configKey}")
+ @Operation(summary = "根据键获取配置", description = "根据配置键获取配置信息")
+ public ResponseEntity getConfigByKey(@PathVariable String configKey) {
+ SystemConfig config = systemConfigService.getConfigByKey(configKey);
+ if (config == null) {
+ return ResponseEntity.notFound().build();
+ }
+ return ResponseEntity.ok(SystemConfigConverter.toResponse(config));
+ }
+
+ @GetMapping("/type/{configType}")
+ @Operation(summary = "根据类型获取配置", description = "根据配置类型获取配置列表")
+ public ResponseEntity> getConfigsByType(@PathVariable String configType) {
+ List configs = systemConfigService.getConfigsByType(configType);
+ List responses = configs.stream()
+ .map(SystemConfigConverter::toResponse)
+ .toList();
+ return ResponseEntity.ok(responses);
+ }
+
+ @PostMapping
+ @Operation(summary = "创建配置", description = "创建新的系统配置")
+ public ResponseEntity createConfig(@RequestBody SystemConfigRequest request) {
+ SystemConfig systemConfig = SystemConfigConverter.toEntity(request);
+ boolean success = systemConfigService.createConfig(systemConfig);
+ return ResponseEntity.ok(success);
+ }
+
+ @PutMapping("/{id}")
+ @Operation(summary = "更新配置", description = "更新指定ID的系统配置")
+ public ResponseEntity updateConfig(@PathVariable Long id, @RequestBody SystemConfigRequest request) {
+ SystemConfig systemConfig = SystemConfigConverter.toEntity(request);
+ systemConfig.setId(id);
+ boolean success = systemConfigService.updateConfig(systemConfig);
+ return ResponseEntity.ok(success);
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "删除配置", description = "删除指定ID的系统配置")
+ public ResponseEntity deleteConfig(@PathVariable Long id) {
+ boolean success = systemConfigService.deleteConfig(id);
+ return ResponseEntity.ok(success);
+ }
+
+ @DeleteMapping("/key/{configKey}")
+ @Operation(summary = "根据键删除配置", description = "根据配置键删除系统配置")
+ public ResponseEntity deleteConfigByKey(@PathVariable String configKey) {
+ boolean success = systemConfigService.deleteConfigByKey(configKey);
+ return ResponseEntity.ok(success);
+ }
+
+ @GetMapping("/link/defaults")
+ @Operation(summary = "获取链接默认配置", description = "获取链接生成相关的默认配置")
+ public ResponseEntity