搜索功能增强 + 设置页自动提现 + 部署规则
## 小程序 1. 搜索页面:添加热门章节推荐 2. 我的页面:点击ID可复制 3. 设置页面:添加自动提现开关 ## 后台 1. 新增热门章节API (/api/book/hot) 2. 章节保存时自动去掉Markdown标题 ## 规则 1. .cursorrules添加完整部署流程
This commit is contained in:
60
.cursorrules
60
.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,读取优先级:数据库 > 本地文件
|
||||
|
||||
@@ -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',
|
||||
|
||||
74
app/api/book/hot/route.ts
Normal file
74
app/api/book/hot/route.ts
Normal file
@@ -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<string, any> = {
|
||||
'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: []
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -44,8 +44,9 @@
|
||||
<text class="user-name">{{userInfo.nickname || '微信用户'}}</text>
|
||||
<text class="edit-name-icon">✎</text>
|
||||
</view>
|
||||
<view class="user-id-row">
|
||||
<view class="user-id-row" bindtap="copyUserId">
|
||||
<text class="user-id">ID: {{userIdShort}}</text>
|
||||
<text class="copy-icon">📋</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -164,6 +164,12 @@
|
||||
.user-id-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.copy-icon {
|
||||
font-size: 22rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.user-wechat {
|
||||
|
||||
@@ -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('加载热门章节失败,使用默认数据')
|
||||
}
|
||||
},
|
||||
|
||||
// 输入关键词
|
||||
|
||||
@@ -40,6 +40,27 @@
|
||||
>{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 热门章节推荐(未搜索时显示) -->
|
||||
<view class="hot-chapters" wx:if="{{!searched && hotChapters.length > 0}}">
|
||||
<text class="section-title">热门章节</text>
|
||||
<view class="chapter-list">
|
||||
<view
|
||||
class="chapter-item"
|
||||
wx:for="{{hotChapters}}"
|
||||
wx:key="id"
|
||||
bindtap="goToRead"
|
||||
data-id="{{item.id}}"
|
||||
>
|
||||
<view class="chapter-rank">{{index + 1}}</view>
|
||||
<view class="chapter-info">
|
||||
<text class="chapter-title">{{item.title}}</text>
|
||||
<text class="chapter-part">{{item.part}}</text>
|
||||
</view>
|
||||
<view class="chapter-tag {{item.tag === '免费' ? 'tag-free' : item.tag === '热门' ? 'tag-hot' : 'tag-new'}}">{{item.tag}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索结果 -->
|
||||
<view class="results-section" wx:if="{{searched}}">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -70,6 +70,36 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 自动提现设置 -->
|
||||
<view class="bind-card auto-withdraw-card" wx:if="{{isLoggedIn && (wechatId || alipayAccount)}}">
|
||||
<view class="card-header">
|
||||
<text class="card-icon">💰</text>
|
||||
<view class="card-title-wrap">
|
||||
<text class="card-title">自动提现</text>
|
||||
<text class="card-desc">收益自动打款到您的账户</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="auto-withdraw-content">
|
||||
<view class="withdraw-switch-row">
|
||||
<text class="switch-label">开启自动提现</text>
|
||||
<switch checked="{{autoWithdrawEnabled}}" bindchange="toggleAutoWithdraw" color="#00CED1"/>
|
||||
</view>
|
||||
|
||||
<view class="withdraw-info" wx:if="{{autoWithdrawEnabled}}">
|
||||
<view class="info-item">
|
||||
<text class="info-label">提现方式</text>
|
||||
<text class="info-value">{{alipayAccount ? '支付宝' : '微信零钱'}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">提现账户</text>
|
||||
<text class="info-value">{{alipayAccount || wechatId}}</text>
|
||||
</view>
|
||||
<text class="withdraw-tip">收益将在每笔订单完成后自动打款</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提现提示 -->
|
||||
<view class="tip-banner" wx:if="{{isLoggedIn && !wechatId && !alipayAccount}}">
|
||||
<text class="tip-text">提示:绑定至少一个支付方式(微信或支付宝)才能使用提现功能</text>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
Reference in New Issue
Block a user