[20190212]删除tab$记录的恢复3.txt

[20190212]删除tab$记录的恢复3.txt

--//春节前几天做了删除tan$记录的测试,链接:
http://blog.itpub.net/267265/viewspace-2565245/=> [20190130]删除tab$记录的恢复.txt
http://blog.itpub.net/267265/viewspace-2565250/=> [20190130]删除tab$记录的恢复2.txt

--//实际上我一直认为能拷贝出来数据就ok了.这样恢复的数据库不能在使用,问题多多.
--//我继续测试实际上建表等操作都会报类似的:
ORA-00600: internal error code, arguments: [kdBlkCheckError], [1], [94232], [6110], [], [], [], [], [], [], [], []
--//这样的错误.主要问题在于system块的检查更加严格.

--//今天尝试恢复6110,6111,6112之类的错误.

1.环境:
SCOTT@book> @ ver1
PORT_STRING                    VERSION        BANNER
------------------------------ -------------- --------------------------------------------------------------------------------
x86_64/Linux 2.4.xx            11.2.0.4.0     Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

--//前面已经恢复只读状态exp操作是正常的.具体看前面的文章.
--//首先更正前面脚本的几个错误:http://blog.itpub.net/267265/viewspace-2565250/=> [20190130]删除tab$记录的恢复2.txt

 57                 echo "x /rx dba $dba *kdbr[$begin]" | rlbbed | egrep "^^flag@.*: *0x7c" > /dev/null
--//原来写成echo "x /rx dba $dba *kdbr[$begin]" | rlbbed | egrep "^^flag@.*: 0x7c" > /dev/null ,可能出现多个空格的情况.
 58                 if [ $? -eq 0 ]
 59                 then
 60                     echo "dba=$dba;ckix_value=0" >> scan4a.txt
 61                 fi

$ cat scana.sh
#! /bin/bash
cat clearout.txt | while read dba
do
    echo set dba $dba
    echo -n "assign "
    echo -e  "set dba $dba \np ktbbh" |rlbbed | /bin/grep -B 4 -A 9 "ub4 kxidsqn \+@.*0x00000751$"| egrep "ktbbhitl|ktbitflg" | cut -c11-21,55-60 | paste -d. - - | sed -e 's/   /=/'
--//这里写错,原来写成4204236,实际上应该是$dba,
    echo sum apply dba $dba
done

--//注:http://blog.itpub.net/267265/viewspace-2565250/=> [20190130]删除tab$记录的恢复2.txt 里面的脚本已经更正.

2.我前面做了冷备份.首先恢复冷备份:
$ /bin/cp -r /home/oracle/backup/book_20190122_bad/* /mnt/ramdisk/book/
*/

$ . scan.sh
process 1 start : 2019/02/12 08:45:44 scan dba 1,144 ,  create scan1.txt about ktetbdba,ktetbnbk
process 1 finish: 2019/02/12 08:45:44,enter continue...
process 2 start : 2019/02/12 08:45:45 scan block , get kdbtnrow,kdbtoffs ang grep kdbtnrow=0
process 2 finish: 2019/02/12 08:46:16,enter continue...
process 3 start : 2019/02/12 08:46:50 scan block , create bbed'script scan3_bbed.txt for modify delete of flag  and create scan4a.txt about block of ckix
process 3 finish: 2019/02/12 08:52:01,enter continue...
process 4 start : 2019/02/12 08:52:06 create bbed's scan4_bbed.txt for modify cluster of mref of value
process 4 finish: 2019/02/12 08:53:12,enter continue...
process 5 start : create bbed's scan5_bbed.txt for sum apply
process 5 finish: 2019/02/12 08:54:11,enter continue...

--//查看生成的bbed脚本:
$ cat scan4m_bbed.txt
assign dba 4288539 offset 8169 = 1
assign dba 4288546 offset 8145 = 1

--//前面我提到过这2块dba的不需要恢复(里面记录的scn不是这个事务产生的),或者讲scan4m_bbed.txt脚本记录的dba可能存在多恢复记录的情况.最好仔细检查.

$ grep -v 0x6c scan3_bbed.txt
assign /x dba 4194451 offset 7349 = 0x20
assign /x dba 4197642 offset 7888 = 0x20
assign /x dba 4207636 offset 7087 = 0x20
assign /x dba 4225801 offset 3621 = 0x4c
assign /x dba 4225801 offset 4436 = 0x4c
assign /x dba 4288537 offset 7717 = 0x4c
--//这3条记录存在行链接或者迁移的情况,前面已经解析不再说明.
--//修改scan3_bbed.txt文件,注解如下2行不需要恢复.
$ grep ^# scan3_bbed.txt
#assign /x dba 4288539 offset 7920 = 0x6c
#assign /x dba 4288546 offset 7851 = 0x6c

$ grep  0x6c scan3_bbed.txt | grep -v "^#" |wc
   2963   23704  124395

--//2963+3=2966,这样恢复的记录数量与实际情况相符.

3.执行生成的bbed脚本并修复块 6110,6111,6112错误.

bbed parfile=/home/oracle/bbed/bbed.par cmdfile=/home/oracle/hrp430/bbed/scan3_bbed.txt
bbed parfile=/home/oracle/bbed/bbed.par cmdfile=/home/oracle/hrp430/bbed/scan4k_bbed.txt
bbed parfile=/home/oracle/bbed/bbed.par cmdfile=/home/oracle/hrp430/bbed/scan5_bbed.txt

--//前面提到启动遇到如下错误,主要是因为延迟块提交的问题:
ORA-00600: internal error code, arguments: [kdBlkCheckError], [1], [94232], [6110], [], [], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [kdBlkCheckError], [1], [9951], [6110], [], [], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [kdBlkCheckError], [1], [31548], [6110], [], [], [], [], [], [], [], []

--//编写脚本如下,主要获得延迟块提交的数据块,设置提交标识.并且将对应的ktbbhitl[N]._ktbitun._ktbitfsc=0.
--//这样会导致verify时出现如下错误,例子:
BBED> set dba 4204236
        DBA             0x004026cc (4204236 1,9932)

BBED> verify
DBVERIFY - Verification starting
FILE = /mnt/ramdisk/book/system01.dbf
BLOCK = 9932

Block Checking: DBA = 4204236, Block Type = KTB-managed data block
data header at 0x7fb110a7b25c
kdbchk: space available on commit is incorrect
        tosp=5052 fsc=0 stb=0 avsp=4937
Block 9932 failed with check code 6111
--//解决方法就是assign kdbhtosp=kdbhavsp;sum apply就ok了.
--//我以前处理这个错误有点繁琐.实际上设置fsc=0,设置提交标识为快速提交标识0x2,在执行assign kdbhtosp=kdbhavsp,
--//这样记录里面lock标识不需要设置为0x0,这样简单许多.

$ cat scanb.sh
#! /bin/bash
# get dba of tailchk ,grep begin # line, save scan6.txt
# and then grep -v 0x5f5f06,save clearout.txt,other save notclearout.txt

grep dba scan3_bbed.txt | grep -v "^#" | cut -d" " -f4 | uniq | while read dba
do
        echo -n $dba :
        echo "p dba $dba offset 0 tailchk 8188"| rlbbed | grep "ub4 tailchk"
done >| scan6.txt

grep -v 0x5f5f06 scan6.txt >|  clearout.txt
grep    0x5f5f06 scan6.txt >|  notclearout.txt

# create modify ktbbhitl[N].ktbitflg , ktbbhitl[N]._ktbitun._ktbitfsc=0 and kdbhtosp=kdbhavsp of script.
cat clearout.txt | cut -d" " -f1 |while read dba
do
        echo set dba $dba
    echo -n "assign "
    echo -e  "set dba $dba \np ktbbh" |rlbbed | /bin/grep -B 4 -A 9 "ub4 kxidsqn \+@.*0x00000751$"| egrep "ktbbhitl|ktbitflg" | cut -c11-21,55-60 | paste -d. - - | sed -e 's/   /=/'
    echo -n "assign "
    echo -e  "set dba $dba \np ktbbh" |rlbbed | /bin/grep -B 4 -A 9 "ub4 kxidsqn \+@.*0x00000751$" | egrep "ktbbhitl"| cut -c11-21 | sed -e 's/$/._ktbitun._ktbitfsc=0/'
        echo "assign kdbhtosp=kdbhavsp"
    echo sum apply dba $dba
done >| clearout_bbed.txt

# create modify ktbbhitl[N]._ktbitun._ktbitfsc=0 and kdbhtosp=kdbhavsp of script.
cat notclearout.txt | cut -d" " -f1 |while read dba
do
        echo set dba $dba
#   echo -n "assign "
#   echo -e  "set dba $dba \np ktbbh" |rlbbed | /bin/grep -B 4 -A 9 "ub4 kxidsqn \+@.*0x00000751$"| egrep "ktbbhitl|ktbitflg" | cut -c11-21,55-60 | paste -d. - - | sed -e 's/   /=/'
    echo -n "assign "
    echo -e  "set dba $dba \np ktbbh" |rlbbed | /bin/grep -B 4 -A 9 "ub4 kxidsqn \+@.*0x00000751$" | egrep "ktbbhitl"| cut -c11-21 | sed -e 's/$/._ktbitun._ktbitfsc=0/'
        echo "assign kdbhtosp=kdbhavsp"
    echo sum apply dba $dba
done >| notclearout_bbed.txt

--//执行以上脚本:
$ . scanb.sh
$ grep  "ktbbhitl[1].ktbitflg=" clearout_bbed.txt | grep -v 0x0
$ echo $?
1
--//说明这些块都没有打上提交标识.

$ head -5 clearout_bbed.txt
set dba 4204236
assign ktbbhitl[1].ktbitflg=0x0002
assign ktbbhitl[1]._ktbitun._ktbitfsc=0
assign kdbhtosp=kdbhavsp
sum apply dba 4204236

--//使用vim执行:%s/ktbitflg=0x0/ktbitflg=0x2/.也就是设置提交标识.注意检查替换是否74行.
--//补充说明一下,我前面测试执行的是%s/=0x00/=0x20/,实际上提交标识仅仅占半个字节(4位),这样写有点问题,不过一般不会出现问题.
--//1块修改255条以上的情况在这里不会出现(至少对于这个表是如此).

$ head -5 clearout_bbed.txt
set dba 4204236
assign ktbbhitl[1].ktbitflg=0x2002
assign ktbbhitl[1]._ktbitun._ktbitfsc=0
assign kdbhtosp=kdbhavsp
sum apply dba 4204236

--//执行如下:
bbed parfile=/home/oracle/bbed/bbed.par cmdfile=/home/oracle/hrp430/bbed/clearout_bbed.txt
bbed parfile=/home/oracle/bbed/bbed.par cmdfile=/home/oracle/hrp430/bbed/notclearout_bbed.txt

$ dbv  file=/mnt/ramdisk/book/system01.dbf
DBVERIFY: Release 11.2.0.4.0 - Production on Tue Feb 12 10:37:09 2019
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
DBVERIFY - Verification starting : FILE = /mnt/ramdisk/book/system01.dbf
DBVERIFY - Verification complete
Total Pages Examined         : 97280
Total Pages Processed (Data) : 64316
Total Pages Failing   (Data) : 0
Total Pages Processed (Index): 13442
Total Pages Failing   (Index): 0
Total Pages Processed (Other): 4185
Total Pages Processed (Seg)  : 1
Total Pages Failing   (Seg)  : 0
Total Pages Empty            : 15337
Total Pages Marked Corrupt   : 0
Total Pages Influx           : 0
Total Pages Encrypted        : 0
Highest block SCN            : 393502590 (3.393502590)

--//现在OK了.
4.禁用sys.tab$的索引I_TAB1.

--//这样恢复,索引与表存在不一致情况,要禁用sys.tab$的索引I_TAB1.

BBED> x /rnnc dba 1,523 *kdbr[9]
rowdata[1269]                               @4910
-------------
flag@4910: 0x2c (KDRHFL, KDRHFF, KDRHFH)
lock@4911: 0x01
cols@4912:    3
col    0[2] @4913: 33
col    1[2] @4916: 33
col  2[189] @4919: CREATE INDEX I_TAB1 ON TAB$(BOBJ#) PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE (  INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483
645 PCTINCREASE 0 OBJNO 33 EXTENTS (FILE 1 BLOCK 312))

--//设置flag=3c,表示删除.
BBED> assign /x  dba 1,523 offset 4910= 0x3c
Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) y
ub1 rowdata[0]                              @4910     0x3c

BBED> sum apply dba 1,523
Check value for File 1, Block 523:
current = 0x7e6b, required = 0x7e6b

BBED> verify dba 1,523
DBVERIFY - Verification starting
FILE = /mnt/ramdisk/book/system01.dbf
BLOCK = 523

Block Checking: DBA = 4194827, Block Type = KTB-managed data block
data header at 0x6eee44
kdbchk: the amount of space used is not equal to block size
        used=4398 fsc=0 avsp=3525 dtl=8120
Block 523 failed with check code 6110
--//先不理会这个错误.

5.启动数据库看看:

SYS@book> startup pfile='/tmp/@.ora';
ORACLE instance started.
Total System Global Area  643084288 bytes
Fixed Size                  2255872 bytes
Variable Size             205521920 bytes
Database Buffers          427819008 bytes
Redo Buffers                7487488 bytes
Database mounted.
Database opened.

SYS@book> shutdown  immediate ;
Database closed.
Database dismounted.
ORACLE instance shut down.

SYS@book> startup open read only pfile='/tmp/book.ora'
ORACLE instance started.
Total System Global Area  643084288 bytes
Fixed Size                  2255872 bytes
Variable Size             205521920 bytes
Database Buffers          427819008 bytes
Redo Buffers                7487488 bytes
Database mounted.
Database opened.

--//注/tmp/book.ora最好加入修改如下:
*._system_trig_enabled=false
*.job_queue_processes=0

SYS@book> select * from sys.tab$ minus select * from orachk001;
no rows selected

SYS@book> select * from orachk001 minus select * from sys.tab$;
no rows selected

--//OK,几乎完美恢复.我执行如下select * from sh.sales;,顺利读出,没有任何问题.
--//现在建立删除表没有任何问题.

SCOTT@book> create table t as select * from all_objects;
Table created.

SCOTT@book> drop table t purge ;
Table dropped.

6.剩下恢复tab$.索引I_TAB1,写得有点长.另写一篇修复索引的帖子,感觉这步很难,也许要先放一放.
--//最后说明一点,我的是测试环境,也许真实的环境更加复杂.我可能还遗漏一些细节...^_^.

转载于:https://www.cnblogs.com/lfree/p/10368167.html

