+
setEditingSection({ ...editingSection, price: Number(e.target.value) })}
+ value={editingSection.isFree ? 0 : editingSection.price}
+ onChange={(e) => setEditingSection({ ...editingSection, price: Number(e.target.value), isFree: Number(e.target.value) === 0 })}
+ disabled={editingSection.isFree}
/>
+
+
+
+
+
+
diff --git a/app/api/db/users/referrals/route.ts b/app/api/db/users/referrals/route.ts
index 59364c6..7731441 100644
--- a/app/api/db/users/referrals/route.ts
+++ b/app/api/db/users/referrals/route.ts
@@ -1,6 +1,8 @@
/**
* 用户绑定关系API
* 获取指定用户的所有绑定用户列表
+ *
+ * 优先从referral_bindings表查询,同时兼容users表的referred_by字段
*/
import { NextResponse } from 'next/server'
import { query } from '@/lib/db'
@@ -31,55 +33,87 @@ export async function GET(request: Request) {
code = userRows[0].referral_code
}
- if (!code) {
- return NextResponse.json({
- success: true,
- referrals: [],
- stats: {
- total: 0,
- purchased: 0,
- pendingEarnings: 0,
- totalEarnings: 0
- }
- })
+ let referrals: any[] = []
+
+ // 1. 首先从referral_bindings表查询绑定关系
+ try {
+ const bindingsReferrals = await query(`
+ SELECT
+ rb.id as binding_id,
+ rb.referee_id,
+ rb.status as binding_status,
+ rb.binding_date,
+ rb.expiry_date,
+ rb.commission_amount,
+ u.id, u.nickname, u.avatar, u.phone, u.open_id,
+ u.has_full_book, u.purchased_sections,
+ u.created_at, u.updated_at,
+ DATEDIFF(rb.expiry_date, NOW()) as days_remaining
+ FROM referral_bindings rb
+ JOIN users u ON rb.referee_id = u.id
+ WHERE rb.referrer_id = ?
+ ORDER BY rb.binding_date DESC
+ `, [userId]) as any[]
+
+ if (bindingsReferrals.length > 0) {
+ referrals = bindingsReferrals
+ }
+ } catch (e) {
+ console.log('[Referrals] referral_bindings表查询失败,使用users表')
}
- // 查询通过该推广码绑定的所有用户
- const referrals = await query(`
- SELECT
- id, nickname, avatar, phone, open_id,
- has_full_book, purchased_sections,
- created_at, updated_at
- FROM users
- WHERE referred_by = ?
- ORDER BY created_at DESC
- `, [code]) as any[]
+ // 2. 如果referral_bindings表没有数据,再从users表查询
+ if (referrals.length === 0 && code) {
+ referrals = await query(`
+ SELECT
+ id, nickname, avatar, phone, open_id,
+ has_full_book, purchased_sections,
+ created_at, updated_at,
+ NULL as binding_status,
+ NULL as binding_date,
+ NULL as expiry_date,
+ NULL as days_remaining,
+ NULL as commission_amount
+ FROM users
+ WHERE referred_by = ?
+ ORDER BY created_at DESC
+ `, [code]) as any[]
+ }
// 统计信息
- const purchasedCount = referrals.filter(r => r.has_full_book || (r.purchased_sections && r.purchased_sections !== '[]')).length
+ const purchasedCount = referrals.filter(r =>
+ r.has_full_book ||
+ r.binding_status === 'converted' ||
+ (r.purchased_sections && r.purchased_sections !== '[]')
+ ).length
// 查询该用户的收益信息
const earningsRows = await query(`
SELECT earnings, pending_earnings, withdrawn_earnings
- FROM users WHERE ${userId ? 'id = ?' : 'referral_code = ?'}
- `, [userId || code]) as any[]
+ FROM users WHERE id = ?
+ `, [userId]) as any[]
const earnings = earningsRows[0] || { earnings: 0, pending_earnings: 0, withdrawn_earnings: 0 }
// 格式化返回数据
const formattedReferrals = referrals.map(r => ({
- id: r.id,
+ id: r.referee_id || r.id,
nickname: r.nickname || '微信用户',
avatar: r.avatar,
phone: r.phone ? r.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') : null,
hasOpenId: !!r.open_id,
- hasPurchased: r.has_full_book || (r.purchased_sections && r.purchased_sections !== '[]'),
+ hasPurchased: r.has_full_book || r.binding_status === 'converted' || (r.purchased_sections && r.purchased_sections !== '[]'),
hasFullBook: !!r.has_full_book,
purchasedSections: typeof r.purchased_sections === 'string'
? JSON.parse(r.purchased_sections || '[]').length
: 0,
- createdAt: r.created_at,
- status: r.has_full_book ? 'vip' : (r.purchased_sections && r.purchased_sections !== '[]' ? 'paid' : 'free')
+ createdAt: r.binding_date || r.created_at,
+ bindingStatus: r.binding_status || 'active',
+ daysRemaining: r.days_remaining,
+ commission: parseFloat(r.commission_amount) || 0,
+ status: r.binding_status === 'converted' ? 'converted'
+ : r.has_full_book ? 'vip'
+ : (r.purchased_sections && r.purchased_sections !== '[]' ? 'paid' : 'active')
}))
return NextResponse.json({
diff --git a/miniprogram/pages/my/my.js b/miniprogram/pages/my/my.js
index b32adfd..a78eeaf 100644
--- a/miniprogram/pages/my/my.js
+++ b/miniprogram/pages/my/my.js
@@ -102,138 +102,41 @@ Page({
}
},
- // 编辑用户资料(头像和昵称)
- editProfile() {
- wx.showActionSheet({
- itemList: ['获取微信头像', '获取微信昵称', '从相册选择头像', '手动输入昵称'],
- success: (res) => {
- if (res.tapIndex === 0) {
- this.getWechatAvatar()
- } else if (res.tapIndex === 1) {
- this.getWechatNickname()
- } else if (res.tapIndex === 2) {
- this.chooseAvatar()
- } else if (res.tapIndex === 3) {
- this.editNickname()
- }
- }
- })
+ // 微信头像选择回调(button open-type="chooseAvatar")
+ async onChooseAvatar(e) {
+ const avatarUrl = e.detail.avatarUrl
+ if (!avatarUrl) return
+
+ wx.showLoading({ title: '更新中...', mask: true })
+
+ try {
+ // 更新本地显示
+ const userInfo = this.data.userInfo
+ userInfo.avatar = avatarUrl
+ this.setData({ userInfo })
+ app.globalData.userInfo = userInfo
+ wx.setStorageSync('userInfo', userInfo)
+
+ // 同步到服务器
+ await app.request('/api/user/update', {
+ method: 'POST',
+ data: { userId: userInfo.id, avatar: avatarUrl }
+ })
+
+ wx.hideLoading()
+ wx.showToast({ title: '头像已更新', icon: 'success' })
+ } catch (e) {
+ wx.hideLoading()
+ wx.showToast({ title: '更新失败', icon: 'none' })
+ }
},
- // 获取微信头像(原生能力)
- getWechatAvatar() {
- // 使用chooseAvatar API(微信原生头像选择)
- wx.chooseAvatar({
- success: async (res) => {
- const avatarUrl = res.avatarUrl
- wx.showLoading({ title: '更新中...', mask: true })
-
- try {
- // 更新本地显示
- const userInfo = this.data.userInfo
- userInfo.avatar = avatarUrl
- this.setData({ userInfo })
- app.globalData.userInfo = userInfo
- wx.setStorageSync('userInfo', userInfo)
-
- // 同步到服务器
- await app.request('/api/user/update', {
- method: 'POST',
- data: { userId: userInfo.id, avatar: avatarUrl }
- })
-
- wx.hideLoading()
- wx.showToast({ title: '头像已更新', icon: 'success' })
- } catch (e) {
- wx.hideLoading()
- wx.showToast({ title: '更新失败', icon: 'none' })
- }
- },
- fail: () => {
- wx.showToast({ title: '取消选择', icon: 'none' })
- }
- })
- },
-
- // 获取微信昵称(原生能力)
- getWechatNickname() {
- // 引导用户在弹窗中输入微信昵称
- wx.showModal({
- title: '获取微信昵称',
- content: '请在下方输入您的微信昵称',
- editable: true,
- placeholderText: '请输入微信昵称',
- success: async (res) => {
- if (res.confirm && res.content) {
- const nickname = res.content.trim()
- if (nickname.length < 1 || nickname.length > 20) {
- wx.showToast({ title: '昵称1-20个字符', icon: 'none' })
- return
- }
-
- // 更新本地
- const userInfo = this.data.userInfo
- userInfo.nickname = nickname
- this.setData({ userInfo })
- app.globalData.userInfo = userInfo
- wx.setStorageSync('userInfo', userInfo)
-
- // 同步到服务器
- try {
- await app.request('/api/user/update', {
- method: 'POST',
- data: { userId: userInfo.id, nickname }
- })
- } catch (e) {
- console.log('同步昵称到服务器失败', e)
- }
-
- wx.showToast({ title: '昵称已更新', icon: 'success' })
- }
- }
- })
- },
-
- // 从相册选择头像
- chooseAvatar() {
- wx.chooseMedia({
- count: 1,
- mediaType: ['image'],
- sourceType: ['album', 'camera'],
- success: async (res) => {
- const tempFilePath = res.tempFiles[0].tempFilePath
- wx.showLoading({ title: '上传中...', mask: true })
-
- try {
- // 更新本地显示
- const userInfo = this.data.userInfo
- userInfo.avatar = tempFilePath
- this.setData({ userInfo })
- app.globalData.userInfo = userInfo
- wx.setStorageSync('userInfo', userInfo)
-
- // 同步到服务器
- await app.request('/api/user/update', {
- method: 'POST',
- data: { userId: userInfo.id, avatar: tempFilePath }
- })
-
- wx.hideLoading()
- wx.showToast({ title: '头像已更新', icon: 'success' })
- } catch (e) {
- wx.hideLoading()
- wx.showToast({ title: '上传失败', icon: 'none' })
- }
- }
- })
- },
-
- // 手动输入昵称
+ // 点击昵称修改
editNickname() {
wx.showModal({
title: '修改昵称',
editable: true,
- placeholderText: '请输入新昵称',
+ placeholderText: '请输入昵称',
success: async (res) => {
if (res.confirm && res.content) {
const newNickname = res.content.trim()
diff --git a/miniprogram/pages/my/my.wxml b/miniprogram/pages/my/my.wxml
index 65de111..38452bd 100644
--- a/miniprogram/pages/my/my.wxml
+++ b/miniprogram/pages/my/my.wxml
@@ -27,8 +27,8 @@