From 8613e1560b9baaca26624bb70514df23e11f33d2 Mon Sep 17 00:00:00 2001 From: xiao12feng8 <16507319+xiao12feng8@user.noreply.gitee.com> Date: Tue, 3 Feb 2026 18:00:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=9A=E9=82=80=E8=AF=B7?= =?UTF-8?q?=E6=9C=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lover/migrations/add_invite_fields.sql | 33 + lover/migrations/check_invite_fields.sql | 20 + lover/migrations/fix_invite_complete.sql | 107 +++ lover/migrations/fix_invite_field_types.sql | 56 ++ lover/migrations/fix_invite_fields.sql | 123 ++++ lover/migrations/test_invite_function.sql | 73 +++ xuniYou/pages/index/index.vue | 9 +- 开发/2026年2月3日/登录无法进入问题修复.md | 547 ++++++++++++++++ 开发/2026年2月3日/邀请码功能修复.md | 236 +++++++ 开发/2026年2月3日/邀请码问题诊断.md | 329 ++++++++++ 部署指南.md | 690 ++++++++++++++++++++ 11 files changed, 2221 insertions(+), 2 deletions(-) create mode 100644 lover/migrations/add_invite_fields.sql create mode 100644 lover/migrations/check_invite_fields.sql create mode 100644 lover/migrations/fix_invite_complete.sql create mode 100644 lover/migrations/fix_invite_field_types.sql create mode 100644 lover/migrations/fix_invite_fields.sql create mode 100644 lover/migrations/test_invite_function.sql create mode 100644 开发/2026年2月3日/登录无法进入问题修复.md create mode 100644 开发/2026年2月3日/邀请码功能修复.md create mode 100644 开发/2026年2月3日/邀请码问题诊断.md create mode 100644 部署指南.md diff --git a/lover/migrations/add_invite_fields.sql b/lover/migrations/add_invite_fields.sql new file mode 100644 index 0000000..7b2b5b5 --- /dev/null +++ b/lover/migrations/add_invite_fields.sql @@ -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'); diff --git a/lover/migrations/check_invite_fields.sql b/lover/migrations/check_invite_fields.sql new file mode 100644 index 0000000..fe6899e --- /dev/null +++ b/lover/migrations/check_invite_fields.sql @@ -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; diff --git a/lover/migrations/fix_invite_complete.sql b/lover/migrations/fix_invite_complete.sql new file mode 100644 index 0000000..a3cae92 --- /dev/null +++ b/lover/migrations/fix_invite_complete.sql @@ -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 ''; diff --git a/lover/migrations/fix_invite_field_types.sql b/lover/migrations/fix_invite_field_types.sql new file mode 100644 index 0000000..cb6d52c --- /dev/null +++ b/lover/migrations/fix_invite_field_types.sql @@ -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 '状态'; diff --git a/lover/migrations/fix_invite_fields.sql b/lover/migrations/fix_invite_fields.sql new file mode 100644 index 0000000..e5c40ea --- /dev/null +++ b/lover/migrations/fix_invite_fields.sql @@ -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; diff --git a/lover/migrations/test_invite_function.sql b/lover/migrations/test_invite_function.sql new file mode 100644 index 0000000..380e664 --- /dev/null +++ b/lover/migrations/test_invite_function.sql @@ -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; diff --git a/xuniYou/pages/index/index.vue b/xuniYou/pages/index/index.vue index ad9877e..4f6e161 100644 --- a/xuniYou/pages/index/index.vue +++ b/xuniYou/pages/index/index.vue @@ -929,8 +929,13 @@ } } }, - getBobbiesList: '', - loverBasicList: '', + getBobbiesList: { + reg_step: 1, // 默认第一步:完善个人资料 + level: 0, + intimacy: 0, + next_level_intimacy: 100 + }, + loverBasicList: null, // 改为 null 而不是空字符串 underAgeEnabled: false, // 添加青少年模式状态变量 historyModalVisible: false, historyModalType: 'sing', diff --git a/开发/2026年2月3日/登录无法进入问题修复.md b/开发/2026年2月3日/登录无法进入问题修复.md new file mode 100644 index 0000000..01acac8 --- /dev/null +++ b/开发/2026年2月3日/登录无法进入问题修复.md @@ -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 + +### 原因 3:API 请求失败 + +可能的原因: +- 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 + + 加载中... + + + + + +``` + +在 `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 + + + + + +``` + +--- + +## 常见错误和解决方法 + +### 错误 1:Cannot read property 'reg_step' of '' + +**原因**:`getBobbiesList` 为空字符串 + +**解决**:使用方案 1,将初始值改为对象 + +### 错误 2:Network Error + +**原因**:后端服务未启动或网络问题 + +**解决**: +1. 检查 PHP 服务是否运行 +2. 检查端口是否正确(30100) +3. 检查防火墙设置 + +### 错误 3:401 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 + + + +``` + +--- + +## 测试步骤 + +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. 添加完善的错误处理 + +修复后,用户登录成功就能正常进入系统了! diff --git a/开发/2026年2月3日/邀请码功能修复.md b/开发/2026年2月3日/邀请码功能修复.md new file mode 100644 index 0000000..8346208 --- /dev/null +++ b/开发/2026年2月3日/邀请码功能修复.md @@ -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. 更新部署文档,添加邀请码迁移步骤 diff --git a/开发/2026年2月3日/邀请码问题诊断.md b/开发/2026年2月3日/邀请码问题诊断.md new file mode 100644 index 0000000..47af673 --- /dev/null +++ b/开发/2026年2月3日/邀请码问题诊断.md @@ -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); + } + }); + ``` + +--- + +### 问题 4:Python 服务认证失败 + +**检查 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 + ``` + +--- + +## 联系支持 + +提供以上信息可以帮助快速定位问题。 diff --git a/部署指南.md b/部署指南.md new file mode 100644 index 0000000..009ab18 --- /dev/null +++ b/部署指南.md @@ -0,0 +1,690 @@ +# 🚀 AI 女友项目部署指南 + +## 📋 项目架构 + +- **PHP 后端**:FastAdmin + ThinkPHP(用户管理、基础功能) +- **Python 后端**:FastAPI(AI 功能、唱歌、跳舞等) +- **前端**:uni-app(H5/小程序/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 +``` + +--- + +## 🔧 常见问题排查 + +### 问题 1:502 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/ +``` + +### 问题 4:Python 依赖问题 +```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