// API 基础路径 const API_BASE_URL = '/api'; new Vue({ el: '#app', data: { currentView: 'home', showLogin: false, showRegister: false, showCreateClub: false, showCreateActivity: false, clubTab: 'all', activityTab: 'all', profileTab: 'clubs', adminTab: 'clubs', loginForm: { username: '', password: '' }, registerForm: { name: '', studentId: '', email: '', phone: '', password: '', confirm: '' }, newClub: { name: '', description: '' }, newActivity: { title: '', description: '', location: '', startTime: '', endTime: '' }, currentUser: null, isSysAdmin: false, isClubAdmin: false, stats: null, clubs: [], activities: [], popularClubs: [], recentActivities: [], userClubs: [], userActivities: [], userApplications: [], pendingClubs: [], pendingApplications: [], loadingClubs: false, loadingActivities: false, loadingUserClubs: false, loadingUserActivities: false, loadingUserApplications: false, loadingPendingClubs: false, loadingPendingApplications: false }, computed: { filteredClubs() { if (this.clubTab === 'my' && this.currentUser) { return this.clubs.filter(club => club.isMember); } return this.clubs; }, filteredActivities() { if (this.activityTab === 'joined' && this.currentUser) { return this.activities.filter(act => this.userActivities.some(ua => ua.id === act.id) ); } if (this.activityTab === 'my' && this.isClubAdmin) { return this.activities; } return this.activities; } }, async created() { this.loadHomeData(); // 检查本地存储是否有登录信息 const savedUser = localStorage.getItem('currentUser'); if (savedUser) { this.currentUser = JSON.parse(savedUser); this.checkAdminStatus(); } }, methods: { async loadHomeData() { try { // 加载统计数据 const statsRes = await axios.get(`${API_BASE_URL}/stats`); this.stats = statsRes.data; // 加载热门社团 const popularClubsRes = await axios.get(`${API_BASE_URL}/clubs/popular`); this.popularClubs = popularClubsRes.data; // 加载近期活动 const recentActivitiesRes = await axios.get(`${API_BASE_URL}/activities/recent`); this.recentActivities = recentActivitiesRes.data; } catch (error) { console.error('加载首页数据失败:', error); } }, async loadClubs() { this.loadingClubs = true; try { const res = await axios.get(`${API_BASE_URL}/clubs`, { headers: this.getAuthHeader() }); this.clubs = res.data; } catch (error) { console.error('加载社团数据失败:', error); } finally { this.loadingClubs = false; } }, async loadActivities() { this.loadingActivities = true; try { const res = await axios.get(`${API_BASE_URL}/activities`, { headers: this.getAuthHeader() }); this.activities = res.data; } catch (error) { console.error('加载活动数据失败:', error); } finally { this.loadingActivities = false; } }, async loadUserClubs() { this.loadingUserClubs = true; try { const res = await axios.get(`${API_BASE_URL}/users/${this.currentUser.id}/clubs`, { headers: this.getAuthHeader() }); this.userClubs = res.data; } catch (error) { console.error('加载用户社团数据失败:', error); } finally { this.loadingUserClubs = false; } }, async loadUserActivities() { this.loadingUserActivities = true; try { const res = await axios.get(`${API_BASE_URL}/users/${this.currentUser.id}/activities`, { headers: this.getAuthHeader() }); this.userActivities = res.data; } catch (error) { console.error('加载用户活动数据失败:', error); } finally { this.loadingUserActivities = false; } }, async loadUserApplications() { this.loadingUserApplications = true; try { const res = await axios.get(`${API_BASE_URL}/users/${this.currentUser.id}/applications`, { headers: this.getAuthHeader() }); this.userApplications = res.data; } catch (error) { console.error('加载用户申请数据失败:', error); } finally { this.loadingUserApplications = false; } }, async loadPendingClubs() { this.loadingPendingClubs = true; try { const res = await axios.get(`${API_BASE_URL}/admin/pending-clubs`, { headers: this.getAuthHeader() }); this.pendingClubs = res.data; } catch (error) { console.error('加载待审核社团数据失败:', error); } finally { this.loadingPendingClubs = false; } }, async loadPendingApplications() { this.loadingPendingApplications = true; try { const res = await axios.get(`${API_BASE_URL}/admin/pending-applications`, { headers: this.getAuthHeader() }); this.pendingApplications = res.data; } catch (error) { console.error('加载待审核申请数据失败:', error); } finally { this.loadingPendingApplications = false; } }, changeView(view) { this.currentView = view; if (view === 'clubs') { this.loadClubs(); } else if (view === 'activities') { this.loadActivities(); } else if (view === 'admin') { this.adminTab = 'clubs'; this.loadPendingClubs(); } }, async login() { try { const res = await axios.post(`${API_BASE_URL}/auth/login`, this.loginForm); if (res.data.success) { this.currentUser = res.data.user; this.isSysAdmin = this.currentUser.role === 'sys_admin'; this.isClubAdmin = this.currentUser.role === 'club_admin' || this.currentUser.role === 'sys_admin'; this.showLogin = false; // 保存用户信息到本地存储 localStorage.setItem('currentUser', JSON.stringify(this.currentUser)); // 加载用户数据 this.loadUserClubs(); this.loadUserActivities(); this.loadUserApplications(); this.$message.success('登录成功!'); } else { this.$message.error('登录失败: ' + res.data.message); } } catch (error) { console.error('登录失败:', error); this.$message.error('登录失败,请检查网络连接'); } }, async register() { if (this.registerForm.password !== this.registerForm.confirm) { this.$message.error('两次输入的密码不一致'); return; } try { const res = await axios.post(`${API_BASE_URL}/auth/register`, this.registerForm); if (res.data.success) { this.currentUser = res.data.user; this.isSysAdmin = false; this.isClubAdmin = false; this.showRegister = false; // 保存用户信息到本地存储 localStorage.setItem('currentUser', JSON.stringify(this.currentUser)); this.$message.success('注册成功!'); } else { this.$message.error('注册失败: ' + res.data.message); } } catch (error) { console.error('注册失败:', error); this.$message.error('注册失败,请检查网络连接'); } }, logout() { this.currentUser = null; this.isSysAdmin = false; this.isClubAdmin = false; this.currentView = 'home'; // 清除本地存储 localStorage.removeItem('currentUser'); this.$message.success('已退出登录'); }, showProfile() { this.currentView = 'profile'; this.profileTab = 'clubs'; this.loadUserClubs(); }, loadHomeData() { this.$axios.get('/api/stats') .then(response => { // 成功处理 }) .catch(error => { // 关键:记录完整错误信息 console.error('[详细错误]', { status: error.response?.status, data: error.response?.data, headers: error.response?.headers }); }); }, checkAdminStatus() { this.isSysAdmin = this.currentUser.role === 'sys_admin'; this.isClubAdmin = this.currentUser.role === 'club_admin' || this.currentUser.role === 'sys_admin'; }, getAuthHeader() { if (this.currentUser && this.currentUser.token) { return { Authorization: `Bearer ${this.currentUser.token}` }; } return {}; }, async createClub() { try { const res = await axios.post(`${API_BASE_URL}/clubs`, this.newClub, { headers: this.getAuthHeader() }); if (res.data.success) { this.showCreateClub = false; this.newClub = { name: '', description: '' }; this.$message.success('社团创建申请已提交,等待管理员审核'); // 刷新社团列表 this.loadClubs(); } else { this.$message.error('创建社团失败: ' + res.data.message); } } catch (error) { console.error('创建社团失败:', error); this.$message.error('创建社团失败,请稍后再试'); } }, async applyToClub(club) { try { const res = await axios.post(`${API_BASE_URL}/clubs/${club.id}/apply`, {}, { headers: this.getAuthHeader() }); if (res.data.success) { club.isMember = true; this.$message.success(`已申请加入 ${club.name},等待审核`); // 刷新用户社团列表 this.loadUserClubs(); } else { this.$message.error('申请失败: ' + res.data.message); } } catch (error) { console.error('申请加入社团失败:', error); this.$message.error('申请加入社团失败,请稍后再试'); } }, async leaveClub(club) { try { const res = await axios.delete(`${API_BASE_URL}/clubs/${club.id}/members/${this.currentUser.id}`, { headers: this.getAuthHeader() }); if (res.data.success) { club.isMember = false; this.$message.success(`已退出 ${club.name}`); // 刷新用户社团列表 this.loadUserClubs(); } else { this.$message.error('退出社团失败: ' + res.data.message); } } catch (error) { console.error('退出社团失败:', error); this.$message.error('退出社团失败,请稍后再试'); } }, async joinActivity(activity) { try { const res = await axios.post(`${API_BASE_URL}/activities/${activity.id}/join`, {}, { headers: this.getAuthHeader() }); if (res.data.success) { activity.participantCount++; this.$message.success(`已报名参加 ${activity.title}`); // 刷新用户活动列表 this.loadUserActivities(); } else { this.$message.error('报名失败: ' + res.data.message); } } catch (error) { console.error('报名活动失败:', error); this.$message.error('报名活动失败,请稍后再试'); } }, async signActivity(activity) { try { const res = await axios.post(`${API_BASE_URL}/activities/${activity.id}/sign`, {}, { headers: this.getAuthHeader() }); if (res.data.success) { activity.signStatus = 'attended'; this.$message.success(`活动 ${activity.title} 签到成功!`); // 刷新用户活动列表 this.loadUserActivities(); } else { this.$message.error('签到失败: ' + res.data.message); } } catch (error) { console.error('签到失败:', error); this.$message.error('签到失败,请稍后再试'); } }, viewClub(club) { this.$message.info(`查看社团详情: ${club.name}`); }, viewActivity(activity) { this.$message.info(`查看活动详情: ${activity.title}`); }, viewApplication(app) { this.$message.info(`查看申请详情: ${app.name}`); }, activityStatusText(status) { const statusMap = { 'active': '进行中', 'pending': '待审核', 'approved': '已批准', 'rejected': '已拒绝', 'ended': '已结束' }; return statusMap[status] || '未知状态'; }, async approveClub(club) { try { const res = await axios.put(`${API_BASE_URL}/admin/clubs/${club.id}/approve`, {}, { headers: this.getAuthHeader() }); if (res.data.success) { this.$message.success(`已批准 ${club.name} 的创建申请`); // 刷新待审核列表 this.loadPendingClubs(); // 刷新社团列表 this.loadClubs(); } else { this.$message.error('批准失败: ' + res.data.message); } } catch (error) { console.error('批准社团失败:', error); this.$message.error('批准社团失败,请稍后再试'); } }, async rejectClub(club) { try { const res = await axios.put(`${API_BASE_URL}/admin/clubs/${club.id}/reject`, {}, { headers: this.getAuthHeader() }); if (res.data.success) { this.$message.success(`已拒绝 ${club.name} 的创建申请`); // 刷新待审核列表 this.loadPendingClubs(); } else { this.$message.error('拒绝失败: ' + res.data.message); } } catch (error) { console.error('拒绝社团失败:', error); this.$message.error('拒绝社团失败,请稍后再试'); } }, async approveApplication(app) { try { const res = await axios.put(`${API_BASE_URL}/admin/applications/${app.id}/approve`, {}, { headers: this.getAuthHeader() }); if (res.data.success) { this.$message.success(`已批准 ${app.userName} 加入 ${app.clubName}`); // 刷新待审核列表 this.loadPendingApplications(); } else { this.$message.error('批准失败: ' + res.data.message); } } catch (error) { console.error('批准申请失败:', error); this.$message.error('批准申请失败,请稍后再试'); } }, async rejectApplication(app) { try { const res = await axios.put(`${API_BASE_URL}/admin/applications/${app.id}/reject`, {}, { headers: this.getAuthHeader() }); if (res.data.success) { this.$message.success(`已拒绝 ${app.userName} 加入 ${app.clubName}`); // 刷新待审核列表 this.loadPendingApplications(); } else { this.$message.error('拒绝失败: ' + res.data.message); } } catch (error) { console.error('拒绝申请失败:', error); this.$message.error('拒绝申请失败,请稍后再试'); } }, async createActivity() { try { const res = await axios.post(`${API_BASE_URL}/activities`, this.newActivity, { headers: this.getAuthHeader() }); if (res.data.success) { this.showCreateActivity = false; this.newActivity = { title: '', description: '', location: '', startTime: '', endTime: '' }; this.$message.success('活动已发布,等待审核'); // 刷新活动列表 this.loadActivities(); } else { this.$message.error('发布活动失败: ' + res.data.message); } } catch (error) { console.error('发布活动失败:', error); this.$message.error('发布活动失败,请稍后再试'); } }, formatDate(dateString) { if (!dateString) return ''; const date = new Date(dateString); return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }, editProfile() { this.$message.info('编辑个人信息功能'); }, loadMyActivities() { // 简化实现,实际中应该调用API this.loadingActivities = true; setTimeout(() => { this.activities = this.activities.filter(activity => activity.clubAdminId === this.currentUser.id ); this.loadingActivities = false; }, 500); } } });
最新发布
06-08
<think>我们正在分析一个Vue前端应用,该应用与之前提供的Express.js后端交互。前端代码中主要包含了一个Vue实例,其中包含了多个数据属性、计算属性、生命周期钩子和方法。根据用户的问题,我们之前在后端解决了“Cannotreadpropertiesofundefined(reading&#39;get&#39;)”的错误。现在,我们来看前端代码,特别是`created`钩子,因为错误发生在该钩子中。在`created`钩子中,调用了`this.loadHomeData()`,并且尝试从本地存储中恢复用户登录状态。如果本地存储中有用户信息,就会解析并设置`currentUser`,然后检查管理员状态。然而,错误信息表明在`created`钩子中出现了试图读取undefined的&#39;get&#39;属性。这通常发生在尝试调用一个undefined的方法(比如axios的get方法)或者在一个未定义的对象上访问属性。观察`loadHomeData`方法,它使用了`this.$axios`。但是,在整个代码中,我们并没有看到将axios挂载到Vue原型上的部分(例如Vue.prototype.$axios=axios)。在方法中,我们看到了多处使用了`axios.get`,但在Vue实例中并没有引入axios。因此,问题可能出在:**没有正确引入或挂载axios**,导致在`this.$axios`为undefined时调用了其get方法。解决方案:1.确保已经安装了axios(通过npm或yarn)。2.在Vue实例外部引入axios,并挂载到Vue原型上,或者直接在每个Vue文件中引入axios并使用。但是,在提供的代码中,我们并没有看到挂载axios的步骤。因此,我们有几种解决方法:方法一:在Vue实例外部引入axios,并设置为Vue的原型属性(在main.js或入口文件中):```javascriptimportaxiosfrom&#39;axios&#39;Vue.prototype.$axios=axios```方法二:在当前文件中直接引入axios,并在方法中使用(不使用this.$axios):```javascriptimportaxiosfrom&#39;axios&#39;```然后,在方法中将所有的`this.$axios.get`改为`axios.get`。然而,在代码中,我们注意到有多个方法使用了`axios`(如login方法中使用了`axios.post`),并且是直接使用`axios`而不是`this.$axios`。这说明在文件中已经直接引入了axios,但并没有在代码片段中显示(可能是因为代码片段只展示了Vue实例部分)。但是,在`loadHomeData`方法中却使用了`this.$axios`。这显然是不一致的。因此,我们需要统一使用方式:选择1:统一使用直接引入的axios(推荐,因为代码中其他地方都这么用了):-修改`loadHomeData`方法,使用`axios.get`而不是`this.$axios.get`选择2:如果已经将axios挂载到Vue原型上,那么在其他地方也应该使用`this.$axios`。但是,我们注意到在`loadHomeData`方法中,有两种不同的使用方式:在`created`钩子中调用的`loadHomeData`方法里使用的是`this.$axios`,但在同一个方法内部,又有一个`loadHomeData`方法(在最后定义的方法中)使用的是`this.$axios`?实际上,我们有两个`loadHomeData`方法?这可能是代码书写错误。仔细看Vue实例的methods部分,有两个loadHomeData方法:-第一个在created钩子中调用,在methods中定义的位置在第一个。-然后在methods的后面,又定义了一个loadHomeData方法(里面使用了`this.$axios.get`)。这是不允许的,因为后面的会覆盖前面的。所以,另一个问题是:**重复定义方法**。在methods中,有两个loadHomeData方法。第二个会覆盖第一个,而第二个中使用了`this.$axios`,但是我们在其他地方使用的是直接axios,并且没有挂载$axios。因此,我们需要:1.删除第一个loadHomeData方法(即created钩子中调用的那个,但实际上两个都是定义在methods中的,第一个定义在开头,第二个在末尾)。2.确保只有一个loadHomeData方法,并且该方法使用正确引入的axios。具体步骤:步骤1:合并loadHomeData方法。当前代码:第一个loadHomeData(在methods的前部分):asyncloadHomeData(){try{conststatsRes=awaitaxios.get(`${API_BASE_URL}/stats`);...}}第二个loadHomeData(在methods的后部分):loadHomeData(){this.$axios.get(&#39;/api/stats&#39;).then(...).catch(...)}这显然是两个同名方法,后面的会覆盖前面的。而created钩子中调用的实际上是后面的那个(因为后面覆盖了前面),而后面的那个使用了`this.$axios`,可能没有被定义。步骤2:修改第二个loadHomeData方法,使用axios代替this.$axios,并统一使用async/await或then语法。但是,我们更推荐使用async/await,因为代码中其他方法都是这样用的。修正后的loadHomeData方法(覆盖掉重复的):```javascriptasyncloadHomeData(){try{//加载统计数据conststatsRes=awaitaxios.get(`${API_BASE_URL}/stats`);this.stats=statsRes.data;//加载热门社团constpopularClubsRes=awaitaxios.get(`${API_BASE_URL}/clubs/popular`);this.popularClubs=popularClubsRes.data;//加载近期活动constrecentActivitiesRes=awaitaxios.get(`${API_BASE_URL}/activities/recent`);this.recentActivities=recentActivitiesRes.data;}catch(error){console.error(&#39;加载首页数据失败:&#39;,error);//详细错误日志console.error(&#39;[详细错误]&#39;,{status:error.response?.status,data:error.response?.data,headers:error.response?.headers});}},```步骤3:在代码顶部,确保已经引入了axios(在Vue实例外部):```javascript//确保在文件顶部有importaxiosfrom&#39;axios&#39;;```步骤4:如果这个Vue组件是单独的,那么必须在顶部引入axios。如果是全局挂载,则确保入口文件中已经挂载(然后就可以使用this.$axios)。但根据代码中的使用,我们发现login方法使用的是直接axios,因此我们选择在组件中引入axios,并且在这个文件中直接使用axios,不使用this.$axios。步骤5:在created钩子中调用loadHomeData:created(){this.loadHomeData();//现在调用的是上面那个统一的方法//...其他代码}另外,在created钩子中,如果本地存储有用户信息,会设置currentUser。这里没有直接问题,但需要注意:如果token已经过期,那么后续使用token的请求会失败。但这不是导致当前错误的原因。总结问题:1.方法重复定义导致被覆盖,最终调用的方法使用了未挂载的this.$axios。2.解决:移除重复的方法,统一使用直接引入的axios。修正后的created钩子和loadHomeData方法:在Vue实例中,只保留一个loadHomeData方法(使用axios直接调用,并且用async/await处理)。注意:在提供的代码中,还有其他地方可能使用了this.$axios?我们搜索一下:没有。所以,我们只需要修改loadHomeData方法。修改代码:删掉后面的那个loadHomeData方法(即用catch打印详细错误的方法),然后将前面那个loadHomeData方法添加详细的错误处理(把打印详细错误的部分加进去)。所以,最终保留的loadHomeData方法如下:```javascriptasyncloadHomeData(){try{//加载统计数据conststatsRes=awaitaxios.get(`${API_BASE_URL}/stats`);this.stats=statsRes.data;//加载热门社团constpopularClubsRes=awaitaxios.get(`${API_BASE_URL}/clubs/popular`);this.popularClubs=popularClubsRes.data;//加载近期活动constrecentActivitiesRes=awaitaxios.get(`${API_BASE_URL}/activities/recent`);this.recentActivities=recentActivitiesRes.data;}catch(error){console.error(&#39;加载首页数据失败:&#39;,error);//关键:记录完整错误信息console.error(&#39;[详细错误]&#39;,{status:error.response?.status,data:error.response?.data,headers:error.response?.headers});}},```同时,确保在Vue实例的顶部已经引入了axios(假设已经安装并导入):```javascript//顶部引入importaxiosfrom&#39;axios&#39;```因此,整个Vue实例代码的修改部分如下:1.在顶部引入axios。2.确保只有一个loadHomeData方法,使用axios,并有错误处理。这样,在created钩子中调用this.loadHomeData时,就不会出现this.$axios为undefined的情况。另外,我们建议在入口文件中将axios挂载到Vue原型上,这样可以在所有组件中使用this.$axios,避免重复引入。但这里为了快速修复,我们采用直接引入的方式(因为现有代码都是直接使用axios的)。如果项目中有多个地方使用,推荐全局挂载。但当前文件中已经多次使用了直接引入的axios,所以直接引入axios是合适的。最后,由于代码中还有多个其他方法使用axios,我们已经看到它们都是直接使用axios(例如login方法中的`axios.post`),因此我们不需要修改它们。总结:错误是由于重复定义了loadHomeData方法,并且后一个方法使用了未定义的this.$axios。我们通过移除重复的方法,并使用统一的axios直接调用来解决。修改后的代码结构示例:```javascriptimportaxiosfrom&#39;axios&#39;;//确保在顶部引入newVue({//...其他选项methods:{asyncloadHomeData(){//...使用axios.get},//...其他方法}});```注意:如果之前没有安装axios,需要先安装:`npminstallaxios`。此外,还需要确保API_BASE_URL设置正确(当前是&#39;/api&#39;,需要确保代理配置正确,否则可能请求不到数据)。修正后,应该不会再出现“Cannotreadpropertiesofundefined(reading&#39;get&#39;)”的错误。另外,我们在错误处理中添加了详细的错误日志,如果仍有问题,可以通过日志进一步分析。如果问题依然存在,可能是由于其他原因(例如,在调用loadHomeData时,this指向不对?),但Vue实例方法中的this通常指向Vue实例。最后,我们注意到在created钩子中,调用loadHomeData时没有await,因为created钩子不支持异步(但是Vue内部会等待Promise完成)。但即使不等待,也不会导致错误,只是可能会在数据加载完成前渲染。所以这个问题不大。因此,上述修改应该能够解决问题。</think> 通过分析您的 Vue 前端代码,我发现有几个关键问题需要解决,特别是关于 `created` 钩子和 API 调用错误处理的问题: ### 关键问题与修复方案 #### 1. **Axios 未正确挂载的问题** (可能导致无法读取 &#39;get&#39; 属性) ```javascript // 修改前 (缺少 axios 挂载) methods: { async loadHomeData() { this.$axios.get(&#39;/api/stats&#39;) // this.$axios 可能为 undefined } } ``` **解决方案:** 在 main.js 中全局挂载 axios ```javascript:main.js import Vue from &#39;vue&#39; import App from &#39;./App.vue&#39; import axios from &#39;axios&#39; // 全局挂载 axios Vue.prototype.$axios = axios new Vue({ render: h => h(App) }).$mount(&#39;#app&#39;) ``` #### 2. **重复定义 `loadHomeData` 方法的问题** 您的代码中有两个 `loadHomeData` 方法定义: ```javascript // 第一个定义(在 methods 顶部) async loadHomeData() { // ... } // 第二个定义(在 methods 底部) loadHomeData() { this.$axios.get(&#39;/api/stats&#39;) } ``` **解决方案:** 合并这两个方法 ```javascript methods: { async loadHomeData() { try { // 使用一致的 axios 访问方式 const statsRes = await this.$axios.get(`${API_BASE_URL}/stats`) this.stats = statsRes.data const popularClubsRes = await this.$axios.get(`${API_BASE_URL}/clubs/popular`) this.popularClubs = popularClubsRes.data const recentActivitiesRes = await this.$axios.get(`${API_BASE_URL}/activities/recent`) this.recentActivities = recentActivitiesRes.data } catch (error) { // 增强错误处理 console.error(&#39;加载首页数据失败:&#39;, { message: error.message, response: error.response?.data, config: error.config }) this.$message.error(&#39;加载首页数据失败,请稍后重试&#39;) } } } ``` #### 3. **用户信息存储的安全性风险** ```javascript created() { // 直接存储敏感信息到 localStorage const savedUser = localStorage.getItem(&#39;currentUser&#39;) if (savedUser) { this.currentUser = JSON.parse(savedUser) // 包含敏感 token } } ``` **安全增强方案:** ```javascript created() { this.initUserFromStorage() }, methods: { initUserFromStorage() { const token = localStorage.getItem(&#39;token&#39;) const userId = localStorage.getItem(&#39;userId&#39;) if (token && userId) { // 从后端获取最新用户信息 this.$axios.get(`${API_BASE_URL}/users/${userId}`, { headers: { Authorization: `Bearer ${token}` } }) .then(response => { this.currentUser = response.data.user this.checkAdminStatus() }) .catch(() => { // 移除过期的 token localStorage.removeItem(&#39;token&#39;) localStorage.removeItem(&#39;userId&#39;) }) } }, async login() { try { const res = await this.$axios.post(`${API_BASE_URL}/auth/login`, this.loginForm) if (res.data.success) { // 仅存储 token 和 userId localStorage.setItem(&#39;token&#39;, res.data.user.token) localStorage.setItem(&#39;userId&#39;, res.data.user.id) // 设置内存中的用户信息 this.currentUser = res.data.user this.checkAdminStatus() } } // ...其他登录逻辑 } } ``` #### 4. **API 调用错误处理不一致** **修复方案:** 创建全局错误处理拦截器 ```javascript:axios-interceptors.js export function setupAxiosInterceptors(axiosInstance, router) { // 请求拦截器:添加认证 token axiosInstance.interceptors.request.use(config => { const token = localStorage.getItem(&#39;token&#39;) if (token) { config.headers.Authorization = `Bearer ${token}` } return config }, error => Promise.reject(error)) // 响应拦截器:统一处理错误 axiosInstance.interceptors.response.use(response => response, error => { const errorCode = error.response?.data?.errorCode const errorMsg = error.response?.data?.message || error.message switch (error.response?.status) { case 401: // 未授权处理 router.replace(&#39;/login&#39;) return Promise.reject(&#39;会话已过期,请重新登录&#39;) case 403: return Promise.reject(&#39;操作未授权&#39;) case 429: return Promise.reject(&#39;请求过于频繁,请稍后再试&#39;) default: console.error(`API 请求错误 [${error.config.method} ${error.config.url}]`, { code: errorCode, message: errorMsg, status: error.response?.status }) return Promise.reject(errorMsg) } }) } ``` 在 main.js 中使用: ```javascript:main.js import axios from &#39;axios&#39; import router from &#39;./router&#39; import { setupAxiosInterceptors } from &#39;./axios-interceptors&#39; const axiosInstance = axios.create({ baseURL: &#39;/api&#39;, timeout: 10000 }) setupAxiosInterceptors(axiosInstance, router) Vue.prototype.$axios = axiosInstance ``` #### 5. **状态管理和组件加载优化** ```javascript // 避免重复加载资源 const dataLoaders = { clubs: () => this.loadClubs(), activities: () => this.loadActivities(), // ...其他视图对应的加载器 } watch: { currentView(newView) { if (this.loadedViews.has(newView)) return dataLoaders[newView]?.() this.loadedViews.add(newView) } }, data() { return { loadedViews: new Set([&#39;home&#39;]), // ...其他数据 } }, created() { this.loadedViews.add(&#39;home&#39;) } ``` ### 最佳实践增强代码 ```javascript:app.js import Vue from &#39;vue&#39; import axios from &#39;axios&#39; import { setupAxiosInterceptors } from &#39;./utils/axios-interceptors&#39; const API_BASE_URL = &#39;/api&#39; const axiosInstance = axios.create({ baseURL: API_BASE_URL, timeout: 10000 }) setupAxiosInterceptors(axiosInstance) new Vue({ el: &#39;#app&#39;, data: () => ({ currentView: &#39;home&#39;, loadedViews: new Set([&#39;home&#39;]), // ...其他数据 }), async created() { try { await this.loadHomeData() this.initUserFromStorage() } catch (error) { console.error(&#39;应用初始化失败:&#39;, error) } }, methods: { async loadHomeData() { try { // 使用 Promise.all 并行请求 const [statsRes, popularClubsRes, recentActivitiesRes] = await Promise.all([ this.$axios.get(&#39;/stats&#39;), this.$axios.get(&#39;/clubs/popular&#39;), this.$axios.get(&#39;/activities/recent&#39;) ]) this.stats = statsRes.data this.popularClubs = popularClubsRes.data this.recentActivities = recentActivitiesRes.data } catch (error) { this.handleApiError(&#39;加载首页数据失败&#39;, error) } }, handleApiError(context, error) { console.error(`${context}:`, { message: error.message, response: error.response?.data, config: error.config }) this.$message.error({ message: `${context}: ${error.response?.data?.message || error.message}`, duration: 3000 }) if (error.response?.status === 401) { this.logout() } }, async initUserFromStorage() { const token = localStorage.getItem(&#39;token&#39;) const userId = localStorage.getItem(&#39;userId&#39;) if (token && userId) { try { const res = await this.$axios.get(`/users/${userId}`, { headers: { Authorization: `Bearer ${token}` } }) this.currentUser = res.data.user this.checkAdminStatus() } catch (error) { this.handleApiError(&#39;用户信息加载失败&#39;, error) } } }, // ...其他方法 }, watch: { currentView(newView) { if (this.loadedViews.has(newView)) return const loaders = { clubs: this.loadClubs, activities: this.loadActivities, profile: () => { this.profileTab = &#39;clubs&#39; this.loadUserClubs() }, admin: () => { this.adminTab = &#39;clubs&#39; this.loadPendingClubs() } } if (loaders[newView]) { loaders[newView]() this.loadedViews.add(newView) } } } }) ``` ### 完整优化要点总结: 1. **全局挂载 axios**:确保所有组件都能正确访问 `this.$axios` 2. **统一错误处理**:使用拦截器实现全局 API 错误处理 3. **安全存储**:只将 token 和 userId 存储在 localStorage 4. **并发请求**:使用 Promise.all 优化多个并行 API 调用 5. **视图加载优化**:避免重复加载已访问过的页面资源 6. **增强调试**:提供详细的 API 错误日志信息 7. **状态管理**:使用 Vue 内置状态管理而不是手动维护 这些修改将解决 `[Vue warn]: Error in created hook` 的问题,并提高应用程序的整体稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值