Commit da65cb92 by DaiJiezhang

Refine fetch handling and phone workflow

parent f2fe141d
......@@ -40,6 +40,5 @@ public class WxPhoneSaveRequest {
private Boolean numberStatus;
private String linkedWecom;
private Boolean numberRetentionStatus;
}
......@@ -64,8 +64,7 @@ public class WxPhoneEntity {
@TableField("linked_wecom")
private String linkedWecom;
@TableField("number_retention_status")
private Boolean numberRetentionStatus;
@TableField(exist = false)
private String wechatStatus;
......@@ -73,3 +72,4 @@ public class WxPhoneEntity {
@TableField(exist = false)
private String outbound;
}
......@@ -15,10 +15,7 @@ public class WxPhoneService {
private final WxPhoneMapper wxPhoneMapper;
/**
* 代码作用(白话):把手机号列表按 id 倒序读出来,给前端列表页直接渲染。
* 关联文件:F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java、F:/Project/xyw_console/src/modules/shared/phone-api-client.js。
* 关联逻辑(调用链/消息链/数据流):GET /api/wx-phones -> listRecords() -> Mapper.selectList() -> 前端 normalizePhoneRecord() -> 列表展示。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氭妸鎵嬫満鍙峰垪琛ㄦ寜 id 鍊掑簭璇诲嚭鏉ワ紝缁欏墠绔垪琛ㄩ〉鐩存帴娓叉煋銆? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java銆丗:/Project/xyw_console/src/modules/shared/phone-api-client.js銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛欸ET /api/wx-phones -> listRecords() -> Mapper.selectList() -> 鍓嶇 normalizePhoneRecord() -> 鍒楄〃灞曠ず銆? */
public List<WxPhoneEntity> listRecords() {
return wxPhoneMapper.selectList(new QueryWrapper<WxPhoneEntity>().orderByDesc("id"))
.stream()
......@@ -27,19 +24,13 @@ public class WxPhoneService {
}
/**
* 代码作用(白话):按主键读一条手机号卡,给详情抽屉和编辑弹窗用。
* 关联文件:F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java、F:/Project/xyw_console/src/modules/phone/phone-detail-drawer.js。
* 关联逻辑(调用链/消息链/数据流):GET /api/wx-phones/{id} -> getRecordById() -> Mapper.selectById() -> 详情/编辑打开。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氭寜涓婚敭璇讳竴鏉℃墜鏈哄彿鍗★紝缁欒鎯呮娊灞夊拰缂栬緫寮圭獥鐢ㄣ€? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java銆丗:/Project/xyw_console/src/modules/phone/phone-detail-drawer.js銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛欸ET /api/wx-phones/{id} -> getRecordById() -> Mapper.selectById() -> 璇︽儏/缂栬緫鎵撳紑銆? */
public WxPhoneEntity getRecordById(Long id) {
return normalizeEntity(wxPhoneMapper.selectById(id));
}
/**
* 代码作用(白话):把前端新增表单写入 wx_phone 表,并返回带主键的新记录。
* 关联文件:F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java、F:/Project/xyw_console/src/modules/phone/phone-add-dialog.js。
* 关联逻辑(调用链/消息链/数据流):POST /api/wx-phones -> createRecord() -> copyRequestToEntity() -> Mapper.insert() -> 前端刷新列表。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氭妸鍓嶇鏂板琛ㄥ崟鍐欏叆 wx_phone 琛紝骞惰繑鍥炲甫涓婚敭鐨勬柊璁板綍銆? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java銆丗:/Project/xyw_console/src/modules/phone/phone-add-dialog.js銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛歅OST /api/wx-phones -> createRecord() -> copyRequestToEntity() -> Mapper.insert() -> 鍓嶇鍒锋柊鍒楄〃銆? */
public WxPhoneEntity createRecord(WxPhoneSaveRequest request) {
WxPhoneEntity entity = new WxPhoneEntity();
copyRequestToEntity(request, entity);
......@@ -48,10 +39,7 @@ public class WxPhoneService {
}
/**
* 代码作用(白话):更新指定手机号卡,并把数据库里的最新记录返回给前端。
* 关联文件:F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java、F:/Project/xyw_console/src/modules/phone/phone-add-dialog.js。
* 关联逻辑(调用链/消息链/数据流):PUT /api/wx-phones/{id} -> updateRecord() -> Mapper.updateById() -> 前端刷新列表。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氭洿鏂版寚瀹氭墜鏈哄彿鍗★紝骞舵妸鏁版嵁搴撻噷鐨勬渶鏂拌褰曡繑鍥炵粰鍓嶇銆? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java銆丗:/Project/xyw_console/src/modules/phone/phone-add-dialog.js銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛歅UT /api/wx-phones/{id} -> updateRecord() -> Mapper.updateById() -> 鍓嶇鍒锋柊鍒楄〃銆? */
public WxPhoneEntity updateRecord(Long id, WxPhoneSaveRequest request) {
WxPhoneEntity entity = wxPhoneMapper.selectById(id);
if (entity == null) {
......@@ -63,19 +51,13 @@ public class WxPhoneService {
}
/**
* 代码作用(白话):删除指定手机号卡,给控制器判断是否需要返回 404。
* 关联文件:F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java、F:/Project/xyw_console/src/modules/phone/phone-list-runtime.js。
* 关联逻辑(调用链/消息链/数据流):DELETE /api/wx-phones/{id} -> deleteRecord() -> Mapper.deleteById() -> 列表重新加载。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氬垹闄ゆ寚瀹氭墜鏈哄彿鍗★紝缁欐帶鍒跺櫒鍒ゆ柇鏄惁闇€瑕佽繑鍥?404銆? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/controller/WxPhoneController.java銆丗:/Project/xyw_console/src/modules/phone/phone-list-runtime.js銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛欴ELETE /api/wx-phones/{id} -> deleteRecord() -> Mapper.deleteById() -> 鍒楄〃閲嶆柊鍔犺浇銆? */
public boolean deleteRecord(Long id) {
return wxPhoneMapper.deleteById(id) > 0;
}
/**
* 代码作用(白话):把接口入参安全地拷贝到数据库实体里,顺手做空白清洗和默认值处理。
* 关联文件:F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/dto/WxPhoneSaveRequest.java、F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/entity/WxPhoneEntity.java。
* 关联逻辑(调用链/消息链/数据流):createRecord()/updateRecord() -> copyRequestToEntity() -> Entity -> Mapper 持久化。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氭妸鎺ュ彛鍏ュ弬瀹夊叏鍦版嫹璐濆埌鏁版嵁搴撳疄浣撻噷锛岄『鎵嬪仛绌虹櫧娓呮礂鍜岄粯璁ゅ€煎鐞嗐€? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/dto/WxPhoneSaveRequest.java銆丗:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/entity/WxPhoneEntity.java銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛歝reateRecord()/updateRecord() -> copyRequestToEntity() -> Entity -> Mapper 鎸佷箙鍖栥€? */
public void copyRequestToEntity(WxPhoneSaveRequest request, WxPhoneEntity entity) {
entity.setPhoneNumber(normalizeText(request.getPhoneNumber()));
entity.setRealPerson(normalizeText(request.getRealPerson()));
......@@ -94,16 +76,12 @@ public class WxPhoneService {
entity.setChannelOperator(normalizeText(request.getChannelOperator()));
entity.setNumberStatus(normalizeBoolean(request.getNumberStatus(), Boolean.TRUE));
entity.setLinkedWecom(normalizeText(request.getLinkedWecom()));
entity.setNumberRetentionStatus(normalizeBoolean(request.getNumberRetentionStatus(), Boolean.TRUE));
entity.setWechatStatus(Boolean.TRUE.equals(entity.getWechat()) ? "正常" : "异常");
entity.setOutbound(Boolean.TRUE.equals(entity.getOutboundCall()) ? "可外呼" : "不可外呼");
}
/**
* 代码作用(白话):把数据库里取回来的手机号卡统一整理成前端能直接吃的状态文本和空值格式。
* 关联文件:F:/Project/xyw_console/src/modules/shared/record-adapters.js、F:/Project/xyw_console/src/modules/phone/phone-list-runtime.js。
* 关联逻辑(调用链/消息链/数据流):Mapper.selectList()/selectById() -> normalizeEntity() -> controller response -> 前端 normalizePhoneRecord()。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氭妸鏁版嵁搴撻噷鍙栧洖鏉ョ殑鎵嬫満鍙峰崱缁熶竴鏁寸悊鎴愬墠绔兘鐩存帴鍚冪殑鐘舵€佹枃鏈拰绌哄€兼牸寮忋€? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/src/modules/shared/record-adapters.js銆丗:/Project/xyw_console/src/modules/phone/phone-list-runtime.js銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛歁apper.selectList()/selectById() -> normalizeEntity() -> controller response -> 鍓嶇 normalizePhoneRecord()銆? */
public WxPhoneEntity normalizeEntity(WxPhoneEntity entity) {
if (entity == null) {
return null;
......@@ -125,10 +103,7 @@ public class WxPhoneService {
}
/**
* 代码作用(白话):把前端可能传来的 0/1、正常/异常等卡状态,统一成页面能稳定显示的文本。
* 关联文件:F:/Project/xyw_console/src/modules/phone/phone-add-dialog.js、F:/Project/xyw_console/src/modules/phone/phone-list-runtime.js。
* 关联逻辑(调用链/消息链/数据流):request.cardStatus/DB.card_status -> normalizeCardStatus() -> response.cardStatus -> 列表和详情显示。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氭妸鍓嶇鍙兘浼犳潵鐨?0/1銆佹甯?寮傚父绛夊崱鐘舵€侊紝缁熶竴鎴愰〉闈㈣兘绋冲畾鏄剧ず鐨勬枃鏈€? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/src/modules/phone/phone-add-dialog.js銆丗:/Project/xyw_console/src/modules/phone/phone-list-runtime.js銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛歳equest.cardStatus/DB.card_status -> normalizeCardStatus() -> response.cardStatus -> 鍒楄〃鍜岃鎯呮樉绀恒€? */
public String normalizeCardStatus(String value) {
String normalized = normalizeText(value);
if (normalized == null) {
......@@ -142,10 +117,7 @@ public class WxPhoneService {
}
/**
* 代码作用(白话):把字符串里的首尾空格去掉,空白内容直接当成没填。
* 关联文件:F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/dto/WxPhoneSaveRequest.java、F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/entity/WxPhoneEntity.java。
* 关联逻辑(调用链/消息链/数据流):request/entity 字段 -> normalizeText() -> 入库/出库前的统一清洗。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氭妸瀛楃涓查噷鐨勯灏剧┖鏍煎幓鎺夛紝绌虹櫧鍐呭鐩存帴褰撴垚娌″~銆? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/dto/WxPhoneSaveRequest.java銆丗:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/entity/WxPhoneEntity.java銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛歳equest/entity 瀛楁 -> normalizeText() -> 鍏ュ簱/鍑哄簱鍓嶇殑缁熶竴娓呮礂銆? */
public String normalizeText(String value) {
if (value == null) {
return null;
......@@ -155,11 +127,9 @@ public class WxPhoneService {
}
/**
* 代码作用(白话):给可空布尔字段补默认值,避免数据库和页面出现一堆 null。
* 关联文件:F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/dto/WxPhoneSaveRequest.java、F:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/entity/WxPhoneEntity.java。
* 关联逻辑(调用链/消息链/数据流):request.Boolean -> normalizeBoolean() -> Entity.Boolean -> MyBatis-Plus 保存。
*/
* 浠g爜浣滅敤锛堢櫧璇濓級锛氱粰鍙┖甯冨皵瀛楁琛ラ粯璁ゅ€硷紝閬垮厤鏁版嵁搴撳拰椤甸潰鍑虹幇涓€鍫?null銆? * 鍏宠仈鏂囦欢锛欶:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/dto/WxPhoneSaveRequest.java銆丗:/Project/xyw_console/backend/src/main/java/com/xyw/console/phone/entity/WxPhoneEntity.java銆? * 鍏宠仈閫昏緫锛堣皟鐢ㄩ摼/娑堟伅閾?鏁版嵁娴侊級锛歳equest.Boolean -> normalizeBoolean() -> Entity.Boolean -> MyBatis-Plus 淇濆瓨銆? */
public Boolean normalizeBoolean(Boolean value, Boolean defaultValue) {
return value == null ? defaultValue : value;
}
}
......@@ -23,7 +23,7 @@
channelOperator: '',
numberStatus: true,
linkedWecom: '',
numberRetentionStatus: true,
imageAttachment1: '',
imageAttachment2: ''
};
......@@ -124,7 +124,7 @@
formData.channelOperator = record.channelOperator || record.carrier || '';
formData.numberStatus = record.numberStatus !== false;
formData.linkedWecom = record.linkedWecom || '';
formData.numberRetentionStatus = record.numberRetentionStatus !== false;
imageItems.value = buildImageItemsFromRecord(record);
syncImagesToFormData();
}
......@@ -347,12 +347,7 @@
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="是否保留号码" prop="numberRetentionStatus">
<el-switch v-model="formData.numberRetentionStatus" active-text="保留" inactive-text="注销"></el-switch>
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="24">
<el-form-item label="可变更 5 元套餐" prop="packageChange5yuan">
<el-switch v-model="formData.packageChange5yuan"></el-switch>
</el-form-item>
......@@ -412,3 +407,6 @@
app.use(ElementPlus);
app.mount('#phoneDialogRoot');
})();
......@@ -34,6 +34,17 @@
return recordTime >= startTime && recordTime <= endTime + 24 * 60 * 60 * 1000 - 1;
}
/**
* 代码作用(白话):把列表里的长文本压成 20 个字符内的单行展示,鼠标悬浮时还能看到完整内容;关联文件:F:/Project/xyw_console/src/modules/phone/phone-list-runtime.js;关联逻辑(调用链/消息链/数据流):表格单元格原始值 -> formatCellText() -> 列表展示/tooltip 提示。
*/
function formatCellText(value) {
const text = String(value ?? '-');
if (text.length <= 20) {
return text;
}
return `${text.slice(0, 20)}...`;
}
const PhoneListTable = {
name: 'PhoneListTable',
props: {
......@@ -454,9 +465,21 @@
<a href="#" class="domain-link" @click.prevent="focusRow(scope.row.id)">{{ scope.row.phone || '-' }}</a>
</template>
</el-table-column>
<el-table-column property="realPerson" label="真实联系人" min-width="130" />
<el-table-column property="iccid" label="ICCID" min-width="190" />
<el-table-column property="city" label="所在城市" min-width="120" />
<el-table-column label="真实联系人" min-width="150">
<template #default="scope">
<span :title="scope.row.realPerson || '-'" style="display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ formatCellText(scope.row.realPerson) }}</span>
</template>
</el-table-column>
<el-table-column label="ICCID" min-width="220">
<template #default="scope">
<span :title="scope.row.iccid || '-'" style="display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ formatCellText(scope.row.iccid) }}</span>
</template>
</el-table-column>
<el-table-column label="所在城市" min-width="140">
<template #default="scope">
<span :title="scope.row.city || '-'" style="display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ formatCellText(scope.row.city) }}</span>
</template>
</el-table-column>
<el-table-column label="号卡状态" min-width="120">
<template #default="scope">
<span class="status-chip-wrap">
......@@ -464,9 +487,7 @@
<span>{{ scope.row.status }}</span>
</span>
</template>
</el-table-column>
<el-table-column property="cardUsageLocation" label="卡使用地点" min-width="160" />
<el-table-column label="企业微信" min-width="110">
</el-table-column> <el-table-column label="企业微信" min-width="110">
<template #default="scope">{{ formatBooleanText(scope.row.wecom, '已开通', '未开通') }}</template>
</el-table-column>
<el-table-column label="微信" min-width="100">
......@@ -475,20 +496,28 @@
<el-table-column label="可外呼" min-width="100">
<template #default="scope">{{ formatBooleanText(scope.row.outboundCall, '可外呼', '不可外呼') }}</template>
</el-table-column>
<el-table-column property="douyinAccount" label="抖音账号" min-width="160" />
<el-table-column label="抖音账号" min-width="180">
<template #default="scope">
<span :title="scope.row.douyinAccount || '-'" style="display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ formatCellText(scope.row.douyinAccount) }}</span>
</template>
</el-table-column>
<el-table-column label="小程序备案" min-width="120">
<template #default="scope">{{ formatBooleanText(scope.row.miniProgramFiling, '已备案', '未备案') }}</template>
</el-table-column>
<el-table-column property="channelOperator" label="渠道运营商" min-width="130" />
<el-table-column label="渠道运营商" min-width="150">
<template #default="scope">
<span :title="scope.row.channelOperator || '-'" style="display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ formatCellText(scope.row.channelOperator) }}</span>
</template>
</el-table-column>
<el-table-column label="手机号状态" min-width="120">
<template #default="scope">{{ formatStatusText(scope.row.numberStatus) }}</template>
</el-table-column>
<el-table-column property="linkedWecom" label="绑定企业微信" min-width="160" />
<el-table-column label="号码保留状态" min-width="130">
<template #default="scope">{{ formatRetentionText(scope.row.numberRetentionStatus) }}</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<el-table-column label="绑定企业微信" min-width="180">
<template #default="scope">
<span :title="scope.row.linkedWecom || '-'" style="display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ formatCellText(scope.row.linkedWecom) }}</span>
</template>
</el-table-column> <el-table-column label="操作" width="100" fixed="right">
<template #default="scope">
<div class="action-cluster" style="display: flex; flex-direction: column; align-items: flex-start; gap: 8px;">
<button class="action-pill action-pill-primary" type="button" @click.stop="handleRowAction('view', scope.row.id)" style="border: none; background: transparent; color: #2563eb; cursor: pointer; padding: 0; font-weight: 600; line-height: 1;">查看</button>
......@@ -544,3 +573,5 @@
......@@ -13,11 +13,33 @@
}
/**
* 代码作用(白话):把浏览器在本地后端没启动时抛出的网络错误翻成稳定中文,避免页面直接展示原始 Failed to fetch;关联文件:F:/Project/xyw_console/src/modules/shared/phone-api-client.js、F:/Project/xyw_console/src/modules/phone/phone-list-runtime.js、F:/Project/xyw_console/src/modules/phone/phone-add-dialog.js;关联逻辑(调用链/消息链/数据流):fetch() 失败 -> requestJson() -> 抛出中文错误 -> 外层 catch -> 页面提示。
*/
function isFetchUnavailableError(error) {
const message = String(error?.message || '').toLowerCase();
return error instanceof TypeError && message.includes('failed to fetch');
}
/**
* 代码作用(白话):先请求接口再统一解析 JSON,并在网络断开或本地后端未启动时返回稳定错误文案;关联文件:F:/Project/xyw_console/src/modules/shared/phone-api-client.js;关联逻辑(调用链/消息链/数据流):listPhones()/savePhone()/deletePhone() -> requestJson() -> parseJsonResponse() -> 外层页面提示。
*/
async function requestJson(url, options, networkMessage) {
try {
const response = await fetch(url, options);
return await parseJsonResponse(response);
} catch (error) {
if (isFetchUnavailableError(error)) {
throw new Error(networkMessage);
}
throw error;
}
}
/**
* 代码作用(白话):从本地手机号接口拉取完整列表,返回后端 data 数组给外层适配;关联文件:F:/Project/xyw_console/app-v2.js、F:/Project/xyw_console/src/modules/phone/phone-list-runtime.js;关联逻辑(调用链/消息链/数据流):phone 页面挂载/刷新 -> listPhones() -> app-v2.js -> normalizePhoneRecord() -> 列表渲染。
*/
async function listPhones() {
const response = await fetch(BASE_URL);
const json = await parseJsonResponse(response);
const json = await requestJson(BASE_URL, undefined, '手机号服务暂时不可用,请确认本地后端已启动');
return Array.isArray(json.data) ? json.data : [];
}
......@@ -28,12 +50,11 @@
const payload = { ...record };
const isEdit = Boolean(payload.id);
const targetUrl = isEdit ? `${BASE_URL}/${payload.id}` : BASE_URL;
const response = await fetch(targetUrl, {
const json = await requestJson(targetUrl, {
method: isEdit ? 'PUT' : 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const json = await parseJsonResponse(response);
}, '手机号保存失败,请确认本地后端已启动');
return json.data || json;
}
......@@ -41,10 +62,9 @@
* 代码作用(白话):删除指定手机号记录,并把失败信息统一抛给外层处理;关联文件:F:/Project/xyw_console/app-v2.js、F:/Project/xyw_console/src/modules/phone/phone-list-runtime.js;关联逻辑(调用链/消息链/数据流):手机号列表删除按钮 -> deletePhoneRecord() -> deletePhone() -> 刷新列表或提示失败。
*/
async function deletePhone(id) {
const response = await fetch(`${BASE_URL}/${id}`, {
const json = await requestJson(`${BASE_URL}/${id}`, {
method: 'DELETE'
});
const json = await parseJsonResponse(response);
}, '手机号删除失败,请确认本地后端已启动');
return json.data || json;
}
......
......@@ -99,7 +99,6 @@
const miniProgramFiled = toBooleanFlag(source.miniProgramFiling ?? source.mini_program_filing, false);
const packageChangeEnabled = toBooleanFlag(source.packageChange5yuan ?? source.package_change_5yuan, false);
const numberStatus = toBooleanFlag(source.numberStatus ?? source.mobile_status ?? source.number_status, true);
const numberRetentionStatus = toBooleanFlag(source.numberRetentionStatus ?? source.number_retention_status, true);
const wechatStatus = source.wechatStatus || (numberStatus ? '正常' : '异常停机');
const outbound = source.outbound || (outboundEnabled ? '可外呼' : '不可外呼');
const linkedWecom = source.linkedWecom || source.linked_wecom || '';
......@@ -152,8 +151,6 @@
mobile_status: numberStatus,
linkedWecom,
linked_wecom: linkedWecom,
numberRetentionStatus,
number_retention_status: numberRetentionStatus
};
}
......@@ -162,3 +159,4 @@
normalizePhoneRecord
};
})();
......@@ -14,11 +14,33 @@
}
/**
* 代码作用(白话):把浏览器在本地后端没启动时抛出的网络错误翻成稳定中文,避免页面直接展示原始 Failed to fetch;关联文件:F:/Project/xyw_console/src/modules/shared/wechat-api-client.js、F:/Project/xyw_console/src/modules/wechat/wechat-list-runtime.js、F:/Project/xyw_console/src/modules/wechat/wechat-add-dialog.js;关联逻辑(调用链/消息链/数据流):fetch() 失败 -> requestJson() -> 抛出中文错误 -> 外层 catch -> 页面提示。
*/
function isFetchUnavailableError(error) {
const message = String(error?.message || '').toLowerCase();
return error instanceof TypeError && message.includes('failed to fetch');
}
/**
* 代码作用(白话):先请求接口再统一解析 JSON,并在网络断开或本地后端未启动时返回稳定错误文案;关联文件:F:/Project/xyw_console/src/modules/shared/wechat-api-client.js;关联逻辑(调用链/消息链/数据流):listWechatRecords()/getWechatRecord()/saveWechatRecord() -> requestJson() -> parseJsonResponse() -> 外层页面提示。
*/
async function requestJson(url, options, networkMessage) {
try {
const response = await fetch(url, options);
return await parseJsonResponse(response);
} catch (error) {
if (isFetchUnavailableError(error)) {
throw new Error(networkMessage);
}
throw error;
}
}
/**
* 代码作用(白话):从后端拉取企微资料全量列表,交给页面继续做本地筛选和分页;关联文件:F:/Project/xyw_console/app-v2.js、F:/Project/xyw_console/src/modules/wechat/wechat-list-runtime.js;关联逻辑(调用链/消息链/数据流):wechat 页面挂载/刷新 -> listWechatRecords() -> app-v2.js -> normalizeWechatRecord() -> 列表渲染。
*/
async function listWechatRecords() {
const response = await fetch(BASE_URL);
const json = await parseJsonResponse(response);
const json = await requestJson(BASE_URL, undefined, '企微服务暂时不可用,请确认本地后端已启动');
return Array.isArray(json.data) ? json.data : [];
}
......@@ -26,8 +48,7 @@
* 代码作用(白话):按主键读取单条企微资料,让详情抽屉和编辑弹窗拿到数据库最新值;关联文件:F:/Project/xyw_console/app-v2.js、F:/Project/xyw_console/src/modules/wechat/wechat-detail-drawer.js;关联逻辑(调用链/消息链/数据流):查看/编辑按钮 -> getWechatRecord() -> app-v2.js -> 详情抽屉或编辑弹窗打开。
*/
async function getWechatRecord(id) {
const response = await fetch(`${BASE_URL}/${id}`);
const json = await parseJsonResponse(response);
const json = await requestJson(`${BASE_URL}/${id}`, undefined, '企微详情读取失败,请确认本地后端已启动');
return json.data || null;
}
......@@ -41,12 +62,11 @@
if (!isEdit) {
delete payload.id;
}
const response = await fetch(targetUrl, {
const json = await requestJson(targetUrl, {
method: isEdit ? 'PUT' : 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const json = await parseJsonResponse(response);
}, '企微保存失败,请确认本地后端已启动');
return json.data || json;
}
......@@ -56,4 +76,3 @@
saveWechatRecord
};
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment