diff --git a/API接口文档_预约房间页面.md b/API接口文档_预约房间页面.md
deleted file mode 100644
index 626449e..0000000
--- a/API接口文档_预约房间页面.md
+++ /dev/null
@@ -1,390 +0,0 @@
-# API接口文档 - 预约房间页面 (book-room-page)
-
-## 文档说明
-
-本文档为 `uniapp/mahjong_group/pages/appointment/book-room-page.vue` 页面提供完整的API接口设计方案。
-
----
-
-## 📊 数据库设计评估
-
-### 现有字段(SQRooms表)
-✅ **已有字段:**
-- `id`: 房间ID
-- `name`: 房间名称
-- `price_per_hour`: 价格/小时
-- `capacity`: 可容纳人数
-- `status`: 可用状态 (bool)
-
-⚠️ **缺失字段(需要扩展):**
-- `image_url`: 房间图片URL
-- `room_type`: 房间类型(如:小包、中包、大包、豪华包)
-- `description`: 房间描述
-
-### 已新增字段
-
-```sql
--- 扩展 SQRooms 表,添加以下字段:
-ALTER TABLE SQRooms ADD image_url NVARCHAR(500) NULL;
-ALTER TABLE SQRooms ADD room_type NVARCHAR(50) NULL;
-ALTER TABLE SQRooms ADD description NVARCHAR(500) NULL;
-```
-
----
-
-## 📡 API接口设计
-
-### 1️⃣ 获取房间列表(带时段占用信息)
-
-**接口名称:** GetRoomListWithTimeSlots
-
-**请求方式:** `GET`
-
-**接口路径:** `/api/SQ/GetRoomListWithTimeSlots`
-
-**功能说明:** 获取指定日期的所有房间列表,包含房间详细信息、价格、当前状态以及时段占用情况
-
-#### 请求参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| date | long | 是 | 查询日期(Unix时间戳-秒级),传入当天0点的时间戳 |
-| showTimeSlots | bool | 否 | 是否返回时段占用信息(默认false) |
-
-**示例:**
-```
-GET /api/SQ/GetRoomListWithTimeSlots?date=1733443200&showTimeSlots=true
-```
-
-#### 响应数据
-
-```json
-{
- "code": 0,
- "msg": "ok",
- "data": [
- {
- "id": 1,
- "name": "304号-大包",
- "room_type": "大包",
- "image_url": "https://example.com/rooms/304.jpg",
- "price_per_hour": 30.00,
- "vip_price_per_hour": 25.00,
- "capacity": 6,
- "description": "豪华大包厢,配有独立卫生间",
- "status": "available", // available-可预约, using-使用中, unavailable-不可用
- "is_available": true,
- "time_slots": { // 仅当 showTimeSlots=true 时返回
- "dawn": { // 凌晨 0:00-6:00
- "is_occupied": true,
- "reservations": [
- { "start_time": "2025-12-06 02:00:00", "end_time": "2025-12-06 06:00:00" }
- ]
- },
- "morning": { // 上午 6:00-12:00
- "is_occupied": false,
- "reservations": []
- },
- "afternoon": { // 下午 12:00-18:00
- "is_occupied": true,
- "reservations": [
- { "start_time": "2025-12-06 14:00:00", "end_time": "2025-12-06 18:00:00" }
- ]
- },
- "evening": { // 晚上 18:00-24:00
- "is_occupied": false,
- "reservations": []
- }
- }
- },
- {
- "id": 2,
- "name": "305号-中包",
- "room_type": "中包",
- "image_url": "https://example.com/rooms/305.jpg",
- "price_per_hour": 20.00,
- "vip_price_per_hour": null,
- "capacity": 4,
- "description": "标准中包厢",
- "status": "using",
- "is_available": false,
- "time_slots": null
- }
- ]
-}
-```
-
-#### 响应字段说明
-
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| id | int | 房间ID |
-| name | string | 房间名称 |
-| room_type | string | 房间类型 |
-| image_url | string | 房间图片URL(若为空,前端显示默认图) |
-| price_per_hour | decimal | 标准价格/小时 |
-| vip_price_per_hour | decimal | 会员价/小时(null表示无会员价) |
-| capacity | int | 可容纳人数 |
-| description | string | 房间描述 |
-| status | string | 房间状态:
- `available`: 可预约
- `using`: 当前使用中
- `unavailable`: 不可用(维护中等) |
-| is_available | bool | 是否可预约 |
-| time_slots | object | 时段占用信息(仅当请求参数 showTimeSlots=true 时返回) |
-
----
-
-### 2️⃣ 新增获取可预约房间列表
-
-**接口名称:** GetReservationRoomListNew
-
-**请求方式:** `GET`
-
-**接口路径:** `/api/SQ/GetReservationRoomListNew`
-
-**功能说明:** 获取指定时间段内可预约的房间列表(不包含已预约和不可用时段的房间)
-
-#### 请求参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| startTime | long | 是 | 开始时间(Unix时间戳-秒级) |
-| endTime | long | 是 | 结束时间(Unix时间戳-秒级) |
-
-**示例:**
-```
-GET /api/SQ/GetReservationRoomListNew?startTime=1733457600&endTime=1733472000
-```
-
-#### 响应数据
-
-```json
-{
- "code": 0,
- "msg": "ok",
- "data": [
- {
- "id": 1,
- "name": "304号-大包",
- "room_type": "大包",
- "image_url": "https://example.com/rooms/304.jpg",
- "price_per_hour": 30.00,
- "vip_price_per_hour": 25.00,
- "capacity": 6,
- "description": "豪华大包厢",
- "display_name": "304号-大包 30.00/小时" // 用于前端直接显示
- },
- {
- "id": 3,
- "name": "306号-小包",
- "room_type": "小包",
- "image_url": null,
- "price_per_hour": 15.00,
- "vip_price_per_hour": null,
- "capacity": 4,
- "description": "标准小包厢",
- "display_name": "306号-小包 15.00/小时"
- }
- ]
-}
-```
-
----
-
-### 3️⃣ 获取营业时间配置
-
-**接口名称:** GetBusinessHours
-
-**请求方式:** `GET`
-
-**接口路径:** `/api/Common/GetBusinessHours`
-
-**功能说明:** 获取店铺营业时间配置
-
-#### 请求参数
-
-无
-
-#### 响应数据
-
-```json
-{
- "code": 0,
- "msg": "ok",
- "data": {
- "open_time": "09:00",
- "close_time": "23:00",
- "is_24_hours": false,
- "description": "早9点 至 晚23点"
- }
-}
-```
-
-#### 响应字段说明
-
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| open_time | string | 开始营业时间(HH:mm格式) |
-| close_time | string | 结束营业时间(HH:mm格式) |
-| is_24_hours | bool | 是否24小时营业 |
-| description | string | 营业时间描述文本 |
-
----
-
-### 4️⃣ 获取房间详情
-
-**接口名称:** GetRoomDetail
-
-**请求方式:** `GET`
-
-**接口路径:** `/api/SQ/GetRoomDetail`
-
-**功能说明:** 获取指定房间的详细信息
-
-#### 请求参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| roomId | int | 是 | 房间ID |
-| date | long | 否 | 查询日期(默认今天) |
-
-#### 响应数据
-
-```json
-{
- "code": 0,
- "msg": "ok",
- "data": {
- "id": 1,
- "name": "304号-大包",
- "room_type": "大包",
- "image_url": "https://example.com/rooms/304.jpg",
- "images": [ // 多图展示
- "https://example.com/rooms/304_1.jpg",
- "https://example.com/rooms/304_2.jpg"
- ],
- "price_per_hour": 30.00,
- "vip_price_per_hour": 25.00,
- "capacity": 6,
- "description": "豪华大包厢,配有独立卫生间、智能麻将桌、空调、Wi-Fi等设施",
- "amenities": ["独立卫生间", "智能麻将桌", "空调", "Wi-Fi"],
- "status": "available",
- "today_reservations": [ // 今日预约情况
- {
- "start_time": "2025-12-06 14:00:00",
- "end_time": "2025-12-06 18:00:00",
- "status": 0
- }
- ]
- }
-}
-```
-
----
-
-## 🔄 前端调用流程
-
-### 页面加载流程
-
-```javascript
-// 1. 页面初始化
-onLoad() {
- this.getDateList(); // 生成7天日期列表(前端)
- this.loadRoomList(); // 加载房间列表
- this.getBusinessHours(); // 获取营业时间
-}
-
-// 2. 加载房间列表(基础模式)
-async loadRoomList() {
- const selectedDate = this.dateList[this.currentTimeIndex];
- const timestamp = this.dateToTimestamp(selectedDate.time);
-
- const res = await uni.request({
- url: '/api/SQ/GetRoomListWithTimeSlots',
- data: {
- date: timestamp,
- showTimeSlots: this.showFreeTime // 根据开关决定是否获取时段
- }
- });
-
- this.roomList = res.data.data;
-}
-
-// 3. 切换日期时重新加载
-clickTime(index) {
- this.currentTimeIndex = index;
- this.loadRoomList();
-}
-
-// 4. 切换时段显示开关
-change(value) {
- this.showFreeTime = value;
- this.loadRoomList(); // 重新加载,包含时段信息
-}
-
-// 5. 点击预约按钮
-handleBookRoom(room) {
- if (room.status !== 'available') {
- uni.showToast({ title: '该房间不可预约', icon: 'none' });
- return;
- }
-
- // 跳转到预约详情页,传递房间信息
- uni.navigateTo({
- url: '/pages/appointment/appointment-page?' +
- 'roomId=' + room.id +
- '&roomName=' + encodeURIComponent(room.name) +
- '&date=' + this.dateList[this.currentTimeIndex].time
- });
-}
-```
-
-
-## ✅ 总结
-
-### 数据库设计评估结果
-
-**现有设计基本满足需求**,但建议扩展以下字段以提升用户体验:
-
-1. ⚠️ **必要扩展**(影响核心功能):
- - 无,现有字段可满足基本功能
-
-2. ✨ **建议扩展**(提升用户体验):
- - `image_url`: 房间图片
- - `room_type`: 房间类型
- - `vip_price_per_hour`: 会员价
- - `description`: 房间描述
-
-### API实现优先级
-
-| 优先级 | 接口 | 说明 |
-|--------|------|------|
-| 🔴 **P0** | GetRoomListWithTimeSlots | 核心接口,提供房间列表和时段信息 |
-| 🟡 **P1** | 优化 GetReservationRoomList | 返回更丰富的房间信息 |
-| 🟢 **P2** | GetBusinessHours | 营业时间配置 |
-| 🔵 **P3** | GetRoomDetail | 房间详情 |
-
----
-
-## 📝 注意事项
-
-1. **时区处理**:
- - 前端传递时间戳时需统一使用UTC+8时区
- - 后端 `DateTimeOffset.FromUnixTimeSeconds()` 自动处理时区转换
-
-2. **默认图片**:
- - 前端需准备默认房间图片:`/static/default-room.png`
- - 当 `image_url` 为空时使用默认图
-
-3. **缓存策略**:
- - 房间列表建议缓存5分钟
- - 时段信息实时查询,不缓存
-
-4. **性能优化**:
- - 首次加载不查询时段信息(showTimeSlots=false)
- - 仅当用户打开开关时再查询时段详情
-
----
-
-**文档版本**: v1.0
-**创建日期**: 2025-12-06
-**最后更新**: 2025-12-06
-
diff --git a/common/env.js b/common/env.js
index 2f79dc6..b0d7375 100644
--- a/common/env.js
+++ b/common/env.js
@@ -9,8 +9,8 @@ const development = {
// baseUrl: 'https://sqqp.zpc-xy.com',
// baseUrl: 'http://1.15.21.245:2401',
// host: ['https://sqqp.zpc-xy.com'],
- baseUrl: 'http://192.168.1.21:2016',
- host: ['http://192.168.1.21:2016'],
+ baseUrl: 'http://192.168.1.24:2016',
+ host: ['http://192.168.1.24:2016'],
imageUrl: 'https://guyu-1308826010.cos.ap-shanghai.myqcloud.com',
};
diff --git a/common/server/interface/common.js b/common/server/interface/common.js
index 55d3bcd..fd10550 100644
--- a/common/server/interface/common.js
+++ b/common/server/interface/common.js
@@ -59,16 +59,4 @@ export const uploadImages = async (formData) => {
return res.data;
}
return null;
-}
-
-/**
- * 获取营业时间配置
- * @returns {Promise} 返回营业时间信息 {open_time, close_time, is_24_hours, description}
- */
-export const getBusinessHours = async () => {
- const res = await request.get("Common/GetBusinessHours");
- if (res.code == 0) {
- return res.data;
- }
- return null;
}
\ No newline at end of file
diff --git a/common/server/interface/message.js b/common/server/interface/message.js
new file mode 100644
index 0000000..a57689e
--- /dev/null
+++ b/common/server/interface/message.js
@@ -0,0 +1,53 @@
+import request from '@/common/system/request';
+
+/**
+ * 获取消息列表
+ * @param {number} pageIndex 页码,从1开始
+ * @param {number} pageSize 每页数量
+ * @param {number} messageType 消息类型:0=全部,1=私信
+ * @returns {Promise}
+ */
+export const getMessageList = async (pageIndex = 1, pageSize = 20, messageType = 0) => {
+ const res = await request.getOrCache(
+ "sq/GetMessageList",
+ { pageIndex, pageSize, messageType },
+ 1 // 缓存1秒
+ );
+ if (res.code == 0) {
+ return res.data || [];
+ }
+ return [];
+}
+
+/**
+ * 获取未读消息数量
+ * @returns {Promise}
+ */
+export const getUnreadCount = async () => {
+ const res = await request.get("sq/GetUnreadCount");
+ if (res.code == 0) {
+ return res.data?.count || 0;
+ }
+ return 0;
+}
+
+/**
+ * 标记所有消息为已读
+ * @returns {Promise}
+ */
+export const markAllAsRead = async () => {
+ const res = await request.post("sq/MarkAllAsRead");
+ if (res.code == 0) {
+ return true;
+ }
+ return false;
+}
+
+// 导出消息接口对象
+export const messageInterface = {
+ getMessageList,
+ getUnreadCount,
+ markAllAsRead
+}
+
+export default messageInterface;
diff --git a/common/server/interface/sq.js b/common/server/interface/sq.js
index 6e53eef..f4d6a7c 100644
--- a/common/server/interface/sq.js
+++ b/common/server/interface/sq.js
@@ -141,6 +141,52 @@ export const getPaymentRecords = async (pageIndex = 1, pageSize = 20) => {
return null;
}
+/**
+ * 获取可选日期列表(今天+未来7天)
+ * @returns {Promise} [{date:时间戳(秒), dateText:"今天/明天/后天/日期", dateDisplay:"12月06日 周五"}]
+ */
+export const getAvailableDates = async () => {
+ const res = await request.get("sq/GetAvailableDates");
+ if (res.code == 0) {
+ return res.data;
+ }
+ return null;
+}
+
+/**
+ * 获取房间列表及时段状态(按时段预约版本)
+ * @param {number} date 预约日期 时间戳(秒)
+ * @param {boolean} showOnlyAvailable 是否只显示当前时段可用房间
+ * @param {number} currentTimeSlot 当前时段类型(0-3),配合showOnlyAvailable使用
+ * @returns {Promise} 房间列表,包含4个时段状态
+ */
+export const getRoomListWithSlotsNew = async (date, showOnlyAvailable = false, currentTimeSlot = null) => {
+ const params = { date: date };
+ if (showOnlyAvailable && currentTimeSlot !== null) {
+ params.showOnlyAvailable = showOnlyAvailable;
+ params.currentTimeSlot = currentTimeSlot;
+ }
+ const res = await request.get("sq/GetRoomListWithSlotsNew", params);
+ if (res.code == 0) {
+ return res.data;
+ }
+ return null;
+}
+
+/**
+ * 获取房间详情
+ * @param {number} roomId 房间ID
+ * @param {number} date 预约日期 时间戳(秒)
+ * @returns {Promise} 房间详情数据
+ */
+export const getRoomDetail = async (roomId, date) => {
+ const res = await request.get("sq/GetRoomDetail", { roomId: roomId, date: date });
+ if (res.code == 0) {
+ return res.data;
+ }
+ return null;
+}
+
/**
* 获取可预约的房间列表
* @param {number} startTime 开始时间 时间戳(秒)
@@ -254,41 +300,6 @@ export const checkInReservation = async (checkInData) => {
-/**
- * 获取房间列表(带时段占用信息)
- * @param {number} date 查询日期(Unix时间戳-秒级),传入当天0点的时间戳
- * @param {boolean} showTimeSlots 是否返回时段占用信息(默认false)
- * @returns {Promise} 返回房间列表
- */
-export const getRoomListWithTimeSlots = async (date, showTimeSlots = false) => {
- const res = await request.get("sq/GetRoomListWithTimeSlots", {
- date: date,
- showTimeSlots: showTimeSlots
- });
- if (res.code == 0) {
- return res.data;
- }
- return null;
-}
-
-/**
- * 获取可预约房间列表(新版,包含更丰富信息)
- * @param {number} startTime 开始时间(Unix时间戳-秒级)
- * @param {number} endTime 结束时间(Unix时间戳-秒级)
- * @returns {Promise} 返回房间列表,包含图片、类型、价格等详细信息
- */
-export const getReservationRoomListNew = async (startTime, endTime) => {
- const res = await request.get("sq/GetReservationRoomListNew", {
- startTime: startTime,
- endTime: endTime
- });
- if (res.code == 0) {
- return res.data;
- }
- return null;
-}
-
-
export const sqInterface = {
canCreateSQReservation,
getReservationList,
@@ -299,9 +310,10 @@ export const sqInterface = {
addEvaluateServices,
getReputationByUser,
getPaymentRecords,
+ getAvailableDates,
+ getRoomListWithSlotsNew,
+ getRoomDetail,
getReservationRoomList,
- getRoomListWithTimeSlots,
- getReservationRoomListNew,
addSQReservation,
joinReservation,
cancelReservation,
diff --git a/pages/appointment/appointment-page.vue b/pages/appointment/appointment-page.vue
index 20c8464..e2a6ed3 100644
--- a/pages/appointment/appointment-page.vue
+++ b/pages/appointment/appointment-page.vue
@@ -1,25 +1,35 @@
- 发起预约
+
-
-
-
+
+
+ {{ getDateDisplayText() }}
+
-
- {{ getDayDescription(reservationInfo.start_time * 1000) }}
-
+
+
+ 加载时段信息中...
+
+
+
+ 当前日期该房间暂无可预约时段
+
+
-
- {{ getDayDescription(reservationInfo.end_time * 1000) }}
-
-
-
- {{ getRoomPickerName() }}
-
+
+
+ {{ getLatestDateTimeDisplayText() }}
+
+
@@ -51,7 +61,6 @@
-
@@ -128,7 +137,14 @@
-
+ datePickerVisible = false" @close="() => datePickerVisible = false">
+ latestDateTimePickerVisible = false" @close="() => latestDateTimePickerVisible = false">
agePickerVisible = false"
@close="() => agePickerVisible = false">
@@ -165,7 +181,8 @@
+```
+
+### Q10: 如何实现实时刷新房间状态?
+
+**A:** 可以使用定时轮询或WebSocket:
+
+```javascript
+// 方式1:定时轮询(简单)
+let timer = null;
+
+onMounted(() => {
+ loadRooms();
+ timer = setInterval(() => {
+ loadRooms();
+ }, 30000); // 每30秒刷新一次
+});
+
+onUnmounted(() => {
+ if (timer) clearInterval(timer);
+});
+
+// 方式2:WebSocket(实时性更好,需要后端支持)
+// 连接WebSocket监听房间状态变化
+```
+
+---
+
+## 调试技巧
+
+### 1. 使用浏览器开发者工具
+
+#### 查看网络请求
+```
+F12 → Network → XHR
+- 查看请求URL、参数、响应
+- 检查状态码
+- 查看响应时间
+```
+
+#### 查看控制台日志
+```javascript
+// 添加详细日志
+console.log('请求参数:', data);
+console.log('响应数据:', response.data);
+console.error('错误信息:', error);
+```
+
+### 2. Postman测试接口
+
+#### 测试步骤
+1. 先调用登录接口获取Token
+2. 复制Token到环境变量
+3. 测试各个接口
+4. 保存常用请求到Collection
+
+#### 示例请求
+```
+GET http://localhost:5000/api/SQ/GetAvailableDates
+
+GET http://localhost:5000/api/SQ/GetRoomListWithSlotsNew?date=1733443200
+
+POST http://localhost:5000/api/SQ/AddSQReservationBySlot
+Headers:
+ Authorization: Bearer eyJhbGc...
+Body: { ... }
+```
+
+### 3. 常见错误排查
+
+| 错误 | 可能原因 | 解决方法 |
+|------|---------|---------|
+| 401 Unauthorized | Token过期或无效 | 重新登录获取Token |
+| 404 Not Found | 接口路径错误 | 检查URL是否正确 |
+| 500 Internal Server Error | 服务器内部错误 | 查看后端日志 |
+| CORS错误 | 跨域配置问题 | 联系后端配置CORS |
+| 参数错误 | 参数格式不正确 | 检查参数类型和值 |
+
+### 4. 开发环境代理配置
+
+#### Vite配置示例
+```javascript
+// vite.config.js
+export default {
+ server: {
+ proxy: {
+ '/api': {
+ target: 'http://localhost:5000',
+ changeOrigin: true
+ }
+ }
+ }
+}
+```
+
+#### Vue CLI配置示例
+```javascript
+// vue.config.js
+module.exports = {
+ devServer: {
+ proxy: {
+ '/api': {
+ target: 'http://localhost:5000',
+ changeOrigin: true
+ }
+ }
+ }
+}
+```
+
+---
+
+## 附录
+
+### A. 错误码对照表
+
+| 错误码 | 说明 | 处理建议 |
+|-------|------|---------|
+| 0 | 成功 | 继续业务流程 |
+| 400 | 业务错误 | 显示错误信息 |
+| 401 | 未授权 | 跳转到登录页 |
+| 402 | 时间冲突 | 提示用户选择其他时段 |
+| 403 | 权限不足 | 提示权限不足 |
+| 404 | 资源不存在 | 提示资源不存在 |
+| 500 | 系统错误 | 提示系统错误,稍后重试 |
+
+### B. 测试数据
+
+```javascript
+// 测试用日期时间戳
+const testDates = {
+ today: Math.floor(Date.now() / 1000),
+ tomorrow: Math.floor(Date.now() / 1000) + 86400,
+ nextWeek: Math.floor(Date.now() / 1000) + 604800
+};
+
+// 测试用房间ID
+const testRoomIds = [1, 2, 3];
+
+// 测试用时段类型
+const testTimeSlots = [0, 1, 2, 3];
+```
+
+### C. 联系方式
+
+**技术支持**:
+- 邮箱:jianweie@163.com
+- 文档版本:v2.0
+- 更新日期:2025-12-06
+
+---
+
+**祝开发顺利!** 🚀
+
diff --git a/历史需求/我的消息页面接口参数说明.md b/历史需求/我的消息页面接口参数说明.md
new file mode 100644
index 0000000..97829e8
--- /dev/null
+++ b/历史需求/我的消息页面接口参数说明.md
@@ -0,0 +1,197 @@
+# 我的消息页面接口参数说明
+
+## 📋 页面功能概述
+
+**页面路径**:`pages/me/my-message-page.vue`
+
+**主要功能**:
+- 显示用户的消息列表
+- 支持按类型筛选(全部/私信)
+- 展示消息的标题、正文内容和时间
+
+---
+
+## 🔌 接口需求
+
+### 接口1:获取消息列表
+
+#### 基本信息
+- **接口路径**:建议 `GET /api/user/GetMessageList` 或 `POST /api/user/GetMessageList`
+- **调用时机**:
+ - 页面初始化时
+ - 切换标签(全部/私信)时
+ - 下拉刷新时
+- **是否需要登录**:是(需要Token)
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 | 示例 |
+|-------|------|------|------|------|
+| pageIndex | number | 否 | 页码,从1开始 | 1 |
+| pageSize | number | 否 | 每页数量 | 20 |
+| messageType | number | 否 | 消息类型:0=全部,1=私信 | 0 |
+
+**请求参数说明**:
+- `messageType`:根据页面标签 `currentIndex` 传递
+ - `currentIndex = 0`(全部)→ `messageType = 0` 或不传
+ - `currentIndex = 1`(私信)→ `messageType = 1`
+
+#### 返回数据结构
+
+```typescript
+interface Response {
+ code: number; // 0=成功
+ msg: string; // 消息
+ data: MessageItem[]; // 消息列表
+}
+
+interface MessageItem {
+ id: number; // 消息ID(必填)
+ title: string; // 消息标题(必填)
+ content: string; // 消息正文内容(必填)
+ createTime: string; // 创建时间,格式:YYYY-MM-DD HH:mm 或时间戳(必填)
+ messageType: number; // 消息类型:0=系统消息,1=私信(可选)
+ isRead: boolean; // 是否已读(可选)
+}
+```
+
+#### 返回示例
+
+```json
+{
+ "code": 0,
+ "msg": "ok",
+ "data": [
+ {
+ "id": 1,
+ "title": "系统通知",
+ "content": "您的预约已成功,请按时到达。",
+ "createTime": "2025-01-01 11:12",
+ "messageType": 0,
+ "isRead": false
+ },
+ {
+ "id": 2,
+ "title": "私信消息",
+ "content": "您好,我想咨询一下房间预约的相关问题。",
+ "createTime": "2025-01-01 10:30",
+ "messageType": 1,
+ "isRead": true
+ }
+ ]
+}
+```
+
+---
+
+## 📝 页面数据映射
+
+### 当前页面显示字段
+
+根据 `my-message-page.vue` 模板代码分析:
+
+| 页面显示位置 | 对应字段 | 数据类型 | 说明 |
+|------------|---------|---------|------|
+| 第23行:标题 | `title` | string | 消息标题 |
+| 第25-27行:正文 | `content` | string | 消息正文内容 |
+| 第29-31行:时间 | `createTime` | string | 消息创建时间 |
+
+### 标签筛选逻辑
+
+| 标签索引 | 标签名称 | 对应参数值 | 说明 |
+|---------|---------|-----------|------|
+| 0 | 全部 | `messageType = 0` 或不传 | 显示所有消息 |
+| 1 | 私信 | `messageType = 1` | 只显示私信类型消息 |
+
+---
+
+## 🔄 接口调用建议
+
+### 1. 接口定义位置
+
+建议在 `common/server/interface/user.js` 中添加:
+
+```javascript
+/**
+ * 获取消息列表
+ * @param {number} pageIndex 页码
+ * @param {number} pageSize 每页数量
+ * @param {number} messageType 消息类型:0=全部,1=私信
+ * @returns {Promise}
+ */
+export const getMessageList = async (pageIndex = 1, pageSize = 20, messageType = 0) => {
+ const res = await request.getOrCache(
+ "user/GetMessageList",
+ { pageIndex, pageSize, messageType },
+ 1 // 缓存1秒
+ );
+ if (res.code == 0) {
+ return res.data;
+ }
+ return [];
+}
+```
+
+### 2. 页面调用示例
+
+```javascript
+import { getMessageList } from '@/common/server/interface/user.js'
+
+// 在 methods 中添加
+async loadMessageList() {
+ try {
+ const type = this.currentIndex === 0 ? 0 : 1;
+ const data = await getMessageList(1, 20, type);
+ this.messageList = data || [];
+ } catch (error) {
+ console.error('获取消息列表失败:', error);
+ uni.showToast({
+ title: '获取消息失败',
+ icon: 'none'
+ });
+ }
+}
+
+// 在 clickTab 方法中调用
+clickTab(index) {
+ this.currentIndex = index;
+ this.loadMessageList(); // 切换标签时重新加载
+}
+```
+
+---
+
+## ✅ 必填字段总结
+
+### 接口返回必须包含的字段:
+
+1. **id** (number) - 消息ID,用于唯一标识
+2. **title** (string) - 消息标题,显示在第23行
+3. **content** (string) - 消息正文,显示在第25-27行
+4. **createTime** (string) - 创建时间,显示在第29-31行
+
+### 可选但建议包含的字段:
+
+- **messageType** (number) - 用于前端筛选和分类
+- **isRead** (boolean) - 用于显示未读标识(如果后续需要)
+
+---
+
+## 📌 注意事项
+
+1. **时间格式**:建议后端返回格式化的时间字符串(如:`2025-01-01 11:12`),或返回时间戳由前端格式化
+2. **分页支持**:如果消息数量较多,建议支持分页加载
+3. **空数据处理**:当没有消息时,返回空数组 `[]`
+4. **错误处理**:接口失败时返回 `code != 0`,前端需要处理错误情况
+
+---
+
+## 🔍 后续扩展建议
+
+如果后续需要添加以下功能,可以考虑增加字段:
+
+- **未读消息数**:在标签上显示未读数量
+- **消息详情**:点击消息跳转到详情页,需要 `id` 字段
+- **删除消息**:需要 `id` 字段
+- **标记已读**:需要 `id` 和 `isRead` 字段
+
diff --git a/历史需求/消息功能前端接入说明.md b/历史需求/消息功能前端接入说明.md
new file mode 100644
index 0000000..bfa4632
--- /dev/null
+++ b/历史需求/消息功能前端接入说明.md
@@ -0,0 +1,252 @@
+# 消息功能前端接入说明
+
+## 完成时间
+2025-12-07
+
+## 已完成的工作
+
+### 1. API 接口文件
+**文件位置**:`common/server/interface/message.js`
+
+**包含的接口**:
+- `getMessageList(pageIndex, pageSize, messageType)` - 获取消息列表
+- `getUnreadCount()` - 获取未读消息数量
+- `markAllAsRead()` - 标记所有消息为已读
+
+**接口说明**:
+```javascript
+// 获取消息列表
+// pageIndex: 页码,从1开始,默认1
+// pageSize: 每页数量,默认20
+// messageType: 消息类型,0=全部,1=私信
+const messageList = await getMessageList(1, 20, 0);
+
+// 获取未读消息数量
+const unreadCount = await getUnreadCount();
+
+// 标记所有消息为已读
+const success = await markAllAsRead();
+```
+
+### 2. 消息页面
+**文件位置**:`pages/me/my-message-page.vue`
+
+**已实现的功能**:
+- ✅ 页面加载时自动获取消息列表
+- ✅ 页面加载时自动标记所有消息为已读
+- ✅ 标签切换(全部/私信)时重新加载对应消息
+- ✅ 显示消息标题、内容、时间
+- ✅ 显示未读消息红点标识
+- ✅ 空数据时显示提示
+- ✅ 加载状态显示
+- ✅ 时间格式化显示(支持时间戳和字符串格式)
+
+### 3. 数据绑定说明
+
+**消息对象结构**:
+```javascript
+{
+ id: 1, // 消息ID(必填)
+ title: "消息标题", // 标题(必填)
+ content: "消息内容", // 正文内容(必填)
+ createTime: "2025-12-07 10:30", // 创建时间(必填)
+ messageType: 0, // 消息类型:0=系统通知,1=私信
+ isRead: false // 是否已读
+}
+```
+
+---
+
+## 接口路径重要提示
+
+⚠️ **注意**:实际接口路径使用 `/api/sq/` 前缀,而不是需求文档中建议的 `/api/user/`
+
+**实际接口地址**:
+- `GET /api/sq/GetMessageList` - 获取消息列表
+- `GET /api/sq/GetUnreadCount` - 获取未读数量
+- `POST /api/sq/MarkAllAsRead` - 标记全部已读
+
+**原因**:站内信功能统一放在 SQ(预约)模块下管理,便于后续维护。
+
+---
+
+## 页面使用方式
+
+### 从其他页面跳转到消息页面
+
+```javascript
+// 跳转到消息页面
+uni.navigateTo({
+ url: '/pages/me/my-message-page'
+});
+```
+
+### 显示未读消息数量(红点提示)
+
+在"我的"页面或其他需要显示未读消息的地方:
+
+```vue
+
+
+
+
+ {{ unreadCount }}
+
+
+
+
+
+
+```
+
+---
+
+## 数据流程说明
+
+### 1. 用户进入消息页面
+1. 调用 `getMessageList(1, 20, 0)` 获取第一页"全部"消息
+2. 调用 `markAllAsRead()` 标记所有消息为已读
+3. 渲染消息列表
+
+### 2. 用户切换到"私信"标签
+1. `currentIndex` 变为 1
+2. 调用 `getMessageList(1, 20, 1)` 获取私信消息
+3. 更新消息列表显示
+
+### 3. 用户返回"我的"页面
+1. 未读消息数量应该变为 0(因为进入消息页面时已标记为已读)
+
+---
+
+## 后续优化建议
+
+### 短期优化(可选)
+1. **下拉刷新**:添加下拉刷新功能
+```vue
+
+
+
+```
+
+2. **上拉加载更多**:支持分页加载
+```javascript
+async loadMore() {
+ if (!this.hasMore || this.loading) return;
+ this.pageIndex++;
+ await this.loadMessageList();
+}
+```
+
+3. **消息详情页**:点击消息查看完整内容
+```javascript
+goToDetail(messageId) {
+ uni.navigateTo({
+ url: `/pages/me/message-detail?id=${messageId}`
+ });
+}
+```
+
+### 长期优化(可选)
+1. 消息删除功能
+2. 消息搜索功能
+3. 消息分类标签
+4. 推送通知集成
+
+---
+
+## 测试清单
+
+### 功能测试
+- [ ] 页面正常显示消息列表
+- [ ] 切换"全部/私信"标签功能正常
+- [ ] 未读消息显示红点
+- [ ] 时间格式正确显示
+- [ ] 空数据时显示"暂无消息"提示
+- [ ] 返回按钮功能正常
+
+### 接口测试
+- [ ] 能正常获取消息列表
+- [ ] 能正常获取未读数量
+- [ ] 能正常标记消息为已读
+
+### 异常测试
+- [ ] 网络异常时显示错误提示
+- [ ] 接口返回错误时不会崩溃
+- [ ] 数据为空时正常显示
+
+---
+
+## 常见问题
+
+### Q1: 为什么进入消息页面后未读数量还是显示?
+A: 需要在"我的"页面的 `onShow()` 生命周期中重新调用 `getUnreadCount()` 获取最新未读数量。
+
+### Q2: 时间格式显示不正确?
+A: 后端可能返回时间戳或字符串格式,前端 `formatTime()` 方法已做兼容处理,支持两种格式。
+
+### Q3: 切换标签后消息没有更新?
+A: 检查 `clickTab()` 方法是否正确调用了 `loadMessageList()`,并且 `pageIndex` 已重置为 1。
+
+### Q4: 接口返回404?
+A: 检查接口路径是否为 `/api/sq/GetMessageList`,确保后端已正确部署。
+
+---
+
+## 相关文档
+
+- [我的消息页面接口参数说明.md](./我的消息页面接口参数说明.md) - 详细接口参数说明
+- [站内信功能开发总结.md](./站内信功能开发总结.md) - 后端功能开发总结
+
+---
+
+## 联系支持
+
+如遇到问题,请检查:
+1. 后端是否已部署站内信功能
+2. 数据库表是否已创建
+3. Token 是否有效(需要登录)
+4. 网络请求是否正常
diff --git a/历史需求/站内信功能开发总结.md b/历史需求/站内信功能开发总结.md
new file mode 100644
index 0000000..450a1d9
--- /dev/null
+++ b/历史需求/站内信功能开发总结.md
@@ -0,0 +1,293 @@
+# 站内信功能开发总结
+
+## 开发完成时间
+2025-12-07
+
+## 功能概述
+新增独立的站内信消息系统,支持指定用户消息和全员广播,集成到预约系统的组局成功/失败自动通知。
+
+---
+
+## 一、数据库表
+
+### 1. SQMessage(站内信消息表)
+**位置**:`数据库\SqlServer\创建站内信表.sql`
+
+**字段说明**:
+- `id`:消息ID(主键,自增)
+- `user_id`:接收用户ID(全员广播时为NULL)
+- `target_type`:目标类型(0=指定用户,1=全员广播)
+- `title`:消息标题
+- `content`:消息正文
+- `message_type`:消息类型(0=系统通知,1=私信)
+- `is_read`:是否已读(仅对指定用户消息有效)
+- `sender_id`:发送者ID(后台管理员ID)
+- `related_type`:关联业务类型(1=组局)
+- `related_id`:关联业务ID
+- `created_at`:创建时间
+- `updated_at`:更新时间
+
+### 2. SQMessageRead(已读记录表)
+**用途**:专门记录全员广播消息的已读状态
+
+**字段说明**:
+- `id`:记录ID(主键,自增)
+- `message_id`:消息ID
+- `user_id`:用户ID
+- `read_at`:阅读时间
+
+**索引**:
+- 唯一索引:`IX_SQMessageRead_msg_user (message_id, user_id)` 防止重复
+- 普通索引:`IX_SQMessageRead_user_id` 查询优化
+
+---
+
+## 二、代码文件清单
+
+### Model层(实体)
+- `CoreCms.Net.Model\Entities\SQ\SQMessage.cs`
+- `CoreCms.Net.Model\Entities\SQ\SQMessageRead.cs`
+
+### Repository层(数据访问)
+- `CoreCms.Net.IRepository\SQ\ISQMessageRepository.cs`
+- `CoreCms.Net.IRepository\SQ\ISQMessageReadRepository.cs`
+- `CoreCms.Net.Repository\SQ\SQMessageRepository.cs`
+- `CoreCms.Net.Repository\SQ\SQMessageReadRepository.cs`
+
+### Services层(业务逻辑)
+- `CoreCms.Net.IServices\SQ\ISQMessageServices.cs`
+- `CoreCms.Net.Services\SQ\SQMessageServices.cs`
+
+### Controller层
+- **前端API**:`CoreCms.Net.Web.WebApi\Controllers\SQController.cs`(新增3个接口)
+- **后台管理**:`CoreCms.Net.Web.Admin\Controllers\SQ\SQMessageController.cs`
+
+### 后台页面
+- `CoreCms.Net.Web.Admin\wwwroot\views\sq\sqmessage\index.html`(消息列表页)
+- `CoreCms.Net.Web.Admin\wwwroot\views\sq\sqmessage\details.html`(消息详情页)
+- `CoreCms.Net.Web.Admin\wwwroot\views\sq\sqmessage\broadcast.html`(发送全员广播页)
+
+### 业务集成
+- `CoreCms.Net.Services\SQ\SQReservationsServices.cs`(修改,添加站内信通知)
+
+---
+
+## 三、前端API接口
+
+### 1. 获取消息列表
+**接口**:`GET /api/SQ/GetMessageList`
+**参数**:
+- `pageIndex`:页码(默认1)
+- `pageSize`:每页数量(默认20)
+- `messageType`:消息类型(0=全部,1=私信)
+
+**返回**:
+```json
+{
+ "code": 0,
+ "msg": "获取成功",
+ "data": [
+ {
+ "id": 1,
+ "title": "组局成功通知",
+ "content": "恭喜您!组局"周末欢乐局"已成功!",
+ "createTime": "2025-12-07 10:30",
+ "messageType": 0,
+ "isRead": false
+ }
+ ]
+}
+```
+
+### 2. 获取未读消息数量
+**接口**:`GET /api/SQ/GetUnreadCount`
+**返回**:
+```json
+{
+ "code": 0,
+ "msg": "获取成功",
+ "data": {
+ "count": 5
+ }
+}
+```
+
+### 3. 标记所有消息为已读
+**接口**:`POST /api/SQ/MarkAllAsRead`
+**返回**:
+```json
+{
+ "code": 0,
+ "msg": "标记成功"
+}
+```
+
+---
+
+## 四、后台管理功能
+
+### 1. 消息列表管理(已开发页面)
+**访问路径**:后台菜单 → SQ管理 → 站内信管理
+
+**功能特性**:
+- 查看所有消息记录
+- 筛选条件:消息ID、标题、目标类型(指定用户/全员广播)、消息类型(系统通知/私信)
+- 支持查看详情、删除、批量删除
+- 数据分页显示(可选10-200条/页)
+
+### 2. 发送全员广播(已开发页面)
+**操作方式**:
+1. 在消息列表页点击"发送全员广播"按钮
+2. 填写消息标题和内容
+3. 点击"立即发送",消息将发送给所有用户
+
+**接口**:`POST /api/SQMessage/SendBroadcast`
+
+**请求示例**:
+```json
+{
+ "title": "系统维护通知",
+ "content": "系统将于今晚22:00-23:00进行维护,请提前做好准备。",
+ "senderId": 1
+}
+```
+
+### 3. 其他管理接口
+**后台可调用的接口**:
+- `POST /api/SQMessage/SendToUser`:发送给指定用户(可通过API调用)
+- `POST /api/SQMessage/SendToUsers`:发送给多个用户(可通过API调用)
+- `POST /api/SQMessage/GetUserList`:查询用户列表(用于扩展发送对象选择)
+
+---
+
+## 五、自动通知场景
+
+### 1. 组局成功通知
+**触发时机**:预约系统调用 `NotifyReservationSuccessAsync`
+**通知内容**:
+```
+标题:组局成功通知
+内容:恭喜您!组局"{组局名称}"已成功!
+
+房间:{房间名称}
+时间:{开始时间}
+请准时到达,祝您游戏愉快!
+```
+
+### 2. 组局失败通知
+**触发时机**:预约系统调用 `NotifyReservationFailedAsync`
+**通知内容**:
+```
+标题:组局失败通知
+内容:您参与的组局"{组局名称}"因人数未满已自动解散。
+
+房间:{房间名称}
+时间:{开始时间}
+原因:{失败原因}
+```
+
+---
+
+## 六、查询逻辑说明
+
+### 用户消息列表查询
+合并查询:
+- 指定给当前用户的消息(`target_type=0` 且 `user_id=当前用户`)
+- 全员广播消息(`target_type=1`)
+
+### 已读状态判断
+- **私信**:直接读取 `SQMessage.is_read` 字段
+- **全员广播**:查询 `SQMessageRead` 表是否存在记录
+
+### 标记全部已读
+1. 更新私信:`UPDATE SQMessage SET is_read=1`
+2. 插入广播已读记录:`INSERT INTO SQMessageRead`
+
+---
+
+## 七、扩展功能建议
+
+### 短期扩展
+1. 消息详情页(点击消息查看完整内容)
+2. 删除消息功能
+3. 消息搜索功能
+4. 批量操作(批量标记已读/删除)
+
+### 长期扩展
+1. 消息分类标签
+2. 消息推送到手机端
+3. 消息提醒声音/震动设置
+4. 定时发送消息
+5. 消息模板管理
+
+---
+
+## 八、使用说明
+
+### 前端对接
+1. 在"我的"页面添加"我的消息"入口
+2. 调用 `GetUnreadCount` 接口显示红点
+3. 进入列表页调用 `GetMessageList` 获取消息
+4. 自动调用 `MarkAllAsRead` 标记已读
+
+### 后台使用
+1. 访问后台消息管理模块
+2. 可以手动发送通知给指定用户或全体用户
+3. 查看历史消息记录
+
+---
+
+## 九、注意事项
+
+1. **数据库**:请先执行 `数据库\SqlServer\创建站内信表.sql` 创建表
+2. **依赖注入**:系统已自动注入服务,无需手动配置
+3. **性能优化**:已添加索引,支持大数据量查询
+4. **消息类型**:
+ - `message_type=0`:系统通知(自动发送)
+ - `message_type=1`:私信(手动发送)
+5. **目标类型**:
+ - `target_type=0`:指定用户
+ - `target_type=1`:全员广播
+
+---
+
+## 十、测试建议
+
+### 功能测试
+1. 发送指定用户消息
+2. 发送全员广播消息
+3. 查看消息列表(分页、筛选)
+4. 标记已读功能
+5. 未读数量显示
+6. 组局成功/失败自动通知
+
+### 压力测试
+1. 大量用户同时查询消息
+2. 全员广播(1000+用户)
+3. 并发标记已读
+
+---
+
+## 完成状态
+
+### 代码开发
+✅ **100% 完成** - 所有代码文件已创建并实现
+
+### 需要完成的部署工作
+⏳ **待执行**:
+1. **数据库部署**:执行 `数据库\SqlServer\创建站内信表.sql` 创建表
+2. **依赖注入验证**:确认服务已正确注入(通常自动扫描已包含)
+3. **接口测试**:使用 Postman 测试所有接口
+4. **前端对接**:
+ - 注意接口路径是 `/api/SQ/GetMessageList`(不是 `/api/user/GetMessageList`)
+ - 实现消息列表页面
+ - 实现未读数量红点显示
+
+### 重要修正
+📝 **接口路径说明**:
+- 需求文档建议:`/api/user/GetMessageList`
+- 实际实现路径:`/api/SQ/GetMessageList`
+- 原因:统一放在 SQ 预约模块下管理
+- 前端对接时请使用实际路径
+
+详细检查清单请查看:`站内信功能完成情况检查.md`
diff --git a/我的收益页面接口参数说明.md b/我的收益页面接口参数说明.md
new file mode 100644
index 0000000..1981cd1
--- /dev/null
+++ b/我的收益页面接口参数说明.md
@@ -0,0 +1,608 @@
+# 我的收益页面接口参数说明
+
+## 📋 页面功能概述
+
+**页面路径**:`pages/me/my-earnings-page.vue`
+
+**主要功能**:
+1. 显示收益统计(待提取收益、已提取收益)
+2. 提现功能(申请提现、查看最高可提现金额)
+3. 查看收益规则说明
+4. 收益记录列表(时间、房号/房名、房费、收益)
+5. 提现记录列表(时间、提现金额、状态)
+
+---
+
+## 🔌 接口需求清单
+
+### 接口1:获取收益统计信息
+
+#### 基本信息
+- **接口路径**:建议 `GET /api/user/GetEarningsSummary` 或 `POST /api/user/GetEarningsSummary`
+- **调用时机**:
+ - 页面初始化时
+ - 提现成功后刷新
+- **是否需要登录**:是(需要Token)
+
+#### 请求参数
+无需参数(从Token中获取用户信息)
+
+#### 返回数据结构
+
+```typescript
+interface Response {
+ code: number; // 0=成功
+ msg: string; // 消息
+ data: {
+ pendingAmount: number; // 待提取收益(元)
+ extractedAmount: number; // 已提取收益(元)
+ }
+}
+```
+
+#### 返回示例
+
+```json
+{
+ "code": 0,
+ "msg": "ok",
+ "data": {
+ "pendingAmount": 120.50,
+ "extractedAmount": 500.00
+ }
+}
+```
+
+#### 页面映射
+- `pendingAmount` → 第19行:待提取收益(元)
+- `extractedAmount` → 第23行:已提取收益(元)
+
+---
+
+### 接口2:获取收益规则说明
+
+#### 基本信息
+- **接口路径**:建议 `GET /api/user/GetEarningsRule` 或 `POST /api/user/GetEarningsRule`
+- **调用时机**:
+ - 点击"点击查看收益规则"时(第35-37行)
+ - 打开规则弹窗时(第86-96行)
+- **是否需要登录**:否(可公开)
+
+#### 请求参数
+无需参数
+
+#### 返回数据结构
+
+```typescript
+interface Response {
+ code: number; // 0=成功
+ msg: string; // 消息
+ data: {
+ content: string; // 规则说明正文
+ }
+}
+```
+
+#### 返回示例
+
+```json
+{
+ "code": 0,
+ "msg": "ok",
+ "data": {
+ "content": "收益规则说明:\n1. 收益来源于房间预约成功后的分成\n2. 收益可随时提现\n3. 提现将在3-5个工作日内到账\n4. 最低提现金额为10元"
+ }
+}
+```
+
+#### 页面映射
+- `content` → 第89-93行:规则说明弹窗正文
+
+---
+
+### 接口3:获取收益记录列表
+
+#### 基本信息
+- **接口路径**:建议 `GET /api/user/GetEarningsRecordList` 或 `POST /api/user/GetEarningsRecordList`
+- **调用时机**:
+ - 页面初始化时(currentIndex = 0)
+ - 切换到"收益记录"标签时
+ - 下拉刷新时
+- **是否需要登录**:是(需要Token)
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 | 示例 |
+|-------|------|------|------|------|
+| pageIndex | number | 否 | 页码,从1开始 | 1 |
+| pageSize | number | 否 | 每页数量 | 20 |
+
+#### 返回数据结构
+
+```typescript
+interface Response {
+ code: number; // 0=成功
+ msg: string; // 消息
+ data: EarningsRecordItem[];
+}
+
+interface EarningsRecordItem {
+ id: number; // 记录ID(必填)
+ date: string; // 时间,格式:YYYY/MM/DD 或 YYYY-MM-DD(必填)
+ roomNumber: string; // 房号,如:305(必填)
+ roomName: string; // 房名,如:大包(必填)
+ roomFee: number; // 房费(元)(必填)
+ earnings: number; // 收益(元)(必填)
+ reservationId?: number; // 预约ID(可选)
+}
+```
+
+#### 返回示例
+
+```json
+{
+ "code": 0,
+ "msg": "ok",
+ "data": [
+ {
+ "id": 1,
+ "date": "2025/1/1",
+ "roomNumber": "305",
+ "roomName": "大包",
+ "roomFee": 12.00,
+ "earnings": 0.12,
+ "reservationId": 123
+ },
+ {
+ "id": 2,
+ "date": "2025/1/2",
+ "roomNumber": "306",
+ "roomName": "中包",
+ "roomFee": 10.00,
+ "earnings": 0.10,
+ "reservationId": 124
+ }
+ ]
+}
+```
+
+#### 页面映射
+- `date` → 第68行:时间
+- `roomNumber + roomName` → 第69行:305(大包)
+- `roomFee` → 第70行:¥12
+- `earnings` → 第71行:¥0.12
+
+**注意**:页面显示格式为 `{roomNumber}({roomName})`,如:`305(大包)`
+
+---
+
+### 接口4:获取提现记录列表
+
+#### 基本信息
+- **接口路径**:建议 `GET /api/user/GetWithdrawRecordList` 或 `POST /api/user/GetWithdrawRecordList`
+- **调用时机**:
+ - 切换到"提现记录"标签时(currentIndex = 1)
+ - 下拉刷新时
+- **是否需要登录**:是(需要Token)
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 | 示例 |
+|-------|------|------|------|------|
+| pageIndex | number | 否 | 页码,从1开始 | 1 |
+| pageSize | number | 否 | 每页数量 | 20 |
+
+#### 返回数据结构
+
+```typescript
+interface Response {
+ code: number; // 0=成功
+ msg: string; // 消息
+ data: WithdrawRecordItem[];
+}
+
+interface WithdrawRecordItem {
+ id: number; // 记录ID(必填)
+ date: string; // 时间,格式:YYYY/MM/DD 或 YYYY-MM-DD(必填)
+ amount: number; // 提现金额(元)(必填)
+ status: string; // 状态:提现中/已到账/已拒绝(必填)
+ statusCode?: number; // 状态码:0=提现中,1=已到账,2=已拒绝(可选)
+ remark?: string; // 备注(可选)
+}
+```
+
+#### 返回示例
+
+```json
+{
+ "code": 0,
+ "msg": "ok",
+ "data": [
+ {
+ "id": 1,
+ "date": "2025/1/1",
+ "amount": 100.00,
+ "status": "提现中",
+ "statusCode": 0
+ },
+ {
+ "id": 2,
+ "date": "2024/12/30",
+ "amount": 50.00,
+ "status": "已到账",
+ "statusCode": 1
+ },
+ {
+ "id": 3,
+ "date": "2024/12/28",
+ "amount": 200.00,
+ "status": "已拒绝",
+ "statusCode": 2,
+ "remark": "银行卡信息错误"
+ }
+ ]
+}
+```
+
+#### 页面映射
+- `date` → 第77行:时间
+- `amount` → 第78行:¥12
+- `status` → 第79行:提现中
+
+---
+
+### 接口5:申请提现
+
+#### 基本信息
+- **接口路径**:建议 `POST /api/user/ApplyWithdraw`
+- **调用时机**:
+ - 点击"申请提现"按钮时(第114行)
+- **是否需要登录**:是(需要Token)
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 | 示例 |
+|-------|------|------|------|------|
+| amount | number | 是 | 提现金额(元) | 100.00 |
+
+#### 返回数据结构
+
+```typescript
+interface Response {
+ code: number; // 0=成功,其他=失败
+ msg: string; // 消息说明
+ data?: {
+ withdrawId?: number; // 提现记录ID(可选)
+ }
+}
+```
+
+#### 返回示例
+
+```json
+// 成功
+{
+ "code": 0,
+ "msg": "提现申请已提交",
+ "data": {
+ "withdrawId": 123
+ }
+}
+
+// 失败 - 余额不足
+{
+ "code": 500,
+ "msg": "提现金额不能超过待提取收益"
+}
+
+// 失败 - 金额过小
+{
+ "code": 500,
+ "msg": "最低提现金额为10元"
+}
+```
+
+---
+
+### 接口6:获取最高可提现金额(可选)
+
+#### 基本信息
+- **接口路径**:建议 `GET /api/user/GetMaxWithdrawAmount` 或使用接口1的 `pendingAmount`
+- **调用时机**:
+ - 打开提现弹窗时(第98-118行)
+ - 用于显示"最高可提现0.00元"(第108行)
+- **是否需要登录**:是(需要Token)
+
+#### 说明
+此接口可以复用**接口1(获取收益统计信息)**中的 `pendingAmount` 字段,无需单独接口。
+
+如果后端需要单独接口,可参考接口1的返回结构,只返回 `pendingAmount`。
+
+---
+
+## 📝 页面数据映射总览
+
+### 收益统计卡片(第13-32行)
+
+| 显示位置 | 数据来源 | 字段名 |
+|---------|---------|--------|
+| 第19行:待提取收益 | 接口1 | `pendingAmount` |
+| 第23行:已提取收益 | 接口1 | `extractedAmount` |
+| 第108行:最高可提现 | 接口1 | `pendingAmount` |
+
+### 收益记录列表(第65-73行,currentIndex = 0)
+
+| 显示位置 | 数据来源 | 字段名 | 显示格式 |
+|---------|---------|--------|---------|
+| 第68行:时间 | 接口3 | `date` | 直接显示 |
+| 第69行:房号/房名 | 接口3 | `roomNumber` + `roomName` | `{roomNumber}({roomName})` |
+| 第70行:房费 | 接口3 | `roomFee` | `¥{roomFee}` |
+| 第71行:收益 | 接口3 | `earnings` | `¥{earnings}` |
+
+### 提现记录列表(第74-81行,currentIndex = 1)
+
+| 显示位置 | 数据来源 | 字段名 | 显示格式 |
+|---------|---------|--------|---------|
+| 第77行:时间 | 接口4 | `date` | 直接显示 |
+| 第78行:提现金额 | 接口4 | `amount` | `¥{amount}` |
+| 第79行:状态 | 接口4 | `status` | 直接显示 |
+
+### 规则说明弹窗(第86-96行)
+
+| 显示位置 | 数据来源 | 字段名 |
+|---------|---------|--------|
+| 第89-93行:规则正文 | 接口2 | `content` |
+
+---
+
+## 🔄 接口调用建议
+
+### 1. 接口定义位置
+
+建议在 `common/server/interface/user.js` 中添加:
+
+```javascript
+/**
+ * 获取收益统计信息
+ * @returns {Promise<{pendingAmount: number, extractedAmount: number}>}
+ */
+export const getEarningsSummary = async () => {
+ const res = await request.getOrCache("user/GetEarningsSummary", {}, 1);
+ if (res.code == 0) {
+ return res.data;
+ }
+ return { pendingAmount: 0, extractedAmount: 0 };
+}
+
+/**
+ * 获取收益规则说明
+ * @returns {Promise}
+ */
+export const getEarningsRule = async () => {
+ const res = await request.getOrCache("user/GetEarningsRule", {}, 300);
+ if (res.code == 0) {
+ return res.data.content;
+ }
+ return '';
+}
+
+/**
+ * 获取收益记录列表
+ * @param {number} pageIndex 页码
+ * @param {number} pageSize 每页数量
+ * @returns {Promise}
+ */
+export const getEarningsRecordList = async (pageIndex = 1, pageSize = 20) => {
+ const res = await request.getOrCache(
+ "user/GetEarningsRecordList",
+ { pageIndex, pageSize },
+ 1
+ );
+ if (res.code == 0) {
+ return res.data;
+ }
+ return [];
+}
+
+/**
+ * 获取提现记录列表
+ * @param {number} pageIndex 页码
+ * @param {number} pageSize 每页数量
+ * @returns {Promise}
+ */
+export const getWithdrawRecordList = async (pageIndex = 1, pageSize = 20) => {
+ const res = await request.getOrCache(
+ "user/GetWithdrawRecordList",
+ { pageIndex, pageSize },
+ 1
+ );
+ if (res.code == 0) {
+ return res.data;
+ }
+ return [];
+}
+
+/**
+ * 申请提现
+ * @param {number} amount 提现金额
+ * @returns {Promise}
+ */
+export const applyWithdraw = async (amount) => {
+ const res = await request.post("user/ApplyWithdraw", { amount });
+ if (res.code == 0) {
+ return true;
+ }
+ return false;
+}
+```
+
+### 2. 页面调用示例
+
+```javascript
+import {
+ getEarningsSummary,
+ getEarningsRule,
+ getEarningsRecordList,
+ getWithdrawRecordList,
+ applyWithdraw
+} from '@/common/server/interface/user.js'
+
+export default {
+ data() {
+ return {
+ currentIndex: 0,
+ show: false,
+ reflectShow: false,
+ dataList: [],
+ value: '',
+ pendingAmount: 0.00,
+ extractedAmount: 0.00,
+ maxWithdrawAmount: 0.00,
+ ruleContent: ''
+ }
+ },
+ onLoad() {
+ this.loadEarningsSummary();
+ this.loadRuleContent();
+ this.loadDataList();
+ },
+ methods: {
+ // 加载收益统计
+ async loadEarningsSummary() {
+ const data = await getEarningsSummary();
+ if (data) {
+ this.pendingAmount = data.pendingAmount || 0.00;
+ this.extractedAmount = data.extractedAmount || 0.00;
+ this.maxWithdrawAmount = data.pendingAmount || 0.00;
+ }
+ },
+
+ // 加载规则内容
+ async loadRuleContent() {
+ this.ruleContent = await getEarningsRule();
+ },
+
+ // 加载列表数据
+ async loadDataList() {
+ if (this.currentIndex === 0) {
+ // 收益记录
+ this.dataList = await getEarningsRecordList(1, 20);
+ } else {
+ // 提现记录
+ this.dataList = await getWithdrawRecordList(1, 20);
+ }
+ },
+
+ // 切换标签
+ clickTab(index) {
+ this.currentIndex = index;
+ this.loadDataList();
+ },
+
+ // 打开提现弹窗
+ openWithdrawPopup() {
+ this.reflectShow = true;
+ this.loadEarningsSummary(); // 刷新最高可提现金额
+ },
+
+ // 申请提现
+ async submitWithdraw() {
+ const amount = parseFloat(this.value);
+ if (!amount || amount <= 0) {
+ uni.showToast({
+ title: '请输入正确的提现金额',
+ icon: 'none'
+ });
+ return;
+ }
+
+ if (amount > this.maxWithdrawAmount) {
+ uni.showToast({
+ title: '提现金额不能超过待提取收益',
+ icon: 'none'
+ });
+ return;
+ }
+
+ const success = await applyWithdraw(amount);
+ if (success) {
+ uni.showToast({
+ title: '提现申请已提交',
+ icon: 'success'
+ });
+ this.reflectShow = false;
+ this.value = '';
+ // 刷新数据
+ this.loadEarningsSummary();
+ if (this.currentIndex === 1) {
+ this.loadDataList();
+ }
+ } else {
+ uni.showToast({
+ title: '提现申请失败',
+ icon: 'none'
+ });
+ }
+ },
+
+ // 全部提现
+ allWithdraw() {
+ this.value = this.maxWithdrawAmount.toFixed(2);
+ }
+ }
+}
+```
+
+---
+
+## ✅ 必填字段总结
+
+### 接口1:获取收益统计
+- `pendingAmount` (number) - 待提取收益
+- `extractedAmount` (number) - 已提取收益
+
+### 接口2:获取收益规则
+- `content` (string) - 规则说明正文
+
+### 接口3:获取收益记录列表
+- `id` (number) - 记录ID
+- `date` (string) - 时间
+- `roomNumber` (string) - 房号
+- `roomName` (string) - 房名
+- `roomFee` (number) - 房费
+- `earnings` (number) - 收益
+
+### 接口4:获取提现记录列表
+- `id` (number) - 记录ID
+- `date` (string) - 时间
+- `amount` (number) - 提现金额
+- `status` (string) - 状态
+
+### 接口5:申请提现
+- 请求参数:`amount` (number) - 提现金额
+
+---
+
+## 📌 注意事项
+
+1. **金额格式**:所有金额字段建议使用 `number` 类型,保留2位小数
+2. **时间格式**:建议后端返回格式化的时间字符串(如:`2025/1/1` 或 `2025-01-01`)
+3. **分页支持**:收益记录和提现记录建议支持分页加载
+4. **空数据处理**:当没有记录时,返回空数组 `[]`
+5. **错误处理**:接口失败时返回 `code != 0`,前端需要处理错误情况
+6. **提现金额验证**:
+ - 前端需要验证:不能超过 `pendingAmount`
+ - 后端需要验证:最低提现金额、余额是否充足等
+7. **状态显示**:提现状态建议使用中文显示(提现中/已到账/已拒绝)
+
+---
+
+## 🔍 后续扩展建议
+
+如果后续需要添加以下功能,可以考虑增加字段:
+
+- **收益详情**:点击收益记录查看详情,需要 `reservationId` 字段
+- **提现详情**:点击提现记录查看详情,需要 `remark` 字段
+- **提现方式**:如果支持多种提现方式,需要增加提现方式字段
+- **收益统计图表**:如果需要图表展示,可以增加按时间段的统计数据
+
diff --git a/收益 需求文档.md b/收益 需求文档.md
new file mode 100644
index 0000000..f41a300
--- /dev/null
+++ b/收益 需求文档.md
@@ -0,0 +1,41 @@
+我的收益
+(1)入口
+1.我的页面,新增【我的收益】。
+
+站内信 入口
+(2)详情页
+
+我的收益 详情页
+1.待提现收益,未提现的剩余金额。
+2.已提现收益,所有已提现的金额总和。
+3.点击【查看规则】,弹出“规则说明弹窗”。
+
+弹窗说明
+3.1.内容后台配置。
+4.收益记录,展示每次收益的数据。
+
+收益记录
+5.提现记录,展示每次提现的数据。
+
+提现记录
+5.1.多种状态,“提现中”“已提现”“已取消”。
+5.1.1.提现中:申请后,处于该状态。
+5.1.2.已提现:已提现,线下打款后处于该状态。
+5.1.3.已取消:后台取消/拒绝该提现申请。
+(3)收益如何获取
+1.线下员工通过后台,给发起者的账号添加佣金。
+1.1.只有发起者有佣金,参与者没有。
+2.佣金 = 房费10%。
+(4)提现
+1.点击【去提现】,弹出提现弹窗。
+
+提现弹窗
+1.1.支持小数点后两位。
+1.2.点击【全部提现】,自动输入所有待提现金额。
+1.3.点击【申请提现】:
+1.3.1.高于待提现金额,弹出系统提示“超出可提现金额”。
+1.3.2.无其他问题,关闭弹窗,弹出系统提示“已提交申请”,页面自动刷新,更新待提现金额。
+2.后台展示收到的提现申请记录,可对申请进行操作。
+2.1.同意:同意该申请,线下联系客户打款。
+2.2.拒绝/取消:因其他原因拒绝该申请。
+2.3.已打款:通过线下转帐后,将该记录手动改变为本状态。
\ No newline at end of file