半夜突然没网了,给我整不会了,一看套餐过期了可还行。顺手上平台注销了校园网我几乎用不上的双终端套餐,想重新申请单终端的服务,然后在学校的自助服务平台(mynet.jnu.edu.cn)上,一个破“申请服务”按钮,硬控了我一个Web小白将近一个小时。工作人员不在服务时间,只能硬着头皮自己上了😂

前情提要

我的操作很简单:登录系统,注销旧的双终端服务,回到服务列表,再点击“申请服务”。

image-20251115100054034

然后——

万籁俱静。浏览器没有跳转,页面也没有新的弹窗,就像突然断了网一样——但我热点明明是通的。

下意识的按了下F12开了Chrome的开发者工具,切到网络那里刷新看看请求和响应。

抓包:成功的请求,失败的响应?

我看到浏览器向服务端发出了一个 GET 请求,而且这个请求在不断重复出现,或者说,页面卡在了一个状态,持续触发着它。

  • 请求 URL: https://mynet.jnu.edu.cn/app/customer/servicerecord/openServiceInit?_...

这是一个服务初始化请求,很明显,它是在尝试获取开通新服务所需的数据。

我仔细观察了响应:

  • HTTP Status Code: 200 OK

  • Response Body (JSON):

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "msg": "success",
    "accountInfo": { ... },
    "code": 0,
    "serviceListEntity": { ... },
    "networkTypeList": [ ... ],
    "buildingsList": [ ... ]
    }

code是0,msg是success。这说明服务端是正常的,数据也成功返回了。几乎可以断定问题出在前端接收到数据后的处理逻辑上

数据中的“幽灵服务”

虽然我的目的是解决前端bug,但本着谨慎原则,我还是看了一眼json的返回数据,这里有一个明显疑点:

我明明已经注销了服务,但 accountInfoserviceListEntity 字段中依然显示了一个服务记录,toDate 甚至到2025年。

image-20251115100133448

此时已经可以大致确定问题的方向了:

后端: “注销”操作可能只是标记了服务不可用,但记录未彻底删除。

前端: 前端可能根据这个“未结束”的记录,触发了某种“续费检查”或“已开通服务”的阻断逻辑。

即使存在这个“幽灵服务”,正常的流程也应该是先弹出一个提示框(e.g.“您有未结束的服务,是否继续”),而不是让页面“假死”。我的核心目标依然是找到让页面卡死的js逻辑。

寻找“申请服务”背后的代码

通过检查页面源码,我定位到控制这个页面逻辑的主要前端文件是 /app/statics/js/modules/customer/servicerecord.js

大致翻看和询问AI总结和检阅这个js后,我留意到了以下关键逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

openServiceInit: function () {
// 一些准备工作
$.get(baseURL + "customer/servicerecord/openServiceInit?_" + $.now(), function (r) {
if (r.code === 0) {
// 成功后,给 Vue model 赋值
vm.networkTypeList = r.networkTypeList;
vm.openServiceEntity = r.serviceListEntity;
// ... 更多数据赋值

// !!!关键点!!!
vm.checkService(); //成功后会调用这个函数
} else {
// 失败处理的代码
return;
}
});
},

openAdd: function () {
vm.showOpenService = true; // 关键点:切换到申请表单视图
layer.close(vm.openAgreementIndex);
},

// Vue中控制视图切换的状态
data: {
showList: true, // 默认显示服务记录列表
showOpenService: false, // 默认不显示开服务界面
}

所以,刚才大致的逻辑应该是:

  1. 点击“申请服务” - 触发 vm.openServiceInit()
  2. openServiceInit() - 发起成功的 GET 请求。
  3. 请求成功 - 赋值数据 - 调用 vm.checkService()
  4. 页面卡死 - 罪魁祸首很可能是这个 vm.checkService()

vm.checkService()

js中并没有提供 vm.checkService() 的完整代码。这个函数一定是在检查完用户状态后,根据条件决定是直接调用 vm.openAdd() 切换视图,还是弹出 layer 提示框。

现在页面卡死,意味着两种可能,也可能两者兼有:

  • vm.checkService() 内部逻辑写错了,可能在一个循环中不断重新初始化,或者不断触发一个无法关闭的 layer 弹窗。
  • checkService 逻辑判断到我的“幽灵服务”,走到了一个错误的阻断分支,该分支没有正确结束执行,导致浏览器阻塞。

所以……Console大法直接绕过!

既一看知道 服务端数据已经拿到,并且成功赋值给了前端的Vue实例,而我们最终的目的,就是执行 vm.openAdd() 中的那行代码:

1
vm.showOpenService = true;

那么,点击“申请服务”后,直接在浏览器控制台手动执行这行代码,绕开有问题的 vm.checkService() 逻辑,就解决了。

需要注意的是,因为服务列表套在一个iframe框架中,所以要把控制台的执行环境切换到框架里。直接右键框架按“检查”,或者在Console顶上手动切就行了,这点硬控了我好一会。

image-20251115100143226

按下回车键的一瞬间,卡死的“服务记录”页面瞬间消失,马上跳转到了新服务申请页面,我顿时长呼了一口气。

问题还没结束

还记得之前说的“请求不断重复”吗?

我看到浏览器向服务端发出了一个 GET 请求,而且这个请求在不断重复出现,或者说,页面卡在了一个状态,持续触发着它。

后面还伴随着几个POST请求。好了,到了付费页面还一直在硬控我,给我一直重新刷新并选择默认的双端+1年,300块太贵了。

到这个时候我已经困的不行了,懒得研究是啥奇怪的逻辑、是服务端还是客户端的问题了,直接用了非常抽象但简单粗暴的方式解决:

Chrome的开发工具不是带一个throttling的弱网模拟功能吗,我直接建了个“1G”网出来,在服务端反应过来发包之前先选好套餐

image-20251115100148314

完美解决,点确定付款的时候再瞬间关掉。

最终总结

这次排查过程虽然折腾,但也再次印证了Web开发中debug的经典套路:

  1. 抓包先行: 确认是“服务端明确拒绝”(4xx/5xxcode != 0)还是“前端逻辑错误”(200 OK + code = 0)。
  2. 定位代码: 找到关键的js文件和触发函数(这里的openServiceInit)。
  3. 锁定嫌疑人: 发现成功回调后的黑箱函数(这里的 vm.checkService())。
  4. 绕道而行: 如果黑箱逻辑无法修复,就直接在控制台操作,将数据和视图状态(vm.showOpenService)手动同步,实现功能绕过。

开发和运维扣鸡腿快去修bug开发和运维扣鸡腿快去修bug开发和运维扣鸡腿快去修bug开发和运维扣鸡腿快去修bug开发和运维扣鸡腿快去修bug