功能:邀请有问题

This commit is contained in:
xiao12feng8 2026-02-03 18:00:47 +08:00
parent 57053f08ab
commit 8613e1560b
11 changed files with 2221 additions and 2 deletions

View File

@ -0,0 +1,33 @@
-- 添加邀请码相关字段到 nf_user 表
-- 执行时间2026-02-03
USE fastadmin;
-- 添加邀请码字段
ALTER TABLE `nf_user`
ADD COLUMN `invite_code` VARCHAR(10) DEFAULT NULL COMMENT '邀请码' AFTER `vip_endtime`,
ADD COLUMN `invited_by` VARCHAR(10) DEFAULT NULL COMMENT '被谁邀请(邀请码)' AFTER `invite_code`,
ADD COLUMN `invite_count` INT(11) DEFAULT 0 COMMENT '邀请人数' AFTER `invited_by`,
ADD COLUMN `invite_reward_total` DECIMAL(10,2) DEFAULT 0.00 COMMENT '邀请奖励总额' AFTER `invite_count`;
-- 添加唯一索引
ALTER TABLE `nf_user`
ADD UNIQUE INDEX `idx_invite_code` (`invite_code`);
-- 添加普通索引
ALTER TABLE `nf_user`
ADD INDEX `idx_invited_by` (`invited_by`);
-- 验证字段是否添加成功
SELECT
COLUMN_NAME,
COLUMN_TYPE,
IS_NULLABLE,
COLUMN_DEFAULT,
COLUMN_COMMENT
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND COLUMN_NAME IN ('invite_code', 'invited_by', 'invite_count', 'invite_reward_total');

View File

@ -0,0 +1,20 @@
-- 检查 nf_user 表中邀请码相关字段的情况
USE fastadmin;
-- 查看表结构
DESCRIBE nf_user;
-- 查看邀请码相关字段
SELECT
COLUMN_NAME,
COLUMN_TYPE,
IS_NULLABLE,
COLUMN_DEFAULT,
COLUMN_COMMENT
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND COLUMN_NAME IN ('invite_code', 'invited_by', 'invite_count', 'invite_reward_total')
ORDER BY ORDINAL_POSITION;

View File

