diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 0000000..8183f53
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,9 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(tree:*)",
+ "Bash(find:*)",
+ "Bash(grep:*)"
+ ]
+ }
+}
diff --git a/PROJECT_DESCRIPTION.md b/PROJECT_DESCRIPTION.md
new file mode 100644
index 0000000..65ff5f2
--- /dev/null
+++ b/PROJECT_DESCRIPTION.md
@@ -0,0 +1,744 @@
+# 麻将组局预约小程序项目说明
+
+## 项目概述
+
+这是一个**麻将组局预约小程序**,帮助麻将爱好者在线上发起、加入麻将局,线下到店进行游戏。小程序提供了完整的预约流程管理,包括房间选择、预约发起、参与管理、签到评价等功能。
+
+**技术栈:**
+- **前端**: UniApp(支持多端发布)
+- **后端**: .NET Core + SqlSugar ORM
+- **数据库**: Microsoft SQL Server
+
+---
+
+## 核心功能模块
+
+### 1. 首页预约列表
+
+**文件位置:**
+- 前端: `uniapp/mahjong_group/pages/index/index.vue`
+- 后端: `server/CoreCms.Net.Web.WebApi/Controllers/SQController.cs:170` (GetReservationList)
+
+**功能描述:**
+- 展示所有未结束的预约列表(按开始时间升序)
+- 自动过滤黑名单用户发起的预约
+- 分页加载,支持下拉刷新
+- 卡片式展示预约信息(标题、时间、房间、人数、玩法等)
+
+**关键逻辑:**
+```sql
+-- 查询未结束的预约(status < 3,end_time > now)
+-- 如果用户已登录,排除黑名单用户发起的预约
+-- 按开始时间升序排列
+```
+
+---
+
+### 2. 发起预约
+
+**文件位置:**
+- 前端: `uniapp/mahjong_group/pages/appointment/appointment-page.vue`
+- 后端API: `SQController.cs:524` (AddSQReservation)
+
+**预约流程:**
+
+#### 第一步:选择房间和日期
+- 页面: `book-room-page.vue`
+- API: `GetRoomListWithSlotsNew` (获取房间列表及时段状态)
+- 支持按**四个时段**查看房间可用性:
+ - 凌晨: 00:00-06:00
+ - 上午: 06:00-12:00
+ - 下午: 12:00-18:00
+ - 晚上: 18:00-00:00
+
+#### 第二步:填写预约信息
+填写内容包括:
+
+**基本信息:**
+- 组局名称
+- 人数(最多4人)
+- 玩法类型(血战、血流成河等)
+- 具体规则(几番起胡、几倍封顶等)
+- 其他补充说明
+
+**参与者限制:**
+- 是否禁烟(可选)
+- 性别限制(不限/男/女)
+- 年龄范围(最小年龄-最大年龄)
+- 信誉要求(0.0-5.0分)
+
+**鸽子费(押金):**
+- 固定金额(0元、5元、10元、20元)
+- 自定义金额(0-50元)
+- 说明: 参与者需缴纳押金,爽约者押金由到场者平分,预约完成后全额返还
+
+#### 第三步:提交预约
+1. 调用 `canCreateSQReservation` 验证是否可以创建
+2. 如有鸽子费,调用 `usePay` 发起微信支付
+3. 调用 `addSQReservation` 创建预约记录
+4. 创建发起者的参与记录(role=1)
+
+**验证规则:**
+- 房间是否存在且可用
+- 时间段是否已被预约
+- 用户是否有时间冲突的预约
+- 房间在该时段是否有不可用时间
+
+---
+
+### 3. 加入预约
+
+**后端API:** `SQController.cs:804` (JoinReservation)
+
+**加入流程:**
+1. 校验预约是否存在且未结束
+2. 校验用户是否已加入
+3. 校验是否为"独享模式"(无需组局)
+4. 校验用户是否符合参与条件:
+ - 信誉分是否达到要求
+ - 性别是否符合限制
+ - 年龄是否在范围内
+5. 校验是否有时间冲突的预约
+6. 校验预约是否已满员
+7. 如有鸽子费,验证支付信息
+8. 创建参与者记录(role=0)
+
+**重要提示:**
+- 参与者需满足发起者设置的所有限制条件
+- 加入预约后可以取消,但在开始前30分钟内无法取消
+- 如有鸽子费需先完成支付
+
+---
+
+### 4. 预约签到
+
+**后端API:** `SQController.cs:1207` (CheckInReservation)
+
+**签到权限:** 仅发起者可操作
+
+**签到时机:** 预约开始后,由发起者确认实际到场人员
+
+**签到流程:**
+1. 发起者在预约详情页点击"签到"按钮
+2. 勾选实际到场的参与者(发起者默认到场)
+3. 提交签到
+
+**签到效果:**
+- 预约状态变更为"进行中"(status=2)
+- 到场人员标记为`is_arrive=1`
+- 未到场人员标记为`is_arrive=2`并退出预约
+- 爽约者扣除0.5信誉分,增加鸽子次数
+- 到场者增加0.2信誉分(最高5.0)
+- 如有鸽子费,到场者标记为"发起退款"状态
+
+**信誉系统:**
+```javascript
+// 爽约处罚
+credit_score -= 0.5
+dove_count++
+
+// 守约奖励(信誉<5.0时)
+credit_score += 0.2 (最高5.0)
+```
+
+---
+
+### 5. 评价系统
+
+**后端API:**
+- 获取评价列表: `SQController.cs:252` (GetEvaluateServices)
+- 添加评价: `SQController.cs:330` (AddEvaluateServices)
+
+**评价条件:**
+- 预约已完成且已签到
+- 只能评价实际到场的参与者
+- 每个参与者只能被评价一次
+
+**评价维度:**
+- 游戏水平(play_level): 1-5分
+- 技能水平(skills_level): 1-5分
+
+**评价计算:**
+用户的最终评分采用加权平均:
+```javascript
+// 初始值为4分
+play_level = (sum(评价分数) + 4) / (评价次数 + 1)
+skills_level = (sum(评价分数) + 4) / (评价次数 + 1)
+
+// 如果只有1次评价,分母+1避免偏差
+if (评价次数 == 1) {
+ 分母 = 评价次数 + 2
+}
+```
+
+**评价作用:**
+- 其他用户可在预约详情看到参与者的平均评分
+- 评分影响用户在列表中的展示排序
+- 高评分用户更容易被其他人接受加入
+
+---
+
+### 6. 取消预约
+
+**后端API:** `SQController.cs:1036` (CancelReservation)
+
+**取消规则:**
+
+**发起者取消:**
+- 预约开始前30分钟内无法取消
+- 取消后预约状态变为"已取消"(status=4)
+- 所有参与者自动退出
+- 如有押金,已支付者发起退款
+- 通知所有参与者"发起者解散组局"
+
+**参与者取消:**
+- 预约开始前30分钟内无法取消
+- 只退出自己,不影响其他人
+- 如有押金,发起退款
+
+**退款状态说明:**
+- `is_refund=1`: 待支付
+- `is_refund=2`: 已支付
+- `is_refund=3`: 待退款(发起退款流程)
+- `is_refund=4`: 已退款
+- `is_refund=5`: 退款异常
+
+---
+
+### 7. 我的预约记录
+
+**后端API:** `SQController.cs:120` (GetMyReservation)
+
+**分类查看:**
+- `type=0`: 我参与的预约(role=0)
+- `type=1`: 我发起的预约(role=1)
+
+**记录状态:**
+- 待开始(status=0)
+- 已锁定(status=1)- 人满
+- 进行中(status=2)- 已签到
+- 已结束(status=3)
+- 已取消(status=4)
+
+**我正在进行的预约:**
+API: `GetMyUseReservation`
+- 查询未取消且未结束的预约
+- 按状态排序:进行中 > 已锁定 > 待开始 > 已结束
+
+---
+
+### 8. 黑名单功能
+
+**说明:**
+- 用户可将不友好的参与者加入黑名单
+- 首页自动过滤黑名单用户发起的预约
+- 黑名单用户无法加入我发起的预约(待实现)
+
+**相关表:** `CoreCmsUserBlacklist`
+
+---
+
+### 9. 消息系统
+
+**后端API:**
+- 获取消息列表: `SQController.cs:1778` (GetMessageList)
+- 获取未读数量: `SQController.cs:1814` (GetUnreadCount)
+- 全部标记已读: `SQController.cs:1850` (MarkAllAsRead)
+
+**消息类型:**
+- 系统消息:预约相关通知(组局成功、被取消等)
+- 私信消息(暂未实现)
+
+**消息状态:**
+- 未读:红点提示
+- 已读:正常显示
+
+**相关表:**
+- `SQMessage`: 消息表
+- `SQMessageRead`: 已读记录表
+
+---
+
+### 10. 收益系统
+
+**后端API:**
+- 获取收益统计: `SQController.cs:1897` (GetEarningsSummary)
+- 获取收益记录: `SQController.cs:1970` (GetEarningsRecordList)
+- 申请提现: `SQController.cs:2050` (ApplyWithdraw)
+
+**收益来源:**
+- 发起预约的抽成(具体规则待配置)
+- 爽约者的鸽子费分成
+
+**提现规则:**
+- 最低提现金额:0.01元
+- 提现到账时间:3-5个工作日
+- 提现记录可查询
+
+**相关表:**
+- `SQEarningsRecord`: 收益记录表
+- `SQWithdrawRecord`: 提现记录表
+
+---
+
+## 数据库设计
+
+### 核心数据表
+
+#### 1. SQReservations (预约表)
+主要字段:
+```sql
+id -- 预约ID
+room_id -- 房间ID
+room_name -- 房间名称
+start_time -- 开始时间
+end_time -- 结束时间
+duration_minutes -- 时长(分钟)
+title -- 组局名称
+game_type -- 游戏类型
+game_rule -- 游戏规则
+player_count -- 需要人数
+status -- 状态(0待开始 1已锁定 2进行中 3已结束 4已取消)
+deposit_fee -- 押金费用
+credit_limit -- 最低信誉要求
+gender_limit -- 性别限制(0不限 1男 2女)
+min_age -- 最小年龄
+max_age -- 最大年龄
+is_smoking -- 是否禁烟
+latest_arrival_time -- 最晚到店时间
+extra_info -- 其他说明
+is_solo_mode -- 是否独享模式(无需组局)
+created_at -- 创建时间
+updated_at -- 更新时间
+```
+
+#### 2. SQReservationParticipants (参与者表)
+主要字段:
+```sql
+id -- 参与记录ID
+reservation_id -- 预约ID
+user_id -- 用户ID
+role -- 角色(0参与者 1发起者)
+status -- 状态(0正常 1已退出)
+join_time -- 加入时间
+quit_time -- 退出时间
+is_arrive -- 是否到场(0未签到 1到场 2未到场)
+check_reservation -- 签到时间
+is_refund -- 退款状态(1待支付 2已支付 3待退款 4已退款 5异常)
+paymentId -- 支付订单号
+important_data -- 重要数据(JSON)
+```
+
+#### 3. SQRooms (房间表)
+主要字段:
+```sql
+id -- 房间ID
+name -- 房间名称
+capacity -- 容量(人数)
+price_per_hour -- 每小时价格
+description -- 描述
+image_url -- 图片
+status -- 状态(true可用 false不可用)
+created_at -- 创建时间
+```
+
+#### 4. SQReservationEvaluate (评价表)
+主要字段:
+```sql
+id -- 评价ID
+reservation_id -- 预约ID
+user_id -- 评价人ID
+to_user_id -- 被评价人ID
+role -- 被评价人角色
+play_level -- 游戏水平评分
+skills_level -- 技能水平评分
+created_at -- 评价时间
+```
+
+#### 5. SQReservationReputation (声誉记录表)
+主要字段:
+```sql
+id -- 记录ID
+user_id -- 用户ID
+reservation_id -- 相关预约ID
+reputation_value -- 声誉变化值(±0.5、±0.2等)
+remark -- 变化原因
+created_at -- 记录时间
+```
+
+#### 6. SQRoomUnavailableTimes (房间不可用时间表)
+主要字段:
+```sql
+id -- 记录ID
+room_id -- 房间ID
+start_time -- 不可用开始时间
+end_time -- 不可用结束时间
+reason -- 原因
+created_at -- 创建时间
+```
+
+#### 7. SQMessage (消息表)
+主要字段:
+```sql
+id -- 消息ID
+user_id -- 接收用户ID(0表示全体用户)
+title -- 消息标题
+content -- 消息内容
+message_type -- 消息类型(0系统 1私信)
+created_at -- 创建时间
+```
+
+#### 8. SQEarningsRecord (收益记录表)
+主要字段:
+```sql
+id -- 记录ID
+user_id -- 用户ID
+reservation_id -- 相关预约ID
+amount -- 收益金额
+type -- 收益类型
+description -- 描述
+created_at -- 创建时间
+```
+
+---
+
+## 前端页面结构
+
+### TabBar(底部导航)
+1. **首页** (`pages/index/index`)
+ - 预约列表展示
+ - 支持下拉刷新、上拉加载
+ - 点击卡片查看详情/加入预约
+
+2. **预约** (`pages/appointment/book-room-page`)
+ - 选择房间和日期
+ - 查看房间时段可用性
+ - 进入预约表单
+
+3. **我的** (`pages/me/me-page`)
+ - 用户信息展示
+ - 预约记录入口
+ - 我的收益入口
+ - 消息通知入口
+
+### 主要功能页面
+
+#### 预约相关
+- `pages/appointment/book-room-page.vue` - 选择房间页面
+- `pages/appointment/appointment-page.vue` - 发起预约页面
+
+#### 个人中心
+- `pages/me/appointment-record-page.vue` - 预约记录
+- `pages/me/my-earnings-page.vue` - 我的收益
+- `pages/me/my-message-page.vue` - 消息列表
+- `pages/me/my-record.vue` - 历史记录
+- `pages/me/blacklist-page.vue` - 黑名单管理
+- `pages/me/edit-info.vue` - 编辑个人信息
+- `pages/me/login.vue` - 登录页面
+
+#### 其他页面
+- `pages/other/agreement.vue` - 用户协议
+- `pages/other/payment-records.vue` - 支付记录
+- `pages/other/faq.vue` - 常见问题
+
+---
+
+## API接口汇总
+
+### 预约相关接口
+
+| 接口名称 | 路径 | 方法 | 权限 | 说明 |
+|---------|------|------|------|------|
+| 获取预约列表 | `api/sq/GetReservationList` | GET | 无需 | 首页预约列表 |
+| 获取预约详情 | `api/sq/GetReservationDetail` | GET | 无需 | 根据ID获取详情 |
+| 我的预约记录 | `api/sq/GetMyReservation` | GET | 需要 | 我参与/发起的预约 |
+| 正在进行的预约 | `api/sq/GetMyUseReservation` | GET | 需要 | 未结束的预约 |
+| 验证是否可创建 | `api/sq/CanCreateSQReservation` | POST | 需要 | 创建预约前验证 |
+| 创建预约 | `api/sq/AddSQReservation` | POST | 需要 | 发起新预约 |
+| 加入预约 | `api/sq/JoinReservation` | POST | 需要 | 参与现有预约 |
+| 取消预约 | `api/sq/CancelReservation` | POST | 需要 | 发起者/参与者取消 |
+| 预约签到 | `api/sq/CheckInReservation` | POST | 需要 | 发起者签到确认 |
+
+### 房间相关接口
+
+| 接口名称 | 路径 | 方法 | 权限 | 说明 |
+|---------|------|------|------|------|
+| 获取可选日期 | `api/sq/GetAvailableDates` | GET | 无需 | 今天+未来6天 |
+| 获取房间列表 | `api/sq/GetRoomListWithSlotsNew` | GET | 无需 | 按时段显示房间状态 |
+| 获取房间详情 | `api/sq/GetRoomDetail` | GET | 无需 | 房间信息及可用时段 |
+| 获取可预约房间 | `api/sq/GetReservationRoomList` | GET | 无需 | 指定时间段可预约房间 |
+
+### 评价相关接口
+
+| 接口名称 | 路径 | 方法 | 权限 | 说明 |
+|---------|------|------|------|------|
+| 获取预约评价 | `api/sq/GetEvaluateServices` | GET | 需要 | 获取可评价参与者 |
+| 添加评价 | `api/sq/AddEvaluateServices` | POST | 需要 | 评价参与者 |
+| 获取声誉记录 | `api/sq/GetReputationByUser` | GET | 需要 | 我的信誉变化记录 |
+| 获取评价给我的 | `api/sq/GetEvaluateToMe` | GET | 需要 | 别人给我的评价 |
+
+### 消息相关接口
+
+| 接口名称 | 路径 | 方法 | 权限 | 说明 |
+|---------|------|------|------|------|
+| 获取消息列表 | `api/sq/GetMessageList` | GET | 需要 | 站内信列表 |
+| 获取未读数量 | `api/sq/GetUnreadCount` | GET | 需要 | 未读消息数量 |
+| 全部标记已读 | `api/sq/MarkAllAsRead` | POST | 需要 | 标记所有消息已读 |
+
+### 收益相关接口
+
+| 接口名称 | 路径 | 方法 | 权限 | 说明 |
+|---------|------|------|------|------|
+| 获取收益统计 | `api/sq/GetEarningsSummary` | GET | 需要 | 总收益、可提现等 |
+| 获取收益记录 | `api/sq/GetEarningsRecordList` | POST | 需要 | 收益明细列表 |
+| 获取提现记录 | `api/sq/GetWithdrawRecordList` | POST | 需要 | 提现记录列表 |
+| 申请提现 | `api/sq/ApplyWithdraw` | POST | 需要 | 发起提现申请 |
+| 获取收益规则 | `api/sq/GetEarningsRule` | GET | 无需 | 收益规则说明 |
+
+### 其他接口
+
+| 接口名称 | 路径 | 方法 | 权限 | 说明 |
+|---------|------|------|------|------|
+| 获取支付记录 | `api/sq/GetPaymentRecords` | GET | 需要 | 鸽子费支付记录 |
+| 获取营业时间 | `api/sq/GetBusinessHours` | GET | 无需 | 店铺营业时间配置 |
+
+---
+
+## 业务流程图
+
+### 完整预约流程
+
+```
+用户浏览首页预约列表
+ ↓
+选择"发起预约"或"加入预约"
+ ↓
+[发起预约流程] [加入预约流程]
+ ↓ ↓
+选择房间和日期 查看预约详情
+ ↓ ↓
+选择时段 检查参与条件
+ ↓ ↓
+填写组局信息 支付鸽子费(如有)
+ ↓ ↓
+设置参与限制 加入成功
+ ↓ ↓
+设置鸽子费 等待预约开始
+ ↓ ↓
+支付鸽子费(如有) 收到开始通知
+ ↓
+发起成功
+ ↓
+等待参与者加入
+ ↓
+人满/时间到达开始
+ ↓
+发起者签到确认到场人员
+ ↓
+预约进行中
+ ↓
+预约时间结束
+ ↓
+参与者互相评价
+ ↓
+预约完成(鸽子费退还)
+```
+
+### 签到流程详解
+
+```
+预约开始时间到达
+ ↓
+发起者收到签到通知
+ ↓
+发起者打开预约详情
+ ↓
+点击"签到"按钮
+ ↓
+勾选实际到场的参与者
+ ↓
+提交签到
+ ↓
+系统处理:
+ - 预约状态→进行中
+ - 到场者:is_arrive=1,信誉+0.2
+ - 未到场者:is_arrive=2,信誉-0.5,鸽子次数+1
+ - 未到场者押金→到场者平分(待定时任务处理)
+ - 到场者押金→发起退款
+ ↓
+签到完成
+ ↓
+预约正常进行
+```
+
+### 取消预约流程
+
+```
+用户查看我的预约
+ ↓
+选择要取消的预约
+ ↓
+点击"取消预约"
+ ↓
+检查取消条件:
+ - 是否在开始前30分钟
+ - 是否已开始(只有发起者可取消已开始的)
+ - 是否已结束或已取消
+ ↓
+[发起者取消] [参与者取消]
+ ↓ ↓
+预约状态→已取消 只退出自己
+ ↓ ↓
+所有参与者→已退出 预约继续有效
+ ↓ ↓
+发起押金退款 发起押金退款(自己的)
+ ↓ ↓
+通知所有参与者 无需通知
+ ↓ ↓
+取消完成
+```
+
+---
+
+## 业务规则总结
+
+### 时间规则
+1. 预约可选时间:今天 + 未来6天
+2. 时段划分:凌晨(0-6h)、上午(6-12h)、下午(12-18h)、晚上(18-24h)
+3. 营业时间:09:00-23:00
+4. 取消限制:开始前30分钟内无法取消
+
+### 人数规则
+1. 每个预约最少1人(独享模式),最多4人
+2. 独享模式(player_count=1)不接受其他人加入
+3. 人满后预约状态→已锁定(status=1)
+
+### 押金规则
+1. 押金范围:0-50元
+2. 押金用途:防止爽约
+3. 退款时机:
+ - 签到后到场者:全额退还
+ - 爽约者:押金被扣除,由到场者平分
+ - 预约取消:全额退还
+
+### 信誉规则
+1. 初始信誉:5.0分(满分)
+2. 守约奖励:+0.2分/次(最高5.0)
+3. 爽约惩罚:-0.5分/次
+4. 信誉作用:发起者可设置最低信誉要求
+
+### 评价规则
+1. 评价时机:预约完成后
+2. 评价对象:实际到场的参与者
+3. 评价维度:游戏水平、技能水平(各1-5分)
+4. 评价次数:每个参与者只能被评价一次
+5. 平均分计算:(所有评价之和 + 4) / (评价次数 + 1)
+
+### 参与限制规则
+1. 性别限制:不限/男/女
+2. 年龄限制:最小-最大年龄
+3. 信誉限制:最低信誉要求
+4. 时间冲突检查:不能同时参与多个时间重叠的预约
+
+---
+
+## 定时任务(推测)
+
+根据代码逻辑,应该有以下定时任务:
+
+1. **押金退款任务**
+ - 扫描 `is_refund=3`(待退款)的记录
+ - 调用微信退款API
+ - 更新退款状态为 `is_refund=4`(已退款)
+
+2. **预约自动结束任务**
+ - 扫描 `end_time < now` 且 `status=2`(进行中)的预约
+ - 更新状态为 `status=3`(已结束)
+
+3. **预约失败通知任务**
+ - 扫描开始时间到达但人数不足的预约
+ - 通知参与者"组局失败"
+ - 退还所有押金
+
+---
+
+## 项目特色
+
+### 1. 完善的信誉体系
+- 守约加分、爽约扣分
+- 鸽子次数统计
+- 参与条件限制
+
+### 2. 灵活的押金机制
+- 防止恶意爽约
+- 爽约者押金补偿到场者
+- 守约者全额退还
+
+### 3. 双向评价系统
+- 游戏水平、技能水平分开评价
+- 加权平均算法避免偏差
+- 评价影响用户可信度
+
+### 4. 智能房间管理
+- 按时段展示房间可用性
+- 自动过滤已预约/不可用时段
+- 支持房间不可用时间配置
+
+### 5. 黑名单机制
+- 避免与不友好用户组局
+- 首页自动过滤黑名单预约
+
+---
+
+## 注意事项
+
+### 代码特点
+1. 后端代码是从商城系统改造的,包含一些未使用的表和字段
+2. 核心预约功能集中在 `SQController` 中
+3. 前端使用 UniApp 开发,支持多端发布
+4. 数据库使用 SqlSugar ORM,部分查询直接使用原生SQL
+
+### 需要改进的地方
+1. 部分业务逻辑写在 Controller 中,应该抽离到 Service 层
+2. 定时任务的具体实现需要补充
+3. 消息推送功能需要完善(微信模板消息/订阅消息)
+4. 收益分配规则需要明确配置
+5. 单元测试和接口文档需要完善
+
+---
+
+## 部署建议
+
+### 环境要求
+- .NET Core 6.0+
+- SQL Server 2016+
+- Redis(如需缓存)
+
+### 配置项
+1. 数据库连接字符串
+2. 微信小程序配置(AppId、AppSecret)
+3. 微信支付配置(商户号、密钥)
+4. 营业时间配置
+5. 收益规则配置
+
+### 运行步骤
+1. 恢复数据库(执行建表脚本)
+2. 配置 `appsettings.json`
+3. 编译后端项目
+4. 部署到IIS或使用Kestrel
+5. 配置前端小程序AppId
+6. 编译上传小程序
+
+---
+
+## 总结
+
+这是一个功能完善的**麻将组局预约小程序**,核心流程包括:
+1. 用户浏览首页预约列表
+2. 发起/加入预约
+3. 支付鸽子费(押金)
+4. 预约开始后发起者签到
+5. 预约完成后互相评价
+6. 押金退还、收益分配
+
+项目采用前后端分离架构,数据库设计合理,业务逻辑清晰。通过信誉体系、押金机制、评价系统等功能,有效防止了恶意爽约问题,提高了用户参与组局的积极性和可靠性。
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/AdvertController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/AdvertController.cs
deleted file mode 100644
index 5c41b17..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/AdvertController.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.Entities;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-
-using SqlSugar;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 广告api控制器
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class AdvertController : ControllerBase
- {
-
- private IHttpContextUser _user;
- private readonly ICoreCmsArticleServices _articleServices;
- private readonly ICoreCmsAdvertPositionServices _advertPositionServices;
- private readonly ICoreCmsAdvertisementServices _advertisementServices;
-
- ///
- /// 构造函数
- ///
- ///
- ///
- ///
- ///
- public AdvertController(IHttpContextUser user
- , ICoreCmsArticleServices articleServices
- , ICoreCmsAdvertPositionServices advertPositionServices
- , ICoreCmsAdvertisementServices advertisementServices
- )
- {
- _user = user;
- _articleServices = articleServices;
- _advertPositionServices = advertPositionServices;
- _advertisementServices = advertisementServices;
- }
-
- #region 获取广告列表=============================================================================
- ///
- /// 获取广告列表
- ///
- ///
- ///
- [HttpGet]
- public async Task GetAdvertList([FromQuery] string code)
- {
- if (string.IsNullOrEmpty(code))
- {
- return null;
- }
- var jm = new WebApiDto();
-
- var list = await _advertisementServices.QueryListByClauseAsync(p => p.code == code, p => p.createTime, OrderByType.Desc);
- jm.Code = 0;
- jm.Data = list;
-
- return jm;
-
- }
- #endregion
- }
-}
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/AgentController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/AgentController.cs
deleted file mode 100644
index 483d983..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/AgentController.cs
+++ /dev/null
@@ -1,441 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.Entities;
-using CoreCms.Net.Model.Entities.Expression;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using CoreCms.Net.Utility.Extensions;
-using CoreCms.Net.Utility.Helper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json;
-using SqlSugar;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 代理请求接口
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class AgentController : ControllerBase
- {
- private IHttpContextUser _user;
- private readonly ICoreCmsAgentServices _agentServices;
- private readonly ICoreCmsAgentOrderServices _agentOrderServices;
- private readonly ICoreCmsAgentGoodsServices _agentGoodsServices;
- private readonly ICoreCmsSettingServices _settingServices;
- private readonly ICoreCmsUserServices _userServices;
- private readonly ICoreCmsGoodsServices _goodsServices;
-
- ///
- /// 构造函数
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public AgentController(IHttpContextUser user, ICoreCmsAgentServices agentServices, ICoreCmsSettingServices settingServices, ICoreCmsAgentOrderServices agentOrderServices, ICoreCmsUserServices userServices, ICoreCmsGoodsServices goodsServices, ICoreCmsAgentGoodsServices agentGoodsServices)
- {
- _user = user;
- _agentServices = agentServices;
- _settingServices = settingServices;
- _agentOrderServices = agentOrderServices;
- _userServices = userServices;
- _goodsServices = goodsServices;
- _agentGoodsServices = agentGoodsServices;
- }
-
- //公共接口====================================================================================================
-
- #region 获取店铺信息
- ///
- /// 获取店铺信息
- ///
- ///
- [HttpPost]
- public async Task GetStoreInfo([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- if (entity.id == 0)
- {
- jm.msg = "店铺信息丢失";
- return jm;
- }
- var store = UserHelper.GetUserIdByShareCode(entity.id);
- if (store <= 0)
- {
- jm.msg = "店铺信息丢失";
- return jm;
- }
- jm = await _agentServices.GetStore(store);
- return jm;
-
- }
- #endregion
-
-
- #region 根据查询条件获取分页数据============================================================
- ///
- /// 根据查询条件获取分页数据
- ///
- ///
- [HttpPost]
- public async Task GetGoodsPageList([FromBody] FMPageByWhereOrder entity)
- {
- var jm = new WebApiCallBack();
-
- var where = PredicateBuilder.True();
- where = where.And(p => p.isDel == false);
- where = where.And(p => p.isMarketable == true);
-
- var className = string.Empty;
- if (!string.IsNullOrEmpty(entity.where))
- {
- var obj = JsonConvert.DeserializeAnonymousType(entity.where, new
- {
- priceFrom = "",
- priceTo = "",
- catId = "",
- brandId = "",
- labelId = "",
- searchName = "",
- });
-
- if (!string.IsNullOrEmpty(obj.priceFrom))
- {
- var priceF = obj.priceFrom.ObjectToDouble(0);
- if (priceF >= 0)
- {
- var f = Convert.ToDecimal(priceF);
- where = where.And(p => p.price >= f);
- }
- }
- if (!string.IsNullOrEmpty(obj.priceTo))
- {
- var priceT = obj.priceTo.ObjectToDouble(0);
- if (priceT >= 0)
- {
- var f = Convert.ToDecimal(priceT);
- where = where.And(p => p.price <= f);
- }
- }
- if (!string.IsNullOrEmpty(obj.brandId))
- {
- var brandId = obj.brandId.ObjectToInt(0);
- if (brandId >= 0)
- {
- where = where.And(p => p.brandId == brandId);
- }
- }
- if (!string.IsNullOrEmpty(obj.labelId))
- {
- var brandId = obj.brandId.ObjectToInt(0);
- if (brandId >= 0)
- {
- where = where.And(p => p.brandId == brandId);
- }
- }
- if (!string.IsNullOrEmpty(obj.searchName))
- {
- where = where.And(p => p.name.Contains(obj.searchName));
- }
- }
-
- var orderBy = " isRecommend desc,isHot desc";
- if (!string.IsNullOrEmpty(entity.order))
- {
- orderBy += "," + entity.order;
- }
-
- var list = await _goodsServices.QueryAgentGoodsPageAsync(where, orderBy, entity.page, entity.limit, false);
- if (list.Any())
- {
- foreach (var goods in list)
- {
- goods.images = !string.IsNullOrEmpty(goods.images) ? goods.images.Split(",")[0] : "/static/images/common/empty.png";
- }
- }
-
- //返回数据
- jm.status = true;
- jm.data = new
- {
- list,
- className,
- entity.page,
- list.TotalCount,
- list.TotalPages,
- entity.limit,
- entity.where,
- entity.order,
- };
- jm.msg = "数据调用成功!";
-
- return jm;
- }
- #endregion
-
-
-
- //验证接口====================================================================================================
-
- #region 查询用户是否可以成为代理商
- ///
- /// 查询用户是否可以成为代理商
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task Info()
- {
- var jm = await _agentServices.GetInfo(_user.ID);
- return jm;
-
- }
- #endregion
-
- #region 申请成为代理商接口
- ///
- /// 申请成为代理商接口
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task ApplyAgent([FromBody] FMAgentApply entity)
- {
- var jm = new WebApiCallBack();
-
- if (entity.agreement != "on")
- {
- jm.msg = "请勾选代理商协议";
- return jm;
- }
- var iData = new CoreCmsAgent();
- iData.mobile = entity.mobile;
- iData.name = entity.name;
- iData.weixin = entity.weixin;
- iData.qq = entity.qq;
- jm = await _agentServices.AddData(iData, _user.ID);
-
- return jm;
-
- }
- #endregion
-
- #region 获取我的下级用户数量
- ///
- /// 获取我的下级用户数量
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetTeamSum()
- {
- var jm = new WebApiCallBack();
-
- //发展人数
- var first = await _userServices.QueryChildCountAsync(_user.ID, 1);
- //订单数
- var second = await _agentOrderServices.GetCountAsync(p => p.userId == _user.ID);
-
- //当月发展人数
- var monthFirst = await _userServices.QueryChildCountAsync(_user.ID, 1, true);
-
- DateTime dt = DateTime.Now;
- //本月第一天时间
- DateTime dtFirst = dt.AddDays(1 - (dt.Day));
- dtFirst = new DateTime(dtFirst.Year, dtFirst.Month, dtFirst.Day, 0, 0, 0);
- //获得某年某月的天数
- int year = dt.Date.Year;
- int month = dt.Date.Month;
- int dayCount = DateTime.DaysInMonth(year, month);
- //本月最后一天时间
- DateTime dtLast = dtFirst.AddDays(dayCount - 1);
-
- var monthSecond = await _agentOrderServices.GetCountAsync(p => p.userId == _user.ID && p.createTime > dtFirst && p.createTime < dtLast, true);
-
- jm.status = true;
- jm.data = new
- {
- count = first,
- first,
- second,
- monthCount = monthFirst,
- monthFirst,
- monthSecond
- };
-
- return jm;
- }
-
- #endregion
-
- #region 获取我的订单统计
- ///
- /// 获取我的订单统计
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetOrderSum()
- {
- var jm = new WebApiCallBack();
-
- DateTime dt = DateTime.Now;
- //本月第一天时间
- DateTime dtFirst = dt.AddDays(1 - (dt.Day));
- dtFirst = new DateTime(dtFirst.Year, dtFirst.Month, dtFirst.Day, 0, 0, 0);
- //获得某年某月的天数
- int dayCount = DateTime.DaysInMonth(dt.Date.Year, dt.Date.Month);
- //本月最后一天时间
- DateTime dtLast = dtFirst.AddDays(dayCount - 1);
-
-
- //全部订单
- var allOrder = await _agentOrderServices.GetCountAsync(p => p.userId == _user.ID, true);
- //代购订单
- var procurementServiceOrder = await _agentOrderServices.GetCountAsync(p => p.userId == _user.ID && p.buyUserId == _user.ID, true);
- //推广订单
- var customerOrder = await _agentOrderServices.GetCountAsync(p => p.userId == _user.ID && p.buyUserId != _user.ID, true);
- //本月订单
- var monthOrder = await _agentOrderServices.GetCountAsync(p => p.userId == _user.ID && p.createTime > dtFirst && p.createTime < dtLast, true);
-
-
- //全部订单金额
- var allOrderMoney = await _agentOrderServices.GetSumAsync(p => p.userId == _user.ID, p => p.amount, true);
- //代购订单金额
- var procurementServiceOrderMoney = await _agentOrderServices.GetSumAsync(p => p.userId == _user.ID && p.buyUserId == _user.ID, p => p.amount, true);
- //推广订单金额
- var customerOrderMoney = await _agentOrderServices.GetSumAsync(p => p.userId == _user.ID && p.buyUserId != _user.ID, p => p.amount, true);
- //本月订单金额
- var monthOrderMoney = await _agentOrderServices.GetSumAsync(p => p.userId == _user.ID && p.createTime > dtFirst && p.createTime < dtLast, p => p.amount, true);
-
- jm.status = true;
- jm.data = new
- {
- allOrder,
- procurementServiceOrder,
- customerOrder,
- monthOrder,
- allOrderMoney,
- procurementServiceOrderMoney,
- customerOrderMoney,
- monthOrderMoney
- };
-
- return jm;
- }
-
- #endregion
-
- #region 我推广的订单
- ///
- /// 我推广的订单
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task MyOrder([FromBody] FMPageByIntId entity)
- {
- var jm = await _agentServices.GetMyOrderList(_user.ID, entity.page, entity.limit, entity.id);
- return jm;
- }
- #endregion
-
- #region 店铺设置
- ///
- /// 店铺设置
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task SetStore([FromBody] FMSetAgentStorePost entity)
- {
- var jm = new WebApiCallBack();
-
- if (string.IsNullOrEmpty(entity.storeName))
- {
- jm.msg = "请填写店铺名称";
- return jm;
- }
- if (string.IsNullOrEmpty(entity.storeLogo))
- {
- jm.msg = "请上传店铺logo";
- return jm;
- }
- if (string.IsNullOrEmpty(entity.storeBanner))
- {
- jm.msg = "请上传店铺banner";
- return jm;
- }
-
- var info = await _agentServices.QueryByClauseAsync(p => p.userId == _user.ID);
- if (info != null)
- {
- info.storeLogo = entity.storeLogo;
- info.storeBanner = entity.storeBanner;
- info.storeDesc = entity.storeDesc;
- info.storeName = entity.storeName;
- await _agentServices.UpdateAsync(info);
- }
- jm.status = true;
- jm.msg = "保存成功";
-
- return jm;
-
- }
- #endregion
-
- #region 获取代理商排行
- ///
- /// 获取代理商排行
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetAgentRanking([FromBody] FMPageByIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var list = await _agentServices.QueryRankingPageAsync(entity.page, entity.limit);
-
- jm.status = true;
- jm.data = new
- {
- data = list,
- list.HasNextPage,
- list.HasPreviousPage,
- list.PageIndex,
- list.PageSize,
- list.TotalPages,
- list.TotalCount,
-
- };
-
- return jm;
- }
-
- #endregion
- }
-}
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/ArticleController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/ArticleController.cs
deleted file mode 100644
index 1e32b6a..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/ArticleController.cs
+++ /dev/null
@@ -1,130 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-using System.Linq;
-using System.Threading.Tasks;
-
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-
-using Microsoft.AspNetCore.Mvc;
-
-using SqlSugar;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 文章api控制器
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class ArticleController : ControllerBase
- {
-
- private IHttpContextUser _user;
- private readonly ICoreCmsArticleServices _articleServices;
- private readonly ICoreCmsArticleTypeServices _articleTypeServices;
-
- ///
- /// 构造函数
- ///
- ///
- ///
- ///
- public ArticleController(IHttpContextUser user, ICoreCmsArticleServices articleServices, ICoreCmsArticleTypeServices articleTypeServices)
- {
- _user = user;
- _articleServices = articleServices;
- _articleTypeServices = articleTypeServices;
- }
-
-
-
- #region 获取通知列表
- ///
- /// 获取通知列表
- ///
- ///
- [HttpPost]
- public async Task NoticeList([FromBody] FMPageByIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var list = await _articleServices.QueryPageAsync(p => p.isDel == false, p => p.createTime, OrderByType.Desc,
- entity.page, entity.limit);
- jm.status = true;
- jm.data = list;
-
- return jm;
- }
-
- #endregion
-
-
- #region 获取文章列表
- ///
- /// 获取文章列表
- ///
- ///
- [HttpPost]
- public async Task GetArticleList([FromBody] FMPageByIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var list = await _articleServices.QueryPageAsync(p => p.isDel == false && p.typeId == entity.id, p => p.createTime, OrderByType.Desc,
- entity.page, entity.limit);
-
- var articleType = await _articleTypeServices.QueryAsync();
- var typeName = string.Empty;
- if (articleType.Any())
- {
- var type = articleType.Find(p => p.id == entity.id);
- typeName = type != null ? type.name : "";
- }
- jm.status = true;
- jm.data = new
- {
- list,
- articleType,
- type_name = typeName,
- count = list.TotalCount
- };
-
- return jm;
- }
-
- #endregion
-
-
- ///
- /// 获取单个文章内容
- ///
- ///
- ///
- [HttpGet]
- public async Task GetArticleDetail([FromQuery] int id)
- {
- var jm = new WebApiCallBack();
-
- var model = await _articleServices.ArticleDetail(id);
- if (model == null)
- {
- jm.msg = "数据获取失败";
- return jm;
- }
- jm.status = true;
- jm.data = model;
- return jm;
-
- }
- }
-}
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/CartController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/CartController.cs
deleted file mode 100644
index 6a4a93c..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/CartController.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using CoreCms.Net.Model.ViewModels.DTO;
-using CoreCms.Net.Utility.Helper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using System.Threading.Tasks;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 购物车操作
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class CartController : ControllerBase
- {
- private readonly IHttpContextUser _user;
- private readonly ICoreCmsCartServices _cartServices;
-
-
- ///
- /// 构造函数
- ///
- public CartController(IHttpContextUser user, ICoreCmsCartServices cartServices)
- {
- _user = user;
- _cartServices = cartServices;
- }
-
- //公共接口====================================================================================================
-
- //验证接口====================================================================================================
-
- #region 添加单个货品到购物车
-
- ///
- /// 添加单个货品到购物车
- ///
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task AddCart([FromBody] FMCartAdd entity)
- {
- var jm = await _cartServices.Add(_user.ID, entity.ProductId, entity.Nums, entity.type, entity.cartType, entity.objectId);
- return jm;
- }
-
- #endregion 添加单个货品到购物车
-
- #region 获取购物车列表======================================================================
-
- ///
- /// 获取购物车列表
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetList([FromBody] FMCartGetList entity)
- {
- var ids = CommonHelper.StringToIntArray(entity.ids);
- //判断免费运费
- var freeFreight = entity.receiptType != 1;
- //获取数据
- var jm = await _cartServices.GetCartInfos(_user.ID, ids, entity.type, entity.areaId, entity.point, entity.couponCode, freeFreight, entity.receiptType, entity.objectId);
-
- return jm;
- }
-
- #endregion 获取购物车列表======================================================================
-
- #region 删除购物车信息
-
- ///
- /// 获取购物车列表
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task DoDelete([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- if (entity.id <= 0)
- {
- jm.msg = "请提交要删除的货品";
- return jm;
- }
- jm = await _cartServices.DeleteByIdsAsync(entity.id, _user.ID);
-
- return jm;
- }
-
- #endregion 删除购物车信息
-
- #region 设置购物车商品数量
-
- ///
- /// 设置购物车商品数量
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task SetCartNum([FromBody] FMSetCartNum entity)
- {
- var jm = await _cartServices.SetCartNum(entity.id, entity.nums, _user.ID, 2, 1);
- return jm;
- }
-
- #endregion 设置购物车商品数量
-
- #region 根据提交的数据判断哪些购物券可以使用==================================================
-
- ///
- /// 根据提交的数据判断哪些购物券可以使用
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetCartAvailableCoupon([FromBody] FMCouponForUserCouponPost entity)
- {
- var ids = CommonHelper.StringToIntArray(entity.ids);
- var jm = await _cartServices.GetCartAvailableCoupon(_user.ID, ids);
- return jm;
- }
-
- #endregion 根据提交的数据判断哪些购物券可以使用==================================================
- }
-}
\ No newline at end of file
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/CouponController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/CouponController.cs
deleted file mode 100644
index 11f3dd5..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/CouponController.cs
+++ /dev/null
@@ -1,253 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IRepository.UnitOfWork;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.Entities;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 优惠券接口
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class CouponController : ControllerBase
- {
-
- private readonly IHttpContextUser _user;
- private readonly ICoreCmsCouponServices _couponServices;
- private readonly ICoreCmsPromotionServices _promotionServices;
- private readonly IUnitOfWork _unionOfWork;
- ///
- /// 构造函数
- ///
- ///
- ///
- ///
- ///
- public CouponController(IHttpContextUser user
- , ICoreCmsCouponServices couponServices, ICoreCmsPromotionServices promotionServices, IUnitOfWork unionOfWork)
- {
- _user = user;
- _couponServices = couponServices;
- _promotionServices = promotionServices;
- _unionOfWork = unionOfWork;
- }
-
- //公共接口====================================================================================================
-
- #region 获取 可领取的优惠券==================================================
- ///
- /// 获取 可领取的优惠券
- ///
- ///
- [HttpPost]
- //[Authorize]
- public async Task CouponList([FromBody] FMCouponForUserCouponPost entity)
- {
- var jm = new WebApiCallBack() { msg = "获取失败" };
-
- var list = await _promotionServices.GetReceiveCouponList(entity.page, entity.limit);
- jm.status = true;
- jm.data = list;
- jm.msg = "获取成功";
- jm.otherData = new
- {
- totalCount = 0,
- totalPages = 0,
- };
- if (list != null && list.Any())
- {
- jm.data = list;
- jm.otherData = new
- {
- list.TotalCount,
- list.TotalPages
- };
- }
- return jm;
- }
- #endregion
-
- //验证接口====================================================================================================
-
- #region 获取优惠券 详情==================================================
- ///
- /// 获取优惠券 详情
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task CouponDetail([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack() { msg = "获取失败" };
-
- if (entity.id == 0)
- {
- jm.status = false;
- jm.msg = GlobalErrorCodeVars.Code15006;
- return jm;
- }
-
- var promotionModel = await _promotionServices.QueryByClauseAsync(p => p.id == entity.id);
- if (promotionModel != null)
- {
- jm.status = true;
- jm.data = promotionModel;
- jm.msg = "获取成功";
- }
- return jm;
-
- }
- #endregion
-
- #region 获取用户已领取的优惠券==================================================
- ///
- /// 获取用户已领取的优惠券
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task UserCoupon([FromBody] FMCouponForUserCouponPost entity)
- {
- var jm = await _couponServices.GetMyCoupon(_user.ID, 0, entity.display, entity.page, entity.limit);
- return jm;
- }
- #endregion
-
- #region 用户领取优惠券==================================================
- ///
- /// 用户领取优惠券
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetCoupon([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- if (entity.id == 0)
- {
- jm.msg = GlobalErrorCodeVars.Code15006;
- return jm;
- }
-
- try
- {
- _unionOfWork.BeginTran();
-
-
- //判断优惠券是否可以领取?
- var promotionModel = await _promotionServices.ReceiveCoupon(entity.id);
- if (promotionModel.status == false)
- {
- _unionOfWork.RollbackTran();
- return promotionModel;
- }
-
-
-
- var promotion = (CoreCmsPromotion)promotionModel.data;
- if (promotion == null)
- {
- _unionOfWork.RollbackTran();
- jm.msg = GlobalErrorCodeVars.Code15019;
- return jm;
- }
-
- if (promotion.maxNums > 0)
- {
- //判断用户是否已领取?领取次数
- var couponResult = await _couponServices.GetMyCoupon(_user.ID, entity.id, "all", 1, 9999);
- if (couponResult.status && couponResult.code >= promotion.maxNums)
- {
- _unionOfWork.RollbackTran();
- jm.msg = GlobalErrorCodeVars.Code15018;
- return jm;
- }
- }
-
- jm = await _couponServices.AddData(_user.ID, entity.id, promotion);
-
- _unionOfWork.CommitTran();
-
- jm.otherData = promotionModel;
-
- }
- catch (Exception e)
- {
- _unionOfWork.RollbackTran();
- jm.msg = GlobalErrorCodeVars.Code10000;
- }
-
- return jm;
- }
- #endregion
-
- #region 用户输入code领取优惠券==================================================
- ///
- /// 用户输入code领取优惠券
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetCouponKey([FromBody] FMCouponForGetCouponKeyPost entity)
- {
- var jm = new WebApiCallBack();
-
- if (string.IsNullOrEmpty(entity.key))
- {
- jm.msg = GlobalErrorCodeVars.Code15006;
- return jm;
- }
-
- var coupon = await _couponServices.QueryByClauseAsync(p => p.couponCode == entity.key);
- if (coupon == null || coupon.promotionId <= 0)
- {
- jm.msg = GlobalErrorCodeVars.Code15009;
- return jm;
- }
-
- //判断优惠券是否可以领取?
- var promotionModel = await _promotionServices.ReceiveCoupon(coupon.promotionId);
- if (promotionModel.status == false)
- {
- return promotionModel;
- }
- //判断用户是否已领取?
- if (promotionModel.data is CoreCmsPromotion { maxNums: > 0 } info)
- {
- //判断用户是否已领取?领取次数
- var couponResult = await _couponServices.GetMyCoupon(_user.ID, coupon.promotionId, "all", 1, 9999);
- if (couponResult.status && couponResult.code > info.maxNums)
- {
- jm.msg = GlobalErrorCodeVars.Code15018;
- return jm;
- }
- }
- //
- jm = await _couponServices.ReceiveCoupon(_user.ID, entity.key);
-
- return jm;
- }
- #endregion
-
- }
-}
\ No newline at end of file
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/DemoController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/DemoController.cs
deleted file mode 100644
index 88cefb8..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/DemoController.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-using Microsoft.AspNetCore.Mvc;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 默认接口示例
- ///
- public class DemoController : ControllerBase
- {
- ///
- /// 默认首页
- ///
- ///
- public IActionResult Index()
- {
- return Content("已结束");
- }
- }
-}
\ No newline at end of file
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/DistributionController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/DistributionController.cs
deleted file mode 100644
index bdf07cc..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/DistributionController.cs
+++ /dev/null
@@ -1,313 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-
-using System.Threading.Tasks;
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.Entities;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using CoreCms.Net.Utility.Helper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 分销请求接口
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class DistributionController : ControllerBase
- {
- private readonly ICoreCmsDistributionOrderServices _distributionOrderServices;
- private readonly ICoreCmsDistributionServices _distributionServices;
- private readonly ICoreCmsSettingServices _settingServices;
- private readonly ICoreCmsUserServices _userServices;
- private readonly IHttpContextUser _user;
-
- ///
- /// 构造函数
- ///
- public DistributionController(IHttpContextUser user, ICoreCmsDistributionServices distributionServices,
- ICoreCmsSettingServices settingServices, ICoreCmsUserServices userServices,
- ICoreCmsDistributionOrderServices distributionOrderServices)
- {
- _user = user;
- _distributionServices = distributionServices;
- _settingServices = settingServices;
- _userServices = userServices;
- _distributionOrderServices = distributionOrderServices;
- }
-
- //公共接口====================================================================================================
-
- #region 获取店铺信息
-
- ///
- /// 获取店铺信息
- ///
- ///
- [HttpPost]
- public async Task GetStoreInfo([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- if (entity.id == 0)
- {
- jm.msg = "店铺信息丢失";
- return jm;
- }
-
- var store = UserHelper.GetUserIdByShareCode(entity.id);
- if (store <= 0)
- {
- jm.msg = "店铺信息丢失";
- return jm;
- }
-
- jm = await _distributionServices.GetStore(store);
- return jm;
- }
-
- #endregion
-
- //验证接口====================================================================================================
-
- #region 查询用户是否可以成为分销商
-
- ///
- /// 查询用户是否可以成为分销商
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task Info()
- {
- var jm = await _distributionServices.GetInfo(_user.ID, true);
- return jm;
- }
-
- #endregion
-
- #region 申请成为分销商接口
-
- ///
- /// 申请成为分销商接口
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task ApplyDistribution([FromBody] FMDistributionApply entity)
- {
- var jm = new WebApiCallBack();
-
- if (entity.agreement != "on")
- {
- jm.msg = "请勾选分销协议";
- return jm;
- }
-
- var iData = new CoreCmsDistribution();
- iData.mobile = entity.mobile;
- iData.name = entity.name;
- iData.weixin = entity.weixin;
- iData.qq = entity.qq;
- jm = await _distributionServices.AddData(iData, _user.ID);
-
- return jm;
- }
-
- #endregion
-
- #region 我推广的订单
-
- ///
- /// 我推广的订单
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task MyOrder([FromBody] FMPageByIntId entity)
- {
- var jm = await _distributionServices.GetMyOrderList(_user.ID, entity.page, entity.limit, entity.id);
- return jm;
- }
-
- #endregion
-
- #region 店铺设置
-
- ///
- /// 店铺设置
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task SetStore([FromBody] FMSetDistributionStorePost entity)
- {
- var jm = new WebApiCallBack();
-
- if (string.IsNullOrEmpty(entity.storeName))
- {
- jm.msg = "请填写店铺名称";
- return jm;
- }
-
- if (string.IsNullOrEmpty(entity.storeLogo))
- {
- jm.msg = "请上传店铺logo";
- return jm;
- }
-
- if (string.IsNullOrEmpty(entity.storeBanner))
- {
- jm.msg = "请上传店铺banner";
- return jm;
- }
-
- var info = await _distributionServices.QueryByClauseAsync(p => p.userId == _user.ID);
- if (info != null)
- {
- info.storeLogo = entity.storeLogo;
- info.storeBanner = entity.storeBanner;
- info.storeDesc = entity.storeDesc;
- info.storeName = entity.storeName;
- await _distributionServices.UpdateAsync(info);
- }
-
- jm.status = true;
- jm.msg = "保存成功";
-
- return jm;
- }
-
- #endregion
-
- #region 获取我的订单统计
-
- ///
- /// 获取我的订单统计
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetOrderSum()
- {
- var jm = new WebApiCallBack();
-
- //全部订单
- var allOrder = await _distributionOrderServices.QueryChildOrderCountAsync(_user.ID, 0);
- //一级订单
- var firstOrder = await _distributionOrderServices.QueryChildOrderCountAsync(_user.ID);
- //二级订单
- var secondOrder = await _distributionOrderServices.QueryChildOrderCountAsync(_user.ID, 2);
- //本月订单
- var monthOrder = await _distributionOrderServices.QueryChildOrderCountAsync(_user.ID, 0, true);
-
- //全部订单金额
- var allOrderMoney = await _distributionOrderServices.QueryChildOrderMoneySumAsync(_user.ID, 0);
- //代购订单金额
- var firstOrderMoney = await _distributionOrderServices.QueryChildOrderMoneySumAsync(_user.ID);
- //推广订单金额
- var secondOrderMoney = await _distributionOrderServices.QueryChildOrderMoneySumAsync(_user.ID, 2);
- //本月订单金额
- var monthOrderMoney = await _distributionOrderServices.QueryChildOrderMoneySumAsync(_user.ID, 0, true);
-
-
- jm.status = true;
- jm.data = new
- {
- allOrder,
- firstOrder,
- secondOrder,
- monthOrder,
- allOrderMoney,
- firstOrderMoney,
- secondOrderMoney,
- monthOrderMoney
- };
-
- return jm;
- }
-
- #endregion
-
- #region 获取我的下级用户数量
-
- ///
- /// 获取我的下级用户数量
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetTeamSum()
- {
- var jm = new WebApiCallBack();
-
- //一级统计人数
- var first = await _userServices.QueryChildCountAsync(_user.ID);
- //二级发展人数
- var second = await _userServices.QueryChildCountAsync(_user.ID, 2);
-
- //当月发展一级人数
- var monthFirst = await _userServices.QueryChildCountAsync(_user.ID, 1, true);
- //当月发展二级分数
- var monthSecond = await _userServices.QueryChildCountAsync(_user.ID, 2, true);
-
- jm.status = true;
- jm.data = new
- {
- count = first + second,
- first,
- second,
- monthCount = monthFirst + monthSecond,
- monthFirst,
- monthSecond
- };
-
- return jm;
- }
-
- #endregion
-
- #region 获取分销商排行
-
- ///
- /// 获取分销商排行
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetDistributionRanking([FromBody] FMPageByIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var list = await _distributionServices.QueryRankingPageAsync(entity.page, entity.limit);
-
- jm.status = true;
- jm.data = new
- {
- data = list,
- list.HasNextPage,
- list.HasPreviousPage,
- list.PageIndex,
- list.PageSize,
- list.TotalPages,
- list.TotalCount
- };
-
- return jm;
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/FormController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/FormController.cs
deleted file mode 100644
index e0a5d08..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/FormController.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using SqlSugar;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 表单接口
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class FormController : ControllerBase
- {
- private readonly ICoreCmsFormServices _formServices;
-
- ///
- /// 构造函数
- ///
- ///
- public FormController(ICoreCmsFormServices formServices)
- {
- _formServices = formServices;
- }
-
-
- #region 万能表单/获取活动商品详情=============================================================================
- ///
- /// 万能表单/获取活动商品详情
- ///
- ///
- ///
- [HttpPost]
- public async Task GetFormDetial([FromBody] FmGetForm entity)
- {
- var jm = await _formServices.GetFormInfo(entity.id, entity.token);
- return jm;
- }
- #endregion
-
-
- #region 万能表单/提交表单=============================================================================
- ///
- /// 万能表单/提交表单
- ///
- ///
- ///
- [HttpPost]
- public async Task AddSubmit([FromBody] FmAddSubmit entity)
- {
- var jm = await _formServices.AddSubmit(entity);
-
- jm.otherData = entity;
-
- return jm;
- }
- #endregion
-
-
- }
-}
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/GoodController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/GoodController.cs
deleted file mode 100644
index 1a146c2..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/GoodController.cs
+++ /dev/null
@@ -1,492 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Threading.Tasks;
-using AutoMapper;
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.Entities;
-using CoreCms.Net.Model.Entities.Expression;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using CoreCms.Net.Model.ViewModels.DTO;
-using CoreCms.Net.Utility.Extensions;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json;
-using SqlSugar;
-using CoreCms.Net.Utility.Helper;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 商品相关接口处理
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class GoodController : ControllerBase
- {
- private IMapper _mapper;
- private readonly IHttpContextUser _user;
-
- private ICoreCmsSettingServices _settingServices;
- private ICoreCmsGoodsCategoryServices _goodsCategoryServices;
- private ICoreCmsGoodsServices _goodsServices;
- private ICoreCmsProductsServices _productsServices;
- private ICoreCmsBrandServices _brandServices;
- private ICoreCmsOrderItemServices _orderItemServices;
- private ICoreCmsGoodsCommentServices _goodsCommentServices;
- private ICoreCmsGoodsParamsServices _goodsParamsServices;
- private ICoreCmsGoodsCollectionServices _goodsCollectionServices;
- private ICoreCmsUserServices _userServices;
- private ICoreCmsGoodsCategoryExtendServices _goodsCategoryExtendServices;
-
- ///
- /// 构造函数
- ///
- public GoodController(IMapper mapper
- , IHttpContextUser user
- , ICoreCmsSettingServices settingServices
- , ICoreCmsGoodsCategoryServices goodsCategoryServices
- , ICoreCmsGoodsServices goodsServices
- , ICoreCmsProductsServices productsServices
- , ICoreCmsBrandServices brandServices
- , ICoreCmsOrderItemServices orderItemServices
- , ICoreCmsGoodsCommentServices goodsCommentServices
- , ICoreCmsGoodsParamsServices goodsParamsServices
- , ICoreCmsGoodsCollectionServices goodsCollectionServices
- , ICoreCmsUserServices userServices, ICoreCmsGoodsCategoryExtendServices goodsCategoryExtendServices)
- {
- _mapper = mapper;
- _user = user;
- _settingServices = settingServices;
- _goodsCategoryServices = goodsCategoryServices;
- _goodsServices = goodsServices;
- _productsServices = productsServices;
- _brandServices = brandServices;
- _orderItemServices = orderItemServices;
- _goodsCommentServices = goodsCommentServices;
- _goodsParamsServices = goodsParamsServices;
- _goodsCollectionServices = goodsCollectionServices;
- _userServices = userServices;
- _goodsCategoryExtendServices = goodsCategoryExtendServices;
- }
-
- //公共接口====================================================================================================
-
- #region 获取所有商品分类栏目数据
- ///
- /// 获取所有商品分类栏目数据
- ///
- ///
- [HttpPost]
- public async Task GetAllCategories()
- {
- var jm = new WebApiCallBack() { status = true };
-
- var data = await _goodsCategoryServices.QueryListByClauseAsync(p => p.isShow == true, p => p.sort,
- OrderByType.Asc);
- var wxGoodCategoryDto = new List();
-
- var parents = data.Where(p => p.parentId == 0).ToList();
- if (parents.Any())
- {
- parents.ForEach(p =>
- {
- var model = new WxGoodCategoryDto();
- model.id = p.id;
- model.name = p.name;
- model.imageUrl = !string.IsNullOrEmpty(p.imageUrl) ? p.imageUrl : "/static/images/common/empty.png";
- model.sort = p.sort;
-
- var childs = data.Where(p => p.parentId == model.id).ToList();
- if (childs.Any())
- {
- var childsList = new List();
- childs.ForEach(o =>
- {
- childsList.Add(new WxGoodCategoryChild()
- {
- id = o.id,
- imageUrl = !string.IsNullOrEmpty(o.imageUrl) ? o.imageUrl : "/static/images/common/empty.png",
- name = o.name,
- sort = o.sort
- });
- });
- model.child = childsList;
- }
- wxGoodCategoryDto.Add(model);
- });
- }
- jm.status = true;
- jm.data = wxGoodCategoryDto;
-
- return jm;
- }
-
- #endregion
-
- #region 根据查询条件获取分页数据============================================================
- ///
- /// 根据查询条件获取分页数据
- ///
- ///
- [HttpPost]
- public async Task GetGoodsPageList([FromBody] FMPageByWhereOrder entity)
- {
- var jm = new WebApiCallBack();
-
- var where = PredicateBuilder.True();
- where = where.And(p => p.isDel == false);
- where = where.And(p => p.isMarketable == true);
-
- var className = string.Empty;
- if (!string.IsNullOrWhiteSpace(entity.where))
- {
- var obj = JsonConvert.DeserializeAnonymousType(entity.where, new
- {
- priceFrom = "",
- priceTo = "",
- catId = "",
- brandId = "",
- labelId = "",
- searchName = "",
- });
-
- if (!string.IsNullOrWhiteSpace(obj.priceFrom))
- {
- var priceF = obj.priceFrom.ObjectToDouble(0);
- if (priceF >= 0)
- {
- var f = Convert.ToDecimal(priceF);
- where = where.And(p => p.price >= f);
- }
- }
- if (!string.IsNullOrWhiteSpace(obj.priceTo))
- {
- var priceT = obj.priceTo.ObjectToDouble(0);
- if (priceT > 0)
- {
- var f = Convert.ToDecimal(priceT);
- where = where.And(p => p.price <= f);
- }
- }
- if (!string.IsNullOrWhiteSpace(obj.catId))
- {
- var catId = obj.catId.ObjectToInt(0);
- if (catId > 0)
- {
- var category = await _goodsCategoryServices.QueryByIdAsync(catId, true);
- if (category != null)
- {
- className = category.name;
- }
-
- var categories = await _goodsCategoryServices.QueryAsync(true);
- var ids = GoodsHelper.GetChildIds(categories, catId);
- //扩展分类
- var extends = await _goodsCategoryExtendServices.QueryListByClauseAsync(p => p.goodsCategroyId == catId);
- if (extends.Any())
- {
- var extGoodIds = extends.Select(p => p.goodsId).ToList();
- where = where.And(p => ids.Contains(p.goodsCategoryId) || extGoodIds.Contains(p.id));
- }
- else
- {
- where = where.And(p => ids.Contains(p.goodsCategoryId));
- }
- }
- }
- if (!string.IsNullOrWhiteSpace(obj.brandId))
- {
- var brandId = obj.brandId.ObjectToInt(0);
- if (brandId > 0)
- {
- where = where.And(p => p.brandId == brandId);
- }
- }
- if (!string.IsNullOrWhiteSpace(obj.labelId))
- {
- where = where.And(p => (',' + p.labelIds.Trim(',') + ',').Contains(',' + obj.labelId.Trim(',') + ','));
- }
- if (!string.IsNullOrWhiteSpace(obj.searchName))
- {
- where = where.And(p => p.name.Contains(obj.searchName));
- }
- }
-
- //获取数据
- var list = await _goodsServices.QueryPageForLinqAsync(where, entity.order, entity.page, entity.limit, false);
- if (list.Any())
- {
- foreach (var goods in list)
- {
- goods.images = !string.IsNullOrEmpty(goods.images) ? goods.images.Split(",")[0] : "/static/images/common/empty.png";
- }
- }
-
- //获取品牌
- var brands = await _brandServices.QueryListByClauseAsync(p => p.isShow == true, p => p.sort, OrderByType.Desc);
-
-
- //返回数据
- jm.status = true;
- jm.data = new
- {
- list,
- className,
- entity.page,
- list.TotalCount,
- list.TotalPages,
- entity.limit,
- entity.where,
- entity.order,
- brands
- };
- jm.msg = "数据调用成功!";
-
- return jm;
- }
- #endregion
-
- #region 获取商品详情======================================================================
- ///
- /// 获取商品详情
- ///
- ///
- ///
- [HttpPost]
- public async Task GetDetial([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var userId = 0;
- if (_user != null)
- {
- userId = _user.ID;
- }
-
- var model = await _goodsServices.GetGoodsDetial(entity.id, userId, false);
- if (model == null)
- {
- jm.msg = "商品获取失败";
- return jm;
- }
-
- jm.status = true;
- jm.msg = "获取商品详情成功";
- jm.data = model;
- jm.methodDescription = JsonConvert.SerializeObject(_user);
-
- return jm;
- }
- #endregion
-
- #region 获取单个货品信息======================================================================
- ///
- /// 获取单个货品信息
- ///
- ///
- ///
- [HttpPost]
- public async Task GetProductInfo([FromBody] FMGetProductInfo entity)
- {
- var jm = new WebApiCallBack();
-
- var userId = 0;
- if (_user != null)
- {
- userId = _user.ID;
- }
-
- bool bl = entity.type == "pinTuan" || entity.type == "group";
-
- var getProductInfo = await _productsServices.GetProductInfo(entity.id, bl, userId, entity.type, entity.groupId);
- if (getProductInfo == null)
- {
- jm.msg = "获取单个货品失败";
- return jm;
- }
-
- jm.status = true;
- jm.msg = "获取单个货品成功";
- jm.data = getProductInfo;
-
- return jm;
- }
-
- #endregion
-
- #region 获取商品评价列表分页数据======================================================================
- ///
- /// 获取商品评价列表分页数据
- ///
- ///
- ///
- [HttpPost]
- public async Task GetGoodsComment([FromBody] FMPageByIntId entity)
- {
- var jm = new WebApiCallBack();
-
- //获取数据
- var list = await _goodsCommentServices.QueryPageAsync(p => p.goodsId == entity.id && p.isDisplay == true, p => p.createTime, OrderByType.Desc, entity.page, entity.limit);
-
- if (list.Any())
- {
- foreach (var item in list)
- {
- item.imagesArr = !string.IsNullOrEmpty(item.images) ? item.images.Split(",") : null;
- }
- }
-
- jm.status = true;
- jm.msg = "获取评论成功";
- jm.data = new
- {
- list,
- commentsCount = list.TotalCount,
- totalPages = list.TotalPages
- };
-
- return jm;
- }
- #endregion
-
- #region 获取商品参数======================================================================
- ///
- /// 获取单个商品参数
- ///
- ///
- ///
- [HttpPost]
- public async Task GetGoodsParams([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- //获取数据
- var goods = await _goodsServices.QueryByIdAsync(entity.id);
- if (goods == null)
- {
- jm.msg = GlobalConstVars.DataisNo;
- return jm;
- }
- var list = new List();
- var goodsParams = await _goodsParamsServices.QueryAsync();
-
- if (!string.IsNullOrEmpty(goods.parameters))
- {
- var arrItem = goods.parameters.Split("|");
- foreach (var item in arrItem)
- {
- if (!item.Contains(":")) continue;
-
- var childArr = item.Split(":");
- if (childArr.Length == 2)
- {
- var paramsId = Convert.ToInt32(childArr[0]);
- var paramsModel = goodsParams.First(p => p.id == paramsId);
- if (paramsModel != null)
- {
- list.Add(new WxNameValueDto()
- {
- name = paramsModel.name,
- value = childArr[1]
- });
- }
- }
- }
- }
- jm.status = true;
- jm.msg = "获取商品参数成功";
- jm.data = list;
-
- return jm;
- }
- #endregion
-
- #region 获取随机推荐商品==================================================
- ///
- /// 获取随机推荐商品
- ///
- ///
- [HttpPost]
- public async Task GetGoodsRecommendList([FromBody] FMIntId entity)
- {
- if (entity.id <= 0)
- {
- entity.id = 10;
- }
-
- var bl = entity.data.ObjectToBool();
-
- var jm = new WebApiCallBack()
- {
- status = true,
- code = 0,
- msg = "获取成功",
- data = await _goodsServices.GetGoodsRecommendList(entity.id, bl)
- };
- return jm;
- }
- #endregion
-
- //验证接口====================================================================================================
-
-
- #region 根据Token获取商品详情======================================================================
- ///
- /// 根据Token获取商品详情
- ///
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetDetialByToken([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var userId = 0;
- if (_user != null)
- {
- userId = _user.ID;
- }
-
- var model = await _goodsServices.GetGoodsDetial(entity.id, userId, false);
- if (model == null)
- {
- jm.msg = "商品获取失败";
- return jm;
- }
-
- await _goodsServices.UpdateAsync(p => new CoreCmsGoods() { viewCount = p.viewCount + 1 },
- p => p.id == entity.id);
-
-
- jm.status = true;
- jm.msg = "获取商品详情成功";
- jm.data = model;
- jm.methodDescription = JsonConvert.SerializeObject(_user);
-
- return jm;
- }
- #endregion
-
-
-
- }
-}
\ No newline at end of file
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/GroupController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/GroupController.cs
deleted file mode 100644
index cbeab97..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/GroupController.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using SqlSugar;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 团购调用接口数据
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class GroupController : ControllerBase
- {
-
- private readonly IHttpContextUser _user;
- private readonly ICoreCmsPromotionServices _coreCmsPromotionServices;
- private ICoreCmsGoodsServices _goodsServices;
-
-
- ///
- /// 构造函数
- ///
- public GroupController(IHttpContextUser user, ICoreCmsPromotionServices coreCmsPromotionServices, ICoreCmsGoodsServices goodsServices)
- {
- _user = user;
- _coreCmsPromotionServices = coreCmsPromotionServices;
- _goodsServices = goodsServices;
- }
-
-
- //公共接口====================================================================================================
-
- #region 获取秒杀团购列表===========================================================
- ///
- /// 获取秒杀团购列表
- ///
- ///
- [HttpPost]
- public async Task GetList([FromBody] FMGroupGetListPost entity)
- {
- var jm = await _coreCmsPromotionServices.GetGroupList(entity.type, _user.ID, entity.status, entity.page, entity.limit);
-
- return jm;
- }
- #endregion
-
- #region 获取秒杀团购详情===========================================================
- ///
- /// 获取秒杀团购详情
- ///
- ///
- [HttpPost]
- public async Task GetGoodsDetial([FromBody] FMGetGoodsDetial entity)
- {
- var jm = await _coreCmsPromotionServices.GetGroupDetail(entity.id, 0, "group", entity.groupId);
- return jm;
- }
- #endregion
-
- //验证接口====================================================================================================
-
-
- }
-}
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/PageController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/PageController.cs
deleted file mode 100644
index 236e505..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/PageController.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Threading.Tasks;
-using AutoMapper;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.Entities;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using CoreCms.Net.Model.ViewModels.DTO;
-using CoreCms.Net.Utility.Helper;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using SqlSugar;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 页面接口
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class PageController : ControllerBase
- {
- private IMapper _mapper;
- private readonly ICoreCmsSettingServices _settingServices;
- private readonly ICoreCmsPagesServices _pagesServices;
- private readonly ICoreCmsOrderServices _orderServices;
- private readonly ICoreCmsUserServices _userServices;
-
- ///
- /// 构造函数
- ///
- public PageController(IMapper mapper
- , ICoreCmsSettingServices settingServices
- , ICoreCmsPagesServices pagesServices
- , ICoreCmsOrderServices orderServices
- , ICoreCmsUserServices userServices)
- {
- _mapper = mapper;
- _settingServices = settingServices;
- _pagesServices = pagesServices;
- _orderServices = orderServices;
- _userServices = userServices;
- }
-
- //公共接口====================================================================================================
-
- #region 获取页面布局数据=============================================================
-
- ///
- /// 获取页面布局数据
- ///
- ///
- ///
- [HttpPost]
- [Description("获取页面布局数据")]
- public async Task GetPageConfig([FromBody] FMWxPost entity)
- {
- var jm = await _pagesServices.GetPageConfig(entity.code);
- return jm;
- }
- #endregion
-
- #region 获取用户购买记录=============================================================
-
- ///
- /// 获取用户购买记录
- ///
- [HttpPost]
- [Description("获取用户购买记录")]
- public async Task GetRecod([FromBody] FMGetRecodPost entity)
- {
- var jm = new WebApiCallBack() { status = true, msg = "获取成功", otherData = entity };
-
- /***
- * 随机数
- * 其它随机数据,需要自己补充
- */
- //logo作为头像
- Random rand = new Random();
-
- var allConfigs = await _settingServices.GetConfigDictionaries();
-
- var avatar = CommonHelper.GetConfigDictionary(allConfigs, SystemSettingConstVars.ShopLogo);
- var names = new string[] { "无人像你", "啭裑①羣豞", "朕射妳无罪", "骑着蜗牛狂奔", "残孤星", "上网可以,别开QVOD", "请把QQ留下!", "蹭网可以,一小时两块钱", "I~在。哭泣", "不倾国倾城只倾他一人", "你再发光我就拔你插头", "家,世间最温暖的地方", "挥着鸡翅膀的女孩", "难不难过都是一个人过", "原谅我盛装出席只为错过你", "残孤星", "只适合被遗忘", "爱情,算个屁丶", "执子辶掱", "朕今晚翻你牌子", "①苆兜媞命", "中华一样的高傲", "始于心动止于枯骨", "我们幸福呢", "表白失败,勿扰", "髮型吥能亂", "陽咣丅啲憂喐", "你棺材是翻盖的还是滑盖的", "孤枕", "泪颜葬相思", "喵星人", "超拽霸气的微博名字", "晚安晚安晚晚难安", "却输给了秒", "为什么我吃德芙没有黑丝飘", "请输入我大" };
- var listUsers = new List();
-
- foreach (var itemName in names)
- {
- var min = rand.Next(100, 1000);
- var createTime = DateTime.Now.AddMinutes(-min);
- listUsers.Add(new RandUser()
- {
- avatar = avatar,
- createTime = CommonHelper.TimeAgo(createTime),
- nickname = itemName,
- desc = "下单成功",
- dt = createTime
- });
- }
-
- if (entity.type == "home")
- {
- //数据库里面随机取出来几条数据
- var orders = await _orderServices.QueryListByClauseAsync(p => p.isdel == false, 20, p => p.createTime,
- OrderByType.Desc);
- if (orders != null && orders.Any())
- {
- Random rd = new Random();
- var index = rd.Next(orders.Count);
- var orderItem = orders[index];
- if (orderItem != null)
- {
- var user = await _userServices.QueryByIdAsync(orderItem.userId);
- if (user != null && !string.IsNullOrEmpty(user.nickName))
- {
- jm.data = new RandUser()
- {
- avatar = !string.IsNullOrEmpty(user.avatarImage) ? user.avatarImage : avatar,
- createTime = CommonHelper.TimeAgo(orderItem.createTime),
- nickname = user.nickName,
- desc = "下单成功",
- dt = orderItem.createTime
- };
- }
- }
- }
- else
- {
- Random rd = new Random();
- var listI = rd.Next(listUsers.Count);
- jm.data = listUsers[listI];
- }
- }
- return jm;
- }
- #endregion
-
-
- //验证接口====================================================================================================
-
- }
-
-
-
-}
\ No newline at end of file
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/PinTuanController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/PinTuanController.cs
deleted file mode 100644
index 05d66f6..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/PinTuanController.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.Entities;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using CoreCms.Net.Utility.Extensions;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using SqlSugar;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 拼团接口
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class PinTuanController : ControllerBase
- {
-
- private readonly IHttpContextUser _user;
- private readonly ICoreCmsPinTuanGoodsServices _pinTuanGoodsServices;
- private readonly ICoreCmsPinTuanRuleServices _pinTuanRuleServices;
- private readonly ICoreCmsProductsServices _productsServices;
- private readonly ICoreCmsPinTuanRecordServices _pinTuanRecordServices;
- private readonly ICoreCmsGoodsServices _goodsServices;
-
-
- ///
- /// 构造函数
- ///
- public PinTuanController(IHttpContextUser user
- , ICoreCmsPinTuanGoodsServices pinTuanGoodsServices
- , ICoreCmsPinTuanRuleServices pinTuanRuleServices
- , ICoreCmsProductsServices productsServices
- , ICoreCmsPinTuanRecordServices pinTuanRecordServices, ICoreCmsGoodsServices goodsServices)
- {
- _user = user;
- _pinTuanGoodsServices = pinTuanGoodsServices;
- _pinTuanRuleServices = pinTuanRuleServices;
- _productsServices = productsServices;
- _pinTuanRecordServices = pinTuanRecordServices;
- _goodsServices = goodsServices;
- }
-
-
- #region 拼团列表
- ///
- /// 拼团列表
- ///
- ///
- [HttpPost]
- public async Task GetList([FromBody] FMIntId entity)
- {
- WebApiCallBack jm;
-
- var userId = 0;
- if (_user != null)
- {
- userId = _user.ID;
- }
- var id = 0;
- if (entity.id > 0)
- {
- id = entity.id;
- }
- jm = await _pinTuanRuleServices.GetPinTuanList(id, userId);
- return jm;
-
- }
-
- #endregion
-
- #region 获取拼团商品信息
- ///
- /// 获取拼团商品信息
- ///
- ///
- [HttpPost]
- public async Task GetGoodsInfo([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var userId = 0;
- if (_user != null)
- {
- userId = _user.ID;
- }
- var pinTuanStatus = entity.data.ObjectToInt(1);
-
- jm.status = true;
- jm.msg = "获取详情成功";
- jm.data = await _pinTuanGoodsServices.GetGoodsInfo(entity.id, userId, pinTuanStatus);
-
- return jm;
-
- }
-
- #endregion
-
- #region 获取货品信息
- ///
- /// 获取货品信息
- ///
- ///
- [HttpPost]
- public async Task GetProductInfo([FromBody] FMGetProductInfo entity)
- {
- var jm = new WebApiCallBack();
-
- var products = await _productsServices.GetProductInfo(entity.id, false, 0, entity.type);
- if (products == null)
- {
- jm.msg = GlobalErrorCodeVars.Code10000;
- return jm;
- }
- //把拼团的一些属性等加上
- var info = await _pinTuanRuleServices.QueryMuchFirstAsync(
- (join1, join2) => new object[] { JoinType.Left, join1.id == join2.ruleId },
- (join1, join2) => join1, (join1, join2) => join2.goodsId == products.goodsId);
-
- if (info == null)
- {
- jm.msg = GlobalErrorCodeVars.Code10000;
- return jm;
- }
- products.pinTuanRule = info;
- jm.status = true;
- jm.data = products;
- return jm;
-
- }
-
- #endregion
-
- #region 根据订单id取拼团信息,用在订单详情页
- ///
- /// 根据订单id取拼团信息,用在订单详情页
- ///
- ///
- [HttpPost]
- public async Task GetPinTuanTeam([FromBody] FMGetPinTuanTeamPost entity)
- {
- var jm = new WebApiCallBack();
-
- if (string.IsNullOrEmpty(entity.orderId) && entity.teamId == 0)
- {
- jm.msg = GlobalErrorCodeVars.Code15606;
- return jm;
- }
- jm = await _pinTuanRecordServices.GetTeamList(entity.teamId, entity.orderId);
-
- return jm;
- }
-
- #endregion
-
- }
-}
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/ServiceController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/ServiceController.cs
deleted file mode 100644
index 422911e..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/ServiceController.cs
+++ /dev/null
@@ -1,404 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.Entities;
-using CoreCms.Net.Model.Entities.Expression;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using CoreCms.Net.Utility.Extensions;
-using CoreCms.Net.Utility.Helper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using SqlSugar;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 服务卡控制器
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class ServiceController : ControllerBase
- {
- private readonly ICoreCmsServicesServices _servicesServices;
- private readonly ICoreCmsUserServicesOrderServices _userServicesOrderServices;
- private readonly ICoreCmsUserServicesTicketServices _userServicesTicketServices;
- private readonly ICoreCmsUserServices _userServices;
- private readonly ICoreCmsUserServicesTicketVerificationLogServices _ticketVerificationLogServices;
- private readonly ICoreCmsClerkServices _clerkServices;
- private readonly ICoreCmsStoreServices _storeServices;
- private readonly ICoreCmsUserGradeServices _userGradeServices;
-
-
- private readonly IHttpContextUser _user;
-
- ///
- /// 构造函数
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public ServiceController(ICoreCmsServicesServices servicesServices, IHttpContextUser user, ICoreCmsUserServicesOrderServices userServicesOrderServices, ICoreCmsUserServicesTicketServices userServicesTicketServices, ICoreCmsUserServices userServices, ICoreCmsClerkServices clerkServices, ICoreCmsUserServicesTicketVerificationLogServices ticketVerificationLogServices, ICoreCmsStoreServices storeServices, ICoreCmsUserGradeServices userGradeServices)
- {
- _servicesServices = servicesServices;
- _user = user;
- _userServicesOrderServices = userServicesOrderServices;
- _userServicesTicketServices = userServicesTicketServices;
- _userServices = userServices;
- _clerkServices = clerkServices;
- _ticketVerificationLogServices = ticketVerificationLogServices;
- _storeServices = storeServices;
- _userGradeServices = userGradeServices;
- }
-
-
- #region 取得服务卡列表信息
- ///
- /// 取得服务卡列表信息
- ///
- ///
- [HttpPost]
- //[Authorize]
- public async Task GetPageList([FromBody] FMPageByIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var dt = DateTime.Now;
- var where = PredicateBuilder.True();
-
- where = where.And(p => p.status == (int)GlobalEnumVars.ServicesStatus.Shelve);
- where = where.And(p => p.amount > 0);
- where = where.And(p => p.startTime < dt && p.endTime > dt);
-
- var list = await _servicesServices.QueryPageAsync(where, p => p.createTime, OrderByType.Desc, entity.page, entity.limit);
-
- if (list.Any())
- {
- var storesAll = await _storeServices.QueryAsync();
- var userGradesAll = await _userGradeServices.QueryAsync();
-
- foreach (var data in list)
- {
- TimeSpan ts = data.endTime.Subtract(dt);
- data.timestamp = (int)ts.TotalSeconds;
-
- if (!string.IsNullOrEmpty(data.consumableStore))
- {
- var consumableStoreStr = CommonHelper.GetCaptureInterceptedText(data.consumableStore, ",");
- var consumableStoreIds = CommonHelper.StringToIntArray(consumableStoreStr);
- if (consumableStoreIds.Any())
- {
- var stores = storesAll.Where(p => consumableStoreIds.Contains(p.id)).ToList();
- data.consumableStores = stores.Select(p => p.storeName).ToList();
- }
- }
-
- if (!string.IsNullOrEmpty(data.allowedMembership))
- {
- var allowedMembershipStr = CommonHelper.GetCaptureInterceptedText(data.allowedMembership, ",");
- var allowedMembershipIds = CommonHelper.StringToIntArray(allowedMembershipStr);
- if (allowedMembershipIds.Any())
- {
- var userGrades = userGradesAll.Where(p => allowedMembershipIds.Contains(p.id)).ToList();
- data.allowedMemberships = userGrades.Select(p => p.title).ToList();
- }
- }
- }
- }
-
- jm.status = true;
- jm.data = new
- {
- list = list,
- count = list.TotalCount,
- };
- return jm;
-
- }
-
- #endregion
-
- #region 获取服务卡详情
- ///
- /// 获取服务卡详情
- ///
- ///
- [HttpPost]
- //[Authorize]
- public async Task GetDetails([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var data = await _servicesServices.QueryByClauseAsync(p => p.id == entity.id);
-
- if (data != null)
- {
- var dt = DateTime.Now;
- TimeSpan ts = data.endTime.Subtract(dt);
- data.timestamp = (int)ts.TotalSeconds;
-
- if (!string.IsNullOrEmpty(data.consumableStore))
- {
- var consumableStoreStr = CommonHelper.GetCaptureInterceptedText(data.consumableStore, ",");
- var consumableStoreIds = CommonHelper.StringToIntArray(consumableStoreStr);
- if (consumableStoreIds.Any())
- {
- var stores = await _storeServices.QueryListByClauseAsync(p => consumableStoreIds.Contains(p.id));
- data.consumableStores = stores.Select(p => p.storeName).ToList();
- }
- }
-
- if (!string.IsNullOrEmpty(data.allowedMembership))
- {
- var allowedMembershipStr = CommonHelper.GetCaptureInterceptedText(data.allowedMembership, ",");
- var allowedMembershipIds = CommonHelper.StringToIntArray(allowedMembershipStr);
- if (allowedMembershipIds.Any())
- {
- var userGrades = await _userGradeServices.QueryListByClauseAsync(p => allowedMembershipIds.Contains(p.id));
- data.allowedMemberships = userGrades.Select(p => p.title).ToList();
- }
- }
- }
-
- jm.status = true;
- jm.data = data;
- return jm;
-
- }
-
- #endregion
-
- //验证接口====================================================================================================
-
- #region 添加服务订单
- ///
- /// 取得服务卡列表信息
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task AddServiceOrder([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack();
-
- var data = await _servicesServices.QueryByClauseAsync(p => p.id == entity.id);
-
- if (data == null)
- {
- jm.msg = "服务数据获取失败";
- return jm;
- }
-
- var user = await _userServices.QueryByIdAsync(_user.ID);
- if (user == null)
- {
- jm.msg = "用户数据获取失败";
- return jm;
- }
-
- if (!data.allowedMembership.Contains("," + user.grade + ","))
- {
- jm.msg = "您所在的用户级别不支持购买";
- return jm;
- }
-
- var order = new CoreCmsUserServicesOrder();
- order.serviceOrderId = CommonHelper.GetSerialNumberType((int)GlobalEnumVars.SerialNumberType.服务订单编号);
- order.userId = _user.ID;
- order.servicesId = entity.id;
- order.isPay = false;
- order.status = (int)GlobalEnumVars.ServicesOrderStatus.正常;
- order.createTime = DateTime.Now;
-
- var bl = await _userServicesOrderServices.InsertAsync(order) > 0;
-
- jm.status = bl;
- jm.data = order.serviceOrderId;
- return jm;
-
- }
-
-
-
- #endregion
-
- #region 店铺核销的服务券列表
- ///
- /// 店铺核销的服务券列表
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task VerificationPageList([FromBody] FMPageByIntId entity)
- {
- var jm = await _ticketVerificationLogServices.GetVerificationLogs(_user.ID, entity.page, entity.limit);
- return jm;
- }
- #endregion
-
- #region 软删除服务券核销单数据
- ///
- /// 软删除服务券核销单数据
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task LogDelete([FromBody] FMIntId entity)
- {
- var jm = await _ticketVerificationLogServices.LogDelete(entity.id, _user.ID);
- return jm;
- }
- #endregion
-
-
- #region 获取单个提货单详情
- ///
- /// 获取单个提货单详情
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetTicketInfo([FromBody] FMStringId entity)
- {
- var jm = new WebApiCallBack();
-
- if (string.IsNullOrEmpty(entity.id))
- {
- jm.msg = "请提交查询数据关键词";
- return jm;
- }
-
- var ticket = await _userServicesTicketServices.QueryByClauseAsync(p => p.redeemCode == entity.id);
- if (ticket == null)
- {
- jm.msg = "未查询到服务券";
- return jm;
- }
-
- ticket.statusStr = EnumHelper.GetEnumDescriptionByValue(ticket.status);
-
- var service = await _servicesServices.QueryByClauseAsync(p => p.id == ticket.serviceId);
- var serviceOrder =
- await _userServicesOrderServices.QueryByClauseAsync(p => p.serviceOrderId == ticket.serviceOrderId);
-
- jm.status = true;
- jm.data = new
- {
- ticket,
- service,
- serviceOrder
- };
-
- return jm;
- }
- #endregion
-
- #region 核销服务券
- ///
- /// 核销服务券
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task VerificationTicket([FromBody] FMStringId entity)
- {
- var jm = new WebApiCallBack();
-
- if (string.IsNullOrEmpty(entity.id))
- {
- jm.msg = "请提交查询数据关键词";
- return jm;
- }
- var ticket = await _userServicesTicketServices.QueryByClauseAsync(p => p.redeemCode == entity.id);
- if (ticket == null)
- {
- jm.msg = "未查询到服务券";
- return jm;
- }
-
- if (ticket.status != (int)GlobalEnumVars.ServicesTicketStatus.Normal)
- {
- jm.msg = "服务券状态不支持核销";
- return jm;
- }
-
- var service = await _servicesServices.QueryByIdAsync(ticket.serviceId);
- if (service == null)
- {
- jm.msg = "服务项目获取失败";
- return jm;
- }
-
- var user = await _userServices.QueryByIdAsync(_user.ID);
- if (user == null)
- {
- jm.msg = "未获取到审核权限";
- return jm;
- }
-
- var clerk = await _clerkServices.QueryByClauseAsync(p => p.userId == user.id);
- if (clerk == null)
- {
- jm.msg = "非门店店员无权限核验";
- return jm;
- }
-
- if (!service.consumableStore.Contains("," + clerk.storeId + ","))
- {
- jm.msg = "您所在的门店无权核销此券";
- return jm;
- }
-
- //开始更新数据
- var log = new CoreCmsUserServicesTicketVerificationLog
- {
- storeId = clerk.storeId,
- verificationUserId = _user.ID,
- ticketId = ticket.id,
- ticketRedeemCode = ticket.redeemCode,
- verificationTime = DateTime.Now,
- serviceId = ticket.serviceId,
- isDel = false
- };
-
- ticket.status = (int)GlobalEnumVars.ServicesTicketStatus.Verification;
- ticket.verificationTime = DateTime.Now;
- ticket.isVerification = true;
- var up = await _userServicesTicketServices.UpdateAsync(ticket);
- var bl = false;
- if (up)
- {
- bl = await _ticketVerificationLogServices.InsertAsync(log) > 0;
- }
- jm.status = up && bl;
- jm.msg = jm.status ? "核销成功" : "核销失败";
-
- return jm;
- }
- #endregion
-
-
- }
-}
diff --git a/server/CoreCms.Net.Web.WebApi/Controllers/StoreController.cs b/server/CoreCms.Net.Web.WebApi/Controllers/StoreController.cs
deleted file mode 100644
index 454a74a..0000000
--- a/server/CoreCms.Net.Web.WebApi/Controllers/StoreController.cs
+++ /dev/null
@@ -1,365 +0,0 @@
-/***********************************************************************
- * Project: CoreCms
- * ProjectName: 核心内容管理系统
- * Web: https://www.corecms.net
- * Author: 大灰灰
- * Email: jianweie@163.com
- * CreateTime: 2021/1/31 21:45:10
- * Description: 暂无
- ***********************************************************************/
-
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using CoreCms.Net.Auth.HttpContextUser;
-using CoreCms.Net.Configuration;
-using CoreCms.Net.IServices;
-using CoreCms.Net.Model.Entities;
-using CoreCms.Net.Model.Entities.Expression;
-using CoreCms.Net.Model.FromBody;
-using CoreCms.Net.Model.ViewModels.UI;
-using CoreCms.Net.Model.ViewModels.DTO;
-using CoreCms.Net.Utility.Extensions;
-using CoreCms.Net.Utility.Helper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using SqlSugar;
-
-namespace CoreCms.Net.Web.WebApi.Controllers
-{
- ///
- /// 门店调用接口数据
- ///
- [Route("api/[controller]/[action]")]
- [ApiController]
- public class StoreController : ControllerBase
- {
- private readonly IHttpContextUser _user;
- private readonly ICoreCmsStoreServices _storeServices;
- private readonly ICoreCmsClerkServices _clerkServices;
- private readonly ICoreCmsSettingServices _settingServices;
- private readonly ICoreCmsBillLadingServices _billLadingServices;
- private readonly ICoreCmsOrderServices _orderServices;
-
-
- ///
- /// 构造函数
- ///
- public StoreController(IHttpContextUser user
- , ICoreCmsStoreServices storeServices
- , ICoreCmsClerkServices clerkServices
- , ICoreCmsSettingServices settingServices
- , ICoreCmsBillLadingServices billLadingServices, ICoreCmsOrderServices orderServices)
- {
- _user = user;
- _storeServices = storeServices;
- _clerkServices = clerkServices;
- _settingServices = settingServices;
- _billLadingServices = billLadingServices;
- _orderServices = orderServices;
- }
-
- //公共接口======================================================================================================
-
- #region 获取默认的门店
- ///
- /// 获取默认的门店
- ///
- ///
- [HttpPost]
- public async Task GetDefaultStore()
- {
- var jm = new WebApiCallBack();
-
- var ship = await _storeServices.QueryByClauseAsync(p => p.isDefault == true);
- jm.status = true;
- jm.data = ship;
-
- return jm;
- }
- #endregion
-
- #region 获取门店列表数据
- ///
- /// 获取门店列表数据
- ///
- ///
- [HttpPost]
- public async Task GetStoreList([FromBody] FMGetStoreQueryPageByCoordinate entity)
- {
- var jm = new WebApiCallBack();
- try
- {
- var where = PredicateBuilder.True();
-
- if (!string.IsNullOrEmpty(entity.key))
- {
- where = where.And(p => p.storeName.Contains(entity.key));
- }
-
- jm.status = true;
-
- var data = await _storeServices.QueryPageAsyncByCoordinate(where, p => p.distance, OrderByType.Asc, entity.page, entity.limit, entity.latitude, entity.longitude);
-
- foreach (var item in data)
- {
- if (item.distance > 0)
- {
- if (item.distance > 1000)
- {
- item.distanceStr = Math.Round(item.distance / 1000, 2) + "km";
- }
- else
- {
- item.distanceStr = Math.Round(item.distance, 2) + "m";
- }
- }
- else
- {
- item.distanceStr = "未知";
- }
- }
- jm.data = data;
- jm.otherData = new
- {
- totalCount = data.TotalCount,
- totalPages = data.TotalPages,
- };
- }
- catch (Exception e)
- {
-
- jm.msg = GlobalConstVars.DataHandleEx;
- jm.data = e.ToString();
- }
- return jm;
- }
- #endregion
-
- #region 获取推荐关键词
- ///
- /// 获取推荐关键词
- ///
- ///
- [HttpPost]
- public async Task GetRecommendKeys()
- {
- var jm = new WebApiCallBack();
-
- var allConfigs = await _settingServices.GetConfigDictionaries();
- var recommendKeysStr = CommonHelper.GetConfigDictionary(allConfigs, SystemSettingConstVars.RecommendKeys);
- jm.status = true;
- jm.msg = "获取成功";
- jm.data = !string.IsNullOrEmpty(recommendKeysStr) ? recommendKeysStr.Split("|") : new string[] { };
-
- return jm;
- }
- #endregion
-
- #region 判断是否开启门店自提
- ///
- /// 判断是否开启门店自提
- ///
- ///
- [HttpPost]
- public async Task GetStoreSwitch()
- {
- var jm = new WebApiCallBack { status = true, msg = "获取成功" };
-
- var allConfigs = await _settingServices.GetConfigDictionaries();
- jm.data = CommonHelper.GetConfigDictionary(allConfigs, SystemSettingConstVars.StoreSwitch).ObjectToInt(2); ;
- return jm;
- }
- #endregion
-
- #region 根据序列获取门店数据
- ///
- /// 根据序列获取门店数据
- ///
- ///
- [HttpPost]
- public async Task GetStoreById([FromBody] FMIntId entity)
- {
- var jm = new WebApiCallBack
- {
- status = true,
- msg = "获取成功",
- data = await _storeServices.QueryByClauseAsync(p => p.id == entity.id)
- };
- return jm;
- }
- #endregion
-
- //验证接口======================================================================================================
-
- #region 判断访问用户是否是店员
- ///
- /// 判断访问用户是否是店员
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task IsClerk()
- {
- var jm = await _clerkServices.IsClerk(_user.ID);
- return jm;
- }
- #endregion
-
- #region 根据用户序列获取门店数据
- ///
- /// 根据用户序列获取门店数据
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetStoreByUserId()
- {
- var jm = new WebApiCallBack
- {
- status = true,
- msg = "获取成功",
- data = await _storeServices.GetStoreByUserId(_user.ID)
- };
- return jm;
- }
- #endregion
-
- #region 获取个人订单列表
-
- ///
- /// 获取个人订单列表
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetOrderPageByMerchant([FromBody] GetOrderPageByMerchantPost entity)
- {
- var jm = new WebApiCallBack();
-
- var store = await _storeServices.GetStoreByUserId(_user.ID);
- if (store != null)
- {
- jm = await _orderServices.GetOrderPageByMerchant(entity.dateType, entity.date, entity.status, entity.storeId, entity.page, entity.limit);
- }
- else
- {
- jm.status = false;
- jm.msg = "你不是店员";
- }
-
- return jm;
- }
-
- #endregion
-
- #region 搜索订单
-
- ///
- /// 搜索订单
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task GetOrderPageByMerchantSearch([FromBody] GetOrderPageByMerchantSearcgPost entity)
- {
- var jm = new WebApiCallBack();
-
- var store = await _storeServices.GetStoreByUserId(_user.ID);
- if (store != null)
- {
- jm = await _orderServices.GetOrderPageByMerchantSearch(entity.keyword, entity.status, entity.receiptType, entity.storeId, entity.page, entity.limit);
- }
- else
- {
- jm.status = false;
- jm.msg = "你不是店员";
- }
-
- return jm;
- }
-
- #endregion
-
-
- #region 店铺提货单列表
- ///
- /// 店铺提货单列表
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task StoreLadingList([FromBody] FMPageByIntId entity)
- {
- var jm = await _billLadingServices.GetStoreLadingList(_user.ID, entity.page, entity.limit);
- return jm;
- }
- #endregion
-
- #region 删除提货单数据
- ///
- /// 删除提货单数据
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task LadingDelete([FromBody] FMStringId entity)
- {
- var jm = await _billLadingServices.LadingDelete(entity.id, _user.ID);
- return jm;
- }
- #endregion
-
- #region 获取单个提货单详情
- ///
- /// 获取单个提货单详情
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task LadingInfo([FromBody] FMStringId entity)
- {
- var jm = new WebApiCallBack();
-
- if (string.IsNullOrEmpty(entity.id))
- {
- jm.msg = "请提交查询数据关键词";
- return jm;
- }
- jm = await _billLadingServices.GetInfo(entity.id, _user.ID);
-
- return jm;
- }
- #endregion
-
- #region 核销订单
- ///
- /// 核销订单
- ///
- ///
- [HttpPost]
- [Authorize]
- public async Task Lading([FromBody] FMStringId entity)
- {
- var jm = new WebApiCallBack();
-
- if (string.IsNullOrEmpty(entity.id))
- {
- jm.msg = "请提交查询数据关键词";
- return jm;
- }
- var array = entity.id.Split(",");
- var result = await _billLadingServices.LadingOperating(array, _user.ID);
- jm.status = result.code == 0;
- jm.msg = result.msg;
-
- return jm;
- }
- #endregion
-
-
- }
-}
\ No newline at end of file
diff --git a/uniapp/mahjong_group b/uniapp/mahjong_group
index 316d1e2..05ad134 160000
--- a/uniapp/mahjong_group
+++ b/uniapp/mahjong_group
@@ -1 +1 @@
-Subproject commit 316d1e2b5d7013d0b9f7507ac2f15ead5ff4d703
+Subproject commit 05ad1348cf7b9ec80590919c3f23c2cb2c077e70
diff --git a/v1.0.0_需求文档.md b/v1.0.0_需求文档.md
new file mode 100644
index 0000000..78957ae
--- /dev/null
+++ b/v1.0.0_需求文档.md
@@ -0,0 +1,25 @@
+一、会员档案功能
+1、会员第一次使用需要授权微信号登录,绑定联系电话 并填写个人信息
+如: 打什么类型的麻将(扑克) 性别 年龄 是否抽烟 常消费包厢类型(大中小包)
+2、会员行为标签
+ 设定会员星级及黑名单,主要用于规避恶意组局及恶意逃单人员
+星级包括牌品 牌技 形象 初始全为4分
+组局结束后可找到历史对局,并对历史牌友进行评价
+ 统计顾客组局成功率 被投诉次数 消费频次 消费能力 鸽子次数并且公开可查看
+3、要有相应的后台管理顾客档案
+二、麻友匹配机制
+1、需求发布
+用户可发布组局需求,需填写信息包含时间地点包厢号包厢类型是否禁烟游戏偏好等
+地点为单一店名 主要填写包厢号 包厢号需和包厢类型已经包厢费用绑定
+2、组局参与
+其他客户可查看组局人员信息,时间地点信息等详情 同意即可参与
+3、组局成功后自动发送微信消息给参与者,消息包含组局的时间地点信息,该消息可在后台设置相应模板
+4、组局成功后发起组局者(局头)会收到组局其他参与者的信息,其中包含其他人联系方式微信名,及一张现金券,通知组局者本次消费成功后他将会获得现金奖励或当局立减
+5、组局成功后店铺经营人员微信可收到推送消息,并查看组局情况以确定核销内容及包间情况
+6、鸽子费用(定金),组局成功可设置相应鸽子费用,分为三挡0/5/15,发起组局时发起人可选择是否设置鸽子费,如设置鸽子费则需线上支付鸽子费至平台账户,四人皆到店后鸽子费原路返还,若有人爽约,爽约人定金作为其他三人补偿不予退还,并记录其爽约
+7、组局奖励推送
+三、广告及推送内容
+1、使用软件时会跳出店铺会员注册的二维码弹窗广告(用于线下引流)
+2、免责声明及保证金说明
+3、本店铺相应logo及图片广告位
+
diff --git a/v1.0.1_需求文档.md b/v1.0.1_需求文档.md
new file mode 100644
index 0000000..26310a3
--- /dev/null
+++ b/v1.0.1_需求文档.md
@@ -0,0 +1,114 @@
+v1.1版本更新
+需求简介
+1. 增加新功能:
+1.1. 站内信。
+1.2. 预约页优化。
+1.3. 我的收益。
+一、站内信
+(1) 入口
+1. 我的页面,功能区,新增站内信入口【我的消息】。
+
+站内信 入口
+1.1. 有新消息时,icon右上角显示“红点”。
+(2) 详情页
+1. 显示所有通知,以最近时间为排序。
+
+站内信 详情页
+1.1. 显示标题、正文、通知时间。
+1.2. 正文过长时,自动延长本条消息的长度。
+1.3. 进入本页面时,默认已读所有消息。
+(3) 通知发送
+1. 在后台可配置通知的发送。
+2. 通知分为自动发送、手动发送。
+2.1. 自动发送的通知,根据不同的条件提前固定好模板内容。
+3. 自动发送:
+3.1. 组局成功时,所有参与人收到相应的通知。
+3.2. 组局失败时,所有参与人收到相应的通知。
+3.3. ……待补充其他自动发送规则。
+4. 手动发送:
+4.1. 可指定单个、多个用户发送相同的通知,通知内容可自定义。
+4.2. 可向全部用户发送相同的通知,通知内容可自定义。
+5. 用户只能单方向接收通知,不能对通知进行回复。
+二、预约页优化
+(1) 新增每天房间空闲/使用展示页
+
+每天房间空闲/使用展示
+1. 展示从“今天”起,到未来7天内的房间列表。
+1.1. 点击日期,切换对应日期的房间预约和使用情况。
+2. 时间段规则:
+2.1. 凌晨:0点 ~ 5点59分。
+2.2. 上午:6点 ~ 11点59分。
+2.3. 下午:12点 ~ 17点59分。
+2.4. 晚上:18点 ~ 23点59分。
+3. 【查看当前时段空闲房间】按钮默认为“关闭”状态,点击后变为“开启”状态。开启后只展示当前时段内“可预约”的房间。
+4. 房间列表,展示房间图片、房间号/房间名、房间类型、标准价格、会员价格、每天4个时段的可预约状态、当前时段的使用状态、【预约】按钮。
+
+房间信息和当日时段预约状态
+4.1. 后台可配置房间图片、房间号/房间名、房间类型、标准价格、会员价格。
+4.1.1. 会员,为线下店内的会员,与小程序无关,小程序仅作价格展示。
+4.1.2. 标准价格与会员价格,在配置时,按“xx元/xx小时”为格式进行配置,例:
+4.1.2.1. 标准价格:30元/4小时。
+4.1.2.2. 会员价格:30元/5小时 或 20元/4小时。
+4.2. 4个时段的可预约状态,根据用户预约单、后台操作自动改变。
+5. 若今日内,所有时间段都已被约满,【预约】按钮处于“灰色不可点击”状态。
+6. 点击【预约】跳转到“预约页”。
+7. 后台可设置每个房间号在不同日期、时间段内的预约状态。
+7.1. 用于线下预约房间后,将房间状态同步至线上。
+(2) 预约页
+
+预约页
+1. 预约日期,不可更改,根据上级页面的选项自动展示。
+2. 预约时间,可选择“凌晨”“上午”“下午”“晚上”。
+3. 最晚到店时间:
+3.1. 日期不可选,默认当天。
+3.2. 选择时间,不可小于当前时段,不可超出当天。
+4. 房间号,不可更改,根据上级页面的选项自动展示。
+5. 人数,新增“无需组局”选项。
+5.1. 相当于“人数:1人”,只有发起者自己。
+6. 鸽子费,新增“自定义”选项。
+
+鸽子费
+6.1. 不能超出50元。
+6.2. 只能输入整数。
+三、我的收益
+(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. 已打款:通过线下转帐后,将该记录手动改变为本状态。
+
diff --git a/业务逻辑文档 copy.md b/业务逻辑文档 copy.md
new file mode 100644
index 0000000..ea7f9e5
--- /dev/null
+++ b/业务逻辑文档 copy.md
@@ -0,0 +1,1190 @@
+# 麻将组局预约小程序 - 业务逻辑详解文档
+
+## 目录
+
+1. [项目概述](#1-项目概述)
+2. [系统架构](#2-系统架构)
+3. [核心业务模块](#3-核心业务模块)
+4. [核心业务流程图](#4-核心业务流程图)
+5. [数据模型](#5-数据模型)
+6. [API接口详解](#6-api接口详解)
+7. [业务规则汇总](#7-业务规则汇总)
+8. [状态机定义](#8-状态机定义)
+
+---
+
+## 1. 项目概述
+
+### 1.1 产品定位
+
+这是一个**麻将组局预约小程序**,帮助麻将爱好者在线上发起、加入麻将局,线下到店进行游戏。小程序提供了完整的预约流程管理,包括房间选择、预约发起、参与管理、签到评价等功能。
+
+### 1.2 技术栈
+
+| 层级 | 技术 | 说明 |
+|------|------|------|
+| 前端 | UniApp + Vue 3 | 支持微信小程序、H5、APP多端发布 |
+| 后端 | .NET Core 6.0+ | RESTful API |
+| ORM | SqlSugar | 数据库访问层 |
+| 数据库 | Microsoft SQL Server | 数据持久化 |
+| 支付 | 微信支付 | 鸽子费(押金)支付 |
+
+### 1.3 核心功能清单
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ 麻将组局预约系统 │
+├─────────────────────────────────────────────────────────┤
+│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
+│ │ 首页 │ │ 预约 │ │ 个人中心│ │ 消息 │ │
+│ │ 预约列表│ │ 房间选择│ │ 我的预约│ │ 站内信 │ │
+│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
+├─────────────────────────────────────────────────────────┤
+│ 核心业务: │
+│ • 发起预约 • 加入预约 • 取消预约 │
+│ • 预约签到 • 牌友评价 • 信誉管理 │
+│ • 鸽子费支付 • 退款处理 • 收益提现 │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 2. 系统架构
+
+### 2.1 前端架构
+
+```
+uniapp/mahjong_group/
+├── pages/ # 页面目录
+│ ├── index/
+│ │ └── index.vue # 首页 - 预约列表
+│ ├── appointment/
+│ │ ├── book-room-page.vue # 房间选择页(Tab页)
+│ │ └── appointment-page.vue # 预约表单页
+│ └── me/
+│ ├── me-page.vue # 个人中心
+│ ├── appointment-record-page.vue # 预约记录
+│ ├── my-earnings-page.vue # 我的收益
+│ └── my-message-page.vue # 消息中心
+├── components/ # 组件目录
+│ ├── index/
+│ │ └── MahjongCard.vue # 麻将预约卡片(九宫格)
+│ └── com/
+│ ├── index/
+│ │ └── ReservationPopup.vue # 预约详情弹窗
+│ └── page/
+│ ├── container-base.vue # 基础容器(含签到、评价弹窗)
+│ └── qiandao-popup.vue # 签到弹窗
+└── common/
+ ├── server/
+ │ └── interface/ # API接口定义
+ │ ├── sq.js # 预约相关接口
+ │ ├── user.js # 用户相关接口
+ │ ├── earnings.js # 收益相关接口
+ │ └── message.js # 消息相关接口
+ └── system/
+ ├── request.js # 请求封装(含签名)
+ └── cacheService.js # 缓存服务
+```
+
+### 2.2 后端架构
+
+```
+server/CoreCms.Net.Web.WebApi/
+└── Controllers/
+ └── SQController.cs # 预约核心控制器(全部预约相关API)
+ ├── GetReservationList # 首页预约列表
+ ├── AddSQReservation # 创建预约
+ ├── JoinReservation # 加入预约
+ ├── CancelReservation # 取消预约
+ ├── CheckInReservation # 签到
+ ├── GetEvaluateServices # 获取评价
+ ├── AddEvaluateServices # 添加评价
+ ├── GetMessageList # 消息列表
+ ├── GetEarningsSummary # 收益统计
+ └── ApplyWithdraw # 申请提现
+```
+
+### 2.3 系统架构图
+
+```mermaid
+graph TB
+ subgraph 客户端
+ A[微信小程序] --> B[UniApp]
+ C[H5网页] --> B
+ D[APP] --> B
+ end
+
+ subgraph 前端层
+ B --> E[Vue 3 组件]
+ E --> F[API接口层]
+ F --> G[请求封装/签名]
+ end
+
+ subgraph 后端层
+ G --> H[.NET Core WebAPI]
+ H --> I[SQController]
+ I --> J[Services服务层]
+ J --> K[SqlSugar ORM]
+ end
+
+ subgraph 数据层
+ K --> L[(SQL Server)]
+ end
+
+ subgraph 第三方服务
+ H --> M[微信支付]
+ H --> N[微信登录]
+ end
+```
+
+---
+
+## 3. 核心业务模块
+
+### 3.1 用户认证模块
+
+#### 3.1.1 登录流程
+
+用户通过微信小程序登录,获取手机号授权后绑定账号。
+
+```mermaid
+sequenceDiagram
+ participant U as 用户
+ participant MP as 小程序
+ participant WX as 微信服务器
+ participant BE as 后端服务
+ participant DB as 数据库
+
+ U->>MP: 点击登录
+ MP->>U: 请求授权手机号
+ U->>MP: 同意授权
+ MP->>WX: 获取code和encryptedData
+ WX->>MP: 返回加密数据
+ MP->>BE: POST /user/UseWxPhoneNumberLogin
+ Note over MP,BE: 传递code和sessionAuthId
+ BE->>WX: 解密手机号
+ WX->>BE: 返回手机号
+ BE->>DB: 查询/创建用户
+ DB->>BE: 返回用户信息
+ BE->>MP: 返回token和用户信息
+ MP->>MP: 存储token到本地
+ MP->>U: 登录成功
+```
+
+#### 3.1.2 用户信息结构
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | int | 用户ID |
+| nickName | string | 昵称 |
+| avatarImage | string | 头像URL |
+| sex | int | 性别:1男 2女 |
+| birthday | DateTime? | 生日(用于计算年龄) |
+| credit_score | decimal | 信誉分(0-5分,默认5分) |
+| play_level | decimal | 牌品评分(1-5分,默认4分) |
+| skills_level | decimal | 牌技评分(1-5分,默认4分) |
+| dove_count | int | 鸽子次数 |
+
+---
+
+### 3.2 预约管理模块
+
+#### 3.2.1 预约生命周期
+
+```mermaid
+stateDiagram-v2
+ [*] --> 待开始: 创建预约
+ 待开始 --> 已锁定: 人满
+ 待开始 --> 已取消: 发起者取消
+ 待开始 --> 待开始: 参与者加入/退出
+ 已锁定 --> 待开始: 参与者退出
+ 已锁定 --> 已取消: 发起者取消
+ 已锁定 --> 进行中: 签到
+ 待开始 --> 进行中: 签到(人未满也可签到)
+ 进行中 --> 已结束: 时间结束
+ 已取消 --> [*]
+ 已结束 --> [*]
+```
+
+#### 3.2.2 预约状态说明
+
+| status | 状态名 | 说明 |
+|--------|--------|------|
+| 0 | 待开始 | 预约已创建,等待参与者加入或等待开始时间 |
+| 1 | 已锁定 | 参与人数已满,等待开始 |
+| 2 | 进行中 | 发起者已签到,游戏进行中 |
+| 3 | 已结束 | 预约时间已过,预约完成 |
+| 4 | 已取消 | 预约被取消 |
+
+#### 3.2.3 发起预约流程
+
+```mermaid
+flowchart TD
+ A[用户点击发起预约] --> B[选择日期]
+ B --> C[查看房间列表及时段状态]
+ C --> D{选择房间和时段}
+ D --> E[填写预约信息]
+
+ subgraph 预约信息
+ E --> E1[组局名称]
+ E --> E2[人数 1-4人]
+ E --> E3[玩法类型]
+ E --> E4[游戏规则]
+ E --> E5[参与限制]
+ E --> E6[鸽子费设置]
+ end
+
+ E1 & E2 & E3 & E4 & E5 & E6 --> F[提交前验证]
+ F --> G{调用CanCreateSQReservation}
+ G -->|验证失败| H[显示错误信息]
+ H --> E
+ G -->|验证通过| I{是否有鸽子费}
+ I -->|有| J[发起微信支付]
+ J --> K{支付结果}
+ K -->|成功| L[调用AddSQReservation创建预约]
+ K -->|失败| M[提示支付失败]
+ I -->|无| L
+ L --> N[创建预约记录]
+ N --> O[创建发起者参与记录 role=1]
+ O --> P[返回预约ID]
+ P --> Q[跳转到预约详情]
+```
+
+#### 3.2.4 加入预约流程
+
+```mermaid
+flowchart TD
+ A[用户点击加入预约] --> B{预约是否存在且未结束}
+ B -->|否| C[提示:预约不存在或已结束]
+ B -->|是| D{是否已加入该预约}
+ D -->|是| E[提示:您已加入该预约]
+ D -->|否| F{是否为独享模式}
+ F -->|是| G[提示:该预约不接受其他人加入]
+ F -->|否| H[校验参与条件]
+
+ subgraph 条件校验
+ H --> H1{信誉分是否达标}
+ H1 -->|否| I1[提示:信誉分不足]
+ H1 -->|是| H2{性别是否符合}
+ H2 -->|否| I2[提示:性别不符合要求]
+ H2 -->|是| H3{年龄是否在范围内}
+ H3 -->|否| I3[提示:年龄不符合限制]
+ H3 -->|是| H4{是否有时间冲突}
+ H4 -->|是| I4[提示:有其他预约时间冲突]
+ H4 -->|否| H5{预约是否已满}
+ H5 -->|是| I5[提示:预约已满]
+ end
+
+ H5 -->|否| J{是否有鸽子费}
+ J -->|有| K[发起微信支付]
+ K --> L{支付结果}
+ L -->|成功| M[创建参与者记录 role=0]
+ L -->|失败| N[提示支付失败]
+ J -->|无| M
+ M --> O[加入成功]
+```
+
+#### 3.2.5 取消预约流程
+
+```mermaid
+flowchart TD
+ A[用户点击取消预约] --> B{预约是否存在}
+ B -->|否| C[提示:预约不存在]
+ B -->|是| D{距开始是否<30分钟}
+ D -->|是| E[提示:开始前30分钟无法取消]
+ D -->|否| F{预约状态是否>=3}
+ F -->|是| G[提示:已结束或已取消]
+ F -->|否| H{用户角色}
+
+ H -->|发起者 role=1| I[发起者取消整个预约]
+ I --> J[预约状态改为已取消 status=4]
+ J --> K[所有参与者标记为已退出]
+ K --> L{是否有已支付押金}
+ L -->|有| M[已支付者标记为待退款 is_refund=3]
+ M --> N[发送通知给所有参与者]
+ L -->|无| N
+
+ H -->|参与者 role=0| O{预约是否已开始}
+ O -->|是| P[提示:只有发起者可取消]
+ O -->|否| Q[参与者退出]
+ Q --> R[标记该用户为已退出 status=1]
+ R --> S{该用户是否已支付押金}
+ S -->|是| T[标记为待退款 is_refund=3]
+ T --> U[退出成功]
+ S -->|否| U
+
+ N --> V[取消完成]
+```
+
+---
+
+### 3.3 签到模块
+
+#### 3.3.1 签到流程
+
+签到是预约流程的关键环节,只有发起者可以操作,用于确认实际到场人员。
+
+```mermaid
+flowchart TD
+ A[发起者点击签到] --> B{预约是否存在}
+ B -->|否| C[提示:预约不存在]
+ B -->|是| D{预约状态是否>=3}
+ D -->|是| E[提示:已结束或已取消]
+ D -->|否| F{是否已签到 status=2}
+ F -->|是| G[提示:已签到,无法重复]
+ F -->|否| H{是否为发起者}
+ H -->|否| I[提示:仅发起者可签到]
+ H -->|是| J[显示参与者列表]
+
+ J --> K[勾选实际到场人员]
+ K --> L[提交签到]
+
+ L --> M[开启数据库事务]
+ M --> N[预约状态改为进行中 status=2]
+ N --> O[所有未退出参与者默认标记为到场 is_arrive=1]
+ O --> P{是否有未到场人员}
+
+ P -->|有| Q[未到场人员处理]
+ subgraph 爽约惩罚
+ Q --> Q1[标记为未到场 is_arrive=2]
+ Q1 --> Q2[标记为已退出 status=1]
+ Q2 --> Q3[扣除信誉分 -0.5]
+ Q3 --> Q4[增加鸽子次数 dove_count++]
+ Q4 --> Q5[记录信誉变更日志]
+ end
+
+ P -->|无| R[到场人员处理]
+ Q5 --> R
+
+ subgraph 守约奖励
+ R --> R1[增加信誉分 +0.2 最高5.0]
+ R1 --> R2[记录信誉变更日志]
+ end
+
+ R2 --> S{是否有押金}
+ S -->|有| T[到场且已支付者标记为待退款 is_refund=3]
+ T --> U[提交事务]
+ S -->|无| U
+ U --> V[签到完成]
+```
+
+#### 3.3.2 签到后信誉变化
+
+| 情况 | 信誉变化 | 其他影响 |
+|------|----------|----------|
+| 到场(守约) | +0.2(最高5.0) | - |
+| 未到场(爽约) | -0.5 | 鸽子次数+1 |
+
+---
+
+### 3.4 评价模块
+
+#### 3.4.1 评价流程
+
+```mermaid
+flowchart TD
+ A[用户进入预约详情] --> B{预约是否已完成且已签到}
+ B -->|否| C[不显示评价入口]
+ B -->|是| D[显示评价入口]
+ D --> E[点击牌友评价]
+ E --> F[获取可评价参与者列表]
+ F --> G[只显示实际到场的其他参与者]
+
+ G --> H[选择要评价的人]
+ H --> I[填写评价]
+
+ subgraph 评价维度
+ I --> I1[牌品评分 1-5星]
+ I --> I2[牌技评分 1-5星]
+ end
+
+ I1 & I2 --> J[提交评价]
+ J --> K[创建评价记录]
+ K --> L[重新计算被评价人的平均分]
+
+ subgraph 平均分计算
+ L --> L1[获取所有历史评价]
+ L1 --> L2[计算 牌品 = sum评价 + 4 / 评价次数 + 1]
+ L2 --> L3[计算 牌技 = sum评价 + 4 / 评价次数 + 1]
+ L3 --> L4[如果只有1次评价,分母+1避免偏差]
+ end
+
+ L4 --> M[更新用户评分]
+ M --> N[评价完成]
+```
+
+#### 3.4.2 评价规则
+
+1. **评价时机**:预约状态为"已结束"且已签到后
+2. **评价对象**:只能评价实际到场的其他参与者(排除自己)
+3. **评价次数**:每个参与者只能被评价一次
+4. **评价维度**:
+ - 牌品(play_level):1-5分
+ - 牌技(skills_level):1-5分
+
+#### 3.4.3 评分计算公式
+
+```
+新评分 = (历史评分总和 + 初始分4) / (评价次数 + 1)
+
+特殊情况:当只有1次评价时,分母为(评价次数 + 2)避免偏差
+```
+
+---
+
+### 3.5 押金(鸽子费)模块
+
+#### 3.5.1 押金流程总览
+
+```mermaid
+flowchart TD
+ subgraph 发起预约
+ A[发起者设置鸽子费] --> B[发起者支付鸽子费]
+ B --> C[创建预约成功]
+ C --> D[参与者记录 is_refund=1 待支付]
+ end
+
+ subgraph 加入预约
+ E[参与者加入] --> F{预约是否有鸽子费}
+ F -->|有| G[参与者支付鸽子费]
+ G --> H[参与者记录 is_refund=2 已支付]
+ F -->|无| I[参与者记录 is_refund=0]
+ end
+
+ subgraph 签到/取消
+ J[签到或取消] --> K{参与者是否已支付}
+ K -->|是| L[标记为 is_refund=3 待退款]
+ K -->|否| M[无需退款]
+ end
+
+ subgraph 定时任务
+ N[定时扫描 is_refund=3] --> O[调用微信退款API]
+ O --> P{退款结果}
+ P -->|成功| Q[更新 is_refund=4 已退款]
+ P -->|失败| R[更新 is_refund=5 退款异常]
+ end
+```
+
+#### 3.5.2 押金状态说明
+
+| is_refund | 状态名 | 说明 |
+|-----------|--------|------|
+| 0 | 无押金 | 预约不需要鸽子费 |
+| 1 | 待支付 | 需要支付但未支付 |
+| 2 | 已支付 | 已完成支付 |
+| 3 | 待退款 | 已发起退款流程,等待处理 |
+| 4 | 已退款 | 退款完成 |
+| 5 | 退款异常 | 退款失败 |
+
+#### 3.5.3 押金分配规则
+
+```mermaid
+flowchart TD
+ A[签到确认] --> B{是否有人爽约}
+ B -->|无| C[所有人押金原路退还]
+ B -->|有| D[计算爽约人数和到场人数]
+ D --> E[爽约者押金不退还]
+ E --> F[爽约者押金平分给到场者]
+ F --> G[到场者获得额外收益]
+ G --> H[到场者原押金退还]
+```
+
+---
+
+### 3.6 消息通知模块
+
+#### 3.6.1 消息类型
+
+| 类型 | message_type | 说明 |
+|------|--------------|------|
+| 系统消息 | 0 | 预约相关自动通知 |
+| 私信 | 1 | 暂未实现 |
+
+#### 3.6.2 自动发送场景
+
+```mermaid
+flowchart LR
+ A[组局成功] --> B[通知所有参与者]
+ C[组局失败/解散] --> D[通知所有参与者]
+ E[预约即将开始] --> F[提醒参与者]
+ G[签到完成] --> H[通知到场/未到场情况]
+```
+
+#### 3.6.3 消息流程
+
+```mermaid
+sequenceDiagram
+ participant S as 系统
+ participant DB as 数据库
+ participant U as 用户
+
+ S->>DB: 插入消息记录 (SQMessage)
+ Note over DB: user_id=0 表示全体用户
+ U->>DB: 获取消息列表
+ DB->>U: 返回消息 + 已读状态
+ Note over DB: 已读状态存储在 SQMessageRead 表
+ U->>DB: 标记全部已读
+ Note over DB: 插入已读记录
+```
+
+---
+
+### 3.7 收益模块
+
+#### 3.7.1 收益来源
+
+1. **发起预约佣金**:房费的10%(需线下员工在后台添加)
+2. **爽约者押金分成**:爽约者的鸽子费平分给到场者
+
+#### 3.7.2 收益流程
+
+```mermaid
+flowchart TD
+ A[预约完成] --> B{发起者是否有佣金}
+ B -->|有| C[后台员工添加佣金记录]
+ C --> D[更新待提现金额]
+
+ E[签到确认有人爽约] --> F[计算爽约押金]
+ F --> G[平分给到场者]
+ G --> D
+
+ D --> H[用户申请提现]
+ H --> I{金额是否超出可提现}
+ I -->|是| J[提示:超出可提现金额]
+ I -->|否| K[创建提现申请记录]
+ K --> L[状态:提现中]
+ L --> M[后台审核]
+ M --> N{审核结果}
+ N -->|同意| O[线下打款]
+ O --> P[状态:已提现]
+ N -->|拒绝| Q[状态:已取消]
+```
+
+#### 3.7.3 提现状态
+
+| 状态 | 说明 |
+|------|------|
+| 提现中 | 申请已提交,等待审核 |
+| 已提现 | 线下打款完成 |
+| 已取消 | 后台拒绝/取消申请 |
+
+---
+
+### 3.8 黑名单模块
+
+#### 3.8.1 黑名单功能
+
+```mermaid
+flowchart TD
+ A[用户A将用户B加入黑名单] --> B[写入黑名单表]
+ B --> C[首页预约列表]
+ C --> D{用户B是否在黑名单}
+ D -->|是| E[过滤B发起的预约]
+ D -->|否| F[正常显示]
+```
+
+#### 3.8.2 黑名单影响
+
+1. 首页自动过滤黑名单用户发起的预约
+2. 黑名单用户无法加入我发起的预约(待实现)
+3. 评价页面显示黑名单状态
+
+---
+
+## 4. 核心业务流程图
+
+### 4.1 完整预约生命周期
+
+```mermaid
+flowchart TB
+ subgraph 发起阶段
+ A1[选择房间日期] --> A2[填写预约信息]
+ A2 --> A3[设置参与限制]
+ A3 --> A4[设置鸽子费]
+ A4 --> A5{有鸽子费?}
+ A5 -->|是| A6[微信支付]
+ A5 -->|否| A7[创建预约]
+ A6 --> A7
+ A7 --> A8[预约状态: 待开始]
+ end
+
+ subgraph 参与阶段
+ A8 --> B1[等待参与者加入]
+ B1 --> B2{人数已满?}
+ B2 -->|是| B3[预约状态: 已锁定]
+ B2 -->|否| B4{有人退出?}
+ B4 -->|是| B1
+ B4 -->|否| B5{到达开始时间?}
+ B3 --> B5
+ B5 -->|否| B1
+ end
+
+ subgraph 签到阶段
+ B5 -->|是| C1[发起者签到]
+ C1 --> C2[确认到场人员]
+ C2 --> C3[处理到场/未到场]
+ C3 --> C4[更新信誉分]
+ C4 --> C5[预约状态: 进行中]
+ end
+
+ subgraph 结束阶段
+ C5 --> D1[游戏进行中]
+ D1 --> D2{到达结束时间?}
+ D2 -->|否| D1
+ D2 -->|是| D3[预约状态: 已结束]
+ D3 --> D4[可以互相评价]
+ D4 --> D5[处理押金退款]
+ D5 --> D6[发放佣金]
+ end
+
+ subgraph 取消分支
+ A8 --> E1{发起者取消?}
+ B1 --> E1
+ B3 --> E1
+ E1 -->|是| E2[预约状态: 已取消]
+ E2 --> E3[所有人退出]
+ E3 --> E4[退还已支付押金]
+ end
+```
+
+### 4.2 时序图:完整预约流程
+
+```mermaid
+sequenceDiagram
+ participant U1 as 发起者
+ participant U2 as 参与者
+ participant FE as 前端
+ participant BE as 后端
+ participant DB as 数据库
+ participant WX as 微信支付
+
+ %% 发起预约
+ rect rgb(200, 230, 200)
+ Note over U1,WX: 发起预约阶段
+ U1->>FE: 选择房间和时段
+ FE->>BE: GetRoomListWithSlotsNew
+ BE->>DB: 查询房间状态
+ DB->>BE: 返回房间列表
+ BE->>FE: 房间时段状态
+ U1->>FE: 填写预约信息
+ FE->>BE: CanCreateSQReservation
+ BE->>DB: 校验时间冲突等
+ DB->>BE: 校验结果
+ BE->>FE: 可以创建
+ FE->>WX: 支付鸽子费
+ WX->>FE: 支付成功
+ FE->>BE: AddSQReservation
+ BE->>DB: 创建预约+参与者记录
+ DB->>BE: 返回预约ID
+ BE->>FE: 预约成功
+ end
+
+ %% 加入预约
+ rect rgb(200, 200, 230)
+ Note over U1,WX: 加入预约阶段
+ U2->>FE: 浏览预约列表
+ FE->>BE: GetReservationList
+ BE->>DB: 查询预约列表
+ DB->>BE: 预约数据
+ BE->>FE: 预约列表
+ U2->>FE: 点击加入
+ FE->>BE: JoinReservation
+ BE->>DB: 校验条件
+ DB->>BE: 校验通过
+ BE->>FE: 需要支付鸽子费
+ FE->>WX: 支付鸽子费
+ WX->>FE: 支付成功
+ FE->>BE: JoinReservation(带支付信息)
+ BE->>DB: 创建参与者记录
+ DB->>BE: 成功
+ BE->>FE: 加入成功
+ end
+
+ %% 签到
+ rect rgb(230, 200, 200)
+ Note over U1,WX: 签到阶段
+ U1->>FE: 点击签到
+ FE->>FE: 显示参与者列表
+ U1->>FE: 确认到场人员
+ FE->>BE: CheckInReservation
+ BE->>DB: 开启事务
+ BE->>DB: 更新预约状态
+ BE->>DB: 更新到场状态
+ BE->>DB: 更新信誉分
+ BE->>DB: 标记待退款
+ BE->>DB: 提交事务
+ DB->>BE: 成功
+ BE->>FE: 签到成功
+ end
+
+ %% 评价
+ rect rgb(230, 230, 200)
+ Note over U1,WX: 评价阶段
+ U1->>FE: 点击评价
+ FE->>BE: GetEvaluateServices
+ BE->>DB: 查询可评价人员
+ DB->>BE: 参与者列表
+ BE->>FE: 可评价列表
+ U1->>FE: 提交评价
+ FE->>BE: AddEvaluateServices
+ BE->>DB: 创建评价记录
+ BE->>DB: 更新被评价人分数
+ DB->>BE: 成功
+ BE->>FE: 评价成功
+ end
+```
+
+### 4.3 房间时段状态机
+
+```mermaid
+stateDiagram-v2
+ [*] --> 可预约: 默认状态
+ 可预约 --> 已预约: 有人预约该时段
+ 可预约 --> 不可预约: 后台设置不可用
+ 已预约 --> 可预约: 预约取消
+ 已预约 --> 进行中: 签到开始
+ 进行中 --> 可预约: 预约结束
+ 不可预约 --> 可预约: 后台取消不可用
+
+ note right of 可预约: 绿色
+ note right of 已预约: 橙色
+ note right of 不可预约: 灰色
+```
+
+---
+
+## 5. 数据模型
+
+### 5.1 核心数据表关系图
+
+```mermaid
+erDiagram
+ CoreCmsUser ||--o{ SQReservationParticipants : "参与"
+ CoreCmsUser ||--o{ SQReservationEvaluate : "评价"
+ CoreCmsUser ||--o{ SQReservationReputation : "信誉记录"
+ CoreCmsUser ||--o{ CoreCmsUserBlacklist : "黑名单"
+ CoreCmsUser ||--o{ SQMessage : "接收消息"
+ CoreCmsUser ||--o{ SQEarningsRecord : "收益"
+ CoreCmsUser ||--o{ SQWithdrawRecord : "提现"
+
+ SQReservations ||--o{ SQReservationParticipants : "包含"
+ SQReservations ||--o{ SQReservationEvaluate : "评价关联"
+ SQReservations }o--|| SQRooms : "使用房间"
+
+ SQRooms ||--o{ SQRoomUnavailableTimes : "不可用时间"
+ SQRooms ||--o{ SQRoomPricing : "价格配置"
+
+ CoreCmsUser {
+ int id PK
+ string nickName
+ string avatarImage
+ int sex
+ datetime birthday
+ decimal credit_score
+ decimal play_level
+ decimal skills_level
+ int dove_count
+ }
+
+ SQReservations {
+ int id PK
+ int room_id FK
+ string room_name
+ datetime start_time
+ datetime end_time
+ int duration_minutes
+ string title
+ string game_type
+ string game_rule
+ int player_count
+ int status
+ decimal deposit_fee
+ decimal credit_limit
+ int gender_limit
+ int min_age
+ int max_age
+ bool is_smoking
+ datetime latest_arrival_time
+ string extra_info
+ bool is_solo_mode
+ datetime created_at
+ datetime updated_at
+ }
+
+ SQReservationParticipants {
+ int id PK
+ int reservation_id FK
+ int user_id FK
+ int role
+ int status
+ datetime join_time
+ datetime quit_time
+ int is_arrive
+ datetime check_reservation
+ int is_refund
+ string paymentId
+ string important_data
+ }
+
+ SQRooms {
+ int id PK
+ string name
+ int capacity
+ decimal price_per_hour
+ string description
+ string image_url
+ bool status
+ datetime created_at
+ }
+
+ SQReservationEvaluate {
+ int id PK
+ int reservation_id FK
+ int user_id FK
+ int to_user_id FK
+ int role
+ decimal play_level
+ decimal skills_level
+ datetime created_at
+ }
+
+ SQReservationReputation {
+ int id PK
+ int user_id FK
+ int reservation_id FK
+ decimal reputation_value
+ string remark
+ datetime created_at
+ }
+```
+
+### 5.2 数据表详细说明
+
+#### 5.2.1 SQReservations(预约表)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | int | 主键 |
+| room_id | int | 房间ID(外键) |
+| room_name | string | 房间名称(冗余) |
+| start_time | datetime | 开始时间 |
+| end_time | datetime | 结束时间 |
+| duration_minutes | int | 时长(分钟) |
+| title | string | 组局名称 |
+| game_type | string | 游戏类型(血战/血流成河等) |
+| game_rule | string | 游戏规则 |
+| player_count | int | 需要人数(1-4) |
+| status | int | 状态(0待开始/1已锁定/2进行中/3已结束/4已取消) |
+| deposit_fee | decimal | 押金费用 |
+| credit_limit | decimal | 最低信誉要求 |
+| gender_limit | int | 性别限制(0不限/1男/2女) |
+| min_age | int | 最小年龄限制 |
+| max_age | int | 最大年龄限制 |
+| is_smoking | bool | 是否禁烟 |
+| latest_arrival_time | datetime | 最晚到店时间 |
+| extra_info | string | 其他补充说明 |
+| is_solo_mode | bool | 是否独享模式(人数=1时自动设为true) |
+| remarks | string | 备注(如取消原因) |
+| created_at | datetime | 创建时间 |
+| updated_at | datetime | 更新时间 |
+
+#### 5.2.2 SQReservationParticipants(参与者表)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | int | 主键 |
+| reservation_id | int | 预约ID(外键) |
+| user_id | int | 用户ID(外键) |
+| role | int | 角色(0参与者/1发起者) |
+| status | int | 状态(0正常/1已退出) |
+| join_time | datetime | 加入时间 |
+| quit_time | datetime | 退出时间 |
+| is_arrive | int | 是否到场(0未签到/1到场/2未到场) |
+| check_reservation | datetime | 签到时间 |
+| is_refund | int | 退款状态(0无/1待支付/2已支付/3待退款/4已退款/5异常) |
+| paymentId | string | 支付订单号 |
+| important_data | string | 重要数据(JSON格式,含支付信息等) |
+
+---
+
+## 6. API接口详解
+
+### 6.1 预约相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 首页预约列表 | GET | /api/sq/GetReservationList | 无 | 获取未结束的预约列表,自动过滤黑名单 |
+| 预约详情 | GET | /api/sq/GetReservationDetail | 无 | 根据ID获取预约详情 |
+| 我的预约 | GET | /api/sq/GetMyReservation | 需要 | type=0参与的/type=1发起的 |
+| 正在进行 | GET | /api/sq/GetMyUseReservation | 需要 | 获取未结束的预约 |
+| 验证创建 | POST | /api/sq/CanCreateSQReservation | 需要 | 创建前校验 |
+| 创建预约 | POST | /api/sq/AddSQReservation | 需要 | 发起新预约 |
+| 加入预约 | POST | /api/sq/JoinReservation | 需要 | 加入现有预约 |
+| 取消预约 | POST | /api/sq/CancelReservation | 需要 | 发起者/参与者取消 |
+| 签到 | POST | /api/sq/CheckInReservation | 需要 | 发起者签到确认 |
+
+### 6.2 房间相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 可选日期 | GET | /api/sq/GetAvailableDates | 无 | 今天+未来6天 |
+| 房间时段 | GET | /api/sq/GetRoomListWithSlotsNew | 无 | 房间列表及时段状态 |
+| 房间详情 | GET | /api/sq/GetRoomDetail | 无 | 房间信息及时段 |
+| 可预约房间 | GET | /api/sq/GetReservationRoomList | 无 | 指定时间可预约的房间 |
+| 营业时间 | GET | /api/sq/GetBusinessHours | 无 | 09:00-23:00 |
+
+### 6.3 评价相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 获取评价 | GET | /api/sq/GetEvaluateServices | 需要 | 获取可评价的参与者 |
+| 添加评价 | POST | /api/sq/AddEvaluateServices | 需要 | 评价参与者 |
+| 信誉记录 | GET | /api/sq/GetReputationByUser | 需要 | 我的信誉变化记录 |
+| 评价给我 | GET | /api/sq/GetEvaluateToMe | 需要 | 别人给我的评价 |
+
+### 6.4 消息相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 消息列表 | GET | /api/sq/GetMessageList | 需要 | 站内信列表 |
+| 未读数量 | GET | /api/sq/GetUnreadCount | 需要 | 未读消息数 |
+| 标记已读 | POST | /api/sq/MarkAllAsRead | 需要 | 全部标记已读 |
+
+### 6.5 收益相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 收益统计 | GET | /api/sq/GetEarningsSummary | 需要 | 待提现+已提现 |
+| 收益记录 | POST | /api/sq/GetEarningsRecordList | 需要 | 收益明细 |
+| 提现记录 | POST | /api/sq/GetWithdrawRecordList | 需要 | 提现历史 |
+| 申请提现 | POST | /api/sq/ApplyWithdraw | 需要 | 发起提现 |
+| 收益规则 | GET | /api/sq/GetEarningsRule | 无 | 规则说明 |
+
+---
+
+## 7. 业务规则汇总
+
+### 7.1 时间规则
+
+| 规则 | 值 | 说明 |
+|------|-----|------|
+| 可预约范围 | 今天+未来6天 | 共7天 |
+| 时段划分 | 凌晨(0-6h)/上午(6-12h)/下午(12-18h)/晚上(18-24h) | 4个时段 |
+| 营业时间 | 09:00-23:00 | 可在后台配置 |
+| 取消限制 | 开始前30分钟 | 30分钟内无法取消 |
+
+### 7.2 人数规则
+
+| 规则 | 值 | 说明 |
+|------|-----|------|
+| 最少人数 | 1人 | 独享模式 |
+| 最多人数 | 4人 | 麻将标准人数 |
+| 独享模式 | player_count=1 | 自动设置,不接受其他人加入 |
+| 人满锁定 | 达到player_count | 状态变为已锁定(1) |
+
+### 7.3 押金规则
+
+| 规则 | 值 | 说明 |
+|------|-----|------|
+| 金额范围 | 0-50元 | 可选0/5/10/20或自定义 |
+| 用途 | 防止爽约 | 到场退还,爽约扣除 |
+| 爽约分配 | 平分给到场者 | 由到场者瓜分 |
+| 退款时机 | 签到后/取消后 | 标记待退款,定时任务处理 |
+
+### 7.4 信誉规则
+
+| 情况 | 变化 | 说明 |
+|------|------|------|
+| 初始信誉 | 5.0分 | 满分 |
+| 守约奖励 | +0.2分 | 最高5.0 |
+| 爽约惩罚 | -0.5分 | 最低0分,鸽子次数+1 |
+| 信誉限制 | 发起者设置 | 低于限制无法加入 |
+
+### 7.5 评价规则
+
+| 规则 | 值 | 说明 |
+|------|-----|------|
+| 评价时机 | 预约完成后 | 状态为已结束 |
+| 评价对象 | 到场的其他参与者 | 排除自己和未到场 |
+| 评价维度 | 牌品/牌技 | 各1-5分 |
+| 评价次数 | 每人一次 | 不可重复评价 |
+| 初始分 | 4分 | 用于计算平均 |
+
+### 7.6 参与限制规则
+
+| 限制项 | 可选值 | 说明 |
+|--------|--------|------|
+| 性别限制 | 不限/男/女 | 0/1/2 |
+| 年龄限制 | 最小-最大 | 0表示不限 |
+| 信誉限制 | 0-5分 | 最低信誉要求 |
+| 禁烟 | 是/否 | 是否禁止吸烟 |
+
+---
+
+## 8. 状态机定义
+
+### 8.1 预约状态机
+
+```mermaid
+stateDiagram-v2
+ direction LR
+
+ [*] --> S0_待开始: 创建预约
+
+ S0_待开始 --> S1_已锁定: 人满
+ S0_待开始 --> S2_进行中: 签到
+ S0_待开始 --> S4_已取消: 取消
+
+ S1_已锁定 --> S0_待开始: 有人退出
+ S1_已锁定 --> S2_进行中: 签到
+ S1_已锁定 --> S4_已取消: 发起者取消
+
+ S2_进行中 --> S3_已结束: 时间结束
+
+ S3_已结束 --> [*]
+ S4_已取消 --> [*]
+
+ note right of S0_待开始: status=0
+ note right of S1_已锁定: status=1
+ note right of S2_进行中: status=2
+ note right of S3_已结束: status=3
+ note right of S4_已取消: status=4
+```
+
+### 8.2 参与者状态机
+
+```mermaid
+stateDiagram-v2
+ direction LR
+
+ [*] --> P0_正常: 加入预约
+
+ P0_正常 --> P1_已退出: 主动退出
+ P0_正常 --> P1_已退出: 被踢出(签到时未到场)
+ P0_正常 --> P1_已退出: 预约取消
+
+ P1_已退出 --> [*]
+
+ note right of P0_正常: status=0
+ note right of P1_已退出: status=1
+```
+
+### 8.3 到场状态机
+
+```mermaid
+stateDiagram-v2
+ direction LR
+
+ [*] --> A0_未签到: 加入预约
+
+ A0_未签到 --> A1_到场: 签到确认到场
+ A0_未签到 --> A2_未到场: 签到确认未到场
+
+ A1_到场 --> [*]: 完成
+ A2_未到场 --> [*]: 被踢出
+
+ note right of A0_未签到: is_arrive=0
+ note right of A1_到场: is_arrive=1
+ note right of A2_未到场: is_arrive=2
+```
+
+### 8.4 退款状态机
+
+```mermaid
+stateDiagram-v2
+ direction LR
+
+ [*] --> R0_无押金: 预约无鸽子费
+ [*] --> R1_待支付: 需要支付鸽子费
+
+ R1_待支付 --> R2_已支付: 支付成功
+ R1_待支付 --> R0_无押金: 预约取消(未支付)
+
+ R2_已支付 --> R3_待退款: 签到/取消触发
+
+ R3_待退款 --> R4_已退款: 定时任务退款成功
+ R3_待退款 --> R5_退款异常: 定时任务退款失败
+
+ R4_已退款 --> [*]
+ R5_退款异常 --> R3_待退款: 人工处理后重试
+ R5_退款异常 --> [*]: 人工处理
+ R0_无押金 --> [*]
+
+ note right of R0_无押金: is_refund=0
+ note right of R1_待支付: is_refund=1
+ note right of R2_已支付: is_refund=2
+ note right of R3_待退款: is_refund=3
+ note right of R4_已退款: is_refund=4
+ note right of R5_退款异常: is_refund=5
+```
+
+---
+
+## 附录A:前端核心文件路径
+
+| 功能 | 文件路径 |
+|------|----------|
+| 首页 | `uniapp/mahjong_group/pages/index/index.vue` |
+| 房间选择 | `uniapp/mahjong_group/pages/appointment/book-room-page.vue` |
+| 预约表单 | `uniapp/mahjong_group/pages/appointment/appointment-page.vue` |
+| 个人中心 | `uniapp/mahjong_group/pages/me/me-page.vue` |
+| 预约记录 | `uniapp/mahjong_group/pages/me/appointment-record-page.vue` |
+| 我的收益 | `uniapp/mahjong_group/pages/me/my-earnings-page.vue` |
+| 消息中心 | `uniapp/mahjong_group/pages/me/my-message-page.vue` |
+| 登录 | `uniapp/mahjong_group/pages/me/login.vue` |
+| 预约卡片组件 | `uniapp/mahjong_group/components/index/MahjongCard.vue` |
+| 预约弹窗组件 | `uniapp/mahjong_group/components/com/index/ReservationPopup.vue` |
+| 签到弹窗组件 | `uniapp/mahjong_group/components/com/page/qiandao-popup.vue` |
+| API接口 | `uniapp/mahjong_group/common/server/interface/sq.js` |
+
+## 附录B:后端核心文件路径
+
+| 功能 | 文件路径 |
+|------|----------|
+| 预约控制器 | `server/CoreCms.Net.Web.WebApi/Controllers/SQController.cs` |
+| 预约服务 | `server/CoreCms.Net.Services/SQ/SQReservationsServices.cs` |
+| 房间服务 | `server/CoreCms.Net.Services/SQ/SQRoomsServices.cs` |
+| 消息服务 | `server/CoreCms.Net.Services/SQ/SQMessageServices.cs` |
+| 收益服务 | `server/CoreCms.Net.Services/SQ/SQEarningsServices.cs` |
+
+---
+
+## 附录C:版本更新记录
+
+### v1.0.0 核心功能
+
+1. **会员档案功能**
+ - 微信授权登录,绑定手机号
+ - 会员行为标签:星级评分、黑名单
+ - 组局成功率、被投诉次数、消费频次、鸽子次数统计
+
+2. **麻友匹配机制**
+ - 发布组局需求
+ - 其他客户查看并加入
+ - 组局成功后自动发送消息
+ - 鸽子费(押金)机制
+
+### v1.0.1 新增功能
+
+1. **站内信功能**
+ - 消息入口,红点提示
+ - 自动发送通知(组局成功/失败)
+ - 手动发送通知(后台配置)
+
+2. **预约页优化**
+ - 房间时段展示(4个时段)
+ - 房间可用状态可视化
+ - 无需组局选项(独享模式)
+ - 鸽子费自定义金额(最高50元)
+
+3. **我的收益功能**
+ - 待提现/已提现金额展示
+ - 收益记录查询
+ - 提现申请(后台审核,线下打款)
+ - 佣金规则:房费10%
+
+---
+
+*文档生成时间:2024年*
+*版本:v1.0.1*
diff --git a/业务逻辑文档.md b/业务逻辑文档.md
new file mode 100644
index 0000000..e91ad60
--- /dev/null
+++ b/业务逻辑文档.md
@@ -0,0 +1,1782 @@
+# 麻将组局预约小程序 - 业务逻辑详解文档
+
+## 目录
+
+1. [项目概述](#1-项目概述)
+2. [系统架构](#2-系统架构)
+3. [**界面原型图与交互说明**](#3-界面原型图与交互说明) ⭐ 新增
+4. [核心业务模块](#4-核心业务模块)
+5. [核心业务流程图](#5-核心业务流程图)
+6. [数据模型](#6-数据模型)
+7. [API接口详解](#7-api接口详解)
+8. [业务规则汇总](#8-业务规则汇总)
+9. [状态机定义](#9-状态机定义)
+
+---
+
+## 1. 项目概述
+
+### 1.1 产品定位
+
+这是一个**麻将组局预约小程序**,帮助麻将爱好者在线上发起、加入麻将局,线下到店进行游戏。小程序提供了完整的预约流程管理,包括房间选择、预约发起、参与管理、签到评价等功能。
+
+### 1.2 技术栈
+
+| 层级 | 技术 | 说明 |
+|------|------|------|
+| 前端 | UniApp + Vue 3 | 支持微信小程序、H5、APP多端发布 |
+| 后端 | .NET Core 6.0+ | RESTful API |
+| ORM | SqlSugar | 数据库访问层 |
+| 数据库 | Microsoft SQL Server | 数据持久化 |
+| 支付 | 微信支付 | 鸽子费(押金)支付 |
+
+### 1.3 核心功能清单
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ 麻将组局预约系统 │
+├─────────────────────────────────────────────────────────┤
+│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
+│ │ 首页 │ │ 预约 │ │ 个人中心│ │ 消息 │ │
+│ │ 预约列表│ │ 房间选择│ │ 我的预约│ │ 站内信 │ │
+│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
+├─────────────────────────────────────────────────────────┤
+│ 核心业务: │
+│ • 发起预约 • 加入预约 • 取消预约 │
+│ • 预约签到 • 牌友评价 • 信誉管理 │
+│ • 鸽子费支付 • 退款处理 • 收益提现 │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 2. 系统架构
+
+### 2.1 前端架构
+
+```
+uniapp/mahjong_group/
+├── pages/ # 页面目录
+│ ├── index/
+│ │ └── index.vue # 首页 - 预约列表
+│ ├── appointment/
+│ │ ├── book-room-page.vue # 房间选择页(Tab页)
+│ │ └── appointment-page.vue # 预约表单页
+│ └── me/
+│ ├── me-page.vue # 个人中心
+│ ├── appointment-record-page.vue # 预约记录
+│ ├── my-earnings-page.vue # 我的收益
+│ └── my-message-page.vue # 消息中心
+├── components/ # 组件目录
+│ ├── index/
+│ │ └── MahjongCard.vue # 麻将预约卡片(九宫格)
+│ └── com/
+│ ├── index/
+│ │ └── ReservationPopup.vue # 预约详情弹窗
+│ └── page/
+│ ├── container-base.vue # 基础容器(含签到、评价弹窗)
+│ └── qiandao-popup.vue # 签到弹窗
+└── common/
+ ├── server/
+ │ └── interface/ # API接口定义
+ │ ├── sq.js # 预约相关接口
+ │ ├── user.js # 用户相关接口
+ │ ├── earnings.js # 收益相关接口
+ │ └── message.js # 消息相关接口
+ └── system/
+ ├── request.js # 请求封装(含签名)
+ └── cacheService.js # 缓存服务
+```
+
+### 2.2 后端架构
+
+```
+server/CoreCms.Net.Web.WebApi/
+└── Controllers/
+ └── SQController.cs # 预约核心控制器(全部预约相关API)
+ ├── GetReservationList # 首页预约列表
+ ├── AddSQReservation # 创建预约
+ ├── JoinReservation # 加入预约
+ ├── CancelReservation # 取消预约
+ ├── CheckInReservation # 签到
+ ├── GetEvaluateServices # 获取评价
+ ├── AddEvaluateServices # 添加评价
+ ├── GetMessageList # 消息列表
+ ├── GetEarningsSummary # 收益统计
+ └── ApplyWithdraw # 申请提现
+```
+
+### 2.3 系统架构图
+
+```mermaid
+graph TB
+ subgraph 客户端
+ A[微信小程序] --> B[UniApp]
+ C[H5网页] --> B
+ D[APP] --> B
+ end
+
+ subgraph 前端层
+ B --> E[Vue 3 组件]
+ E --> F[API接口层]
+ F --> G[请求封装/签名]
+ end
+
+ subgraph 后端层
+ G --> H[.NET Core WebAPI]
+ H --> I[SQController]
+ I --> J[Services服务层]
+ J --> K[SqlSugar ORM]
+ end
+
+ subgraph 数据层
+ K --> L[(SQL Server)]
+ end
+
+ subgraph 第三方服务
+ H --> M[微信支付]
+ H --> N[微信登录]
+ end
+```
+
+---
+
+## 3. 界面原型图与交互说明
+
+本章节展示小程序的主要界面原型图,帮助理解各页面的布局和交互逻辑。**⭐ 标记的区域为重点功能区域**。
+
+### 3.1 整体页面结构
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ 小程序整体结构 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
+│ │ 首页 │ │ 预约 │ │ 我的 │ │
+│ │ index.vue │ │book-room │ │ me-page │ │
+│ │ │ │ -page.vue │ │ .vue │ │
+│ │ 预约列表 │ │ 房间选择 │ │ 个人中心 │ │
+│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
+│ │ │ │ │
+│ v v v │
+│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
+│ │ReservationP │ │appointment │ │appointment │ │
+│ │ opup.vue │ │ -page.vue │ │-record.vue │ │
+│ │ 预约详情弹窗│ │ 预约表单 │ │ 预约记录 │ │
+│ └─────────────┘ └─────────────┘ └─────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────┘
+ │ │ │
+ └────────────────┼────────────────┘
+ v
+ ┌─────────────────────────────┐
+ │ TabBar │
+ │ [首页] [预约] [我的] │
+ └─────────────────────────────┘
+```
+
+---
+
+### 3.2 首页 - 预约列表页
+
+**文件**: `pages/index/index.vue`
+
+```
+┌─────────────────────────────────────────┐
+│ ▓▓▓▓▓▓▓▓▓▓ 状态栏 ▓▓▓▓▓▓▓▓▓▓ │
+├─────────────────────────────────────────┤
+│ ┌─────────────────────────────────────┐ │
+│ │ ╔═══════════════════════════════╗ │ │◄── ⭐ Banner轮播图
+│ │ ║ 🀄 麻将棋牌馆广告 ║ │ │ (可配置)
+│ │ ║ 点击查看详情 ║ │ │
+│ │ ╚═══════════════════════════════╝ │ │
+│ │ ○ ● ○ │ │◄── 轮播指示器
+│ └─────────────────────────────────────┘ │
+│ ┌─────────────────────────────────────┐ │
+│ │ 📢 公告:本店营业时间 9:00-23:00 │ │◄── 滚动公告栏
+│ └─────────────────────────────────────┘ │
+│ │
+│ ┌───────────────┐ ┌───────────────┐ │
+│ │ ┌───────────┐ │ │ ┌───────────┐ │ │
+│ │ │ 组局中... │ │ │ │ 待开始 │ │ │◄── 状态标签
+│ │ ├───────────┤ │ │ ├───────────┤ │ │
+│ │ │ 👤 │ │ │ │ 👤 👤 │ │ │
+│ │ │ 👤 👤 │ │ │ │ 🀄 │ │ │◄── ⭐ 九宫格麻将桌
+│ │ │ 🀄 │ │ │ │ 👤 👤 │ │ │ (显示已加入玩家)
+│ │ │ ➕ ➕ │ │ │ │ 👤 │ │ │
+│ │ │ ➕ │ │ │ ├───────────┤ │ │
+│ │ ├───────────┤ │ │ │休闲局 │ │ │
+│ │ │休闲娱乐局 │ │ │ │12/20 周五 │ │ │◄── 预约信息
+│ │ │12/18 周三 │ │ │ │20:00-24:00│ │ │
+│ │ │18:00-22:00│ │ │ │304包厢 │ │ │
+│ │ │302包厢 │ │ │ │血战 禁烟 │ │ │
+│ │ │血流成河 │ │ │ │信誉≧4.0 │ │ │
+│ │ └───────────┘ │ └───────────────┘ │
+│ └───────────────┘ │
+│ │
+│ ┌───────────────┐ ┌───────────────┐ │◄── 更多预约卡片...
+│ │ ... │ │ ... │ │
+│ └───────────────┘ └───────────────┘ │
+│ │
+│ ┌───┐ │
+│ │ 🔘│ │◄── 悬浮球
+│ └───┘ │
+├─────────────────────────────────────────┤
+│ [🏠首页] [📅预约] [👤我的] │◄── TabBar
+└─────────────────────────────────────────┘
+```
+
+#### 重点说明:
+
+| 区域 | 重点级别 | 说明 |
+|------|---------|------|
+| ⭐ 九宫格麻将桌 | 高 | **核心展示区域**,直观显示当前已加入人数,空位显示➕,已加入显示头像 |
+| ⭐ Banner轮播 | 中 | 后台可配置,用于活动推广 |
+| 状态标签 | 高 | 组局中/待开始/进行中/已结束,一眼看出预约状态 |
+| 预约信息 | 中 | 关键信息:时间、房间、玩法、限制条件 |
+
+---
+
+### 3.3 麻将卡片组件详解
+
+**文件**: `components/index/MahjongCard.vue`
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ 九宫格布局说明 (4人局) │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ 位置1 (上) │
+│ 👤 ← 第3个加入的玩家 │
+│ │
+│ 位置2 🀄 位置3 │
+│ 👤 (麻将桌logo) 👤 │
+│ ↑ ↑ │
+│ 第1个加入 第2个加入 │
+│ (通常是发起者) 的玩家 │
+│ │
+│ 位置4 (下) │
+│ 👤 ← 第4个加入的玩家 │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ 不同人数局的布局: │
+│ │
+│ 2人局: 3人局: 4人局(标准): │
+│ 👤 👤 │
+│ 👤 👤 👤 👤 👤 🀄 👤 │
+│ 👤 │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+### 3.4 预约详情弹窗
+
+**文件**: `components/com/index/ReservationPopup.vue`
+
+点击首页的预约卡片后弹出:
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ 预约信息 │
+├─────────────────────────────────────────────────────────────┤
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 发起者 👤 张三 │ │◄── ⭐ 发起者信息
+│ │ │ │
+│ │ 参与者 👤李四 👤王五 [黑名单]👤赵六 │ │◄── ⭐ 参与者列表
+│ │ ↑ │ │ (可显示黑名单标记)
+│ │ 红色标记 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 开始时间 2025-12-20 18:00 │ │
+│ │ 结束时间 2025-12-20 22:00 │ │◄── 时间信息
+│ │ 合计:4小时 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 房间号: 304包厢-大包 │ │
+│ │ 人数: 4人 │ │◄── ⭐ 预约详情
+│ │ 玩法类型: 血战到底 │ │
+│ │ 具体规则: 2番起胡,3倍封顶 │ │
+│ │ 补充信息: 新手勿扰 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 是否禁烟: 禁烟 │ │
+│ │ 性别: 男 │ │◄── ⭐ 参与限制条件
+│ │ 年龄范围: 25岁 ~ 45岁 │ │ (重点检查项)
+│ │ 信誉: ≧4.0 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 鸽子费: 10元 │ │◄── ⭐ 押金信息
+│ │ 组局成功后若有牌友未赴约,其鸽子费平均分给其他牌友。 │ │
+│ │ 组局成功或失败后鸽子费将全额返还。 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌────────┐ ┌─────────────────────────────┐ │
+│ │ 关闭 │ │ 参与组局 │ │◄── ⭐ 操作按钮
+│ └────────┘ └─────────────────────────────┘ │ (根据身份变化)
+│ │
+│ 按钮状态说明: │
+│ • 未加入用户 → [关闭] [参与组局] │
+│ • 参与者 → [关闭] [退出组局] │
+│ • 发起者 → [关闭] [取消组局] [分享] │
+│ • 已结束 → [关闭] │
+└─────────────────────────────────────────────────────────────┘
+```
+
+---
+
+### 3.5 发起预约 - 房间选择页
+
+**文件**: `pages/appointment/book-room-page.vue`
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ ▓▓▓▓▓▓▓▓▓▓▓▓ 状态栏 ▓▓▓▓▓▓▓▓▓▓▓▓▓ │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ 发起预约 │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ ┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐┌────┐│ │
+│ │ │12/18 ││12/19 ││12/20 ││12/21 ││12/22 ││12/23 ││... ││ │◄── ⭐ 日期选择
+│ │ │ 周三 ││ 周四 ││ 周五 ││ 周六 ││ 周日 ││ 周一 ││ ││ │ (今天+6天)
+│ │ │[选中]││ ││ ││ ││ ││ ││ ││ │
+│ │ └──────┘└──────┘└──────┘└──────┘└──────┘└──────┘└────┘│ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ 营业时间:早09点 至 晚23点 │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 查看当前时段空闲时间 [ 🔘 开关 ] │ │◄── 过滤开关
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ ┌─────────────────────────────────────────────────────┐ │ │
+│ │ │ ┌──────┐ │ │ │
+│ │ │ │ 🖼️ │ 302包厢 [当前使用中] │ │ │◄── 房间状态
+│ │ │ │ 图片 │ 小包 2-4人 │ │ │
+│ │ │ └──────┘ 标准价:38元/小时 │ │ │
+│ │ │ 会员价:30元/小时 [预约] │ │ │◄── ⭐ 预约按钮
+│ │ │ │ │ │
+│ │ │ ━━ 已预定 ━━ 可预定 ━━ 不可预定 │ │ │◄── 图例说明
+│ │ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │ │
+│ │ │ │ 🟠 │ │ 🟢 │ │ 🟢 │ │ 🔴 │ │ │ │◄── ⭐ 时段状态
+│ │ │ │凌晨│ │上午│ │下午│ │晚上│ │ │ │ (核心展示)
+│ │ │ └────┘ └────┘ └────┘ └────┘ │ │ │
+│ │ └─────────────────────────────────────────────────────┘ │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 304包厢 - 大包 4-8人 │ │
+│ │ ... │ │◄── 更多房间
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+├─────────────────────────────────────────────────────────────┤
+│ [🏠首页] [📅预约] [👤我的] │
+└─────────────────────────────────────────────────────────────┘
+```
+
+#### 时段状态颜色说明:
+
+| 颜色 | 状态 | 说明 |
+|------|------|------|
+| 🟢 绿色 | available | **可预约** - 该时段空闲 |
+| 🟠 橙色 | reserved | **已预约** - 已被他人预约 |
+| 🔴 灰色 | unavailable | **不可预约** - 后台设置不可用 |
+| 🔵 蓝色 | using | **使用中** - 当前正在使用 |
+
+---
+
+### 3.6 发起预约 - 预约表单页
+
+**文件**: `pages/appointment/appointment-page.vue`
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ ◀ 返回 发起预约 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 时间设置 │ │
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ 日期 12/20 周五 [选择]│ │◄── 日期选择
+│ │ │ │
+│ │ 时间段 下午 (12:00-17:59) [选择]│ │◄── ⭐ 时段选择
+│ │ │ │ (仅显示可预约)
+│ │ 最晚到店 2025-12-20 12:30 [选择]│ │◄── 最晚到店时间
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 基本信息 │ │
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ 组局名称 [ 休闲娱乐,老少皆宜 ] │ │◄── ⭐ 必填
+│ │ │ │
+│ │ 人数 [ 4人 ▼ ] │ │◄── ⭐ 人数选择
+│ │ 说明:选择1人为"无需组局"(独享模式) │ │
+│ │ │ │
+│ │ 玩法类型 [ 血战到底 ▼ ] │ │◄── ⭐ 必填
+│ │ │ │
+│ │ 具体规则 [ 2番起胡,3倍封顶 ▼ ] │ │◄── ⭐ 必填
+│ │ │ │
+│ │ 其他补充 [ 新手勿扰,打牌快 ] │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 参与限制 │ │
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ 是否禁烟 ○ 不禁烟 ● 禁烟 │ │
+│ │ │ │
+│ │ 性别 ● 不限 ○ 男 ○ 女 │ │◄── ⭐ 筛选条件
+│ │ │ │
+│ │ 年龄范围 [ 25岁 ] - [ 45岁 ] [选择] │ │
+│ │ │ │
+│ │ 信誉 大于等于 [ ➖ ] 4.0 [ ➕ ] │ │◄── ⭐ 信誉门槛
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 鸽子费(押金) │ │
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ │ │
+│ │ ○ 0元 ○ 5元 ● 10元 ○ 自定义 │ │◄── ⭐ 核心功能
+│ │ │ │
+│ │ 自定义金额 [ 20 ] 元 (0-50元) │ │
+│ │ │ │
+│ │ 说明:鸽子费(保证金),参与人需缴纳鸽子费。若有参与 │ │
+│ │ 者在预约后没有赴约,其鸽子费由在场的所有人平分。组 │ │
+│ │ 局成功或失败后鸽子费将全额返还。 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────┐ ┌─────────────────────────────────────────┐ │
+│ │ 重置 │ │ 发起预约 │ │◄── ⭐ 提交按钮
+│ └─────────┘ └─────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+#### 表单必填项说明:
+
+| 字段 | 必填 | 说明 |
+|------|------|------|
+| 日期 | ✅ | 从房间页带入 |
+| 时间段 | ✅ | 仅显示该房间该日期可预约的时段 |
+| 组局名称 | ✅ | 用于首页展示 |
+| 人数 | ✅ | 1人=独享模式,2-4人=组局模式 |
+| 玩法类型 | ✅ | 血战/血流成河等 |
+| 具体规则 | ✅ | 依赖玩法类型 |
+| 鸽子费 | ❌ | 默认0元,最高50元 |
+
+---
+
+### 3.7 个人中心页
+
+**文件**: `pages/me/me-page.vue`
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ ▓▓▓▓▓▓▓▓▓▓▓▓ 状态栏 ▓▓▓▓▓▓▓▓▓▓▓▓▓ │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 👤 张三 │ │◄── ⭐ 用户头像和昵称
+│ │ UID: 10086 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ ┌────────────────────────────────────────────────────┐ │ │
+│ │ │ 🖼️ 当前没有预约 │ │ │◄── 当前预约状态
+│ │ └────────────────────────────────────────────────────┘ │ │
+│ │ 或者 │ │
+│ │ ┌────────────────────────────────────────────────────┐ │ │
+│ │ │ 待开始 | 休闲局 │ │ │◄── ⭐ 当前预约卡片
+│ │ │ 12/20 18:00-22:00 | 304包厢 │ │ │ (如果有预约)
+│ │ │ 血战到底 | 4人 │ │ │
+│ │ │ [牌友评价] [签到] │ │ │◄── 快捷操作按钮
+│ │ └────────────────────────────────────────────────────┘ │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 我的信誉 4.8 │ │◄── ⭐ 信誉分
+│ │ │ │ (点击查看详情)
+│ │ 牌品 ⭐⭐⭐⭐⭐ 4.5 │ │◄── ⭐ 牌品评分
+│ │ │ │
+│ │ 牌技 ⭐⭐⭐⭐☆ 4.2 │ │◄── ⭐ 牌技评分
+│ │ │ │
+│ │ 鸽子数 2 次 │ │◄── 爽约次数
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 常用功能 │ │
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ │ │
+│ │ 📋 预约记录 📑 订单记录 ❓ 常见问题 │ │
+│ │ │ │
+│ │ 🚫 黑名单 📞 联系我们 📩 我的消息 │ │◄── 功能入口
+│ │ │ │
+│ │ 💰 我的收益 │ │◄── ⭐ 收益入口
+│ │ │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+├─────────────────────────────────────────────────────────────┤
+│ [🏠首页] [📅预约] [👤我的] │
+└─────────────────────────────────────────────────────────────┘
+```
+
+---
+
+### 3.8 签到弹窗
+
+**文件**: `components/com/page/qiandao-popup.vue`
+
+签到是预约流程的关键步骤,**仅发起者可操作**:
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ 预约签到 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ 请确认实际到场的参与者: │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ ☑️ 👤 张三 (发起者) ← 默认勾选 │ │
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ ☑️ 👤 李四 │ │◄── ⭐ 勾选到场人员
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ ☐ 👤 王五 ← 未勾选=爽约 │ │◄── ⭐ 未勾选=爽约
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ ☑️ 👤 赵六 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ⚠️ 注意: │
+│ • 未勾选的参与者将被标记为"爽约" │
+│ • 爽约者信誉分 -0.5,鸽子次数 +1 │
+│ • 爽约者的押金将平分给到场者 │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 确认签到 │ │◄── ⭐ 提交签到
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+---
+
+### 3.9 评价弹窗
+
+**文件**: `components/com/page/reservation-evaluate.vue`
+
+预约完成后,参与者可以互相评价:
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ 牌友评价 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ 请为到场的牌友评分: │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 👤 李四 │ │
+│ │ │ │
+│ │ 牌品 ⭐⭐⭐⭐⭐ │ │◄── ⭐ 牌品评分
+│ │ │ │ (1-5星)
+│ │ 牌技 ⭐⭐⭐⭐☆ │ │◄── ⭐ 牌技评分
+│ │ │ │ (1-5星)
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 👤 赵六 [已评价] ✅ │ │◄── 已评价标记
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────┐ ┌─────────────────────────────────────────┐ │
+│ │ 关闭 │ │ 提交评价 │ │
+│ └─────────────┘ └─────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+---
+
+### 3.10 我的收益页
+
+**文件**: `pages/me/my-earnings-page.vue`
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ ◀ 返回 我的收益 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ │ │
+│ │ 待提现收益 已提现收益 │ │
+│ │ ¥128.50 ¥350.00 │ │◄── ⭐ 收益统计
+│ │ │ │
+│ │ [ 申请提现 ] │ │◄── ⭐ 提现按钮
+│ │ │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌───────────────────────┬───────────────────────┐ │
+│ │ 收益记录 │ 提现记录 │ │◄── Tab切换
+│ └───────────────────────┴───────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 2025-12-18 14:30 │ │
+│ │ 组局佣金 - 304包厢 +¥12.50 │ │◄── 收益明细
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ 2025-12-17 20:00 │ │
+│ │ 爽约补偿 - 302包厢 +¥5.00 │ │◄── 爽约补偿
+│ ├─────────────────────────────────────────────────────────┤ │
+│ │ 2025-12-15 18:00 │ │
+│ │ 组局佣金 - 306包厢 +¥18.00 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+---
+
+### 3.11 页面跳转关系图
+
+```mermaid
+graph TD
+ subgraph TabBar页面
+ A[首页
index.vue]
+ B[预约
book-room-page.vue]
+ C[我的
me-page.vue]
+ end
+
+ subgraph 弹窗组件
+ D[预约详情弹窗
ReservationPopup]
+ E[签到弹窗
qiandao-popup]
+ F[评价弹窗
reservation-evaluate]
+ end
+
+ subgraph 子页面
+ G[预约表单
appointment-page]
+ H[预约记录
appointment-record]
+ I[我的收益
my-earnings-page]
+ J[我的消息
my-message-page]
+ K[黑名单
blacklist-page]
+ L[登录
login]
+ end
+
+ A -->|点击卡片| D
+ D -->|参与组局| D
+ D -->|发起者取消| D
+
+ B -->|选择房间| G
+ G -->|提交成功| A
+
+ C -->|预约记录| H
+ C -->|我的收益| I
+ C -->|我的消息| J
+ C -->|黑名单| K
+ C -->|未登录| L
+
+ H -->|点击预约| D
+ H -->|牌友评价| F
+ H -->|签到| E
+
+ C -->|当前预约卡片| D
+ C -->|牌友评价| F
+ C -->|签到| E
+
+ style A fill:#e1f5fe
+ style B fill:#e1f5fe
+ style C fill:#e1f5fe
+ style D fill:#fff3e0
+ style E fill:#fff3e0
+ style F fill:#fff3e0
+ style G fill:#e8f5e9
+```
+
+---
+
+### 3.12 界面交互流程总结
+
+```mermaid
+flowchart LR
+ subgraph 浏览流程
+ A1[首页浏览预约] --> A2[点击卡片]
+ A2 --> A3[查看详情弹窗]
+ A3 --> A4{是否加入?}
+ A4 -->|是| A5[参与组局]
+ A4 -->|否| A6[关闭弹窗]
+ end
+
+ subgraph 发起流程
+ B1[点击预约Tab] --> B2[选择日期]
+ B2 --> B3[选择房间]
+ B3 --> B4[点击预约按钮]
+ B4 --> B5[填写预约表单]
+ B5 --> B6[支付鸽子费]
+ B6 --> B7[发起成功]
+ end
+
+ subgraph 参与后流程
+ C1[预约开始] --> C2[发起者签到]
+ C2 --> C3[确认到场人员]
+ C3 --> C4[预约进行中]
+ C4 --> C5[预约结束]
+ C5 --> C6[互相评价]
+ C6 --> C7[押金退还]
+ end
+
+ style A5 fill:#4caf50,color:#fff
+ style B7 fill:#4caf50,color:#fff
+ style C7 fill:#4caf50,color:#fff
+```
+
+---
+
+## 4. 核心业务模块
+
+### 4.1 用户认证模块
+
+#### 4.1.1 登录流程
+
+用户通过微信小程序登录,获取手机号授权后绑定账号。
+
+```mermaid
+sequenceDiagram
+ participant U as 用户
+ participant MP as 小程序
+ participant WX as 微信服务器
+ participant BE as 后端服务
+ participant DB as 数据库
+
+ U->>MP: 点击登录
+ MP->>U: 请求授权手机号
+ U->>MP: 同意授权
+ MP->>WX: 获取code和encryptedData
+ WX->>MP: 返回加密数据
+ MP->>BE: POST /user/UseWxPhoneNumberLogin
+ Note over MP,BE: 传递code和sessionAuthId
+ BE->>WX: 解密手机号
+ WX->>BE: 返回手机号
+ BE->>DB: 查询/创建用户
+ DB->>BE: 返回用户信息
+ BE->>MP: 返回token和用户信息
+ MP->>MP: 存储token到本地
+ MP->>U: 登录成功
+```
+
+#### 3.1.2 用户信息结构
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | int | 用户ID |
+| nickName | string | 昵称 |
+| avatarImage | string | 头像URL |
+| sex | int | 性别:1男 2女 |
+| birthday | DateTime? | 生日(用于计算年龄) |
+| credit_score | decimal | 信誉分(0-5分,默认5分) |
+| play_level | decimal | 牌品评分(1-5分,默认4分) |
+| skills_level | decimal | 牌技评分(1-5分,默认4分) |
+| dove_count | int | 鸽子次数 |
+
+---
+
+### 4.2 预约管理模块
+
+#### 4.2.1 预约生命周期
+
+```mermaid
+stateDiagram-v2
+ [*] --> 待开始: 创建预约
+ 待开始 --> 已锁定: 人满
+ 待开始 --> 已取消: 发起者取消
+ 待开始 --> 待开始: 参与者加入/退出
+ 已锁定 --> 待开始: 参与者退出
+ 已锁定 --> 已取消: 发起者取消
+ 已锁定 --> 进行中: 签到
+ 待开始 --> 进行中: 签到(人未满也可签到)
+ 进行中 --> 已结束: 时间结束
+ 已取消 --> [*]
+ 已结束 --> [*]
+```
+
+#### 4.2.2 预约状态说明
+
+| status | 状态名 | 说明 |
+|--------|--------|------|
+| 0 | 待开始 | 预约已创建,等待参与者加入或等待开始时间 |
+| 1 | 已锁定 | 参与人数已满,等待开始 |
+| 2 | 进行中 | 发起者已签到,游戏进行中 |
+| 3 | 已结束 | 预约时间已过,预约完成 |
+| 4 | 已取消 | 预约被取消 |
+
+#### 4.2.3 发起预约流程
+
+```mermaid
+flowchart TD
+ A[用户点击发起预约] --> B[选择日期]
+ B --> C[查看房间列表及时段状态]
+ C --> D{选择房间和时段}
+ D --> E[填写预约信息]
+
+ subgraph 预约信息
+ E --> E1[组局名称]
+ E --> E2[人数 1-4人]
+ E --> E3[玩法类型]
+ E --> E4[游戏规则]
+ E --> E5[参与限制]
+ E --> E6[鸽子费设置]
+ end
+
+ E1 & E2 & E3 & E4 & E5 & E6 --> F[提交前验证]
+ F --> G{调用CanCreateSQReservation}
+ G -->|验证失败| H[显示错误信息]
+ H --> E
+ G -->|验证通过| I{是否有鸽子费}
+ I -->|有| J[发起微信支付]
+ J --> K{支付结果}
+ K -->|成功| L[调用AddSQReservation创建预约]
+ K -->|失败| M[提示支付失败]
+ I -->|无| L
+ L --> N[创建预约记录]
+ N --> O[创建发起者参与记录 role=1]
+ O --> P[返回预约ID]
+ P --> Q[跳转到预约详情]
+```
+
+#### 4.2.4 加入预约流程
+
+```mermaid
+flowchart TD
+ A[用户点击加入预约] --> B{预约是否存在且未结束}
+ B -->|否| C[提示:预约不存在或已结束]
+ B -->|是| D{是否已加入该预约}
+ D -->|是| E[提示:您已加入该预约]
+ D -->|否| F{是否为独享模式}
+ F -->|是| G[提示:该预约不接受其他人加入]
+ F -->|否| H[校验参与条件]
+
+ subgraph 条件校验
+ H --> H1{信誉分是否达标}
+ H1 -->|否| I1[提示:信誉分不足]
+ H1 -->|是| H2{性别是否符合}
+ H2 -->|否| I2[提示:性别不符合要求]
+ H2 -->|是| H3{年龄是否在范围内}
+ H3 -->|否| I3[提示:年龄不符合限制]
+ H3 -->|是| H4{是否有时间冲突}
+ H4 -->|是| I4[提示:有其他预约时间冲突]
+ H4 -->|否| H5{预约是否已满}
+ H5 -->|是| I5[提示:预约已满]
+ end
+
+ H5 -->|否| J{是否有鸽子费}
+ J -->|有| K[发起微信支付]
+ K --> L{支付结果}
+ L -->|成功| M[创建参与者记录 role=0]
+ L -->|失败| N[提示支付失败]
+ J -->|无| M
+ M --> O[加入成功]
+```
+
+#### 4.2.5 取消预约流程
+
+```mermaid
+flowchart TD
+ A[用户点击取消预约] --> B{预约是否存在}
+ B -->|否| C[提示:预约不存在]
+ B -->|是| D{距开始是否<30分钟}
+ D -->|是| E[提示:开始前30分钟无法取消]
+ D -->|否| F{预约状态是否>=3}
+ F -->|是| G[提示:已结束或已取消]
+ F -->|否| H{用户角色}
+
+ H -->|发起者 role=1| I[发起者取消整个预约]
+ I --> J[预约状态改为已取消 status=4]
+ J --> K[所有参与者标记为已退出]
+ K --> L{是否有已支付押金}
+ L -->|有| M[已支付者标记为待退款 is_refund=3]
+ M --> N[发送通知给所有参与者]
+ L -->|无| N
+
+ H -->|参与者 role=0| O{预约是否已开始}
+ O -->|是| P[提示:只有发起者可取消]
+ O -->|否| Q[参与者退出]
+ Q --> R[标记该用户为已退出 status=1]
+ R --> S{该用户是否已支付押金}
+ S -->|是| T[标记为待退款 is_refund=3]
+ T --> U[退出成功]
+ S -->|否| U
+
+ N --> V[取消完成]
+```
+
+---
+
+### 4.3 签到模块
+
+#### 4.3.1 签到流程
+
+签到是预约流程的关键环节,只有发起者可以操作,用于确认实际到场人员。
+
+```mermaid
+flowchart TD
+ A[发起者点击签到] --> B{预约是否存在}
+ B -->|否| C[提示:预约不存在]
+ B -->|是| D{预约状态是否>=3}
+ D -->|是| E[提示:已结束或已取消]
+ D -->|否| F{是否已签到 status=2}
+ F -->|是| G[提示:已签到,无法重复]
+ F -->|否| H{是否为发起者}
+ H -->|否| I[提示:仅发起者可签到]
+ H -->|是| J[显示参与者列表]
+
+ J --> K[勾选实际到场人员]
+ K --> L[提交签到]
+
+ L --> M[开启数据库事务]
+ M --> N[预约状态改为进行中 status=2]
+ N --> O[所有未退出参与者默认标记为到场 is_arrive=1]
+ O --> P{是否有未到场人员}
+
+ P -->|有| Q[未到场人员处理]
+ subgraph 爽约惩罚
+ Q --> Q1[标记为未到场 is_arrive=2]
+ Q1 --> Q2[标记为已退出 status=1]
+ Q2 --> Q3[扣除信誉分 -0.5]
+ Q3 --> Q4[增加鸽子次数 dove_count++]
+ Q4 --> Q5[记录信誉变更日志]
+ end
+
+ P -->|无| R[到场人员处理]
+ Q5 --> R
+
+ subgraph 守约奖励
+ R --> R1[增加信誉分 +0.2 最高5.0]
+ R1 --> R2[记录信誉变更日志]
+ end
+
+ R2 --> S{是否有押金}
+ S -->|有| T[到场且已支付者标记为待退款 is_refund=3]
+ T --> U[提交事务]
+ S -->|无| U
+ U --> V[签到完成]
+```
+
+#### 4.3.2 签到后信誉变化
+
+| 情况 | 信誉变化 | 其他影响 |
+|------|----------|----------|
+| 到场(守约) | +0.2(最高5.0) | - |
+| 未到场(爽约) | -0.5 | 鸽子次数+1 |
+
+---
+
+### 4.4 评价模块
+
+#### 4.4.1 评价流程
+
+```mermaid
+flowchart TD
+ A[用户进入预约详情] --> B{预约是否已完成且已签到}
+ B -->|否| C[不显示评价入口]
+ B -->|是| D[显示评价入口]
+ D --> E[点击牌友评价]
+ E --> F[获取可评价参与者列表]
+ F --> G[只显示实际到场的其他参与者]
+
+ G --> H[选择要评价的人]
+ H --> I[填写评价]
+
+ subgraph 评价维度
+ I --> I1[牌品评分 1-5星]
+ I --> I2[牌技评分 1-5星]
+ end
+
+ I1 & I2 --> J[提交评价]
+ J --> K[创建评价记录]
+ K --> L[重新计算被评价人的平均分]
+
+ subgraph 平均分计算
+ L --> L1[获取所有历史评价]
+ L1 --> L2[计算 牌品 = sum评价 + 4 / 评价次数 + 1]
+ L2 --> L3[计算 牌技 = sum评价 + 4 / 评价次数 + 1]
+ L3 --> L4[如果只有1次评价,分母+1避免偏差]
+ end
+
+ L4 --> M[更新用户评分]
+ M --> N[评价完成]
+```
+
+#### 4.4.2 评价规则
+
+1. **评价时机**:预约状态为"已结束"且已签到后
+2. **评价对象**:只能评价实际到场的其他参与者(排除自己)
+3. **评价次数**:每个参与者只能被评价一次
+4. **评价维度**:
+ - 牌品(play_level):1-5分
+ - 牌技(skills_level):1-5分
+
+#### 4.4.3 评分计算公式
+
+```
+新评分 = (历史评分总和 + 初始分4) / (评价次数 + 1)
+
+特殊情况:当只有1次评价时,分母为(评价次数 + 2)避免偏差
+```
+
+---
+
+### 4.5 押金(鸽子费)模块
+
+#### 4.5.1 押金流程总览
+
+```mermaid
+flowchart TD
+ subgraph 发起预约
+ A[发起者设置鸽子费] --> B[发起者支付鸽子费]
+ B --> C[创建预约成功]
+ C --> D[参与者记录 is_refund=1 待支付]
+ end
+
+ subgraph 加入预约
+ E[参与者加入] --> F{预约是否有鸽子费}
+ F -->|有| G[参与者支付鸽子费]
+ G --> H[参与者记录 is_refund=2 已支付]
+ F -->|无| I[参与者记录 is_refund=0]
+ end
+
+ subgraph 签到/取消
+ J[签到或取消] --> K{参与者是否已支付}
+ K -->|是| L[标记为 is_refund=3 待退款]
+ K -->|否| M[无需退款]
+ end
+
+ subgraph 定时任务
+ N[定时扫描 is_refund=3] --> O[调用微信退款API]
+ O --> P{退款结果}
+ P -->|成功| Q[更新 is_refund=4 已退款]
+ P -->|失败| R[更新 is_refund=5 退款异常]
+ end
+```
+
+#### 4.5.2 押金状态说明
+
+| is_refund | 状态名 | 说明 |
+|-----------|--------|------|
+| 0 | 无押金 | 预约不需要鸽子费 |
+| 1 | 待支付 | 需要支付但未支付 |
+| 2 | 已支付 | 已完成支付 |
+| 3 | 待退款 | 已发起退款流程,等待处理 |
+| 4 | 已退款 | 退款完成 |
+| 5 | 退款异常 | 退款失败 |
+
+#### 4.5.3 押金分配规则
+
+```mermaid
+flowchart TD
+ A[签到确认] --> B{是否有人爽约}
+ B -->|无| C[所有人押金原路退还]
+ B -->|有| D[计算爽约人数和到场人数]
+ D --> E[爽约者押金不退还]
+ E --> F[爽约者押金平分给到场者]
+ F --> G[到场者获得额外收益]
+ G --> H[到场者原押金退还]
+```
+
+---
+
+### 4.6 消息通知模块
+
+#### 4.6.1 消息类型
+
+| 类型 | message_type | 说明 |
+|------|--------------|------|
+| 系统消息 | 0 | 预约相关自动通知 |
+| 私信 | 1 | 暂未实现 |
+
+#### 4.6.2 自动发送场景
+
+```mermaid
+flowchart LR
+ A[组局成功] --> B[通知所有参与者]
+ C[组局失败/解散] --> D[通知所有参与者]
+ E[预约即将开始] --> F[提醒参与者]
+ G[签到完成] --> H[通知到场/未到场情况]
+```
+
+#### 4.6.3 消息流程
+
+```mermaid
+sequenceDiagram
+ participant S as 系统
+ participant DB as 数据库
+ participant U as 用户
+
+ S->>DB: 插入消息记录 (SQMessage)
+ Note over DB: user_id=0 表示全体用户
+ U->>DB: 获取消息列表
+ DB->>U: 返回消息 + 已读状态
+ Note over DB: 已读状态存储在 SQMessageRead 表
+ U->>DB: 标记全部已读
+ Note over DB: 插入已读记录
+```
+
+---
+
+### 4.7 收益模块
+
+#### 4.7.1 收益来源
+
+1. **发起预约佣金**:房费的10%(需线下员工在后台添加)
+2. **爽约者押金分成**:爽约者的鸽子费平分给到场者
+
+#### 4.7.2 收益流程
+
+```mermaid
+flowchart TD
+ A[预约完成] --> B{发起者是否有佣金}
+ B -->|有| C[后台员工添加佣金记录]
+ C --> D[更新待提现金额]
+
+ E[签到确认有人爽约] --> F[计算爽约押金]
+ F --> G[平分给到场者]
+ G --> D
+
+ D --> H[用户申请提现]
+ H --> I{金额是否超出可提现}
+ I -->|是| J[提示:超出可提现金额]
+ I -->|否| K[创建提现申请记录]
+ K --> L[状态:提现中]
+ L --> M[后台审核]
+ M --> N{审核结果}
+ N -->|同意| O[线下打款]
+ O --> P[状态:已提现]
+ N -->|拒绝| Q[状态:已取消]
+```
+
+#### 4.7.3 提现状态
+
+| 状态 | 说明 |
+|------|------|
+| 提现中 | 申请已提交,等待审核 |
+| 已提现 | 线下打款完成 |
+| 已取消 | 后台拒绝/取消申请 |
+
+---
+
+### 4.8 黑名单模块
+
+#### 4.8.1 黑名单功能
+
+```mermaid
+flowchart TD
+ A[用户A将用户B加入黑名单] --> B[写入黑名单表]
+ B --> C[首页预约列表]
+ C --> D{用户B是否在黑名单}
+ D -->|是| E[过滤B发起的预约]
+ D -->|否| F[正常显示]
+```
+
+#### 4.8.2 黑名单影响
+
+1. 首页自动过滤黑名单用户发起的预约
+2. 黑名单用户无法加入我发起的预约(待实现)
+3. 评价页面显示黑名单状态
+
+---
+
+## 5. 核心业务流程图
+
+### 5.1 完整预约生命周期
+
+```mermaid
+flowchart TB
+ subgraph 发起阶段
+ A1[选择房间日期] --> A2[填写预约信息]
+ A2 --> A3[设置参与限制]
+ A3 --> A4[设置鸽子费]
+ A4 --> A5{有鸽子费?}
+ A5 -->|是| A6[微信支付]
+ A5 -->|否| A7[创建预约]
+ A6 --> A7
+ A7 --> A8[预约状态: 待开始]
+ end
+
+ subgraph 参与阶段
+ A8 --> B1[等待参与者加入]
+ B1 --> B2{人数已满?}
+ B2 -->|是| B3[预约状态: 已锁定]
+ B2 -->|否| B4{有人退出?}
+ B4 -->|是| B1
+ B4 -->|否| B5{到达开始时间?}
+ B3 --> B5
+ B5 -->|否| B1
+ end
+
+ subgraph 签到阶段
+ B5 -->|是| C1[发起者签到]
+ C1 --> C2[确认到场人员]
+ C2 --> C3[处理到场/未到场]
+ C3 --> C4[更新信誉分]
+ C4 --> C5[预约状态: 进行中]
+ end
+
+ subgraph 结束阶段
+ C5 --> D1[游戏进行中]
+ D1 --> D2{到达结束时间?}
+ D2 -->|否| D1
+ D2 -->|是| D3[预约状态: 已结束]
+ D3 --> D4[可以互相评价]
+ D4 --> D5[处理押金退款]
+ D5 --> D6[发放佣金]
+ end
+
+ subgraph 取消分支
+ A8 --> E1{发起者取消?}
+ B1 --> E1
+ B3 --> E1
+ E1 -->|是| E2[预约状态: 已取消]
+ E2 --> E3[所有人退出]
+ E3 --> E4[退还已支付押金]
+ end
+```
+
+### 5.2 时序图:完整预约流程
+
+```mermaid
+sequenceDiagram
+ participant U1 as 发起者
+ participant U2 as 参与者
+ participant FE as 前端
+ participant BE as 后端
+ participant DB as 数据库
+ participant WX as 微信支付
+
+ %% 发起预约
+ rect rgb(200, 230, 200)
+ Note over U1,WX: 发起预约阶段
+ U1->>FE: 选择房间和时段
+ FE->>BE: GetRoomListWithSlotsNew
+ BE->>DB: 查询房间状态
+ DB->>BE: 返回房间列表
+ BE->>FE: 房间时段状态
+ U1->>FE: 填写预约信息
+ FE->>BE: CanCreateSQReservation
+ BE->>DB: 校验时间冲突等
+ DB->>BE: 校验结果
+ BE->>FE: 可以创建
+ FE->>WX: 支付鸽子费
+ WX->>FE: 支付成功
+ FE->>BE: AddSQReservation
+ BE->>DB: 创建预约+参与者记录
+ DB->>BE: 返回预约ID
+ BE->>FE: 预约成功
+ end
+
+ %% 加入预约
+ rect rgb(200, 200, 230)
+ Note over U1,WX: 加入预约阶段
+ U2->>FE: 浏览预约列表
+ FE->>BE: GetReservationList
+ BE->>DB: 查询预约列表
+ DB->>BE: 预约数据
+ BE->>FE: 预约列表
+ U2->>FE: 点击加入
+ FE->>BE: JoinReservation
+ BE->>DB: 校验条件
+ DB->>BE: 校验通过
+ BE->>FE: 需要支付鸽子费
+ FE->>WX: 支付鸽子费
+ WX->>FE: 支付成功
+ FE->>BE: JoinReservation(带支付信息)
+ BE->>DB: 创建参与者记录
+ DB->>BE: 成功
+ BE->>FE: 加入成功
+ end
+
+ %% 签到
+ rect rgb(230, 200, 200)
+ Note over U1,WX: 签到阶段
+ U1->>FE: 点击签到
+ FE->>FE: 显示参与者列表
+ U1->>FE: 确认到场人员
+ FE->>BE: CheckInReservation
+ BE->>DB: 开启事务
+ BE->>DB: 更新预约状态
+ BE->>DB: 更新到场状态
+ BE->>DB: 更新信誉分
+ BE->>DB: 标记待退款
+ BE->>DB: 提交事务
+ DB->>BE: 成功
+ BE->>FE: 签到成功
+ end
+
+ %% 评价
+ rect rgb(230, 230, 200)
+ Note over U1,WX: 评价阶段
+ U1->>FE: 点击评价
+ FE->>BE: GetEvaluateServices
+ BE->>DB: 查询可评价人员
+ DB->>BE: 参与者列表
+ BE->>FE: 可评价列表
+ U1->>FE: 提交评价
+ FE->>BE: AddEvaluateServices
+ BE->>DB: 创建评价记录
+ BE->>DB: 更新被评价人分数
+ DB->>BE: 成功
+ BE->>FE: 评价成功
+ end
+```
+
+### 5.3 房间时段状态机
+
+```mermaid
+stateDiagram-v2
+ [*] --> 可预约: 默认状态
+ 可预约 --> 已预约: 有人预约该时段
+ 可预约 --> 不可预约: 后台设置不可用
+ 已预约 --> 可预约: 预约取消
+ 已预约 --> 进行中: 签到开始
+ 进行中 --> 可预约: 预约结束
+ 不可预约 --> 可预约: 后台取消不可用
+
+ note right of 可预约: 绿色
+ note right of 已预约: 橙色
+ note right of 不可预约: 灰色
+```
+
+---
+
+## 6. 数据模型
+
+### 9.1 核心数据表关系图
+
+```mermaid
+erDiagram
+ CoreCmsUser ||--o{ SQReservationParticipants : "参与"
+ CoreCmsUser ||--o{ SQReservationEvaluate : "评价"
+ CoreCmsUser ||--o{ SQReservationReputation : "信誉记录"
+ CoreCmsUser ||--o{ CoreCmsUserBlacklist : "黑名单"
+ CoreCmsUser ||--o{ SQMessage : "接收消息"
+ CoreCmsUser ||--o{ SQEarningsRecord : "收益"
+ CoreCmsUser ||--o{ SQWithdrawRecord : "提现"
+
+ SQReservations ||--o{ SQReservationParticipants : "包含"
+ SQReservations ||--o{ SQReservationEvaluate : "评价关联"
+ SQReservations }o--|| SQRooms : "使用房间"
+
+ SQRooms ||--o{ SQRoomUnavailableTimes : "不可用时间"
+ SQRooms ||--o{ SQRoomPricing : "价格配置"
+
+ CoreCmsUser {
+ int id PK
+ string nickName
+ string avatarImage
+ int sex
+ datetime birthday
+ decimal credit_score
+ decimal play_level
+ decimal skills_level
+ int dove_count
+ }
+
+ SQReservations {
+ int id PK
+ int room_id FK
+ string room_name
+ datetime start_time
+ datetime end_time
+ int duration_minutes
+ string title
+ string game_type
+ string game_rule
+ int player_count
+ int status
+ decimal deposit_fee
+ decimal credit_limit
+ int gender_limit
+ int min_age
+ int max_age
+ bool is_smoking
+ datetime latest_arrival_time
+ string extra_info
+ bool is_solo_mode
+ datetime created_at
+ datetime updated_at
+ }
+
+ SQReservationParticipants {
+ int id PK
+ int reservation_id FK
+ int user_id FK
+ int role
+ int status
+ datetime join_time
+ datetime quit_time
+ int is_arrive
+ datetime check_reservation
+ int is_refund
+ string paymentId
+ string important_data
+ }
+
+ SQRooms {
+ int id PK
+ string name
+ int capacity
+ decimal price_per_hour
+ string description
+ string image_url
+ bool status
+ datetime created_at
+ }
+
+ SQReservationEvaluate {
+ int id PK
+ int reservation_id FK
+ int user_id FK
+ int to_user_id FK
+ int role
+ decimal play_level
+ decimal skills_level
+ datetime created_at
+ }
+
+ SQReservationReputation {
+ int id PK
+ int user_id FK
+ int reservation_id FK
+ decimal reputation_value
+ string remark
+ datetime created_at
+ }
+```
+
+### 9.2 数据表详细说明
+
+#### 5.2.1 SQReservations(预约表)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | int | 主键 |
+| room_id | int | 房间ID(外键) |
+| room_name | string | 房间名称(冗余) |
+| start_time | datetime | 开始时间 |
+| end_time | datetime | 结束时间 |
+| duration_minutes | int | 时长(分钟) |
+| title | string | 组局名称 |
+| game_type | string | 游戏类型(血战/血流成河等) |
+| game_rule | string | 游戏规则 |
+| player_count | int | 需要人数(1-4) |
+| status | int | 状态(0待开始/1已锁定/2进行中/3已结束/4已取消) |
+| deposit_fee | decimal | 押金费用 |
+| credit_limit | decimal | 最低信誉要求 |
+| gender_limit | int | 性别限制(0不限/1男/2女) |
+| min_age | int | 最小年龄限制 |
+| max_age | int | 最大年龄限制 |
+| is_smoking | bool | 是否禁烟 |
+| latest_arrival_time | datetime | 最晚到店时间 |
+| extra_info | string | 其他补充说明 |
+| is_solo_mode | bool | 是否独享模式(人数=1时自动设为true) |
+| remarks | string | 备注(如取消原因) |
+| created_at | datetime | 创建时间 |
+| updated_at | datetime | 更新时间 |
+
+#### 5.2.2 SQReservationParticipants(参与者表)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | int | 主键 |
+| reservation_id | int | 预约ID(外键) |
+| user_id | int | 用户ID(外键) |
+| role | int | 角色(0参与者/1发起者) |
+| status | int | 状态(0正常/1已退出) |
+| join_time | datetime | 加入时间 |
+| quit_time | datetime | 退出时间 |
+| is_arrive | int | 是否到场(0未签到/1到场/2未到场) |
+| check_reservation | datetime | 签到时间 |
+| is_refund | int | 退款状态(0无/1待支付/2已支付/3待退款/4已退款/5异常) |
+| paymentId | string | 支付订单号 |
+| important_data | string | 重要数据(JSON格式,含支付信息等) |
+
+---
+
+## 7. API接口详解
+
+### 9.1 预约相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 首页预约列表 | GET | /api/sq/GetReservationList | 无 | 获取未结束的预约列表,自动过滤黑名单 |
+| 预约详情 | GET | /api/sq/GetReservationDetail | 无 | 根据ID获取预约详情 |
+| 我的预约 | GET | /api/sq/GetMyReservation | 需要 | type=0参与的/type=1发起的 |
+| 正在进行 | GET | /api/sq/GetMyUseReservation | 需要 | 获取未结束的预约 |
+| 验证创建 | POST | /api/sq/CanCreateSQReservation | 需要 | 创建前校验 |
+| 创建预约 | POST | /api/sq/AddSQReservation | 需要 | 发起新预约 |
+| 加入预约 | POST | /api/sq/JoinReservation | 需要 | 加入现有预约 |
+| 取消预约 | POST | /api/sq/CancelReservation | 需要 | 发起者/参与者取消 |
+| 签到 | POST | /api/sq/CheckInReservation | 需要 | 发起者签到确认 |
+
+### 9.2 房间相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 可选日期 | GET | /api/sq/GetAvailableDates | 无 | 今天+未来6天 |
+| 房间时段 | GET | /api/sq/GetRoomListWithSlotsNew | 无 | 房间列表及时段状态 |
+| 房间详情 | GET | /api/sq/GetRoomDetail | 无 | 房间信息及时段 |
+| 可预约房间 | GET | /api/sq/GetReservationRoomList | 无 | 指定时间可预约的房间 |
+| 营业时间 | GET | /api/sq/GetBusinessHours | 无 | 09:00-23:00 |
+
+### 9.3 评价相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 获取评价 | GET | /api/sq/GetEvaluateServices | 需要 | 获取可评价的参与者 |
+| 添加评价 | POST | /api/sq/AddEvaluateServices | 需要 | 评价参与者 |
+| 信誉记录 | GET | /api/sq/GetReputationByUser | 需要 | 我的信誉变化记录 |
+| 评价给我 | GET | /api/sq/GetEvaluateToMe | 需要 | 别人给我的评价 |
+
+### 9.4 消息相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 消息列表 | GET | /api/sq/GetMessageList | 需要 | 站内信列表 |
+| 未读数量 | GET | /api/sq/GetUnreadCount | 需要 | 未读消息数 |
+| 标记已读 | POST | /api/sq/MarkAllAsRead | 需要 | 全部标记已读 |
+
+### 9.5 收益相关接口
+
+| 接口 | 方法 | 路径 | 权限 | 说明 |
+|------|------|------|------|------|
+| 收益统计 | GET | /api/sq/GetEarningsSummary | 需要 | 待提现+已提现 |
+| 收益记录 | POST | /api/sq/GetEarningsRecordList | 需要 | 收益明细 |
+| 提现记录 | POST | /api/sq/GetWithdrawRecordList | 需要 | 提现历史 |
+| 申请提现 | POST | /api/sq/ApplyWithdraw | 需要 | 发起提现 |
+| 收益规则 | GET | /api/sq/GetEarningsRule | 无 | 规则说明 |
+
+---
+
+## 8. 业务规则汇总
+
+### 9.1 时间规则
+
+| 规则 | 值 | 说明 |
+|------|-----|------|
+| 可预约范围 | 今天+未来6天 | 共7天 |
+| 时段划分 | 凌晨(0-6h)/上午(6-12h)/下午(12-18h)/晚上(18-24h) | 4个时段 |
+| 营业时间 | 09:00-23:00 | 可在后台配置 |
+| 取消限制 | 开始前30分钟 | 30分钟内无法取消 |
+
+### 9.2 人数规则
+
+| 规则 | 值 | 说明 |
+|------|-----|------|
+| 最少人数 | 1人 | 独享模式 |
+| 最多人数 | 4人 | 麻将标准人数 |
+| 独享模式 | player_count=1 | 自动设置,不接受其他人加入 |
+| 人满锁定 | 达到player_count | 状态变为已锁定(1) |
+
+### 9.3 押金规则
+
+| 规则 | 值 | 说明 |
+|------|-----|------|
+| 金额范围 | 0-50元 | 可选0/5/10/20或自定义 |
+| 用途 | 防止爽约 | 到场退还,爽约扣除 |
+| 爽约分配 | 平分给到场者 | 由到场者瓜分 |
+| 退款时机 | 签到后/取消后 | 标记待退款,定时任务处理 |
+
+### 9.4 信誉规则
+
+| 情况 | 变化 | 说明 |
+|------|------|------|
+| 初始信誉 | 5.0分 | 满分 |
+| 守约奖励 | +0.2分 | 最高5.0 |
+| 爽约惩罚 | -0.5分 | 最低0分,鸽子次数+1 |
+| 信誉限制 | 发起者设置 | 低于限制无法加入 |
+
+### 9.5 评价规则
+
+| 规则 | 值 | 说明 |
+|------|-----|------|
+| 评价时机 | 预约完成后 | 状态为已结束 |
+| 评价对象 | 到场的其他参与者 | 排除自己和未到场 |
+| 评价维度 | 牌品/牌技 | 各1-5分 |
+| 评价次数 | 每人一次 | 不可重复评价 |
+| 初始分 | 4分 | 用于计算平均 |
+
+### 9.6 参与限制规则
+
+| 限制项 | 可选值 | 说明 |
+|--------|--------|------|
+| 性别限制 | 不限/男/女 | 0/1/2 |
+| 年龄限制 | 最小-最大 | 0表示不限 |
+| 信誉限制 | 0-5分 | 最低信誉要求 |
+| 禁烟 | 是/否 | 是否禁止吸烟 |
+
+---
+
+## 9. 状态机定义
+
+### 9.1 预约状态机
+
+```mermaid
+stateDiagram-v2
+ direction LR
+
+ [*] --> S0_待开始: 创建预约
+
+ S0_待开始 --> S1_已锁定: 人满
+ S0_待开始 --> S2_进行中: 签到
+ S0_待开始 --> S4_已取消: 取消
+
+ S1_已锁定 --> S0_待开始: 有人退出
+ S1_已锁定 --> S2_进行中: 签到
+ S1_已锁定 --> S4_已取消: 发起者取消
+
+ S2_进行中 --> S3_已结束: 时间结束
+
+ S3_已结束 --> [*]
+ S4_已取消 --> [*]
+
+ note right of S0_待开始: status=0
+ note right of S1_已锁定: status=1
+ note right of S2_进行中: status=2
+ note right of S3_已结束: status=3
+ note right of S4_已取消: status=4
+```
+
+### 9.2 参与者状态机
+
+```mermaid
+stateDiagram-v2
+ direction LR
+
+ [*] --> P0_正常: 加入预约
+
+ P0_正常 --> P1_已退出: 主动退出
+ P0_正常 --> P1_已退出: 被踢出(签到时未到场)
+ P0_正常 --> P1_已退出: 预约取消
+
+ P1_已退出 --> [*]
+
+ note right of P0_正常: status=0
+ note right of P1_已退出: status=1
+```
+
+### 9.3 到场状态机
+
+```mermaid
+stateDiagram-v2
+ direction LR
+
+ [*] --> A0_未签到: 加入预约
+
+ A0_未签到 --> A1_到场: 签到确认到场
+ A0_未签到 --> A2_未到场: 签到确认未到场
+
+ A1_到场 --> [*]: 完成
+ A2_未到场 --> [*]: 被踢出
+
+ note right of A0_未签到: is_arrive=0
+ note right of A1_到场: is_arrive=1
+ note right of A2_未到场: is_arrive=2
+```
+
+### 9.4 退款状态机
+
+```mermaid
+stateDiagram-v2
+ direction LR
+
+ [*] --> R0_无押金: 预约无鸽子费
+ [*] --> R1_待支付: 需要支付鸽子费
+
+ R1_待支付 --> R2_已支付: 支付成功
+ R1_待支付 --> R0_无押金: 预约取消(未支付)
+
+ R2_已支付 --> R3_待退款: 签到/取消触发
+
+ R3_待退款 --> R4_已退款: 定时任务退款成功
+ R3_待退款 --> R5_退款异常: 定时任务退款失败
+
+ R4_已退款 --> [*]
+ R5_退款异常 --> R3_待退款: 人工处理后重试
+ R5_退款异常 --> [*]: 人工处理
+ R0_无押金 --> [*]
+
+ note right of R0_无押金: is_refund=0
+ note right of R1_待支付: is_refund=1
+ note right of R2_已支付: is_refund=2
+ note right of R3_待退款: is_refund=3
+ note right of R4_已退款: is_refund=4
+ note right of R5_退款异常: is_refund=5
+```
+
+---
+
+## 附录A:前端核心文件路径
+
+| 功能 | 文件路径 |
+|------|----------|
+| 首页 | `uniapp/mahjong_group/pages/index/index.vue` |
+| 房间选择 | `uniapp/mahjong_group/pages/appointment/book-room-page.vue` |
+| 预约表单 | `uniapp/mahjong_group/pages/appointment/appointment-page.vue` |
+| 个人中心 | `uniapp/mahjong_group/pages/me/me-page.vue` |
+| 预约记录 | `uniapp/mahjong_group/pages/me/appointment-record-page.vue` |
+| 我的收益 | `uniapp/mahjong_group/pages/me/my-earnings-page.vue` |
+| 消息中心 | `uniapp/mahjong_group/pages/me/my-message-page.vue` |
+| 登录 | `uniapp/mahjong_group/pages/me/login.vue` |
+| 预约卡片组件 | `uniapp/mahjong_group/components/index/MahjongCard.vue` |
+| 预约弹窗组件 | `uniapp/mahjong_group/components/com/index/ReservationPopup.vue` |
+| 签到弹窗组件 | `uniapp/mahjong_group/components/com/page/qiandao-popup.vue` |
+| API接口 | `uniapp/mahjong_group/common/server/interface/sq.js` |
+
+## 附录B:后端核心文件路径
+
+| 功能 | 文件路径 |
+|------|----------|
+| 预约控制器 | `server/CoreCms.Net.Web.WebApi/Controllers/SQController.cs` |
+| 预约服务 | `server/CoreCms.Net.Services/SQ/SQReservationsServices.cs` |
+| 房间服务 | `server/CoreCms.Net.Services/SQ/SQRoomsServices.cs` |
+| 消息服务 | `server/CoreCms.Net.Services/SQ/SQMessageServices.cs` |
+| 收益服务 | `server/CoreCms.Net.Services/SQ/SQEarningsServices.cs` |
+
+---
+
+## 附录C:版本更新记录
+
+### v1.0.0 核心功能
+
+1. **会员档案功能**
+ - 微信授权登录,绑定手机号
+ - 会员行为标签:星级评分、黑名单
+ - 组局成功率、被投诉次数、消费频次、鸽子次数统计
+
+2. **麻友匹配机制**
+ - 发布组局需求
+ - 其他客户查看并加入
+ - 组局成功后自动发送消息
+ - 鸽子费(押金)机制
+
+### v1.0.1 新增功能
+
+1. **站内信功能**
+ - 消息入口,红点提示
+ - 自动发送通知(组局成功/失败)
+ - 手动发送通知(后台配置)
+
+2. **预约页优化**
+ - 房间时段展示(4个时段)
+ - 房间可用状态可视化
+ - 无需组局选项(独享模式)
+ - 鸽子费自定义金额(最高50元)
+
+3. **我的收益功能**
+ - 待提现/已提现金额展示
+ - 收益记录查询
+ - 提现申请(后台审核,线下打款)
+ - 佣金规则:房费10%
+
+---
+
+*文档生成时间:2024年*
+*版本:v1.0.1*
diff --git a/预约时间自由选择_改动文档 copy.md b/预约时间自由选择_改动文档 copy.md
new file mode 100644
index 0000000..1426469
--- /dev/null
+++ b/预约时间自由选择_改动文档 copy.md
@@ -0,0 +1,556 @@
+# 预约时间自由选择 - 改动文档
+
+## 1. 需求概述
+
+### 1.1 当前问题
+- 用户只能选择固定时段(凌晨0-6、上午6-12、下午12-18、晚上18-24)
+- 无法灵活选择具体的开始和结束时间
+- 跨时段预约显示不直观
+
+### 1.2 改动目标
+1. **时段显示优化**:如果预约时间为15:00-20:00,房间列表页应显示该房间"下午"和"晚上"都被预定
+2. **自由时间选择**:创建预约界面允许用户自由选择开始时间和结束时间
+
+---
+
+## 2. 改动前分析
+
+### 2.1 当前时段定义
+```
+时段类型 | 时间范围 | 对应值
+--------|----------|-------
+凌晨 | 00:00-06:00 | 0
+上午 | 06:00-12:00 | 1
+下午 | 12:00-18:00 | 2
+晚上 | 18:00-24:00 | 3
+```
+
+### 2.2 当前预约创建流程
+
+```mermaid
+flowchart TD
+ A[用户选择日期] --> B[选择房间]
+ B --> C[显示可用时段]
+ C --> D{选择固定时段}
+ D -->|凌晨| E1[设置 00:00-06:00]
+ D -->|上午| E2[设置 06:00-12:00]
+ D -->|下午| E3[设置 12:00-18:00]
+ D -->|晚上| E4[设置 18:00-24:00]
+ E1 --> F[提交预约]
+ E2 --> F
+ E3 --> F
+ E4 --> F
+ F --> G[后端创建预约]
+```
+
+### 2.3 当前前端时间计算逻辑
+
+**文件**: `pages/appointment/appointment-page.vue`
+
+```javascript
+// 当前代码:根据时段类型计算固定时间
+const calculateTimeFromSlot = () => {
+ const date = new Date(selectedDate.value);
+ let startHour, endHour;
+
+ switch (selectedTimeSlot.value) {
+ case 0: // 凌晨
+ startHour = 0;
+ endHour = 6;
+ break;
+ case 1: // 上午
+ startHour = 6;
+ endHour = 12;
+ break;
+ case 2: // 下午
+ startHour = 12;
+ endHour = 18;
+ break;
+ case 3: // 晚上
+ startHour = 18;
+ endHour = 24;
+ break;
+ }
+ // ... 设置开始和结束时间
+};
+```
+
+### 2.4 当前房间时段状态显示
+
+**文件**: `pages/appointment/book-room-page.vue`
+
+```
+┌─────────────────────────────────────────┐
+│ 房间名称 │
+├─────────────────────────────────────────┤
+│ [凌晨] [上午] [下午] [晚上] │
+│ 🟢 🟢 🟠 🟢 │
+│ │
+│ 🟢 可预约 🟠 已预约 ⚫ 不可用 🔵 使用中 │
+└─────────────────────────────────────────┘
+
+当前问题:预约15:00-20:00时,仅显示一个时段被占用
+```
+
+### 2.5 当前后端时段判断逻辑
+
+**文件**: `CoreCms.Net.Services/SQ/SQRoomsServices.cs`
+
+```csharp
+// 后端已有重叠判断逻辑(可复用)
+bool isReserved = reservations?.Any(r =>
+ r.start_time < slotEnd && r.end_time > slotStart) ?? false;
+```
+
+**分析**:后端逻辑已支持跨时段检测,问题在于前端只能选择单一时段。
+
+---
+
+## 3. 改动后方案
+
+### 3.1 新的预约创建流程
+
+```mermaid
+flowchart TD
+ A[用户选择日期] --> B[选择房间]
+ B --> C[显示房间详情]
+ C --> D[选择开始时间]
+ D --> E[选择结束时间]
+ E --> F{时间校验}
+ F -->|结束时间 > 开始时间| G[计算跨越时段]
+ F -->|无效| H[提示错误]
+ G --> I[检查时段冲突]
+ I -->|无冲突| J[提交预约]
+ I -->|有冲突| K[提示时段已被预约]
+ J --> L[后端创建预约]
+ L --> M[更新房间时段显示]
+```
+
+### 3.2 新的时间选择UI设计
+
+```
+改动前(固定时段选择):
+┌─────────────────────────────────────────┐
+│ 选择时段 │
+├─────────────────────────────────────────┤
+│ ○ 凌晨 (00:00-06:00) │
+│ ○ 上午 (06:00-12:00) │
+│ ● 下午 (12:00-18:00) ← 只能单选 │
+│ ○ 晚上 (18:00-24:00) │
+└─────────────────────────────────────────┘
+
+改动后(自由时间选择):
+┌─────────────────────────────────────────┐
+│ 选择时间 │
+├─────────────────────────────────────────┤
+│ 开始时间: [15:00 ▼] ← 时间选择器 │
+│ 结束时间: [20:00 ▼] ← 时间选择器 │
+│ │
+│ 预计时长: 5小时 │
+│ 跨越时段: 下午、晚上 │
+│ │
+│ 💡 提示:结束时间必须晚于开始时间 │
+└─────────────────────────────────────────┘
+```
+
+### 3.3 新的房间列表时段显示
+
+```
+改动前(15:00-20:00预约后):
+┌─────────────────────────────────────────┐
+│ [凌晨] [上午] [下午] [晚上] │
+│ 🟢 🟢 🟠 🟢 ← 只显示下午 │
+└─────────────────────────────────────────┘
+
+改动后(15:00-20:00预约后):
+┌─────────────────────────────────────────┐
+│ [凌晨] [上午] [下午] [晚上] │
+│ 🟢 🟢 🟠 🟠 ← 下午+晚上 │
+└─────────────────────────────────────────┘
+```
+
+### 3.4 时段重叠判断逻辑
+
+```mermaid
+flowchart LR
+ subgraph 预约时间 15:00-20:00
+ P1[15:00] --> P2[20:00]
+ end
+
+ subgraph 时段判断
+ S1[凌晨 0-6] --> R1{重叠?}
+ S2[上午 6-12] --> R2{重叠?}
+ S3[下午 12-18] --> R3{重叠?}
+ S4[晚上 18-24] --> R4{重叠?}
+ end
+
+ R1 -->|否| N1[🟢]
+ R2 -->|否| N2[🟢]
+ R3 -->|是| Y3[🟠]
+ R4 -->|是| Y4[🟠]
+```
+
+**重叠判断公式**:
+```
+预约与时段重叠 = (预约开始时间 < 时段结束时间) AND (预约结束时间 > 时段开始时间)
+
+示例:预约15:00-20:00
+- 凌晨(0-6): 15 < 6 = false → 不重叠 🟢
+- 上午(6-12): 15 < 12 = false → 不重叠 🟢
+- 下午(12-18): 15 < 18 = true AND 20 > 12 = true → 重叠 🟠
+- 晚上(18-24): 15 < 24 = true AND 20 > 18 = true → 重叠 🟠
+```
+
+---
+
+## 4. 需要改动的文件清单
+
+### 4.1 前端改动
+
+| 序号 | 文件路径 | 改动内容 | 复杂度 |
+|------|----------|----------|--------|
+| 1 | `pages/appointment/appointment-page.vue` | 将固定时段选择改为时间选择器 | ⭐⭐⭐ |
+| 2 | `pages/appointment/book-room-page.vue` | 时段状态显示逻辑优化(可能无需改动,后端已支持) | ⭐ |
+| 3 | `components/com/page/reservation-item.vue` | 预约详情显示时间格式调整 | ⭐ |
+
+### 4.2 后端改动
+
+| 序号 | 文件路径 | 改动内容 | 复杂度 |
+|------|----------|----------|--------|
+| 1 | `SQController.cs` | 接收自定义开始/结束时间参数 | ⭐⭐ |
+| 2 | `SQRoomsServices.cs` | 时段状态判断逻辑已支持,可能需微调 | ⭐ |
+| 3 | `SQReservationsServices.cs` | 预约冲突检测逻辑验证 | ⭐ |
+
+---
+
+## 5. 详细改动说明
+
+### 5.1 appointment-page.vue 改动
+
+#### 改动前代码
+```vue
+
+
+
+
+ {{ slot.label }}
+
+
+
+
+
+```
+
+#### 改动后代码
+```vue
+
+
+
+
+ 开始时间
+
+ {{ startTime || '请选择' }}
+
+
+
+ 结束时间
+
+ {{ endTime || '请选择' }}
+
+
+
+ 预计时长: {{ calculateDuration() }}
+ 跨越时段: {{ calculateCrossSlots() }}
+
+
+
+
+
+```
+
+### 5.2 后端 SQController.cs 改动
+
+#### 改动说明
+后端接口已支持接收 `start_time` 和 `end_time` 参数,主要验证逻辑需确认:
+
+```csharp
+// 添加预约时的冲突检测
+[HttpPost("AddSQReservation")]
+public async Task AddSQReservation([FromBody] AddReservationRequest request)
+{
+ // 验证时间有效性
+ if (request.end_time <= request.start_time)
+ {
+ return BadRequest("结束时间必须晚于开始时间");
+ }
+
+ // 检查时段冲突(已有逻辑)
+ var conflicts = await _reservationService.CheckTimeConflict(
+ request.room_id,
+ request.start_time,
+ request.end_time
+ );
+
+ if (conflicts.Any())
+ {
+ return BadRequest("所选时段已被预约");
+ }
+
+ // 创建预约...
+}
+```
+
+### 5.3 后端时段显示逻辑验证
+
+**文件**: `SQRoomsServices.cs`
+
+当前逻辑已正确支持跨时段检测:
+```csharp
+// 判断预约是否与时段重叠
+bool isReserved = reservations?.Any(r =>
+ r.start_time < slotEnd && r.end_time > slotStart) ?? false;
+```
+
+**验证场景**:
+- 预约 15:00-20:00
+- 下午时段(12:00-18:00): `15:00 < 18:00 && 20:00 > 12:00` = true ✅
+- 晚上时段(18:00-24:00): `15:00 < 24:00 && 20:00 > 18:00` = true ✅
+
+**结论**:后端逻辑无需改动,已支持跨时段显示。
+
+---
+
+## 6. 数据流对比
+
+### 6.1 改动前数据流
+
+```mermaid
+sequenceDiagram
+ participant U as 用户
+ participant F as 前端
+ participant B as 后端
+ participant DB as 数据库
+
+ U->>F: 选择日期
+ U->>F: 选择房间
+ F->>B: 获取房间时段状态
+ B->>DB: 查询已有预约
+ DB-->>B: 返回预约列表
+ B-->>F: 返回4个时段状态
+ F-->>U: 显示可选时段
+ U->>F: 选择"下午"时段
+ F->>F: calculateTimeFromSlot()
+ Note over F: 固定设置 12:00-18:00
+ F->>B: 提交预约(12:00-18:00)
+ B->>DB: 保存预约
+ DB-->>B: 保存成功
+ B-->>F: 返回成功
+ F-->>U: 预约成功
+```
+
+### 6.2 改动后数据流
+
+```mermaid
+sequenceDiagram
+ participant U as 用户
+ participant F as 前端
+ participant B as 后端
+ participant DB as 数据库
+
+ U->>F: 选择日期
+ U->>F: 选择房间
+ F->>B: 获取房间时段状态
+ B->>DB: 查询已有预约
+ DB-->>B: 返回预约列表
+ B-->>F: 返回4个时段状态
+ F-->>U: 显示时间选择器
+ U->>F: 选择开始时间 15:00
+ U->>F: 选择结束时间 20:00
+ F->>F: validateTimeRange()
+ F->>F: calculateCrossSlots()
+ Note over F: 显示"下午、晚上"
+ F->>B: 提交预约(15:00-20:00)
+ B->>B: 检查时段冲突
+ B->>DB: 保存预约
+ DB-->>B: 保存成功
+ B-->>F: 返回成功
+ F-->>U: 预约成功
+```
+
+---
+
+## 7. 测试用例
+
+### 7.1 时间选择测试
+
+| 测试场景 | 开始时间 | 结束时间 | 预期结果 |
+|----------|----------|----------|----------|
+| 正常选择 | 15:00 | 20:00 | 成功,显示跨越"下午、晚上" |
+| 同时段选择 | 14:00 | 17:00 | 成功,显示跨越"下午" |
+| 跨三时段 | 10:00 | 20:00 | 成功,显示跨越"上午、下午、晚上" |
+| 无效时间 | 20:00 | 15:00 | 失败,提示"结束时间必须晚于开始时间" |
+| 相同时间 | 15:00 | 15:00 | 失败,提示"结束时间必须晚于开始时间" |
+
+### 7.2 时段冲突测试
+
+| 已有预约 | 新预约 | 预期结果 |
+|----------|--------|----------|
+| 15:00-20:00 | 10:00-14:00 | 成功(无重叠) |
+| 15:00-20:00 | 14:00-16:00 | 失败(重叠) |
+| 15:00-20:00 | 19:00-22:00 | 失败(重叠) |
+| 15:00-20:00 | 20:00-22:00 | 成功(边界无重叠) |
+
+### 7.3 房间列表显示测试
+
+| 预约时间 | 凌晨 | 上午 | 下午 | 晚上 |
+|----------|------|------|------|------|
+| 02:00-05:00 | 🟠 | 🟢 | 🟢 | 🟢 |
+| 10:00-14:00 | 🟢 | 🟠 | 🟠 | 🟢 |
+| 15:00-20:00 | 🟢 | 🟢 | 🟠 | 🟠 |
+| 22:00-04:00 | 🟠 | 🟢 | 🟢 | 🟠 |
+
+---
+
+## 8. 工作量评估
+
+| 模块 | 工作内容 | 复杂度 |
+|------|----------|--------|
+| 前端-预约页面 | 时间选择器组件替换、时间计算逻辑 | ⭐⭐⭐ |
+| 前端-房间列表 | 验证显示逻辑(可能无需改动) | ⭐ |
+| 前端-预约详情 | 时间格式显示调整 | ⭐ |
+| 后端-控制器 | 参数验证、冲突检测 | ⭐⭐ |
+| 后端-服务层 | 逻辑验证(可能无需改动) | ⭐ |
+| 测试 | 各场景测试 | ⭐⭐ |
+
+**总体评估**:中等复杂度改动,主要工作集中在前端预约页面的UI和逻辑重构。
+
+---
+
+## 9. 风险与注意事项
+
+1. **向后兼容**:已有的固定时段预约数据仍可正常显示
+2. **边界条件**:跨天预约(如23:00-02:00)需特殊处理
+3. **时间精度**:建议时间选择以30分钟为最小单位
+4. **用户体验**:需添加明确的时长提示和时段跨越提示
+5. **数据验证**:前后端都需要进行时间有效性验证
+
+---
+
+## 10. 附录:时段判断工具函数
+
+```javascript
+/**
+ * 判断时间范围跨越哪些时段
+ * @param {number} startHour 开始小时 (0-23)
+ * @param {number} endHour 结束小时 (0-24)
+ * @returns {Array} 跨越的时段数组
+ */
+function getCrossedSlots(startHour, endHour) {
+ const slots = [];
+ const slotRanges = [
+ { name: '凌晨', start: 0, end: 6 },
+ { name: '上午', start: 6, end: 12 },
+ { name: '下午', start: 12, end: 18 },
+ { name: '晚上', start: 18, end: 24 }
+ ];
+
+ for (const slot of slotRanges) {
+ // 重叠判断:预约开始 < 时段结束 AND 预约结束 > 时段开始
+ if (startHour < slot.end && endHour > slot.start) {
+ slots.push(slot.name);
+ }
+ }
+
+ return slots;
+}
+
+// 使用示例
+getCrossedSlots(15, 20); // ['下午', '晚上']
+getCrossedSlots(10, 14); // ['上午', '下午']
+getCrossedSlots(2, 5); // ['凌晨']
+```
diff --git a/预约时间自由选择_改动文档_开发版.md b/预约时间自由选择_改动文档_开发版.md
new file mode 100644
index 0000000..f8c9e60
--- /dev/null
+++ b/预约时间自由选择_改动文档_开发版.md
@@ -0,0 +1,665 @@
+# 预约时间自由选择 - 开发文档
+
+> 文档版本:v1.0
+> 更新日期:2024年
+> 文档类型:开发人员版
+
+---
+
+## 一、改动概述
+
+### 1.1 需求说明
+1. 将固定时段选择(6小时一段)改为自由时间选择
+2. 房间列表正确显示跨时段预约的占用状态
+
+### 1.2 影响范围评估
+- **前端**:主要改动,需重构时间选择UI
+- **后端**:少量改动,逻辑已基本支持
+
+---
+
+## 二、前端改动详情
+
+### 2.1 appointment-page.vue(主要改动)
+
+**文件路径**:`/uniapp/mahjong_group/pages/appointment/appointment-page.vue`
+
+#### 2.1.1 移除旧的时段选择代码
+
+**需删除的部分**:
+
+```vue
+
+
+
+ {{ slot.label }}
+
+
+```
+
+```javascript
+// 删除:固定时段计算函数
+const calculateTimeFromSlot = () => {
+ const date = new Date(selectedDate.value);
+ let startHour, endHour;
+
+ switch (selectedTimeSlot.value) {
+ case 0: startHour = 0; endHour = 6; break;
+ case 1: startHour = 6; endHour = 12; break;
+ case 2: startHour = 12; endHour = 18; break;
+ case 3: startHour = 18; endHour = 24; break;
+ }
+ // ...
+};
+```
+
+#### 2.1.2 新增时间选择器代码
+
+**Template部分**:
+
+```vue
+
+
+
+
+
+ 开始时间
+
+
+ {{ startTime || '请选择开始时间' }}
+
+
+
+
+
+
+ 结束时间
+
+
+ {{ endTime || '请选择结束时间' }}
+
+
+
+
+
+
+
+ 预计时长
+ {{ calculateDuration() }}
+
+
+ 跨越时段
+ {{ calculateCrossSlots() }}
+
+
+
+
+
+ {{ timeError }}
+
+
+
+```
+
+**Script部分**:
+
+```javascript
+
+```
+
+**Style部分**:
+
+```scss
+
+```
+
+### 2.2 book-room-page.vue(验证,可能无需改动)
+
+**文件路径**:`/uniapp/mahjong_group/pages/appointment/book-room-page.vue`
+
+当前时段显示已从后端获取数据,后端已支持跨时段检测,**理论上无需前端改动**。
+
+**验证方法**:
+1. 创建一个15:00-20:00的预约
+2. 查看房间列表,确认下午和晚上都显示为已预约状态
+
+如显示不正确,检查后端`SQRoomsServices.cs`中的时段状态返回逻辑。
+
+---
+
+## 三、后端改动详情
+
+### 3.1 SQController.cs(验证/微调)
+
+**文件路径**:`/server/CoreCms.Net.Web.WebApi/Controllers/SQController.cs`
+
+#### 3.1.1 AddSQReservation 接口验证
+
+当前接口已支持接收`start_time`和`end_time`参数,主要验证:
+
+```csharp
+[HttpPost("AddSQReservation")]
+public async Task AddSQReservation([FromBody] AddReservationDto dto)
+{
+ // 验证时间有效性(建议新增)
+ if (dto.end_time <= dto.start_time)
+ {
+ return BadRequest(new { code = 400, msg = "结束时间必须晚于开始时间" });
+ }
+
+ // 验证最短时长(可选,如最少1小时)
+ var duration = (dto.end_time - dto.start_time).TotalHours;
+ if (duration < 1)
+ {
+ return BadRequest(new { code = 400, msg = "预约时长不能少于1小时" });
+ }
+
+ // 验证最长时长(可选,如最多12小时)
+ if (duration > 12)
+ {
+ return BadRequest(new { code = 400, msg = "预约时长不能超过12小时" });
+ }
+
+ // ... 现有逻辑
+}
+```
+
+### 3.2 SQRoomsServices.cs(验证,无需改动)
+
+**文件路径**:`/server/CoreCms.Net.Services/SQ/SQRoomsServices.cs`
+
+当前时段状态判断逻辑已正确支持跨时段检测:
+
+```csharp
+// 现有代码(已支持跨时段)
+bool isReserved = reservations?.Any(r =>
+ r.start_time < slotEnd && r.end_time > slotStart) ?? false;
+```
+
+**逻辑验证**:
+
+| 预约时间 | 时段 | 判断公式 | 结果 |
+|:--------:|:----:|:--------:|:----:|
+| 15:00-20:00 | 下午(12-18) | 15 < 18 && 20 > 12 | true ✓ |
+| 15:00-20:00 | 晚上(18-24) | 15 < 24 && 20 > 18 | true ✓ |
+| 15:00-20:00 | 上午(6-12) | 15 < 12 | false ✓ |
+
+**结论**:后端逻辑无需改动。
+
+---
+
+## 四、签到功能代码参考
+
+### 4.1 前端签到组件
+
+**文件路径**:`/uniapp/mahjong_group/components/com/page/qiandao-popup.vue`
+
+**签到按钮显示条件**(reservation-item.vue):
+
+```javascript
+// 签到按钮显示条件
+const isQianDaoVisible = computed(() => {
+ const item = props.reservation;
+
+ // 1. 预约状态必须为 1(已锁定)
+ if (item.status !== 1) return false;
+
+ // 2. 只有发起者可签到
+ if (item.role !== 1) return false;
+
+ // 3. 时间范围检查
+ const now = new Date();
+ const startTime = new Date(item.start_time);
+ const endTime = new Date(item.end_time);
+ const tenMinutes = 10 * 60 * 1000;
+
+ const timeToStart = startTime.getTime() - now.getTime();
+ const timeToEnd = endTime.getTime() - now.getTime();
+
+ // 可签到:开始前10分钟内 或 已开始但未结束
+ return (timeToStart <= tenMinutes && timeToStart > 0) ||
+ (timeToStart <= 0 && timeToEnd > 0);
+});
+```
+
+### 4.2 后端签到接口
+
+**文件路径**:`/server/CoreCms.Net.Web.WebApi/Controllers/SQController.cs`
+**方法位置**:第1205-1431行 `CheckInReservation`
+
+**请求格式**:
+
+```json
+POST /api/sq/checkinreservation
+{
+ "reservation_id": 123,
+ "attendeds": [
+ { "user_id": 2, "isAttended": true },
+ { "user_id": 3, "isAttended": false }
+ ]
+}
+```
+
+**核心处理逻辑**:
+
+```csharp
+// 1. 更新预约状态为"进行中"
+reservation.status = 2;
+
+// 2. 处理参与者
+foreach (var attendee in dto.attendeds)
+{
+ if (attendee.isAttended)
+ {
+ // 已到场:信誉+0.2,标记为已赴约
+ participant.is_arrive = 1;
+ user.credit_score = Math.Min(5, user.credit_score + 0.2);
+ }
+ else
+ {
+ // 未到场:信誉-0.5,鸽子+1,踢出
+ participant.is_arrive = 2;
+ participant.status = 1; // 已退出
+ user.credit_score = Math.Max(0, user.credit_score - 0.5);
+ user.dove_count += 1;
+ }
+}
+
+// 3. 处理押金退款
+if (reservation.deposit_fee > 0)
+{
+ // 已到场的参与者:发起退款
+ // 未到场的参与者:不退款
+}
+```
+
+---
+
+## 五、数据库相关
+
+### 5.1 相关表结构
+
+**SQReservations(预约表)**:
+
+| 字段 | 类型 | 说明 |
+|:----:|:----:|:----:|
+| id | int | 主键 |
+| status | int | 0=待开始,1=已锁定,2=进行中,3=已结束,4=已取消 |
+| start_time | datetime | 开始时间 |
+| end_time | datetime | 结束时间 |
+| room_id | int | 房间ID |
+| deposit_fee | decimal | 押金金额 |
+
+**SQReservationParticipants(参与者表)**:
+
+| 字段 | 类型 | 说明 |
+|:----:|:----:|:----:|
+| id | int | 主键 |
+| reservation_id | int | 预约ID |
+| user_id | int | 用户ID |
+| role | int | 0=参与者,1=发起者 |
+| status | int | 0=正常,1=已退出 |
+| is_arrive | int | 0=默认,1=已赴约,2=未赴约 |
+| is_refund | int | 退款状态 |
+
+### 5.2 无需数据库改动
+
+本次改动不涉及数据库结构变更,现有字段已满足需求:
+- `start_time` / `end_time` 已支持任意时间
+- 时段状态在服务层动态计算
+
+---
+
+## 六、测试用例
+
+### 6.1 时间选择功能测试
+
+| 用例ID | 测试场景 | 输入 | 预期结果 |
+|:------:|:--------:|:----:|:--------:|
+| T001 | 正常选择 | 开始15:00,结束20:00 | 成功,显示"5小时",跨越"下午、晚上" |
+| T002 | 同时段 | 开始14:00,结束17:00 | 成功,显示"3小时",跨越"下午" |
+| T003 | 跨三时段 | 开始10:00,结束20:00 | 成功,显示"10小时",跨越"上午、下午、晚上" |
+| T004 | 时间倒置 | 开始20:00,结束15:00 | 失败,提示"结束时间必须晚于开始时间" |
+| T005 | 时间相同 | 开始15:00,结束15:00 | 失败,提示"结束时间必须晚于开始时间" |
+| T006 | 时长过短 | 开始15:00,结束15:30 | 失败,提示"预约时长不能少于1小时" |
+
+### 6.2 时段冲突测试
+
+| 用例ID | 已有预约 | 新预约 | 预期结果 |
+|:------:|:--------:|:------:|:--------:|
+| T101 | 15:00-20:00 | 10:00-14:00 | 成功(无重叠) |
+| T102 | 15:00-20:00 | 14:00-16:00 | 失败(重叠) |
+| T103 | 15:00-20:00 | 19:00-22:00 | 失败(重叠) |
+| T104 | 15:00-20:00 | 20:00-22:00 | 成功(边界无重叠) |
+| T105 | 15:00-20:00 | 12:00-22:00 | 失败(完全包含) |
+
+### 6.3 房间列表显示测试
+
+| 用例ID | 预约时间 | 凌晨 | 上午 | 下午 | 晚上 |
+|:------:|:--------:|:----:|:----:|:----:|:----:|
+| T201 | 02:00-05:00 | 🟠 | 🟢 | 🟢 | 🟢 |
+| T202 | 05:00-08:00 | 🟠 | 🟠 | 🟢 | 🟢 |
+| T203 | 10:00-14:00 | 🟢 | 🟠 | 🟠 | 🟢 |
+| T204 | 15:00-20:00 | 🟢 | 🟢 | 🟠 | 🟠 |
+| T205 | 08:00-22:00 | 🟢 | 🟠 | 🟠 | 🟠 |
+
+### 6.4 签到功能测试
+
+| 用例ID | 测试场景 | 预期结果 |
+|:------:|:--------:|:--------:|
+| T301 | 开始前11分钟签到 | 签到按钮不显示 |
+| T302 | 开始前10分钟签到 | 签到按钮显示,可签到 |
+| T303 | 开始后5分钟签到 | 签到按钮显示,可签到 |
+| T304 | 结束后签到 | 签到按钮不显示 |
+| T305 | 参与者尝试签到 | 签到按钮不显示 |
+| T306 | 重复签到 | 提示"已签到,无法重复签到" |
+
+---
+
+## 七、改动文件清单
+
+### 7.1 必须改动
+
+| 序号 | 文件路径 | 改动类型 | 说明 |
+|:----:|:--------:|:--------:|:----:|
+| 1 | `pages/appointment/appointment-page.vue` | 修改 | 时间选择器重构 |
+
+### 7.2 验证确认
+
+| 序号 | 文件路径 | 改动类型 | 说明 |
+|:----:|:--------:|:--------:|:----:|
+| 2 | `pages/appointment/book-room-page.vue` | 验证 | 确认跨时段显示正确 |
+| 3 | `SQController.cs` | 验证 | 确认时间参数接收正确 |
+| 4 | `SQRoomsServices.cs` | 验证 | 确认跨时段检测正确 |
+
+### 7.3 可选优化
+
+| 序号 | 文件路径 | 改动类型 | 说明 |
+|:----:|:--------:|:--------:|:----:|
+| 5 | `SQController.cs` | 新增 | 添加时长限制验证 |
+
+---
+
+## 八、工具函数
+
+### 8.1 跨时段计算(前端)
+
+```javascript
+/**
+ * 判断时间范围跨越哪些时段
+ * @param {number} startHour 开始小时 (0-23)
+ * @param {number} endHour 结束小时 (0-24)
+ * @returns {Array} 跨越的时段数组
+ */
+function getCrossedSlots(startHour, endHour) {
+ const slots = [];
+ const slotRanges = [
+ { name: '凌晨', start: 0, end: 6 },
+ { name: '上午', start: 6, end: 12 },
+ { name: '下午', start: 12, end: 18 },
+ { name: '晚上', start: 18, end: 24 }
+ ];
+
+ for (const slot of slotRanges) {
+ if (startHour < slot.end && endHour > slot.start) {
+ slots.push(slot.name);
+ }
+ }
+
+ return slots;
+}
+
+// 使用示例
+getCrossedSlots(15, 20); // ['下午', '晚上']
+getCrossedSlots(10, 14); // ['上午', '下午']
+getCrossedSlots(2, 5); // ['凌晨']
+```
+
+### 8.2 时长格式化(前端)
+
+```javascript
+/**
+ * 格式化时长显示
+ * @param {number} minutes 分钟数
+ * @returns {string} 格式化后的时长
+ */
+function formatDuration(minutes) {
+ if (minutes <= 0) return '-';
+
+ const hours = Math.floor(minutes / 60);
+ const mins = minutes % 60;
+
+ if (hours === 0) {
+ return `${mins}分钟`;
+ }
+ if (mins === 0) {
+ return `${hours}小时`;
+ }
+ return `${hours}小时${mins}分钟`;
+}
+```
+
+---
+
+## 九、注意事项
+
+1. **向后兼容**:已有预约数据不受影响,仍可正常显示
+2. **时间精度**:UniApp的time picker默认支持分钟级别选择
+3. **时区处理**:前后端统一使用本地时间,避免时区转换问题
+4. **边界条件**:特别注意24:00的处理(可视为次日00:00)
+5. **并发控制**:创建预约时需考虑并发冲突,建议后端加锁
diff --git a/预约时间自由选择_改动文档_甲方版.md b/预约时间自由选择_改动文档_甲方版.md
new file mode 100644
index 0000000..405849d
--- /dev/null
+++ b/预约时间自由选择_改动文档_甲方版.md
@@ -0,0 +1,524 @@
+# 预约时间自由选择 - 需求改动文档
+
+> 文档版本:v1.0
+> 更新日期:2024年
+> 文档类型:甲方确认版
+
+---
+
+## 一、需求概述
+
+### 1.1 当前问题
+- 用户只能选择固定时段(凌晨/上午/下午/晚上),每个时段固定6小时
+- 无法灵活选择具体的开始和结束时间
+- 跨时段预约时,房间列表显示不够直观
+
+### 1.2 改动目标
+1. **时段显示优化**:预约15:00-20:00时,房间列表应显示"下午"和"晚上"都被预定
+2. **自由时间选择**:允许用户自由选择具体的开始时间和结束时间
+
+---
+
+## 二、当前系统时段定义
+
+| 时段名称 | 时间范围 | 图标颜色 |
+|:--------:|:--------:|:--------:|
+| 凌晨 | 00:00 - 06:00 | - |
+| 上午 | 06:00 - 12:00 | - |
+| 下午 | 12:00 - 18:00 | - |
+| 晚上 | 18:00 - 24:00 | - |
+
+**时段状态说明**:
+- 🟢 可预约 - 该时段空闲,可以预约
+- 🟠 已预约 - 该时段已被其他人预约
+- ⚫ 不可用 - 该时段不开放
+- 🔵 使用中 - 该时段正在使用
+
+---
+
+## 三、完整业务闭环
+
+### 3.1 发起者完整闭环(创建预约→结束)
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ 发起者完整业务闭环 │
+└─────────────────────────────────────────────────────────────────────────┘
+
+ ┌──────────┐
+ │ 发起者 │
+ └────┬─────┘
+ │
+ ▼
+┌─────────────────┐
+│ 1. 选择日期 │
+└────────┬────────┘
+ │
+ ▼
+┌─────────────────┐
+│ 2. 选择房间 │ ←─── 查看房间时段状态
+└────────┬────────┘ 🟢可预约 🟠已预约
+ │
+ ▼
+┌─────────────────┐
+│ 3. 选择时段 │ ←─── 【改动点】改为自由选择时间
+└────────┬────────┘
+ │
+ ▼
+┌─────────────────┐
+│ 4. 填写预约信息 │ ←─── 标题、人数、要求等
+└────────┬────────┘
+ │
+ ▼
+┌─────────────────┐ ┌─────────────────┐
+│ 5. 支付押金 │────▶│ 创建成功 │
+│ (如需要) │ │ 状态:待开始 │
+└─────────────────┘ └────────┬────────┘
+ │
+ ┌───────────────────────┤
+ │ │
+ ▼ ▼
+┌─────────────────┐ ┌─────────────────┐
+│ 等待参与者加入 │ │ 分享给好友 │
+└────────┬────────┘ └─────────────────┘
+ │
+ │ 【人满/时间到】
+ ▼
+┌─────────────────┐
+│ 状态:已锁定 │ ←─── 不可再加入/退出
+└────────┬────────┘
+ │
+ │ 【开始前10分钟~开始后】
+ ▼
+┌─────────────────┐
+│ 6. 签到确认 ⭐ │ ←─── 确认参与者到场情况
+└────────┬────────┘
+ │
+ ├─────────────────────────────┐
+ │ │
+ ▼ ▼
+┌─────────────────┐ ┌─────────────────┐
+│ 参与者已到场 │ │ 参与者未到场 │
+│ 信誉分 +0.2 │ │ 信誉分 -0.5 │
+│ 退还押金 │ │ 鸽子数 +1 │
+└────────┬────────┘ │ 押金不退 │
+ │ └─────────────────┘
+ │
+ ▼
+┌─────────────────┐
+│ 状态:进行中 │ ←─── 预约正式开始
+└────────┬────────┘
+ │
+ │ 【时间结束】
+ ▼
+┌─────────────────┐
+│ 7. 互相评价 │ ←─── 牌品、牌技评分
+└────────┬────────┘
+ │
+ ▼
+┌─────────────────┐
+│ 状态:已结束 │ ←─── 预约完成
+└─────────────────┘
+```
+
+### 3.2 参与者完整闭环(加入预约→结束)
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ 参与者完整业务闭环 │
+└─────────────────────────────────────────────────────────────────────────┘
+
+ ┌──────────┐
+ │ 参与者 │
+ └────┬─────┘
+ │
+ ├───────────────────┬───────────────────┐
+ │ │ │
+ ▼ ▼ ▼
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ 首页浏览 │ │ 好友分享 │ │ 扫码进入 │
+│ 麻将局列表 │ │ 点击链接 │ │ │
+└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
+ │ │ │
+ └───────────────────┴───────────────────┘
+ │
+ ▼
+ ┌─────────────────┐
+ │ 1. 查看预约详情 │
+ └────────┬────────┘
+ │
+ ▼
+ ┌─────────────────┐ ┌─────────────────┐
+ │ 2. 点击加入 │────▶│ 检查是否在 │
+ └─────────────────┘ │ 黑名单中 │
+ └────────┬────────┘
+ │
+ ┌───────────────────────┴───────────────────┐
+ │ │
+ ▼ ▼
+ ┌─────────────────┐ ┌─────────────────┐
+ │ 在黑名单中 │ │ 不在黑名单 │
+ │ 无法加入 ❌ │ └────────┬────────┘
+ └─────────────────┘ │
+ ▼
+ ┌─────────────────┐
+ │ 3. 支付押金 │
+ │ (如需要) │
+ └────────┬────────┘
+ │
+ ▼
+ ┌─────────────────┐
+ │ 加入成功 ✓ │
+ │ 等待预约开始 │
+ └────────┬────────┘
+ │
+ ┌──────────────────────────────────────────────────┤
+ │ │
+ ▼ ▼
+ ┌─────────────────┐ ┌─────────────────┐
+ │ 主动退出预约 │ │ 等待锁定 │
+ │ (锁定前可退) │ └────────┬────────┘
+ └────────┬────────┘ │
+ │ │
+ ▼ │ 【人满/时间到】
+ ┌─────────────────┐ ▼
+ │ 退还押金 │ ┌─────────────────┐
+ │ 退出成功 │ │ 状态:已锁定 │
+ └─────────────────┘ └────────┬────────┘
+ │
+ │ 【发起者签到】
+ ▼
+ ┌─────────────────┐
+ │ 4. 被签到确认 │
+ └────────┬────────┘
+ │
+ ┌──────────────────────────────────────────────────┤
+ │ │
+ ▼ ▼
+ ┌─────────────────┐ ┌─────────────────┐
+ │ 已到场 ✓ │ │ 未到场 ✗ │
+ │ 信誉分 +0.2 │ │ 信誉分 -0.5 │
+ │ 押金退还 │ │ 鸽子数 +1 │
+ └────────┬────────┘ │ 押金不退 │
+ │ │ 强制退出 │
+ │ └─────────────────┘
+ ▼
+ ┌─────────────────┐
+ │ 状态:进行中 │
+ └────────┬────────┘
+ │
+ │ 【时间结束】
+ ▼
+ ┌─────────────────┐
+ │ 5. 互相评价 │
+ └────────┬────────┘
+ │
+ ▼
+ ┌─────────────────┐
+ │ 状态:已结束 │
+ └─────────────────┘
+```
+
+---
+
+## 四、签到功能详解
+
+### 4.1 签到流程图
+
+```mermaid
+flowchart TD
+ A[预约状态:已锁定] --> B{时间判断}
+ B -->|开始前10分钟内| C[显示签到按钮]
+ B -->|预约已开始且未结束| C
+ B -->|时间未到| D[签到按钮隐藏]
+
+ C --> E[发起者点击签到]
+ E --> F[弹出签到确认窗口]
+ F --> G[显示参与者列表]
+
+ G --> H{逐个确认到场}
+ H -->|已到场| I[标记为到场 ✓]
+ H -->|未到场| J[标记为缺席 ✗]
+
+ I --> K[提交签到]
+ J --> K
+
+ K --> L{系统处理}
+ L --> M[到场者:信誉+0.2,退押金]
+ L --> N[缺席者:信誉-0.5,扣押金,踢出]
+
+ M --> O[预约状态→进行中]
+ N --> O
+```
+
+### 4.2 签到界面原型
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ 到场确认 │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ 请确认以下参与者是否已到场: │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 👤 张三 │ │
+│ │ ┌──────────────┐ ┌──────────────┐ │ │
+│ │ │ ✓ 已到场 │ │ ✗ 未到场 │ │ │
+│ │ └──────────────┘ └──────────────┘ │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 👤 李四 │ │
+│ │ ┌──────────────┐ ┌──────────────┐ │ │
+│ │ │ ✓ 已到场 │ │ ✗ 未到场 │ │ │
+│ │ └──────────────┘ └──────────────┘ │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 👤 王五 │ │
+│ │ ┌──────────────┐ ┌──────────────┐ │ │
+│ │ │ ✓ 已到场 │ │ ✗ 未到场 │ │ │
+│ │ └──────────────┘ └──────────────┘ │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ ⚠️ 提示:标记为"未到场"的参与者将被扣除信誉分 │
+│ │
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ 确认签到 │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 4.3 签到规则说明
+
+| 项目 | 说明 |
+|:----:|:----:|
+| **签到人** | 仅发起者可以签到 |
+| **签到时间** | 预约开始前10分钟 ~ 预约结束时间 |
+| **签到次数** | 每个预约只能签到一次 |
+| **到场奖励** | 信誉分 +0.2(上限5分) |
+| **缺席惩罚** | 信誉分 -0.5,鸽子数 +1 |
+| **押金处理** | 到场者退还,缺席者不退 |
+
+---
+
+## 五、时间选择改动对比
+
+### 5.1 改动前(固定时段选择)
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ 选择预约时段 │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ ○ 凌晨 (00:00 - 06:00) │
+│ │
+│ ○ 上午 (06:00 - 12:00) │
+│ │
+│ ● 下午 (12:00 - 18:00) ← 只能选择一个完整时段 │
+│ │
+│ ○ 晚上 (18:00 - 24:00) │
+│ │
+│ ⚠️ 限制:只能选择固定6小时时段,无法自定义时间 │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 5.2 改动后(自由时间选择)
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ 选择预约时间 │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ 开始时间 │
+│ ┌───────────────────────────────────────────────────────┐ │
+│ │ 15 : 00 ▼ │ │
+│ └───────────────────────────────────────────────────────┘ │
+│ │
+│ 结束时间 │
+│ ┌───────────────────────────────────────────────────────┐ │
+│ │ 20 : 00 ▼ │ │
+│ └───────────────────────────────────────────────────────┘ │
+│ │
+│ ┌───────────────────────────────────────────────────────┐ │
+│ │ 📊 预计时长:5 小时 │ │
+│ │ 📅 跨越时段:下午、晚上 │ │
+│ └───────────────────────────────────────────────────────┘ │
+│ │
+│ 💡 提示:结束时间必须晚于开始时间 │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 5.3 房间列表显示对比
+
+**场景**:某房间已被预约 15:00 - 20:00
+
+```
+【改动前】房间时段显示:
+┌─────────────────────────────────────────────────────────────────┐
+│ 304包厢 │
+│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
+│ │ 凌晨 │ │ 上午 │ │ 下午 │ │ 晚上 │ │
+│ │ 🟢 │ │ 🟢 │ │ 🟠 │ │ 🟢 │ │
+│ └────────┘ └────────┘ └────────┘ └────────┘ │
+│ │
+│ ❌ 问题:晚上时段显示为可预约,但实际18:00-20:00已被占用 │
+└─────────────────────────────────────────────────────────────────┘
+
+【改动后】房间时段显示:
+┌─────────────────────────────────────────────────────────────────┐
+│ 304包厢 │
+│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
+│ │ 凌晨 │ │ 上午 │ │ 下午 │ │ 晚上 │ │
+│ │ 🟢 │ │ 🟢 │ │ 🟠 │ │ 🟠 │ │
+│ └────────┘ └────────┘ └────────┘ └────────┘ │
+│ │
+│ ✓ 正确:下午和晚上都显示为已预约状态 │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 六、预约状态流转图
+
+```mermaid
+stateDiagram-v2
+ [*] --> 待开始: 创建预约
+
+ 待开始 --> 已锁定: 人满或时间临近
+ 待开始 --> 已取消: 发起者取消
+
+ 已锁定 --> 进行中: 发起者签到
+ 已锁定 --> 已取消: 发起者取消(需扣分)
+
+ 进行中 --> 已结束: 时间结束
+
+ 已取消 --> [*]
+ 已结束 --> [*]
+
+ note right of 待开始
+ 可加入/退出
+ 可分享
+ end note
+
+ note right of 已锁定
+ 不可加入/退出
+ 等待签到
+ end note
+
+ note right of 进行中
+ 正在进行
+ 可评价
+ end note
+```
+
+### 状态说明表
+
+| 状态 | 状态码 | 可执行操作 | 说明 |
+|:----:|:------:|:----------:|:----:|
+| 待开始 | 0 | 加入、退出、取消、分享 | 预约创建后的初始状态 |
+| 已锁定 | 1 | 签到、取消 | 人满或临近开始时间 |
+| 进行中 | 2 | 评价 | 签到后预约正式开始 |
+| 已结束 | 3 | 查看记录 | 预约时间结束 |
+| 已取消 | 4 | 无 | 预约被取消 |
+
+---
+
+## 七、信誉分规则
+
+### 7.1 信誉分变化场景
+
+| 场景 | 分数变化 | 说明 |
+|:----:|:--------:|:----:|
+| 按时赴约 | +0.2 | 签到时标记为"已到场" |
+| 放鸽子 | -0.5 | 签到时标记为"未到场" |
+| 取消预约(锁定前) | 无变化 | 正常取消 |
+| 取消预约(锁定后) | -0.3 | 临时取消扣分 |
+
+### 7.2 信誉分等级
+
+| 等级 | 分数范围 | 说明 |
+|:----:|:--------:|:----:|
+| 优秀 | 4.5 - 5.0 | 信誉极好 |
+| 良好 | 4.0 - 4.4 | 信誉较好 |
+| 一般 | 3.0 - 3.9 | 信誉一般 |
+| 较差 | 2.0 - 2.9 | 需要注意 |
+| 很差 | < 2.0 | 可能被限制 |
+
+---
+
+## 八、改动工作内容总结
+
+### 8.1 前端改动
+
+| 序号 | 页面/组件 | 改动内容 |
+|:----:|:--------:|:--------:|
+| 1 | 创建预约页面 | 将时段单选改为时间选择器 |
+| 2 | 创建预约页面 | 增加时长计算和跨时段提示 |
+| 3 | 房间列表页面 | 验证跨时段显示(可能无需改动) |
+
+### 8.2 后端改动
+
+| 序号 | 模块 | 改动内容 |
+|:----:|:----:|:--------:|
+| 1 | 预约接口 | 支持接收自定义开始/结束时间 |
+| 2 | 冲突检测 | 验证跨时段冲突检测逻辑 |
+
+### 8.3 测试验证
+
+| 序号 | 测试场景 | 预期结果 |
+|:----:|:--------:|:--------:|
+| 1 | 选择15:00-20:00 | 成功,显示跨越"下午、晚上" |
+| 2 | 选择20:00-15:00 | 失败,提示时间无效 |
+| 3 | 房间已有15:00-20:00预约 | 下午🟠、晚上🟠 |
+| 4 | 预约10:00-14:00 | 显示跨越"上午、下午" |
+
+---
+
+## 九、待确认问题
+
+请甲方确认以下问题:
+
+### 问题1:时间选择精度
+- [ ] 选项A:以30分钟为最小单位(如15:00、15:30、16:00)
+- [ ] 选项B:以1小时为最小单位(如15:00、16:00、17:00)
+- [ ] 选项C:以15分钟为最小单位
+
+### 问题2:跨天预约
+- [ ] 是否支持跨天预约?(如23:00 - 次日02:00)
+- [ ] 如支持,如何处理跨天的时段显示?
+
+### 问题3:最短/最长预约时长
+- [ ] 最短预约时长限制:____小时
+- [ ] 最长预约时长限制:____小时
+
+### 问题4:时间冲突提示
+- [ ] 选项A:选择时间时实时检测冲突
+- [ ] 选项B:提交时统一检测冲突
+
+---
+
+## 十、附录
+
+### A. 名词解释
+
+| 名词 | 解释 |
+|:----:|:----:|
+| 发起者 | 创建预约的用户 |
+| 参与者 | 加入预约的其他用户 |
+| 签到 | 发起者确认参与者到场情况的操作 |
+| 锁定 | 预约人满或临近开始,不可再加入/退出 |
+| 鸽子数 | 用户放鸽子(未到场)的累计次数 |
+
+### B. 时段重叠判断示例
+
+| 预约时间 | 凌晨(0-6) | 上午(6-12) | 下午(12-18) | 晚上(18-24) |
+|:--------:|:---------:|:----------:|:-----------:|:-----------:|
+| 02:00-05:00 | 🟠 | 🟢 | 🟢 | 🟢 |
+| 05:00-08:00 | 🟠 | 🟠 | 🟢 | 🟢 |
+| 10:00-14:00 | 🟢 | 🟠 | 🟠 | 🟢 |
+| 15:00-20:00 | 🟢 | 🟢 | 🟠 | 🟠 |
+| 22:00-02:00 | 🟠 | 🟢 | 🟢 | 🟠 |