P8 平台轻量化改造 + SSG修复 + 编程导师 + 文档完善

- Prisma: Tool 模型加 affiliateLink;免费用户沙盒 10→5 次/日
- 后端: Tools API + /admin/tools CRUD 5 端点;Practices 完整模块
- 导航: 主菜单隐藏企业版/社区(URL 可访问)
- 首页: 重定位为 AI 工具指南;新增精选工具区块;Feature 重写
- 工具页: affiliateLink 绿色推荐 Badge
- SSG 修复: config.ts 构建时直连 localhost:4000,页面 108→127
- 沙盒: 新增编程导师场景(苏格拉底教学法)
- 练习系统: Practices 多场景练习(含结构化评分)
- 技能广场: 6 个付费 Skill(标题大师/回款助手等)
- 管理后台: Models/Posts/Practices CRUD 页面
- 文档: README + progress.md 全面更新;AGENTS.md 同步定位
- 清理: .env.example 移除;tsbuildinfo gitignore
This commit is contained in:
yuzhiran-dev
2026-06-18 18:14:07 +08:00
parent fb8152401b
commit 0f215d2aad
103 changed files with 5240 additions and 778 deletions
+33 -7
View File
@@ -26,7 +26,7 @@ export class SandboxService {
const convId = conversationId || randomUUID();
// 今日配额:按 conversationId 去重计数
// 配额检查:先用每日免费配额,再用购买的额外配额
const today = new Date();
today.setHours(0, 0, 0, 0);
const existing = await this.prisma.sandboxSession.findUnique({
@@ -36,8 +36,16 @@ export class SandboxService {
const todayCount = await this.prisma.sandboxSession.count({
where: { userId, createdAt: { gte: today } },
});
if (todayCount >= (user.sandboxDaily || 10)) {
throw new HttpException('今日沙箱使用次数已用完', HttpStatus.TOO_MANY_REQUESTS);
const dailyLimit = user.sandboxDaily || 10;
if (todayCount >= dailyLimit) {
if ((user.sandboxExtra || 0) > 0) {
await this.prisma.user.update({
where: { id: userId },
data: { sandboxExtra: { decrement: 1 } },
});
} else {
throw new HttpException('今日沙箱使用次数已用完', HttpStatus.TOO_MANY_REQUESTS);
}
}
}
@@ -104,9 +112,17 @@ export class SandboxService {
const todayCount = await this.prisma.sandboxSession.count({
where: { userId, createdAt: { gte: today } },
});
if (todayCount >= (user.sandboxDaily || 10)) {
yield JSON.stringify({ type: 'error', message: '今日沙箱使用次数已用完' } as StreamResult);
return;
const dailyLimit = user.sandboxDaily || 10;
if (todayCount >= dailyLimit) {
if ((user.sandboxExtra || 0) > 0) {
await this.prisma.user.update({
where: { id: userId },
data: { sandboxExtra: { decrement: 1 } },
});
} else {
yield JSON.stringify({ type: 'error', message: '今日沙箱使用次数已用完' } as StreamResult);
return;
}
}
}
@@ -259,7 +275,17 @@ export class SandboxService {
const used = await this.prisma.sandboxSession.count({
where: { userId, createdAt: { gte: today } },
});
return { dailyLimit: user?.sandboxDaily || 10, used, remaining: (user?.sandboxDaily || 10) - used };
const dailyLimit = user?.sandboxDaily || 10;
const extra = user?.sandboxExtra || 0;
const dailyRemaining = Math.max(0, dailyLimit - used);
return {
dailyLimit,
used,
dailyRemaining,
extra,
canUse: dailyRemaining > 0 || extra > 0,
totalRemaining: dailyRemaining + extra,
};
}
async generateShareToken(userId: number, sessionId: number) {