开发资产:校园后勤服务平台
概述
在开发这个场景的过程中,我们沉淀了以下可复用的开发资产:
- Cursor Skill - 教室预约冲突检测规则
- PROMPT 模板 - 多级审批流程生成
- 实施指南 - 如何快速复现这个场景
这些资产可以帮助快速复用这个场景的开发经验。
1. Cursor Skill:教室预约冲突检测规则
使用场景
当你需要开发类似的"资源预约/预订"场景时,可以使用这个 Skill 让 AI 理解业务规则。
Skill 内容
markdown
# 资源预约冲突检测规则
## 业务场景
教室、会议室、设备等资源的预约管理,需要检测时间冲突,防止重复预约。
## 核心业务规则
### 1. 冲突检测规则
**规则一:时间重叠检测**
```typescript
// 检查两个时间段是否重叠
function isTimeOverlap(
start1: Date,
end1: Date,
start2: Date,
end2: Date
): boolean {
// 时间段1的开始时间在时间段2内
if (start1 >= start2 && start1 < end2) return true;
// 时间段1的结束时间在时间段2内
if (end1 > start2 && end1 <= end2) return true;
// 时间段1完全包含时间段2
if (start1 <= start2 && end1 >= end2) return true;
// 时间段2完全包含时间段1
if (start2 <= start1 && end2 >= end1) return true;
return false;
}规则二:时间间隔检测
typescript
// 检查前后预约之间是否有足够的时间间隔(用于打扫等)
function hasEnoughInterval(
previousEnd: Date,
nextStart: Date,
minInterval: number = 10 // 最小间隔(分钟)
): boolean {
const interval = (nextStart.getTime() - previousEnd.getTime()) / (1000 * 60);
return interval >= minInterval;
}规则三:冲突检测主函数
typescript
async function checkConflict(
resourceId: string,
startTime: Date,
endTime: Date,
excludeReservationId?: string // 排除当前预约(用于修改场景)
): Promise<ConflictResult> {
// 1. 获取该资源的所有预约
const reservations = await getReservations({
resourceId,
status: ['reserved', 'in-use'],
excludeId: excludeReservationId
});
// 2. 检查时间重叠
const timeConflicts = reservations.filter(reservation =>
isTimeOverlap(
startTime,
endTime,
reservation.startTime,
reservation.endTime
)
);
// 3. 检查时间间隔
const intervalConflicts = [];
// 检查与前一个预约的间隔
const previousReservation = reservations
.filter(r => r.endTime <= startTime)
.sort((a, b) => b.endTime.getTime() - a.endTime.getTime())[0];
if (previousReservation && !hasEnoughInterval(
previousReservation.endTime,
startTime
)) {
intervalConflicts.push({
type: 'BEFORE',
reservation: previousReservation,
requiredInterval: 10
});
}
// 检查与后一个预约的间隔
const nextReservation = reservations
.filter(r => r.startTime >= endTime)
.sort((a, b) => a.startTime.getTime() - b.startTime.getTime())[0];
if (nextReservation && !hasEnoughInterval(
endTime,
nextReservation.startTime
)) {
intervalConflicts.push({
type: 'AFTER',
reservation: nextReservation,
requiredInterval: 10
});
}
// 4. 返回冲突结果
return {
hasConflict: timeConflicts.length > 0 || intervalConflicts.length > 0,
timeConflicts,
intervalConflicts,
canReserve: timeConflicts.length === 0 && intervalConflicts.length === 0
};
}2. 预约限制规则
规则一:每人每天预约次数限制
typescript
async function checkDailyLimit(
userId: string,
date: Date
): Promise<boolean> {
const todayReservations = await getReservations({
userId,
date: date,
status: ['reserved', 'in-use']
});
const maxDailyReservations = 2; // 每人每天最多2次
return todayReservations.length < maxDailyReservations;
}规则二:单次预约时长限制
typescript
function checkDurationLimit(
startTime: Date,
endTime: Date
): boolean {
const duration = (endTime.getTime() - startTime.getTime()) / (1000 * 60 * 60); // 小时
const maxDuration = 4; // 最多4小时
return duration <= maxDuration;
}规则三:提前预约时间限制
typescript
function checkAdvanceBookingLimit(
startTime: Date
): boolean {
const now = new Date();
const daysInAdvance = (startTime.getTime() - now.getTime()) / (1000 * 60 * 60 * 24);
const maxDaysInAdvance = 7; // 最多提前7天
return daysInAdvance <= maxDaysInAdvance && daysInAdvance >= 0;
}3. 自动释放规则
规则一:超时未签到自动释放
typescript
async function autoReleaseTimeoutReservations() {
const now = new Date();
// 查找超时未签到的预约
const timeoutReservations = await getReservations({
status: 'reserved',
startTime: { $lte: new Date(now.getTime() - 30 * 60 * 1000) } // 开始时间已过30分钟
});
for (const reservation of timeoutReservations) {
// 检查是否已签到
if (!reservation.checkInTime) {
// 自动释放
await updateReservation(reservation.id, {
status: 'cancelled',
cancelReason: '超时未签到自动释放'
});
// 通知申请人
await notifyUser(reservation.userId, {
type: 'RESERVATION_AUTO_RELEASED',
reservationId: reservation.id
});
}
}
}数据模型
预约(Reservation)
typescript
interface Reservation {
id: string;
resourceId: string; // 资源ID(教室/会议室)
resourceName: string; // 资源名称
userId: string; // 申请人ID
userName: string; // 申请人姓名
startTime: Date; // 开始时间
endTime: Date; // 结束时间
purpose: string; // 使用目的
needApproval: boolean; // 是否需要审批
approvalStatus?: 'pending' | 'approved' | 'rejected';
checkInTime?: Date; // 签到时间
checkOutTime?: Date; // 签退时间
status: 'reserved' | 'in-use' | 'completed' | 'cancelled';
createdAt: Date;
updatedAt: Date;
}开发注意事项
- 时区处理:所有时间使用 UTC 或统一时区,避免时区问题
- 并发控制:预约时使用数据库锁或乐观锁,防止并发冲突
- 定时任务:需要定时任务检查超时预约并自动释放
- 通知机制:预约成功、冲突、超时等都需要及时通知用户
相似场景复用
这个规则可以复用到:
- 会议室预约:企业会议室预约
- 设备预约:实验室设备、教学设备预约
- 场地预约:运动场地、活动场地预约
- 车辆预约:公车、校车预约
### 如何使用这个 Skill
**步骤 1:保存为文件**
将上述内容保存为 `.cursorrules` 文件或在项目中创建 `docs/business-rules/resource-booking.md`
**步骤 2:在 Cursor 中引用**在开发预约功能时,告诉 AI: "参考 resource-booking.md 中的业务规则, 实现资源预约冲突检测逻辑"
**步骤 3:AI 会理解并生成代码**
AI 会基于这个规则生成准确的冲突检测逻辑、预约限制等。
---
## 2. PROMPT 模板:多级审批流程生成
### 使用场景
当需要快速生成类似的"多级审批"代码时,使用这个 PROMPT 模板。
### PROMPT 模板
```text
你是一个业务逻辑开发专家。请根据以下业务规则,生成完整的多级审批流程代码。
## 业务场景
[描述你的具体场景,例如:物资申请多级审批]
## 审批流程定义
[描述审批流程,例如:
1. 普通物资:无需审批,自动批准
2. 贵重物资:需要辅导员审批
3. 高价值物资(>500元):需要系主任审批
4. 特殊物资:需要后勤主管审批
]
## 审批规则
[描述审批规则,例如:
- 辅导员审批:24小时内
- 系主任审批:48小时内
- 后勤主管审批:72小时内
- 超时自动提醒,超过3天自动拒绝
]
## 输出要求
1. TypeScript 代码实现
2. 包含完整的审批流程逻辑
3. 包含超时处理
4. 包含通知机制
5. 包含单元测试用例
6. 包含注释说明
请生成代码。使用示例
输入 PROMPT:
text
你是一个业务逻辑开发专家。请根据以下业务规则,生成完整的多级审批流程代码。
## 业务场景
校园物资申请多级审批
## 审批流程定义
1. 普通物资(<100元):无需审批,自动批准
2. 贵重物资(100-500元):需要辅导员审批
3. 高价值物资(>500元):需要系主任审批
4. 特殊物资:需要后勤主管审批
## 审批规则
- 辅导员审批:24小时内
- 系主任审批:48小时内
- 后勤主管审批:72小时内
- 超时自动提醒,超过3天自动拒绝
## 输出要求
1. TypeScript 代码实现
2. 包含完整的审批流程逻辑
3. 包含超时处理
4. 包含通知机制
5. 包含单元测试用例
6. 包含注释说明
请生成代码。3. 实施指南
快速复现步骤
如果你需要在新项目中实现类似的场景,按照以下步骤:
步骤 1:需求确认(1 小时)
- [ ] 确认预约资源类型和规则
- [ ] 确认冲突检测规则(时间重叠、时间间隔)
- [ ] 确认预约限制(次数、时长、提前时间)
- [ ] 确认审批流程(如果需要)
步骤 2:数据库设计(30 分钟)
sql
-- 资源表
CREATE TABLE resources (
id VARCHAR(50) PRIMARY KEY,
name VARCHAR(100),
type VARCHAR(50), -- 教室、会议室、设备等
capacity INT,
location VARCHAR(200),
need_approval BOOLEAN DEFAULT FALSE,
status VARCHAR(20),
created_at TIMESTAMP
);
-- 预约表
CREATE TABLE reservations (
id VARCHAR(50) PRIMARY KEY,
resource_id VARCHAR(50),
user_id VARCHAR(50),
start_time TIMESTAMP,
end_time TIMESTAMP,
purpose TEXT,
need_approval BOOLEAN,
approval_status VARCHAR(20),
check_in_time TIMESTAMP,
check_out_time TIMESTAMP,
status VARCHAR(20),
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- 审批记录表
CREATE TABLE approval_records (
id VARCHAR(50) PRIMARY KEY,
application_id VARCHAR(50), -- 申请ID(预约/物资申请)
application_type VARCHAR(50), -- 申请类型
approver_id VARCHAR(50),
approver_role VARCHAR(50),
approval_status VARCHAR(20),
approval_time TIMESTAMP,
comment TEXT,
created_at TIMESTAMP
);步骤 3:开发冲突检测(2 小时)
使用上述 Cursor Skill,快速生成冲突检测逻辑代码。
步骤 4:开发审批流程(2 小时)
使用 PROMPT 模板生成审批流程逻辑。
步骤 5:集成通知(1 小时)
- 微信公众号模板消息
- 企业微信机器人
- 短信通知(备用)
步骤 6:测试(2 小时)
- 冲突检测测试
- 预约限制测试
- 审批流程测试
- 超时处理测试
总开发时间:约 8-10 小时
关键代码片段
后端 API 示例:
typescript
// POST /api/reservations/create
router.post('/create', async (req, res) => {
const { resourceId, startTime, endTime, purpose } = req.body;
const userId = req.user.id;
// 1. 检查冲突
const conflictResult = await checkConflict(
resourceId,
new Date(startTime),
new Date(endTime)
);
if (!conflictResult.canReserve) {
return res.status(400).json({
success: false,
message: '预约时间冲突',
conflicts: conflictResult
});
}
// 2. 检查预约限制
const canReserve = await checkDailyLimit(userId, new Date(startTime));
if (!canReserve) {
return res.status(400).json({
success: false,
message: '已达到每日预约上限'
});
}
// 3. 创建预约
const reservation = await createReservation({
resourceId,
userId,
startTime: new Date(startTime),
endTime: new Date(endTime),
purpose,
needApproval: false, // 根据资源类型判断
status: 'reserved'
});
// 4. 通知用户
await notifyUser(userId, {
type: 'RESERVATION_CREATED',
reservationId: reservation.id
});
res.json({ success: true, reservation });
});下一步
查看 系统落地 → 了解这个场景在完整系统中的实现。

