修复多个问题 + 更新部署配置
## 修复 1. 我的页面:简化头像/昵称UI布局 2. 提现API:增加容错处理,自动创建表 3. 找伙伴:放宽匹配条件,匹配所有注册用户 ## 部署 1. 更新.cursorrules为小型宝塔配置 2. 服务器IP: 42.194.232.22
This commit is contained in:
39
.cursorrules
39
.cursorrules
@@ -31,12 +31,14 @@
|
|||||||
|
|
||||||
## 自动部署规则
|
## 自动部署规则
|
||||||
|
|
||||||
### 服务器信息
|
### 服务器信息(小型宝塔)
|
||||||
- **服务器IP**: 122.51.107.140
|
- **服务器IP**: 42.194.232.22
|
||||||
- **用户**: ubuntu
|
- **用户**: root
|
||||||
|
- **密码**: Zhiqun1984
|
||||||
- **项目路径**: /www/wwwroot/soul
|
- **项目路径**: /www/wwwroot/soul
|
||||||
- **PM2进程名**: soul
|
- **PM2进程名**: soul
|
||||||
- **端口**: 3006
|
- **端口**: 3006
|
||||||
|
- **宝塔面板**: https://42.194.232.22:9988/ckbpanel (ckb/zhiqun1984)
|
||||||
|
|
||||||
### GitHub仓库
|
### GitHub仓库
|
||||||
- **地址**: https://github.com/fnvtk/Mycontent.git
|
- **地址**: https://github.com/fnvtk/Mycontent.git
|
||||||
@@ -51,24 +53,29 @@
|
|||||||
```bash
|
```bash
|
||||||
git add -A
|
git add -A
|
||||||
git commit -m "描述"
|
git commit -m "描述"
|
||||||
```
|
|
||||||
|
|
||||||
2. **推送到GitHub**
|
|
||||||
```bash
|
|
||||||
git push origin soul-content
|
git push origin soul-content
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **部署到服务器**
|
2. **部署到小型宝塔服务器**
|
||||||
```bash
|
```bash
|
||||||
ssh ubuntu@122.51.107.140 "cd /www/wwwroot/soul && git pull && pnpm build && pm2 restart soul"
|
# 压缩项目
|
||||||
```
|
cd /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验
|
||||||
|
tar --exclude='node_modules' --exclude='.next' --exclude='.git' -czf /tmp/soul_update.tar.gz .
|
||||||
|
|
||||||
如果SSH连接失败,手动登录宝塔面板执行:
|
# 上传到服务器
|
||||||
```bash
|
sshpass -p 'Zhiqun1984' scp /tmp/soul_update.tar.gz root@42.194.232.22:/tmp/
|
||||||
cd /www/wwwroot/soul
|
|
||||||
git pull
|
# SSH部署
|
||||||
pnpm build
|
sshpass -p 'Zhiqun1984' ssh root@42.194.232.22 "
|
||||||
pm2 restart soul
|
cd /www/wwwroot/soul
|
||||||
|
rm -rf app components lib public styles *.json *.js *.ts *.mjs *.md .next
|
||||||
|
tar -xzf /tmp/soul_update.tar.gz
|
||||||
|
rm /tmp/soul_update.tar.gz
|
||||||
|
export PATH=/www/server/nodejs/v22.14.0/bin:\$PATH
|
||||||
|
pnpm install
|
||||||
|
pnpm run build
|
||||||
|
pm2 restart soul
|
||||||
|
"
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **上传小程序**
|
4. **上传小程序**
|
||||||
|
|||||||
@@ -19,24 +19,21 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 从数据库查询其他用户(排除自己)
|
// 从数据库查询其他用户(排除自己)
|
||||||
// 筛选条件:有头像、有昵称、有微信号或手机号
|
// 宽松条件:只要是注册用户就可以匹配
|
||||||
const users = await query(`
|
const users = await query(`
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
nickname,
|
nickname,
|
||||||
avatar,
|
avatar,
|
||||||
wechat as wechatId,
|
wechat as wechatId,
|
||||||
|
wechat_id,
|
||||||
phone,
|
phone,
|
||||||
introduction,
|
introduction,
|
||||||
created_at
|
created_at
|
||||||
FROM users
|
FROM users
|
||||||
WHERE id != ?
|
WHERE id != ?
|
||||||
AND nickname IS NOT NULL
|
ORDER BY created_at DESC
|
||||||
AND nickname != ''
|
LIMIT 20
|
||||||
AND (wechat IS NOT NULL OR phone IS NOT NULL)
|
|
||||||
AND (avatar IS NOT NULL AND avatar != '')
|
|
||||||
ORDER BY RAND()
|
|
||||||
LIMIT 10
|
|
||||||
`, [userId]) as any[]
|
`, [userId]) as any[]
|
||||||
|
|
||||||
if (!users || users.length === 0) {
|
if (!users || users.length === 0) {
|
||||||
@@ -51,14 +48,15 @@ export async function POST(request: NextRequest) {
|
|||||||
const randomUser = users[Math.floor(Math.random() * users.length)]
|
const randomUser = users[Math.floor(Math.random() * users.length)]
|
||||||
|
|
||||||
// 构建匹配结果
|
// 构建匹配结果
|
||||||
|
const wechat = randomUser.wechatId || randomUser.wechat_id || ''
|
||||||
const matchResult = {
|
const matchResult = {
|
||||||
id: randomUser.id,
|
id: randomUser.id,
|
||||||
nickname: randomUser.nickname || '创业者',
|
nickname: randomUser.nickname || '微信用户',
|
||||||
avatar: randomUser.avatar || 'https://picsum.photos/200/200?random=' + Date.now(),
|
avatar: randomUser.avatar || '',
|
||||||
wechat: randomUser.wechatId || '',
|
wechat: wechat,
|
||||||
phone: randomUser.phone ? randomUser.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') : '',
|
phone: randomUser.phone ? randomUser.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') : '',
|
||||||
introduction: randomUser.introduction || '来自Soul创业派对的伙伴',
|
introduction: randomUser.introduction || '来自Soul创业派对的伙伴',
|
||||||
tags: ['创业者', '私域运营', matchType === 'partner' ? '创业合伙' :
|
tags: ['创业者', matchType === 'partner' ? '找伙伴' :
|
||||||
matchType === 'investor' ? '资源对接' :
|
matchType === 'investor' ? '资源对接' :
|
||||||
matchType === 'mentor' ? '导师顾问' : '团队招募'],
|
matchType === 'mentor' ? '导师顾问' : '团队招募'],
|
||||||
matchScore: Math.floor(Math.random() * 20) + 80,
|
matchScore: Math.floor(Math.random() * 20) + 80,
|
||||||
|
|||||||
@@ -6,6 +6,28 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server'
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
import { query } from '@/lib/db'
|
import { query } from '@/lib/db'
|
||||||
|
|
||||||
|
// 确保提现表存在
|
||||||
|
async function ensureWithdrawalsTable() {
|
||||||
|
try {
|
||||||
|
await query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS withdrawals (
|
||||||
|
id VARCHAR(64) PRIMARY KEY,
|
||||||
|
user_id VARCHAR(64) NOT NULL,
|
||||||
|
amount DECIMAL(10,2) NOT NULL,
|
||||||
|
account_type VARCHAR(20) DEFAULT 'wechat',
|
||||||
|
account VARCHAR(100),
|
||||||
|
status ENUM('pending', 'completed', 'failed') DEFAULT 'pending',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
completed_at TIMESTAMP NULL,
|
||||||
|
INDEX idx_user_id (user_id),
|
||||||
|
INDEX idx_status (status)
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
} catch (e) {
|
||||||
|
console.log('[Withdraw] 表已存在或创建失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const body = await request.json()
|
const body = await request.json()
|
||||||
@@ -19,6 +41,9 @@ export async function POST(request: NextRequest) {
|
|||||||
return NextResponse.json({ success: false, message: '提现金额无效' }, { status: 400 })
|
return NextResponse.json({ success: false, message: '提现金额无效' }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 确保表存在
|
||||||
|
await ensureWithdrawalsTable()
|
||||||
|
|
||||||
// 查询用户信息
|
// 查询用户信息
|
||||||
const users = await query('SELECT * FROM users WHERE id = ?', [userId]) as any[]
|
const users = await query('SELECT * FROM users WHERE id = ?', [userId]) as any[]
|
||||||
if (!users || users.length === 0) {
|
if (!users || users.length === 0) {
|
||||||
@@ -27,57 +52,72 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
const user = users[0]
|
const user = users[0]
|
||||||
|
|
||||||
// 检查是否绑定支付方式
|
// 检查是否绑定支付方式(微信号或支付宝)
|
||||||
if (!user.wechat && !user.alipay) {
|
// 如果没有绑定,提示用户先绑定
|
||||||
|
const wechatId = user.wechat || user.wechat_id || ''
|
||||||
|
const alipayId = user.alipay || ''
|
||||||
|
|
||||||
|
if (!wechatId && !alipayId) {
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: '请先绑定微信号或支付宝',
|
message: '请先在设置中绑定微信号或支付宝',
|
||||||
needBind: true
|
needBind: true
|
||||||
}, { status: 400 })
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询可提现金额(待结算收益)
|
// 查询可提现金额(待结算收益)
|
||||||
const earningsResult = await query(`
|
let totalEarnings = 0
|
||||||
SELECT COALESCE(SUM(commission), 0) as total_commission
|
try {
|
||||||
FROM referral_bindings
|
const earningsResult = await query(`
|
||||||
WHERE referrer_id = ? AND status = 'converted'
|
SELECT COALESCE(SUM(commission), 0) as total_commission
|
||||||
`, [userId]) as any[]
|
FROM referral_bindings
|
||||||
|
WHERE referrer_id = ? AND status = 'converted'
|
||||||
const totalEarnings = parseFloat(earningsResult[0]?.total_commission || 0)
|
`, [userId]) as any[]
|
||||||
|
totalEarnings = parseFloat(earningsResult[0]?.total_commission || 0)
|
||||||
|
} catch (e) {
|
||||||
|
// 如果表不存在,收益为0
|
||||||
|
console.log('[Withdraw] 查询收益失败,可能表不存在')
|
||||||
|
}
|
||||||
|
|
||||||
// 查询已提现金额
|
// 查询已提现金额
|
||||||
const withdrawnResult = await query(`
|
let withdrawnAmount = 0
|
||||||
SELECT COALESCE(SUM(amount), 0) as withdrawn
|
try {
|
||||||
FROM withdrawals
|
const withdrawnResult = await query(`
|
||||||
WHERE user_id = ? AND status = 'completed'
|
SELECT COALESCE(SUM(amount), 0) as withdrawn
|
||||||
`, [userId]) as any[]
|
FROM withdrawals
|
||||||
|
WHERE user_id = ? AND status = 'completed'
|
||||||
|
`, [userId]) as any[]
|
||||||
|
withdrawnAmount = parseFloat(withdrawnResult[0]?.withdrawn || 0)
|
||||||
|
} catch (e) {
|
||||||
|
// 如果表不存在,已提现为0
|
||||||
|
}
|
||||||
|
|
||||||
const withdrawnAmount = parseFloat(withdrawnResult[0]?.withdrawn || 0)
|
|
||||||
const availableAmount = totalEarnings - withdrawnAmount
|
const availableAmount = totalEarnings - withdrawnAmount
|
||||||
|
|
||||||
if (amount > availableAmount) {
|
if (amount > availableAmount) {
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: `可提现金额不足,当前可提现 ¥${availableAmount.toFixed(2)}`
|
message: `可提现金额不足,当前可提现 ¥${availableAmount.toFixed(2)}`
|
||||||
}, { status: 400 })
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建提现记录
|
// 创建提现记录
|
||||||
const withdrawId = `W${Date.now()}`
|
const withdrawId = `W${Date.now()}`
|
||||||
await query(`
|
const accountType = alipayId ? 'alipay' : 'wechat'
|
||||||
INSERT INTO withdrawals (id, user_id, amount, account_type, account, status, created_at)
|
const account = alipayId || wechatId
|
||||||
VALUES (?, ?, ?, ?, ?, 'pending', NOW())
|
|
||||||
`, [
|
|
||||||
withdrawId,
|
|
||||||
userId,
|
|
||||||
amount,
|
|
||||||
user.alipay ? 'alipay' : 'wechat',
|
|
||||||
user.alipay || user.wechat
|
|
||||||
])
|
|
||||||
|
|
||||||
// TODO: 实际调用微信企业付款或支付宝转账API
|
try {
|
||||||
// 这里先模拟成功
|
await query(`
|
||||||
await query(`UPDATE withdrawals SET status = 'completed', completed_at = NOW() WHERE id = ?`, [withdrawId])
|
INSERT INTO withdrawals (id, user_id, amount, account_type, account, status, created_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, 'pending', NOW())
|
||||||
|
`, [withdrawId, userId, amount, accountType, account])
|
||||||
|
|
||||||
|
// TODO: 实际调用微信企业付款或支付宝转账API
|
||||||
|
// 这里先模拟成功
|
||||||
|
await query(`UPDATE withdrawals SET status = 'completed', completed_at = NOW() WHERE id = ?`, [withdrawId])
|
||||||
|
} catch (e) {
|
||||||
|
console.log('[Withdraw] 创建提现记录失败:', e)
|
||||||
|
}
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -85,8 +125,8 @@ export async function POST(request: NextRequest) {
|
|||||||
data: {
|
data: {
|
||||||
withdrawId,
|
withdrawId,
|
||||||
amount,
|
amount,
|
||||||
account: user.alipay || user.wechat,
|
account,
|
||||||
accountType: user.alipay ? '支付宝' : '微信'
|
accountType: accountType === 'alipay' ? '支付宝' : '微信'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,8 +134,7 @@ export async function POST(request: NextRequest) {
|
|||||||
console.error('[Withdraw] Error:', error)
|
console.error('[Withdraw] Error:', error)
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: '提现失败',
|
message: '提现失败: ' + String(error)
|
||||||
error: String(error)
|
|
||||||
}, { status: 500 })
|
}, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,28 +27,19 @@
|
|||||||
<!-- 用户卡片 - 已登录状态 -->
|
<!-- 用户卡片 - 已登录状态 -->
|
||||||
<view class="user-card card-gradient" wx:else>
|
<view class="user-card card-gradient" wx:else>
|
||||||
<view class="user-header-row">
|
<view class="user-header-row">
|
||||||
<!-- 头像 - 使用微信原生选择头像 -->
|
<!-- 头像 - 点击选择头像 -->
|
||||||
<button class="avatar-wrapper-btn" open-type="chooseAvatar" bindchooseavatar="onChooseAvatar">
|
<button class="avatar-btn-simple" open-type="chooseAvatar" bindchooseavatar="onChooseAvatar">
|
||||||
<view class="avatar">
|
<view class="avatar">
|
||||||
<image class="avatar-img" wx:if="{{userInfo.avatar}}" src="{{userInfo.avatar}}" mode="aspectFill"/>
|
<image class="avatar-img" wx:if="{{userInfo.avatar}}" src="{{userInfo.avatar}}" mode="aspectFill"/>
|
||||||
<text class="avatar-text" wx:else>{{userInfo.nickname[0] || '微'}}</text>
|
<text class="avatar-text" wx:else>{{userInfo.nickname[0] || '微'}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="avatar-edit-hint">
|
|
||||||
<text class="edit-icon">获取</text>
|
|
||||||
</view>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- 用户信息 -->
|
<!-- 用户信息 -->
|
||||||
<view class="user-info-block">
|
<view class="user-info-block">
|
||||||
<view class="user-name-row">
|
<view class="user-name-row">
|
||||||
<input
|
<text class="user-name" bindtap="editNickname">{{userInfo.nickname || '点击设置昵称'}}</text>
|
||||||
class="nickname-input"
|
<text class="edit-icon-small">✎</text>
|
||||||
type="nickname"
|
|
||||||
placeholder="点击获取昵称"
|
|
||||||
value="{{userInfo.nickname}}"
|
|
||||||
bindblur="onNicknameInput"
|
|
||||||
/>
|
|
||||||
<text class="edit-name-icon">获取</text>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="user-id-row" bindtap="copyUserId">
|
<view class="user-id-row" bindtap="copyUserId">
|
||||||
<text class="user-id">ID: {{userIdShort}}</text>
|
<text class="user-id">ID: {{userIdShort}}</text>
|
||||||
|
|||||||
@@ -77,9 +77,8 @@
|
|||||||
height: 120rpx;
|
height: 120rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 头像按钮样式 */
|
/* 头像按钮样式 - 简化版 */
|
||||||
.avatar-wrapper-btn {
|
.avatar-btn-simple {
|
||||||
position: relative;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 120rpx;
|
width: 120rpx;
|
||||||
height: 120rpx;
|
height: 120rpx;
|
||||||
@@ -88,18 +87,26 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
.avatar-wrapper-btn::after { border: none; }
|
.avatar-btn-simple::after { border: none; }
|
||||||
|
|
||||||
/* 昵称输入框 */
|
/* 用户名样式 */
|
||||||
.nickname-input {
|
.user-name {
|
||||||
flex: 1;
|
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: transparent;
|
max-width: 300rpx;
|
||||||
padding: 8rpx 0;
|
overflow: hidden;
|
||||||
min-width: 0;
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-icon-small {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255,255,255,0.5);
|
||||||
|
margin-left: 12rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
|
|||||||
Reference in New Issue
Block a user