@ -0,0 +1,107 @@
-- 邀请码功能完整修复脚本
-- 执行时间2026-02-03
-- 说明:这个脚本会检查并修复所有邀请码相关的问题
USE fastadmin;
-- ========================================
-- 第 1 步:修复字段类型
-- ========================================
-- 修复 invited_by 字段类型(从 int 改为 varchar(10)
ALTER TABLE `nf_user`
MODIFY COLUMN `invited_by` VARCHAR(10) DEFAULT NULL COMMENT '被谁邀请(邀请码)';
-- 修复 invite_reward_total 字段类型(从 int 改为 decimal(10,2)
ALTER TABLE `nf_user`
MODIFY COLUMN `invite_reward_total` DECIMAL(10,2) DEFAULT 0.00 COMMENT '邀请奖励总额';
-- 确保 invite_code 字段正确
ALTER TABLE `nf_user`
MODIFY COLUMN `invite_code` VARCHAR(10) DEFAULT NULL COMMENT '邀请码';
-- 确保 invite_count 字段正确
ALTER TABLE `nf_user`
MODIFY COLUMN `invite_count` INT(11) DEFAULT 0 COMMENT '邀请人数';
SELECT '✅ 步骤 1字段类型修复完成' AS '进度';
-- ========================================
-- 第 2 步:检查并添加索引
-- ========================================
-- 检查 invite_code 唯一索引
SET @index_exists = 0;
SELECT COUNT(*) INTO @index_exists
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND INDEX_NAME = 'idx_invite_code';
SET @sql = IF(@index_exists = 0,
'ALTER TABLE `nf_user` ADD UNIQUE INDEX `idx_invite_code` (`invite_code`)',
'SELECT ''idx_invite_code 索引已存在'' AS message');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查 invited_by 索引
SET @index_exists = 0;
SELECT COUNT(*) INTO @index_exists
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND INDEX_NAME = 'idx_invited_by';
SET @sql = IF(@index_exists = 0,
'ALTER TABLE `nf_user` ADD INDEX `idx_invited_by` (`invited_by`)',
'SELECT ''idx_invited_by 索引已存在'' AS message');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT '✅ 步骤 2索引检查完成' AS '进度';
-- ========================================
-- 第 3 步:验证修复结果
-- ========================================
SELECT '========================================' AS '';
SELECT '邀请码字段信息' AS '';
SELECT '========================================' AS '';
SELECT
COLUMN_NAME AS '字段名',
COLUMN_TYPE AS '类型',
IS_NULLABLE AS '可空',
COLUMN_DEFAULT AS '默认值',
COLUMN_COMMENT AS '注释'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND COLUMN_NAME IN ('invite_code', 'invited_by', 'invite_count', 'invite_reward_total')
ORDER BY ORDINAL_POSITION;
SELECT '========================================' AS '';
SELECT '索引信息' AS '';
SELECT '========================================' AS '';
SELECT
INDEX_NAME AS '索引名',
COLUMN_NAME AS '列名',
NON_UNIQUE AS '非唯一',
INDEX_TYPE AS '索引类型'
FROM
INFORMATION_SCHEMA.STATISTICS
WHERE
TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND INDEX_NAME IN ('idx_invite_code', 'idx_invited_by')
ORDER BY INDEX_NAME, SEQ_IN_INDEX;
SELECT '========================================' AS '';
SELECT '✅ 邀请码功能修复完成!' AS '状态';
SELECT '请重启 Python 服务以使更改生效' AS '提示';
SELECT '========================================' AS '';

View File

@ -0,0 +1,56 @@
-- 修复邀请码字段类型错误
-- 执行时间2026-02-03
-- 问题invited_by 和 invite_reward_total 字段类型不正确
USE fastadmin;
-- 备份当前数据(可选,但建议)
-- CREATE TABLE nf_user_backup_20260203 AS SELECT * FROM nf_user;
-- 修复 invited_by 字段类型(从 int 改为 varchar(10)
ALTER TABLE `nf_user`
MODIFY COLUMN `invited_by` VARCHAR(10) DEFAULT NULL COMMENT '被谁邀请(邀请码)';
-- 修复 invite_reward_total 字段类型(从 int 改为 decimal(10,2)
ALTER TABLE `nf_user`
MODIFY COLUMN `invite_reward_total` DECIMAL(10,2) DEFAULT 0.00 COMMENT '邀请奖励总额';
-- 确保 invite_code 字段有注释
ALTER TABLE `nf_user`
MODIFY COLUMN `invite_code` VARCHAR(10) DEFAULT NULL COMMENT '邀请码';
-- 确保 invite_count 字段有注释
ALTER TABLE `nf_user`
MODIFY COLUMN `invite_count` INT(11) DEFAULT 0 COMMENT '邀请人数';
-- 验证修复结果
SELECT
COLUMN_NAME AS '字段名',
COLUMN_TYPE AS '类型',
IS_NULLABLE AS '可空',
COLUMN_DEFAULT AS '默认值',
COLUMN_COMMENT AS '注释'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND COLUMN_NAME IN ('invite_code', 'invited_by', 'invite_count', 'invite_reward_total')
ORDER BY ORDINAL_POSITION;
-- 显示修复后的索引信息
SELECT
INDEX_NAME AS '索引名',
COLUMN_NAME AS '列名',
NON_UNIQUE AS '非唯一',
INDEX_TYPE AS '索引类型'
FROM
INFORMATION_SCHEMA.STATISTICS
WHERE
TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND INDEX_NAME IN ('idx_invite_code', 'idx_invited_by')
ORDER BY INDEX_NAME, SEQ_IN_INDEX;
-- 显示成功消息
SELECT '✅ 邀请码字段类型修复完成!' AS '状态';

View File

@ -0,0 +1,123 @@
-- 修复邀请码功能 - 检查并添加缺失的字段和索引
-- 执行时间2026-02-03
USE fastadmin;
-- 检查并添加 invite_code 字段(如果不存在)
SET @col_exists = 0;
SELECT COUNT(*) INTO @col_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND COLUMN_NAME = 'invite_code';
SET @sql = IF(@col_exists = 0,
'ALTER TABLE `nf_user` ADD COLUMN `invite_code` VARCHAR(10) DEFAULT NULL COMMENT ''邀请码'' AFTER `vip_endtime`',
'SELECT ''invite_code 字段已存在'' AS message');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加 invited_by 字段(如果不存在)
SET @col_exists = 0;
SELECT COUNT(*) INTO @col_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND COLUMN_NAME = 'invited_by';
SET @sql = IF(@col_exists = 0,
'ALTER TABLE `nf_user` ADD COLUMN `invited_by` VARCHAR(10) DEFAULT NULL COMMENT ''被谁邀请(邀请码)'' AFTER `invite_code`',
'SELECT ''invited_by 字段已存在'' AS message');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加 invite_count 字段(如果不存在)
SET @col_exists = 0;
SELECT COUNT(*) INTO @col_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND COLUMN_NAME = 'invite_count';
SET @sql = IF(@col_exists = 0,
'ALTER TABLE `nf_user` ADD COLUMN `invite_count` INT(11) DEFAULT 0 COMMENT ''邀请人数'' AFTER `invited_by`',
'SELECT ''invite_count 字段已存在'' AS message');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加 invite_reward_total 字段(如果不存在)
SET @col_exists = 0;
SELECT COUNT(*) INTO @col_exists
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND COLUMN_NAME = 'invite_reward_total';
SET @sql = IF(@col_exists = 0,
'ALTER TABLE `nf_user` ADD COLUMN `invite_reward_total` DECIMAL(10,2) DEFAULT 0.00 COMMENT ''邀请奖励总额'' AFTER `invite_count`',
'SELECT ''invite_reward_total 字段已存在'' AS message');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加 invite_code 唯一索引(如果不存在)
SET @index_exists = 0;
SELECT COUNT(*) INTO @index_exists
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND INDEX_NAME = 'idx_invite_code';
SET @sql = IF(@index_exists = 0,
'ALTER TABLE `nf_user` ADD UNIQUE INDEX `idx_invite_code` (`invite_code`)',
'SELECT ''idx_invite_code 索引已存在'' AS message');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加 invited_by 索引(如果不存在)
SET @index_exists = 0;
SELECT COUNT(*) INTO @index_exists
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND INDEX_NAME = 'idx_invited_by';
SET @sql = IF(@index_exists = 0,
'ALTER TABLE `nf_user` ADD INDEX `idx_invited_by` (`invited_by`)',
'SELECT ''idx_invited_by 索引已存在'' AS message');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 显示最终的字段信息
SELECT
COLUMN_NAME AS '字段名',
COLUMN_TYPE AS '类型',
IS_NULLABLE AS '可空',
COLUMN_DEFAULT AS '默认值',
COLUMN_COMMENT AS '注释'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND COLUMN_NAME IN ('invite_code', 'invited_by', 'invite_count', 'invite_reward_total')
ORDER BY ORDINAL_POSITION;
-- 显示索引信息
SELECT
INDEX_NAME AS '索引名',
COLUMN_NAME AS '列名',
NON_UNIQUE AS '非唯一',
INDEX_TYPE AS '索引类型'
FROM
INFORMATION_SCHEMA.STATISTICS
WHERE
TABLE_SCHEMA = 'fastadmin'
AND TABLE_NAME = 'nf_user'
AND INDEX_NAME IN ('idx_invite_code', 'idx_invited_by')
ORDER BY INDEX_NAME, SEQ_IN_INDEX;

View File

@ -0,0 +1,73 @@
-- 测试邀请码功能
-- 执行时间2026-02-03
USE fastadmin;
-- 1. 查看当前用户的邀请码信息
SELECT
id,
username,
nickname,
invite_code AS '邀请码',
invited_by AS '被谁邀请',
invite_count AS '邀请人数',
invite_reward_total AS '邀请奖励',
money AS '金币余额'
FROM nf_user
WHERE id IN (
SELECT id FROM nf_user
ORDER BY id DESC
LIMIT 10
)
ORDER BY id DESC;
-- 2. 查看有邀请码的用户
SELECT
id,
username,
nickname,
invite_code AS '邀请码',
invite_count AS '邀请人数',
invite_reward_total AS '邀请奖励',
money AS '金币余额'
FROM nf_user
WHERE invite_code IS NOT NULL
ORDER BY invite_count DESC, id DESC
LIMIT 20;
-- 3. 查看使用了邀请码的用户
SELECT
id,
username,
nickname,
invited_by AS '使用的邀请码',
money AS '金币余额',
createtime AS '注册时间'
FROM nf_user
WHERE invited_by IS NOT NULL
ORDER BY id DESC
LIMIT 20;
-- 4. 查看邀请相关的金币日志
SELECT
l.id,
l.user_id AS '用户ID',
u.username AS '用户名',
l.money AS '金币变动',
l.before AS '变动前',
l.after AS '变动后',
l.memo AS '备注',
FROM_UNIXTIME(l.createtime) AS '时间'
FROM nf_user_money_log l
LEFT JOIN nf_user u ON l.user_id = u.id
WHERE l.memo LIKE '%邀请%'
ORDER BY l.createtime DESC
LIMIT 20;
-- 5. 统计邀请数据
SELECT
COUNT(DISTINCT CASE WHEN invite_code IS NOT NULL THEN id END) AS '有邀请码的用户数',
COUNT(DISTINCT CASE WHEN invited_by IS NOT NULL THEN id END) AS '使用邀请码的用户数',
SUM(CASE WHEN invite_code IS NOT NULL THEN invite_count ELSE 0 END) AS '总邀请人数',
SUM(CASE WHEN invite_code IS NOT NULL THEN invite_reward_total ELSE 0 END) AS '总邀请奖励'
FROM nf_user;

View File

@ -929,8 +929,13 @@
} }
} }
}, },
getBobbiesList: '', getBobbiesList: {
loverBasicList: '', reg_step: 1, //
level: 0,
intimacy: 0,
next_level_intimacy: 100
},
loverBasicList: null, // null
underAgeEnabled: false, // underAgeEnabled: false, //
historyModalVisible: false, historyModalVisible: false,
historyModalType: 'sing', historyModalType: 'sing',

View File

@ -0,0 +1,547 @@
# 登录显示成功但无法进入系统问题修复
## 问题描述
用户登录后显示"登录成功",但页面一直停留在登录页面或显示空白,无法进入系统。
## 问题原因分析
### 原因 1`getBobbiesList` 初始值为空字符串
**代码位置**`xuniYou/pages/index/index.vue` 第 932 行
```javascript
getBobbiesList: '', // ❌ 初始值为空字符串
```
**问题**
- 页面使用 `v-show="getBobbiesList.reg_step == 1 || getBobbiesList.reg_step == 2 || getBobbiesList.reg_step == 3"` 来判断显示哪个界面
- 当 `getBobbiesList` 为空字符串时,`getBobbiesList.reg_step` 为 `undefined`
- 导致所有 `v-show` 条件都不满足,页面显示空白
### 原因 2`getUserBasic()` 调用时机问题
**代码位置**`xuniYou/pages/index/index.vue` 第 960-969 行
```javascript
onShow() {
this.checkUnderAgeStatus();
if (uni.getStorageSync('token')) {
this.getUserBasic()
this.loverBasic()
this.loadHomeLooks()
}
}
```
**问题**
- 只有在 `onShow()` 时才调用 `getUserBasic()`
- 如果从登录页跳转过来,可能还没来得及执行 `onShow()`
- 或者 token 还没有正确保存到 storage
### 原因 3API 请求失败
可能的原因:
- PHP 后端服务未启动(端口 30100
- Token 无效或过期
- 网络请求失败
- CORS 跨域问题
---
## 解决方案
### 方案 1修改 `getBobbiesList` 初始值(推荐)
将初始值从空字符串改为对象,并设置默认的 `reg_step`
```javascript
// 修改前
getBobbiesList: '',
// 修改后
getBobbiesList: {
reg_step: 1 // 默认显示第一步:完善个人资料
},
```
### 方案 2添加加载状态
添加一个加载状态,在数据加载完成前显示加载提示:
```javascript
data() {
return {
isLoading: true, // 添加加载状态
getBobbiesList: {
reg_step: 1
},
// ...
}
}
```
在模板中添加加载提示:
```vue
<view v-if="isLoading" class="loading-container">
<view class="loading-text">加载中...</view>
</view>
<view v-else>
<!-- 原有内容 -->
</view>
```
`getUserBasic()` 成功后设置:
```javascript
getUserBasic() {
GetUserBasic({}).then(res => {
if (res.code == 1) {
let data = res.data
// ...
this.getBobbiesList = data
this.isLoading = false // 加载完成
}
}).catch(() => {
this.isLoading = false // 加载失败也要隐藏加载状态
uni.showToast({
title: '加载失败,请重试',
icon: 'none'
})
})
}
```
### 方案 3`onLoad` 时也调用 `getUserBasic()`
```javascript
onLoad(options) {
//#ifdef MP-WEIXIN
this.checkPermission()
//#endif
// 如果有 tab 参数,切换到对应的 tab
if (options && options.tab) {
const tabIndex = parseInt(options.tab);
if (!isNaN(tabIndex) && tabIndex >= 0 && tabIndex < this.tabs.length) {
this.currentTab = tabIndex;
}
}
// 添加:在 onLoad 时也检查并加载用户数据
if (uni.getStorageSync('token')) {
this.getUserBasic()
this.loverBasic()
this.loadHomeLooks()
}
},
onReady() {
this.getServerData();
},
```
---
## 快速修复步骤
### 第 1 步:检查后端服务是否运行
```bash
# Windows - 检查 PHP 服务
netstat -ano | findstr :30100
# 如果没有运行,启动 PHP 服务
cd xunifriend_RaeeC/public
php -S 0.0.0.0:30100
```
### 第 2 步:检查浏览器控制台
1. 打开浏览器开发者工具F12
2. 切换到 Console 标签
3. 查看是否有错误信息
4. 切换到 Network 标签
5. 查看 API 请求是否成功
**关键检查点**
- `/api/user_basic/get_user_basic` 请求是否返回 200
- 响应数据中是否包含 `reg_step` 字段
- Token 是否正确传递
### 第 3 步:检查 Token
在浏览器控制台执行:
```javascript
// 检查 token 是否存在
console.log('Token:', uni.getStorageSync('token'))
// 检查用户信息
console.log('UserInfo:', uni.getStorageSync('userinfo'))
// 手动调用 getUserBasic
// 在 index 页面的控制台执行
this.getUserBasic()
```
### 第 4 步:修改代码(推荐方案 1
修改 `xuniYou/pages/index/index.vue` 第 932 行:
```javascript
// 修改前
getBobbiesList: '',
// 修改后
getBobbiesList: {
reg_step: 1,
level: 0,
intimacy: 0,
next_level_intimacy: 100
},
```
### 第 5 步:重新编译前端
```bash
# 在 xuniYou 目录下
npm run dev:h5
# 或
npm run build:h5
```
---
## 调试方法
### 方法 1添加调试日志
`getUserBasic()` 方法中添加日志:
```javascript
getUserBasic() {
console.log('开始获取用户信息...')
console.log('Token:', uni.getStorageSync('token'))
GetUserBasic({}).then(res => {
console.log('getUserBasic 响应:', res)
if (res.code == 1) {
let data = res.data
console.log('用户数据:', data)
console.log('reg_step:', data.reg_step)
// ...
this.getBobbiesList = data
console.log('getBobbiesList 已更新:', this.getBobbiesList)
} else {
console.error('getUserBasic 失败:', res.msg)
}
}).catch(err => {
console.error('getUserBasic 异常:', err)
})
}
```
### 方法 2检查 API 响应
使用 curl 或 Postman 测试 API
```bash
# 替换 YOUR_TOKEN 为实际的 token
curl -X GET "http://localhost:30100/api/user_basic/get_user_basic" \
-H "token: YOUR_TOKEN"
```
**预期响应**
```json
{
"code": 1,
"msg": "成功",
"data": {
"id": 1,
"username": "18800000001",
"nickname": "用户昵称",
"avatar": "头像URL",
"reg_step": 4,
"level": 1,
"intimacy": 50,
"next_level_intimacy": 100,
// ...
}
}
```
### 方法 3临时绕过检查
如果需要紧急修复,可以临时修改 `v-show` 条件:
```vue
<!-- 修改前 -->
<view v-show="getBobbiesList.reg_step == 1 || getBobbiesList.reg_step == 2 || getBobbiesList.reg_step == 3">
<!-- 修改后(临时) -->
<view v-show="!getBobbiesList || getBobbiesList.reg_step == 1 || getBobbiesList.reg_step == 2 || getBobbiesList.reg_step == 3">
```
---
## 常见错误和解决方法
### 错误 1Cannot read property 'reg_step' of ''
**原因**`getBobbiesList` 为空字符串
**解决**:使用方案 1将初始值改为对象
### 错误 2Network Error
**原因**:后端服务未启动或网络问题
**解决**
1. 检查 PHP 服务是否运行
2. 检查端口是否正确30100
3. 检查防火墙设置
### 错误 3401 Unauthorized
**原因**Token 无效或过期
**解决**
1. 清除本地存储,重新登录
2. 检查 token 是否正确保存
3. 检查后端 token 验证逻辑
### 错误 4页面显示空白
**原因**:所有 `v-show` 条件都不满足
**解决**
1. 检查 `getBobbiesList.reg_step` 的值
2. 添加默认显示逻辑
3. 使用方案 2 添加加载状态
---
## 完整修复代码
### 修改 `xuniYou/pages/index/index.vue`
```javascript
data() {
return {
// ... 其他数据
// 修改这里:添加默认值
getBobbiesList: {
reg_step: 1, // 默认第一步
level: 0,
intimacy: 0,
next_level_intimacy: 100
},
loverBasicList: null, // 改为 null 而不是空字符串
// 添加加载状态
isLoading: true,
// ... 其他数据
}
},
onLoad(options) {
//#ifdef MP-WEIXIN
this.checkPermission()
//#endif
console.log('uni.env.', uni.env)
// 如果有 tab 参数,切换到对应的 tab
if (options && options.tab) {
const tabIndex = parseInt(options.tab);
if (!isNaN(tabIndex) && tabIndex >= 0 && tabIndex < this.tabs.length) {
this.currentTab = tabIndex;
}
}
// 添加:在 onLoad 时也加载用户数据
const token = uni.getStorageSync('token')
console.log('onLoad - Token:', token)
if (token) {
this.getUserBasic()
this.loverBasic()
this.loadHomeLooks()
} else {
// 没有 token显示默认状态
this.isLoading = false
}
},
getUserBasic() {
console.log('开始获取用户信息...')
GetUserBasic({}).then(res => {
console.log('getUserBasic 响应:', res)
if (res.code == 1) {
let data = res.data
const conversationStore = useConversationStore();
let listener = {
nickname: data.username,
headImage: data.avatar,
openid: data.wxapp_openid,
}
conversationStore.setUserInfo(listener);
this.getBobbiesList = data
this.isLoading = false // 加载完成
console.log('用户数据已更新:', this.getBobbiesList)
this.getServerData();
} else {
this.isLoading = false
uni.showToast({
title: res.msg,
icon: 'none',
position: 'top'
})
}
}).catch(err => {
console.error('getUserBasic 异常:', err)
this.isLoading = false
uni.showToast({
title: '加载失败,请重试',
icon: 'none'
})
})
},
```
### 在模板中添加加载状态
```vue
<template>
<view>
<!-- 加载状态 -->
<view v-if="isLoading" class="loading-container">
<view class="loading-spinner"></view>
<view class="loading-text">加载中...</view>
</view>
<!-- 原有内容 -->
<view v-else>
<!-- ... -->
</view>
</view>
</template>
<style>
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background: #f5f5f5;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid #e0e0e0;
border-top-color: #9F47FF;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
margin-top: 20px;
font-size: 14px;
color: #666;
}
</style>
```
---
## 测试步骤
1. **清除缓存**
```javascript
// 在浏览器控制台执行
uni.clearStorageSync()
```
2. **重新登录**
- 输入手机号和密码
- 点击登录
- 观察是否正常跳转
3. **检查日志**
- 查看控制台是否有错误
- 查看 Network 标签的 API 请求
- 查看 `getUserBasic` 的响应数据
4. **验证功能**
- 确认能正常进入首页
- 确认能看到用户信息
- 确认能正常使用各个功能
---
## 预防措施
1. **始终为对象类型的数据设置默认值**
```javascript
// ❌ 不好
userData: ''
// ✅ 好
userData: {
id: 0,
name: '',
// ...
}
```
2. **添加错误处理**
```javascript
GetUserBasic({}).then(res => {
// ...
}).catch(err => {
console.error('错误:', err)
// 显示友好的错误提示
})
```
3. **添加加载状态**
- 让用户知道系统正在处理
- 避免用户重复点击
4. **添加调试日志**
- 方便排查问题
- 生产环境可以关闭
---
## 总结
登录无法进入的主要原因是 `getBobbiesList` 初始值为空字符串,导致页面判断逻辑失效。
**推荐修复方案**
1. 将 `getBobbiesList` 初始值改为对象
2. 添加加载状态
3. 在 `onLoad` 时也调用 `getUserBasic()`
4. 添加完善的错误处理
修复后,用户登录成功就能正常进入系统了!

View File

@ -0,0 +1,236 @@
# 邀请码功能修复
## 问题描述
用户使用邀请码登录后没有成功获得5金币奖励。
## 问题原因
数据库表 `nf_user` 缺少邀请码相关的字段:
- `invite_code` - 邀请码
- `invited_by` - 被谁邀请
- `invite_count` - 邀请人数
- `invite_reward_total` - 邀请奖励总额
这些字段只在 Python 的 `lover/models.py` 中定义了,但数据库中并没有实际创建这些列。
## 解决方案
### 情况 1字段不存在
如果数据库中没有邀请码字段,执行:
```bash
mysql -u aiuser -p fastadmin < lover/migrations/add_invite_fields.sql
```
### 情况 2字段已存在推荐
如果遇到 "Duplicate column name" 错误,说明字段已存在,使用智能修复脚本:
```bash
mysql -u aiuser -p fastadmin < lover/migrations/fix_invite_fields.sql
```
这个脚本会:
- 检查每个字段是否存在,只添加缺失的字段
- 检查索引是否存在,只添加缺失的索引
- 显示最终的字段和索引信息
### 1. 执行修复脚本
```bash
# 推荐:使用智能修复脚本(自动检查并修复)
mysql -u aiuser -p fastadmin < lover/migrations/fix_invite_fields.sql
```
### 2. 检查字段状态
```bash
# 查看邀请码相关字段
mysql -u aiuser -p fastadmin < lover/migrations/check_invite_fields.sql
```
### 3. 测试邀请码功能
```bash
# 查看邀请码数据和日志
mysql -u aiuser -p fastadmin < lover/migrations/test_invite_function.sql
```
1. **获取邀请码**
- 登录系统
- 访问"我的" -> "邀请好友"页面
- 系统会自动生成邀请码
2. **使用邀请码**
- 新用户注册/登录时
- 在登录页面输入邀请码
- 登录成功后会自动调用邀请码接口
3. **验证奖励**
- 邀请人获得 10 金币
- 被邀请人获得 5 金币
- 检查 `nf_user_money_log` 表中的记录
## 邀请码功能说明
### 奖励规则
- **邀请人奖励**10 金币
- **被邀请人奖励**5 金币
- 每个用户只能使用一次邀请码
- 不能使用自己的邀请码
### API 接口
#### 1. 获取邀请信息
```
GET /config/invite/info
Authorization: Bearer {token}
```
响应:
```json
{
"code": 1,
"data": {
"invite_code": "ABC123",
"invite_count": 5,
"invite_reward_total": 50.00,
"invite_url": "https://your-domain.com/register?invite=ABC123"
}
}
```
#### 2. 使用邀请码
```
POST /config/invite/apply
Authorization: Bearer {token}
Content-Type: application/json
{
"invite_code": "ABC123"
}
```
响应:
```json
{
"code": 1,
"data": {
"message": "邀请码使用成功您获得了5.0金币",
"reward": 5.0,
"balance": 5.0
}
}
```
### 前端调用流程
1. **登录页面** (`xuniYou/pages/login/index.vue`)
- 用户输入邀请码(可选)
- 登录成功后自动调用 `applyInviteCode()` 方法
2. **邀请页面** (`xuniYou/pages/mine/invite.vue`)
- 显示用户的邀请码
- 显示邀请统计(邀请人数、奖励总额)
- 提供复制邀请码功能
### 数据库表结构
```sql
-- nf_user 表新增字段
invite_code VARCHAR(10) DEFAULT NULL COMMENT '邀请码'
invited_by VARCHAR(10) DEFAULT NULL COMMENT '被谁邀请(邀请码)'
invite_count INT(11) DEFAULT 0 COMMENT '邀请人数'
invite_reward_total DECIMAL(10,2) DEFAULT 0.00 COMMENT '邀请奖励总额'
-- 索引
UNIQUE INDEX idx_invite_code (invite_code)
INDEX idx_invited_by (invited_by)
```
### 金币日志记录
所有邀请奖励都会记录在 `nf_user_money_log` 表中:
```sql
-- 邀请人的记录
memo = '邀请新用户奖励'
money = 10.00
-- 被邀请人的记录
memo = '使用邀请码奖励'
money = 5.00
```
## 注意事项
1. **邀请码生成规则**
- 6位字符
- 只包含大写字母和数字
- 排除易混淆字符0、O、1、I
- 自动检查重复
2. **安全性**
- 使用数据库行锁防止并发问题
- 验证邀请码是否存在
- 验证用户是否已使用过邀请码
- 不能使用自己的邀请码
3. **事务处理**
- 使用数据库事务确保数据一致性
- 失败时自动回滚
- 记录详细的金币日志
## 测试步骤
### 1. 本地测试
```bash
# 1. 执行数据库迁移
mysql -u aiuser -p fastadmin < lover/migrations/add_invite_fields.sql
# 2. 重启 Python 服务
# Windows
taskkill /F /IM python.exe
python -m uvicorn lover.main:app --host 0.0.0.0 --port 30101 --reload
# Linux
sudo systemctl restart aigirlfriend-python
```
### 2. 功能测试
1. 用户 A 登录,获取邀请码
2. 用户 B 注册/登录时输入用户 A 的邀请码
3. 检查用户 A 和用户 B 的金币余额
4. 检查 `nf_user_money_log` 表中的记录
### 3. 异常测试
1. 使用不存在的邀请码 → 应提示"邀请码不存在"
2. 重复使用邀请码 → 应提示"您已经使用过邀请码"
3. 使用自己的邀请码 → 应提示"不能使用自己的邀请码"
## 相关文件
- **数据库迁移**`lover/migrations/add_invite_fields.sql`
- **数据模型**`lover/models.py`
- **后端 API**`lover/routers/config.py`
- **前端登录页**`xuniYou/pages/login/index.vue`
- **前端邀请页**`xuniYou/pages/mine/invite.vue`
## 更新日志
- **2026-02-03**:创建邀请码字段迁移文件
- **2026-02-03**:编写修复文档
## 下一步
1. 执行数据库迁移
2. 测试邀请码功能
3. 更新部署文档,添加邀请码迁移步骤

View File

@ -0,0 +1,329 @@
# 邀请码功能问题诊断
## 快速诊断步骤
### 第 1 步:检查数据库字段
```bash
mysql -u aiuser -p fastadmin < lover/migrations/check_invite_fields.sql
```
**预期结果**:应该看到 4 个字段
- `invite_code` - VARCHAR(10)
- `invited_by` - VARCHAR(10)
- `invite_count` - INT(11)
- `invite_reward_total` - DECIMAL(10,2)
**如果字段不完整**:执行修复脚本
```bash
mysql -u aiuser -p fastadmin < lover/migrations/fix_invite_fields.sql
```
---
### 第 2 步:测试后端 API
#### 2.1 测试获取邀请信息
```bash
# 替换 YOUR_TOKEN 为实际的用户 token
curl -X GET "http://localhost:30101/config/invite/info" \
-H "token: YOUR_TOKEN"
```
**预期响应**
```json
{
"code": 1,
"data": {
"invite_code": "ABC123",
"invite_count": 0,
"invite_reward_total": 0.0,
"invite_url": "https://your-domain.com/register?invite=ABC123"
}
}
```
**如果返回 401 错误**token 无效或过期
**如果返回 500 错误**:检查 Python 服务日志
#### 2.2 测试使用邀请码
```bash
# 替换 YOUR_TOKEN 和 INVITE_CODE
curl -X POST "http://localhost:30101/config/invite/apply" \
-H "token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"invite_code": "ABC123"}'
```
**预期响应**
```json
{
"code": 1,
"data": {
"message": "邀请码使用成功您获得了5.0金币",
"reward": 5.0,
"balance": 5.0
}
}
```
---
### 第 3 步:检查 Python 服务日志
```bash
# Windows
# 查看控制台输出
# Linux
sudo journalctl -u aigirlfriend-python -n 50 -f
# 或
sudo tail -f /var/log/aigirlfriend-python.log
```
**关键日志**
- `认证调试` - 查看 token 是否正确解析
- `用户中心调试` - 查看是否成功获取用户信息
- 任何 `ERROR``Exception` 信息
---
### 第 4 步:检查数据库数据
```bash
mysql -u aiuser -p fastadmin < lover/migrations/test_invite_function.sql
```
这会显示:
1. 最近 10 个用户的邀请码信息
2. 有邀请码的用户列表
3. 使用了邀请码的用户列表
4. 邀请相关的金币日志
5. 邀请数据统计
---
## 常见问题排查
### 问题 1字段类型错误最常见
**错误表现**:邀请码功能不工作,用户没有获得金币
**原因**:数据库字段类型不正确
- `invited_by` 应该是 `varchar(10)` 但实际是 `int`
- `invite_reward_total` 应该是 `decimal(10,2)` 但实际是 `int`
**检查方法**
```bash
mysql -u aiuser -p fastadmin < lover/migrations/check_invite_fields.sql
```
**预期结果**
```
字段名 |类型 |
-------------------+-------------+
invite_code |varchar(10) | ✅
invited_by |varchar(10) | ✅ 应该是这个
invite_count |int(11) | ✅
invite_reward_total|decimal(10,2)| ✅ 应该是这个
```
**如果类型错误**
```bash
mysql -u aiuser -p fastadmin < lover/migrations/fix_invite_field_types.sql
```
---
### 问题 2字段已存在错误
**错误信息**`Duplicate column name 'invite_code'`
**原因**:字段已经存在
**解决**:使用智能修复脚本
```bash
mysql -u aiuser -p fastadmin < lover/migrations/fix_invite_fields.sql
```
---
### 问题 2用户没有获得金币
**可能原因**
#### A. 邀请码不存在
```sql
-- 检查邀请码是否存在
SELECT id, username, invite_code
FROM nf_user
WHERE invite_code = 'ABC123';
```
#### B. 用户已使用过邀请码
```sql
-- 检查用户是否已使用过邀请码
SELECT id, username, invited_by
FROM nf_user
WHERE id = 用户ID;
```
#### C. 使用了自己的邀请码
- 检查邀请码是否是用户自己的
#### D. API 调用失败
- 检查前端是否正确调用了 `/config/invite/apply` 接口
- 检查 token 是否正确传递
- 查看浏览器控制台的网络请求
---
### 问题 3前端没有调用邀请码接口
**检查点**
1. **登录页面** (`xuniYou/pages/login/index.vue`)
- 第 412 行:检查是否有邀请码判断
- 第 434 行:检查 `applyInviteCode()` 方法
2. **浏览器控制台**
- 打开开发者工具 → Network 标签
- 登录时查看是否有 `/config/invite/apply` 请求
- 查看请求参数和响应
3. **手动测试**
```javascript
// 在浏览器控制台执行
uni.request({
url: 'http://localhost:30101/config/invite/apply',
method: 'POST',
header: {
'Content-Type': 'application/json',
'token': uni.getStorageSync('token')
},
data: {
invite_code: 'ABC123'
},
success: (res) => {
console.log('邀请码响应:', res);
}
});
```
---
### 问题 4Python 服务认证失败
**检查 token 传递**
前端应该使用以下任一方式传递 token
- `Authorization: Bearer {token}`
- `X-Token: {token}`
- `token: {token}` (header)
- `token: {token}` (cookie)
**当前前端使用**`token: {token}` (header)
**验证**
```bash
# 查看 Python 日志中的认证调试信息
# 应该看到:
# 认证调试 - token_header: YOUR_TOKEN
# 认证调试 - 提取的 token: YOUR_TOKEN
```
---
## 完整测试流程
### 1. 准备两个测试账号
- **用户 A**邀请人18800000001
- **用户 B**被邀请人18800000002
### 2. 用户 A 获取邀请码
1. 登录用户 A
2. 进入"我的" → "邀请好友"
3. 记录邀请码例如ABC123
4. 记录当前金币余额
### 3. 用户 B 使用邀请码
1. 退出登录
2. 使用用户 B 登录
3. 在登录页面输入邀请码ABC123
4. 登录成功
### 4. 验证结果
```sql
-- 查看用户 A 的数据
SELECT id, username, invite_code, invite_count, invite_reward_total, money
FROM nf_user
WHERE username = '18800000001';
-- 查看用户 B 的数据
SELECT id, username, invited_by, money
FROM nf_user
WHERE username = '18800000002';
-- 查看金币日志
SELECT
l.user_id,
u.username,
l.money,
l.before,
l.after,
l.memo,
FROM_UNIXTIME(l.createtime) AS time
FROM nf_user_money_log l
LEFT JOIN nf_user u ON l.user_id = u.id
WHERE l.memo LIKE '%邀请%'
ORDER BY l.createtime DESC
LIMIT 10;
```
**预期结果**
- 用户 A`invite_count` +1`invite_reward_total` +10`money` +10
- 用户 B`invited_by` = 'ABC123'`money` +5
- 金币日志中有两条记录
---
## 如果问题仍未解决
### 收集以下信息:
1. **数据库字段检查结果**
```bash
mysql -u aiuser -p fastadmin < lover/migrations/check_invite_fields.sql
```
2. **Python 服务日志**(最近 50 行)
```bash
# 复制日志内容
```
3. **浏览器控制台错误**
- Network 标签中的请求详情
- Console 标签中的错误信息
4. **测试 API 的响应**
```bash
curl -X GET "http://localhost:30101/config/invite/info" \
-H "token: YOUR_TOKEN"
```
5. **数据库测试结果**
```bash
mysql -u aiuser -p fastadmin < lover/migrations/test_invite_function.sql
```
---
## 联系支持
提供以上信息可以帮助快速定位问题。

690
部署指南.md Normal file
View File

@ -0,0 +1,690 @@
# 🚀 AI 女友项目部署指南
## 📋 项目架构
- **PHP 后端**FastAdmin + ThinkPHP用户管理、基础功能
- **Python 后端**FastAPIAI 功能、唱歌、跳舞等)
- **前端**uni-appH5/小程序/APP
- **数据库**MySQL
- **文件存储**:阿里云 OSS
## 🖥️ 服务器要求
### 最低配置
- CPU: 2核
- 内存: 4GB
- 硬盘: 40GB
- 带宽: 5Mbps
### 推荐配置
- CPU: 4核
- 内存: 8GB
- 硬盘: 100GB
- 带宽: 10Mbps
### 操作系统
- Ubuntu 20.04/22.04 LTS推荐
- CentOS 7/8
- Debian 10/11
---
## 📦 第一步:准备服务器环境
### 1.1 更新系统
```bash
# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y
# CentOS
sudo yum update -y
```
### 1.2 安装必要软件
```bash
# Ubuntu/Debian
sudo apt install -y git curl wget vim unzip
# CentOS
sudo yum install -y git curl wget vim unzip
```
---
## 🐘 第二步:安装 PHP 环境
### 2.1 安装 PHP 8.0
```bash
# Ubuntu/Debian
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install -y php8.0 php8.0-fpm php8.0-mysql php8.0-xml php8.0-mbstring \
php8.0-curl php8.0-zip php8.0-gd php8.0-bcmath php8.0-json
# CentOS
sudo yum install -y epel-release
sudo yum install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm
sudo yum module reset php -y
sudo yum module enable php:remi-8.0 -y
sudo yum install -y php php-fpm php-mysql php-xml php-mbstring \
php-curl php-zip php-gd php-bcmath php-json
```
### 2.2 配置 PHP
```bash
# 编辑 php.ini
sudo vim /etc/php/8.0/fpm/php.ini
# 修改以下配置
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 300
memory_limit = 256M
```
### 2.3 启动 PHP-FPM
```bash
# Ubuntu/Debian
sudo systemctl start php8.0-fpm
sudo systemctl enable php8.0-fpm
# CentOS
sudo systemctl start php-fpm
sudo systemctl enable php-fpm
```
---
## 🐍 第三步:安装 Python 环境
### 3.1 安装 Python 3.10+
```bash
# Ubuntu/Debian
sudo apt install -y python3.10 python3.10-venv python3-pip
# CentOS
sudo yum install -y python3 python3-pip python3-devel
```
### 3.2 创建虚拟环境
```bash
cd /var/www/AI_GirlFriend
python3 -m venv venv
source venv/bin/activate
```
### 3.3 安装 Python 依赖
```bash
cd lover
pip install -r requirements.txt
```
---
## 🗄️ 第四步:安装和配置 MySQL
### 4.1 安装 MySQL 8.0
```bash
# Ubuntu/Debian
sudo apt install -y mysql-server
# CentOS
sudo yum install -y mysql-server
```
### 4.2 启动 MySQL
```bash
sudo systemctl start mysql
sudo systemctl enable mysql
```
### 4.3 安全配置
```bash
sudo mysql_secure_installation
```
### 4.4 创建数据库和用户
```bash
sudo mysql -u root -p
# 在 MySQL 中执行
CREATE DATABASE fastadmin CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'aiuser'@'localhost' IDENTIFIED BY '你的强密码';
GRANT ALL PRIVILEGES ON fastadmin.* TO 'aiuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```
### 4.5 导入数据库
```bash
# 导入 PHP 后端数据库
mysql -u aiuser -p fastadmin < 归档/xunifriend.sql
# 执行音乐库迁移
mysql -u aiuser -p fastadmin < lover/migrations/add_music_library.sql
# 执行邀请码功能迁移
mysql -u aiuser -p fastadmin < lover/migrations/add_invite_fields.sql
```
---
## 🌐 第五步:安装和配置 Nginx
### 5.1 安装 Nginx
```bash
# Ubuntu/Debian
sudo apt install -y nginx
# CentOS
sudo yum install -y nginx
```
### 5.2 创建 Nginx 配置
```bash
sudo vim /etc/nginx/sites-available/aigirlfriend
```
### 5.3 Nginx 配置内容
```nginx
# PHP 后端配置
server {
listen 30100;
server_name your-domain.com; # 改为你的域名或 IP
root /var/www/AI_GirlFriend/xunifriend_RaeeC/public;
index index.php index.html;
# 日志
access_log /var/log/nginx/php_access.log;
error_log /var/log/nginx/php_error.log;
# PHP 处理
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 静态文件
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
}
# Python 后端配置
server {
listen 30101;
server_name your-domain.com; # 改为你的域名或 IP
# 日志
access_log /var/log/nginx/python_access.log;
error_log /var/log/nginx/python_error.log;
# 反向代理到 Python 应用
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时设置
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# 静态文件(如果有)
location /static/ {
alias /var/www/AI_GirlFriend/lover/static/;
}
}
```
### 5.4 启用配置
```bash
# Ubuntu/Debian
sudo ln -s /etc/nginx/sites-available/aigirlfriend /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
# CentOS
sudo cp /etc/nginx/sites-available/aigirlfriend /etc/nginx/conf.d/aigirlfriend.conf
sudo nginx -t
sudo systemctl restart nginx
```
---
## 📁 第六步:上传项目文件
### 6.1 创建项目目录
```bash
sudo mkdir -p /var/www/AI_GirlFriend
sudo chown -R $USER:$USER /var/www/AI_GirlFriend
```
### 6.2 上传文件(使用 Git 或 FTP
**方法 1使用 Git**
```bash
cd /var/www
git clone https://your-repo-url.git AI_GirlFriend
```
**方法 2使用 SCP**
```bash
# 在本地电脑执行
scp -r C:\Users\Administrator\Desktop\Project\AI_GirlFriend user@server-ip:/var/www/
```
**方法 3使用 FTP 工具**
- 使用 FileZilla 或 WinSCP
- 上传整个项目文件夹
### 6.3 设置权限
```bash
cd /var/www/AI_GirlFriend
# PHP 项目权限
sudo chown -R www-data:www-data xunifriend_RaeeC/
sudo chmod -R 755 xunifriend_RaeeC/
sudo chmod -R 777 xunifriend_RaeeC/runtime/
sudo chmod -R 777 xunifriend_RaeeC/public/uploads/
# Python 项目权限
sudo chown -R $USER:$USER lover/
sudo chmod -R 755 lover/
sudo mkdir -p public/tts public/music
sudo chmod -R 777 public/
```
---
## ⚙️ 第七步:配置环境变量
### 7.1 配置 PHP 后端
```bash
cd /var/www/AI_GirlFriend/xunifriend_RaeeC
# 编辑数据库配置
vim application/database.php
```
修改数据库配置:
```php
return [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'fastadmin',
'username' => 'aiuser',
'password' => '你的数据库密码',
'hostport' => '3306',
'charset' => 'utf8mb4',
];
```
### 7.2 配置 Python 后端
```bash
cd /var/www/AI_GirlFriend/lover
# 创建 .env 文件
vim .env
```
.env 文件内容:
```env
# 应用配置
APP_ENV=production
DEBUG=False
# 数据库配置
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=aiuser
DB_PASSWORD=你的数据库密码
DB_NAME=fastadmin
# JWT 配置
JWT_SECRET_KEY=你的随机密钥至少32位
JWT_ALGORITHM=HS256
JWT_EXPIRE_MINUTES=43200
# DashScope API阿里云通义千问
DASHSCOPE_API_KEY=sk-2473385fd6d54a58a703ce6b92a62074
# 阿里云 OSS 配置
OSS_ACCESS_KEY_ID=LTAI5tBzjogJDx4JzRYoDyEM
OSS_ACCESS_KEY_SECRET=43euicRkkzlLjGTYzFYkTupcW7N5w3
OSS_BUCKET=hello12312312
OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com
CDN_DOMAIN=https://hello12312312.oss-cn-hangzhou.aliyuncs.com
```
---
## 🔄 第八步:配置 Systemd 服务Python 后端)
### 8.1 创建 Systemd 服务文件
```bash
sudo vim /etc/systemd/system/aigirlfriend-python.service
```
### 8.2 服务文件内容
```ini
[Unit]
Description=AI GirlFriend Python Backend
After=network.target mysql.service
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/AI_GirlFriend
Environment="PATH=/var/www/AI_GirlFriend/venv/bin"
ExecStart=/var/www/AI_GirlFriend/venv/bin/uvicorn lover.main:app --host 0.0.0.0 --port 8000 --workers 4
Restart=always
RestartSec=10
# 日志
StandardOutput=append:/var/log/aigirlfriend-python.log
StandardError=append:/var/log/aigirlfriend-python-error.log
[Install]
WantedBy=multi-user.target
```
### 8.3 启动服务
```bash
sudo systemctl daemon-reload
sudo systemctl start aigirlfriend-python
sudo systemctl enable aigirlfriend-python
sudo systemctl status aigirlfriend-python
```
---
## 🔥 第九步:配置防火墙
### 9.1 开放端口
```bash
# Ubuntu/Debian (UFW)
sudo ufw allow 30100/tcp
sudo ufw allow 30101/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
# CentOS (Firewalld)
sudo firewall-cmd --permanent --add-port=30100/tcp
sudo firewall-cmd --permanent --add-port=30101/tcp
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload
```
---
## 📱 第十步:部署前端
### 10.1 修改 API 地址
```bash
cd /var/www/AI_GirlFriend/xuniYou
vim utils/request.js
```
修改为生产环境地址:
```javascript
// 生产环境
export const baseURL = 'http://your-domain.com:30100' // 或使用域名
export const baseURLPy = 'http://your-domain.com:30101'
```
### 10.2 编译 H5 版本
```bash
# 在本地开发机器上
cd xuniYou
npm install
npm run build:h5
```
### 10.3 上传 H5 文件
```bash
# 将 unpackage/dist/build/h5 目录上传到服务器
scp -r unpackage/dist/build/h5/* user@server-ip:/var/www/AI_GirlFriend/h5/
```
### 10.4 配置 Nginx 服务 H5
```bash
sudo vim /etc/nginx/sites-available/aigirlfriend-h5
```
添加配置:
```nginx
server {
listen 80;
server_name your-domain.com;
root /var/www/AI_GirlFriend/h5;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
```
启用配置:
```bash
sudo ln -s /etc/nginx/sites-available/aigirlfriend-h5 /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
---
## 🔒 第十一步:配置 HTTPS可选但推荐
### 11.1 安装 Certbot
```bash
# Ubuntu/Debian
sudo apt install -y certbot python3-certbot-nginx
# CentOS
sudo yum install -y certbot python3-certbot-nginx
```
### 11.2 获取 SSL 证书
```bash
sudo certbot --nginx -d your-domain.com
```
### 11.3 自动续期
```bash
sudo certbot renew --dry-run
```
---
## 📊 第十二步:监控和日志
### 12.1 查看日志
```bash
# Nginx 日志
sudo tail -f /var/log/nginx/php_access.log
sudo tail -f /var/log/nginx/python_access.log
# Python 应用日志
sudo tail -f /var/log/aigirlfriend-python.log
# PHP 日志
sudo tail -f /var/log/php8.0-fpm.log
# 系统日志
sudo journalctl -u aigirlfriend-python -f
```
### 12.2 性能监控
```bash
# 安装监控工具
sudo apt install -y htop iotop nethogs
# 查看资源使用
htop
```
---
## 🧪 第十三步:测试部署
### 13.1 测试 PHP 后端
```bash
curl http://your-domain.com:30100
```
### 13.2 测试 Python 后端
```bash
curl http://your-domain.com:30101/health
curl http://your-domain.com:30101/docs # API 文档
```
### 13.3 测试前端
```bash
# 在浏览器中访问
http://your-domain.com
```
---
## 🔧 常见问题排查
### 问题 1502 Bad Gateway
```bash
# 检查 PHP-FPM 状态
sudo systemctl status php8.0-fpm
# 检查 Python 服务状态
sudo systemctl status aigirlfriend-python
# 查看错误日志
sudo tail -f /var/log/nginx/error.log
```
### 问题 2数据库连接失败
```bash
# 检查 MySQL 状态
sudo systemctl status mysql
# 测试数据库连接
mysql -u aiuser -p fastadmin
```
### 问题 3权限问题
```bash
# 重新设置权限
sudo chown -R www-data:www-data /var/www/AI_GirlFriend/xunifriend_RaeeC/
sudo chmod -R 777 /var/www/AI_GirlFriend/xunifriend_RaeeC/runtime/
sudo chmod -R 777 /var/www/AI_GirlFriend/public/
```
### 问题 4Python 依赖问题
```bash
# 重新安装依赖
cd /var/www/AI_GirlFriend
source venv/bin/activate
pip install -r lover/requirements.txt --upgrade
```
---
## 🚀 性能优化建议
### 1. 启用 Gzip 压缩
在 Nginx 配置中添加:
```nginx
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json;
```
### 2. 配置 Redis 缓存
```bash
# 安装 Redis
sudo apt install -y redis-server
sudo systemctl start redis
sudo systemctl enable redis
```
### 3. 使用 CDN
- 将静态资源上传到阿里云 OSS
- 配置 CDN 加速
### 4. 数据库优化
```sql
-- 添加索引
ALTER TABLE nf_user ADD INDEX idx_token (token);
ALTER TABLE nf_chat_message ADD INDEX idx_user_session (user_id, session_id);
```
---
## 📋 部署检查清单
- [ ] 服务器环境准备完成
- [ ] PHP 8.0 安装并配置
- [ ] Python 3.10+ 安装并配置
- [ ] MySQL 8.0 安装并配置
- [ ] Nginx 安装并配置
- [ ] 项目文件上传完成
- [ ] 数据库导入完成
- [ ] 环境变量配置完成
- [ ] Systemd 服务配置完成
- [ ] 防火墙端口开放
- [ ] 前端编译并部署
- [ ] HTTPS 证书配置(可选)
- [ ] 所有服务测试通过
- [ ] 日志监控配置完成
---
## 📞 技术支持
如遇到问题,请检查:
1. 服务器日志:`/var/log/nginx/`, `/var/log/aigirlfriend-python.log`
2. 系统日志:`sudo journalctl -xe`
3. 服务状态:`sudo systemctl status [service-name]`
---
**部署完成!** 🎉
访问地址:
- 前端http://your-domain.com
- PHP 后端http://your-domain.com:30100
- Python 后端http://your-domain.com:30101
- API 文档http://your-domain.com:30101/docs