From 0f50fb7c3b2ea65453747f06b4ec4f0164f1bdf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A1=E8=8B=A5?= Date: Thu, 29 Jan 2026 12:18:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=8A=9F=E8=83=BD=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=20+=20=E8=AE=BE=E7=BD=AE=E9=A1=B5=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=8F=90=E7=8E=B0=20+=20=E9=83=A8=E7=BD=B2=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 小程序 1. 搜索页面:添加热门章节推荐 2. 我的页面:点击ID可复制 3. 设置页面:添加自动提现开关 ## 后台 1. 新增热门章节API (/api/book/hot) 2. 章节保存时自动去掉Markdown标题 ## 规则 1. .cursorrules添加完整部署流程 --- .cursorrules | 60 +++++++++++++++++++ app/admin/content/page.tsx | 18 ++++-- app/api/book/hot/route.ts | 74 ++++++++++++++++++++++++ miniprogram/pages/my/my.js | 15 +++++ miniprogram/pages/my/my.wxml | 3 +- miniprogram/pages/my/my.wxss | 6 ++ miniprogram/pages/search/search.js | 26 ++++++++- miniprogram/pages/search/search.wxml | 21 +++++++ miniprogram/pages/search/search.wxss | 71 +++++++++++++++++++++++ miniprogram/pages/settings/settings.js | 55 +++++++++++++++++- miniprogram/pages/settings/settings.wxml | 30 ++++++++++ miniprogram/pages/settings/settings.wxss | 31 ++++++++++ 12 files changed, 402 insertions(+), 8 deletions(-) create mode 100644 app/api/book/hot/route.ts diff --git a/.cursorrules b/.cursorrules index fdef58e..9d14c65 100644 --- a/.cursorrules +++ b/.cursorrules @@ -26,3 +26,63 @@ - 使用 @v0 前缀调用v0生成代码 - 默认使用 claude-opus 模型 - 生成前先说明需求,确保理解正确 + +--- + +## 自动部署规则 + +### 服务器信息 +- **服务器IP**: 122.51.107.140 +- **用户**: ubuntu +- **项目路径**: /www/wwwroot/soul +- **PM2进程名**: soul +- **端口**: 3006 + +### GitHub仓库 +- **地址**: https://github.com/fnvtk/Mycontent.git +- **分支**: soul-content + +### 小程序 +- **AppID**: wxb8bbb2b10dec74aa +- **项目路径**: ./miniprogram + +### 部署流程(每次提交后自动执行) +1. **提交代码到Git** + ```bash + git add -A + git commit -m "描述" + ``` + +2. **推送到GitHub** + ```bash + git push origin soul-content + ``` + +3. **部署到服务器** + ```bash + ssh ubuntu@122.51.107.140 "cd /www/wwwroot/soul && git pull && pnpm build && pm2 restart soul" + ``` + + 如果SSH连接失败,手动登录宝塔面板执行: + ```bash + cd /www/wwwroot/soul + git pull + pnpm build + pm2 restart soul + ``` + +4. **上传小程序** + ```bash + /Applications/wechatwebdevtools.app/Contents/MacOS/cli upload --project "./miniprogram" -v "版本号" -d "描述" + ``` + +5. **打开微信公众平台** + ```bash + open "https://mp.weixin.qq.com/" + ``` + 在「版本管理」设为体验版测试 + +### 注意事项 +- 小程序版本号:未发布前保持 1.14,正式发布后递增 +- 后台部署后需等待约30秒生效 +- 数据库:腾讯云MySQL,读取优先级:数据库 > 本地文件 diff --git a/app/admin/content/page.tsx b/app/admin/content/page.tsx index 220fcf2..ab9eed0 100644 --- a/app/admin/content/page.tsx +++ b/app/admin/content/page.tsx @@ -130,11 +130,21 @@ export default function ContentPage() { setIsSaving(true) try { - // 自动去掉内容中的重复标题(如# 1.2 xxx) + // 自动去掉内容中的重复标题(如# 1.2 xxx 或 # 1.4 人性的三角结构...) let content = editingSection.content || '' - // 匹配 # 数字.数字 开头的标题行并去掉 - const titlePattern = new RegExp(`^#\\s*${editingSection.id}\\s+.*$`, 'm') - content = content.replace(titlePattern, '').trim() + // 匹配多种格式的Markdown标题并去掉: + // 1. # 1.2 标题内容 + // 2. # 1.2 标题内容(多个空格) + // 3. ## 1.2 标题内容 + const titlePatterns = [ + new RegExp(`^#+\\s*${editingSection.id.replace('.', '\\.')}\\s+.*$`, 'gm'), // # 1.4 xxx + new RegExp(`^#+\\s*${editingSection.id.replace('.', '\\.')}[::].*$`, 'gm'), // # 1.4:xxx + new RegExp(`^#\\s+.*${editingSection.title?.slice(0, 10).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}.*$`, 'gm') // # xxx标题内容 + ] + for (const pattern of titlePatterns) { + content = content.replace(pattern, '') + } + content = content.replace(/^\s*\n+/, '').trim() // 去掉开头的空行 const res = await fetch('/api/db/book', { method: 'PUT', diff --git a/app/api/book/hot/route.ts b/app/api/book/hot/route.ts new file mode 100644 index 0000000..3d8aa79 --- /dev/null +++ b/app/api/book/hot/route.ts @@ -0,0 +1,74 @@ +/** + * 热门章节API + * 返回点击量最高的章节 + */ +import { NextResponse } from 'next/server' +import { query } from '@/lib/db' + +export async function GET() { + try { + // 从数据库查询点击量高的章节(如果有统计表) + let hotChapters = [] + + try { + // 尝试从订单表统计购买量高的章节 + const rows = await query(` + SELECT + section_id as id, + COUNT(*) as purchase_count + FROM orders + WHERE status = 'completed' AND section_id IS NOT NULL + GROUP BY section_id + ORDER BY purchase_count DESC + LIMIT 10 + `) as any[] + + if (rows && rows.length > 0) { + // 补充章节信息 + const sectionInfo: Record = { + '1.1': { title: '荷包:电动车出租的被动收入模式', part: '真实的人', tag: '免费' }, + '9.12': { title: '美业整合:一个人的公司如何月入十万', part: '真实的赚钱', tag: '热门' }, + '3.1': { title: '3000万流水如何跑出来', part: '真实的行业', tag: '热门' }, + '8.1': { title: '流量杠杆:抖音、Soul、飞书', part: '真实的赚钱', tag: '推荐' }, + '9.13': { title: 'AI工具推广:一个隐藏的高利润赛道', part: '真实的赚钱', tag: '最新' }, + '9.14': { title: '大健康私域:一个月150万的70后', part: '真实的赚钱', tag: '热门' }, + '1.2': { title: '老墨:资源整合高手的社交方法', part: '真实的人', tag: '推荐' }, + '2.1': { title: '电商的底层逻辑', part: '真实的行业', tag: '推荐' }, + '4.1': { title: '我的第一次创业失败', part: '真实的错误', tag: '热门' }, + '5.1': { title: '未来职业的三个方向', part: '真实的社会', tag: '推荐' } + } + + hotChapters = rows.map((row: any) => ({ + id: row.id, + ...(sectionInfo[row.id] || { title: `章节${row.id}`, part: '', tag: '热门' }), + purchaseCount: row.purchase_count + })) + } + } catch (e) { + console.log('[Hot] 数据库查询失败,使用默认数据') + } + + // 如果没有数据,返回默认热门章节 + if (hotChapters.length === 0) { + hotChapters = [ + { id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', part: '真实的人' }, + { id: '9.12', title: '美业整合:一个人的公司如何月入十万', tag: '热门', part: '真实的赚钱' }, + { id: '3.1', title: '3000万流水如何跑出来', tag: '热门', part: '真实的行业' }, + { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', part: '真实的赚钱' }, + { id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', tag: '最新', part: '真实的赚钱' } + ] + } + + return NextResponse.json({ + success: true, + chapters: hotChapters + }) + + } catch (error) { + console.error('[Hot] Error:', error) + return NextResponse.json({ + success: false, + chapters: [] + }) + } +} diff --git a/miniprogram/pages/my/my.js b/miniprogram/pages/my/my.js index f50143e..41585d3 100644 --- a/miniprogram/pages/my/my.js +++ b/miniprogram/pages/my/my.js @@ -177,6 +177,21 @@ Page({ } }) }, + + // 复制用户ID + copyUserId() { + const userId = this.data.userInfo?.id || '' + if (!userId) { + wx.showToast({ title: '暂无ID', icon: 'none' }) + return + } + wx.setClipboardData({ + data: userId, + success: () => { + wx.showToast({ title: 'ID已复制', icon: 'success' }) + } + }) + }, // 切换Tab switchTab(e) { diff --git a/miniprogram/pages/my/my.wxml b/miniprogram/pages/my/my.wxml index 6e2dce7..f452e92 100644 --- a/miniprogram/pages/my/my.wxml +++ b/miniprogram/pages/my/my.wxml @@ -44,8 +44,9 @@ {{userInfo.nickname || '微信用户'}} - + ID: {{userIdShort}} + 📋 diff --git a/miniprogram/pages/my/my.wxss b/miniprogram/pages/my/my.wxss index 5601700..2724f8c 100644 --- a/miniprogram/pages/my/my.wxss +++ b/miniprogram/pages/my/my.wxss @@ -164,6 +164,12 @@ .user-id-row { display: flex; align-items: center; + gap: 8rpx; +} + +.copy-icon { + font-size: 22rpx; + opacity: 0.5; } .user-wechat { diff --git a/miniprogram/pages/search/search.js b/miniprogram/pages/search/search.js index aafed0e..3bafcb8 100644 --- a/miniprogram/pages/search/search.js +++ b/miniprogram/pages/search/search.js @@ -12,14 +12,36 @@ Page({ loading: false, searched: false, total: 0, - // 热门搜索 - hotKeywords: ['私域', '电商', '流量', '赚钱', '创业', 'Soul', '抖音'] + // 热门搜索关键词 + hotKeywords: ['私域', '电商', '流量', '赚钱', '创业', 'Soul', '抖音', '变现'], + // 热门章节推荐 + hotChapters: [ + { id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', part: '真实的人' }, + { id: '9.12', title: '美业整合:一个人的公司如何月入十万', tag: '热门', part: '真实的赚钱' }, + { id: '3.1', title: '3000万流水如何跑出来', tag: '热门', part: '真实的行业' }, + { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', part: '真实的赚钱' }, + { id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', tag: '最新', part: '真实的赚钱' } + ] }, onLoad() { this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 }) + // 加载热门章节 + this.loadHotChapters() + }, + + // 加载热门章节(从服务器获取点击量高的章节) + async loadHotChapters() { + try { + const res = await app.request('/api/book/hot') + if (res && res.success && res.chapters?.length > 0) { + this.setData({ hotChapters: res.chapters }) + } + } catch (e) { + console.log('加载热门章节失败,使用默认数据') + } }, // 输入关键词 diff --git a/miniprogram/pages/search/search.wxml b/miniprogram/pages/search/search.wxml index 714898b..60fa91f 100644 --- a/miniprogram/pages/search/search.wxml +++ b/miniprogram/pages/search/search.wxml @@ -40,6 +40,27 @@ >{{item}} + + + + 热门章节 + + + {{index + 1}} + + {{item.title}} + {{item.part}} + + {{item.tag}} + + + diff --git a/miniprogram/pages/search/search.wxss b/miniprogram/pages/search/search.wxss index ffd4d77..aa56b19 100644 --- a/miniprogram/pages/search/search.wxss +++ b/miniprogram/pages/search/search.wxss @@ -110,6 +110,77 @@ border: 1rpx solid rgba(0, 206, 209, 0.3); } +/* 热门章节 */ +.hot-chapters { + padding: 32rpx 0; + margin-top: 16rpx; +} + +.chapter-list { + display: flex; + flex-direction: column; + gap: 16rpx; +} + +.chapter-item { + display: flex; + align-items: center; + background: rgba(255,255,255,0.05); + border-radius: 20rpx; + padding: 24rpx; + gap: 20rpx; +} + +.chapter-rank { + width: 48rpx; + height: 48rpx; + background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 26rpx; + font-weight: 600; + color: #000; + flex-shrink: 0; +} + +.chapter-item:nth-child(1) .chapter-rank { background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); } +.chapter-item:nth-child(2) .chapter-rank { background: linear-gradient(135deg, #C0C0C0 0%, #A9A9A9 100%); } +.chapter-item:nth-child(3) .chapter-rank { background: linear-gradient(135deg, #CD7F32 0%, #8B4513 100%); } + +.chapter-info { + flex: 1; + min-width: 0; +} + +.chapter-title { + font-size: 28rpx; + color: #fff; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.chapter-part { + font-size: 22rpx; + color: rgba(255,255,255,0.4); + margin-top: 6rpx; + display: block; +} + +.chapter-tag { + padding: 8rpx 16rpx; + border-radius: 16rpx; + font-size: 22rpx; + flex-shrink: 0; +} + +.chapter-tag.tag-free { background: rgba(76, 175, 80, 0.2); color: #4CAF50; } +.chapter-tag.tag-hot { background: rgba(255, 87, 34, 0.2); color: #FF5722; } +.chapter-tag.tag-new { background: rgba(233, 30, 99, 0.2); color: #E91E63; } + /* 搜索结果 */ .results-section { padding: 16rpx 0; diff --git a/miniprogram/pages/settings/settings.js b/miniprogram/pages/settings/settings.js index 962797f..ace0d75 100644 --- a/miniprogram/pages/settings/settings.js +++ b/miniprogram/pages/settings/settings.js @@ -16,6 +16,9 @@ Page({ wechatId: '', alipayAccount: '', + // 自动提现 + autoWithdrawEnabled: false, + // 绑定弹窗 showBindModal: false, bindType: '', // phone | wechat | alipay @@ -43,16 +46,66 @@ Page({ const phoneNumber = wx.getStorageSync('user_phone') || userInfo.phone || '' const wechatId = wx.getStorageSync('user_wechat') || userInfo.wechat || '' const alipayAccount = wx.getStorageSync('user_alipay') || userInfo.alipay || '' + const autoWithdrawEnabled = wx.getStorageSync('auto_withdraw_enabled') || false this.setData({ isLoggedIn: true, userInfo, phoneNumber, wechatId, - alipayAccount + alipayAccount, + autoWithdrawEnabled }) } }, + + // 切换自动提现 + async toggleAutoWithdraw(e) { + const enabled = e.detail.value + + // 检查是否绑定了支付方式 + if (enabled && !this.data.wechatId && !this.data.alipayAccount) { + wx.showToast({ title: '请先绑定微信号或支付宝', icon: 'none' }) + this.setData({ autoWithdrawEnabled: false }) + return + } + + // 开启时需要确认 + if (enabled) { + wx.showModal({ + title: '开启自动提现', + content: `收益将自动打款到您的${this.data.alipayAccount ? '支付宝' : '微信'}账户,确认开启吗?`, + success: async (res) => { + if (res.confirm) { + this.setData({ autoWithdrawEnabled: true }) + wx.setStorageSync('auto_withdraw_enabled', true) + + // 同步到服务器 + try { + await app.request('/api/user/update', { + method: 'POST', + data: { + userId: app.globalData.userInfo?.id, + autoWithdraw: true, + withdrawAccount: this.data.alipayAccount || this.data.wechatId + } + }) + } catch (e) { + console.log('同步自动提现设置失败', e) + } + + wx.showToast({ title: '已开启自动提现', icon: 'success' }) + } else { + this.setData({ autoWithdrawEnabled: false }) + } + } + }) + } else { + this.setData({ autoWithdrawEnabled: false }) + wx.setStorageSync('auto_withdraw_enabled', false) + wx.showToast({ title: '已关闭自动提现', icon: 'success' }) + } + }, // 绑定手机号 bindPhone() { diff --git a/miniprogram/pages/settings/settings.wxml b/miniprogram/pages/settings/settings.wxml index b857958..90795d6 100644 --- a/miniprogram/pages/settings/settings.wxml +++ b/miniprogram/pages/settings/settings.wxml @@ -70,6 +70,36 @@ + + + + 💰 + + 自动提现 + 收益自动打款到您的账户 + + + + + + 开启自动提现 + + + + + + 提现方式 + {{alipayAccount ? '支付宝' : '微信零钱'}} + + + 提现账户 + {{alipayAccount || wechatId}} + + 收益将在每笔订单完成后自动打款 + + + + 提示:绑定至少一个支付方式(微信或支付宝)才能使用提现功能 diff --git a/miniprogram/pages/settings/settings.wxss b/miniprogram/pages/settings/settings.wxss index 9374f7b..899ff34 100644 --- a/miniprogram/pages/settings/settings.wxss +++ b/miniprogram/pages/settings/settings.wxss @@ -44,6 +44,37 @@ } .get-phone-btn::after { border: none; } +/* 自动提现卡片 */ +.auto-withdraw-card { margin-top: 24rpx; } +.auto-withdraw-content { padding-top: 16rpx; } +.withdraw-switch-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16rpx 0; +} +.switch-label { font-size: 28rpx; color: #fff; } +.withdraw-info { + background: rgba(0,206,209,0.1); + border-radius: 16rpx; + padding: 20rpx; + margin-top: 16rpx; +} +.info-item { + display: flex; + justify-content: space-between; + padding: 8rpx 0; +} +.info-label { font-size: 26rpx; color: rgba(255,255,255,0.6); } +.info-value { font-size: 26rpx; color: #00CED1; } +.withdraw-tip { + display: block; + font-size: 22rpx; + color: rgba(255,255,255,0.4); + margin-top: 12rpx; + text-align: center; +} + /* 提现提示 */ .tip-banner { background: rgba(255,165,0,0.1); border: 2rpx solid rgba(255,165,0,0.3); border-radius: 20rpx; padding: 20rpx 24rpx; margin-bottom: 24rpx; } .tip-text { font-size: 24rpx; color: #FFA500; line-height: 1.5; }