feat: 登录等接口真实调试

This commit is contained in:
zoujing
2026-09-17 16:53:36 +08:00
parent 274269a05e
commit 56c419140e
8 changed files with 26 additions and 167 deletions

View File

@@ -1,16 +1,11 @@
/**
* 内容数据访问层(唯一数据入口)
* ---------------------------------------------------------------------------
* 页面只调用本文件,不直接 import mock。
* USE_MOCK_CONTENT = true → 读 src/mock/v1/*(已是 /v1 接口响应形态,无需再适配);
* false → 走 src/request/api/content.js 的真实 /v1 通道。
* 两条路返回的字段完全一致,切换只动这个开关。
*
* 接口口径分两块:
* 1)内容页链路,4 个端点
* 1)内容页链路,4 个端点 —— 数据源见 USE_MOCK_CONTENT(接口未就绪时读 src/mock/v1/*)
* GET /v1/homepage → GET /v1/categories?section_key= → GET /v1/merchants → GET /v1/merchants/:id/detail
* 商家归属靠 category_id(分类接口 items[].id),不再有 collection 这一层。
* 2)公共内容(协议 / 隐私 / 关于 / 客服)
* 2)公共内容(协议 / 隐私 / 关于 / 客服)—— 走真实接口
* GET /v1/content(列表,登录同意版本号取这里) + GET /v1/content/:key(单条详情)
*/
import { AGREEMENT_DATE, CUSTOMER_SERVICE_PHONE, CUSTOMER_SERVICE_TIME } from "@/constant";
@@ -21,7 +16,6 @@ import homepageMock from "@/mock/v1/homepage.json";
import categoriesMock from "@/mock/v1/categories.json";
import merchantsMock from "@/mock/v1/merchants.json";
import detailsMock from "@/mock/v1/merchant-details.json";
import contentsMock from "@/mock/v1/contents.json";
import * as contentApi from "@/request/api/content";
/** 首页聚合(品牌 / hero / 板块卡 / 精选推荐 / 导览 / 页脚) */
@@ -90,10 +84,10 @@ export const getSectionBannerImages = async (sectionKey) => {
};
/* ---------------------------------------------------------------------------
* 公共内容:协议 / 隐私 / 关于 / 客服
* 公共内容:协议 / 隐私 / 关于 / 客服 —— 全部取真实接口
* 接口的 body 是纯文本,前端统一按行拆成段落,`一、二、…` 行作为小标题。
* 接口缺失(未配置 / 未启用 / 不在有效窗口 → 404)时回落仓库内的定稿正文,
* 保证合规页不空白。
* 接口缺失(未配置 / 未启用 / 不在有效窗口 → 404)时正文为空,
* 标题、客服号码等仍由本地常量兜底,保证页面结构不塌。
* ------------------------------------------------------------------------- */
const FALLBACK_TITLE = {
@@ -118,23 +112,17 @@ export const toContentBlocks = (body) => {
/** body → 段落数组(关于我们这类整段文案) */
const toParagraphs = (body) => toContentBlocks(body).map((b) => b.text);
/** 定稿兜底:接口取不到就用仓库内文本 */
const fallbackContent = (key) => clone((contentsMock.items || []).find((c) => c.key === key) || null);
/**
* 登录同意项:当前生效的 terms / privacy 版本号(整表取一次)。
* ⚠️ 这里**不用本地定稿兜底**:版本号必须以服务端为准,
* 拿不到就不提交 consents,由服务端按「尚无有效协议记录」放行或报 403。
* ⚠️ 版本号必须以服务端为准,拿不到就不提交 consents,
* 由服务端按「尚无有效协议记录」放行或报 403。
*/
export const getConsentVersions = async () => {
let list;
if (USE_MOCK_CONTENT) list = clone(contentsMock);
else {
try {
list = await contentApi.fetchContents();
} catch (e) {
return [];
}
try {
list = await contentApi.fetchContents();
} catch (e) {
return [];
}
return ["terms", "privacy"]
.map((key) => {
@@ -151,14 +139,10 @@ const contentCache = {};
export const getContent = async (key) => {
if (contentCache[key]) return clone(contentCache[key]);
let info = null;
if (USE_MOCK_CONTENT) {
info = fallbackContent(key);
} else {
try {
info = (await contentApi.fetchContent(key)) || fallbackContent(key);
} catch (e) {
info = fallbackContent(key);
}
try {
info = await contentApi.fetchContent(key);
} catch (e) {
info = null;
}
if (info) contentCache[key] = info;
return clone(info);

View File

@@ -1,10 +1,8 @@
/**
* 用户 / 登录 / 本人资料 / 注销 数据访问层(唯一数据入口)
* ---------------------------------------------------------------------------
* 页面与 store 只调用本文件,不直接 import mock。
* USE_MOCK_USER = true → 读 src/mock/v1/*(已是 /v1 接口响应形态,无需再适配);
* false → 走 src/request/api/me.js 的真实 /v1 通道。
* 两条路返回的字段完全一致,切换只动这个开关。
* 页面与 store 只调用本文件,不直接触碰请求层。
* 全部走 src/request/api/me.js 的真实 /v1 通道。
*
* 接口口径:
* POST /v1/auth/wechat → { token, user, expires_at }
@@ -20,13 +18,7 @@
* 出参统一转成页面内部模型(nickName / avatarUrl / phone / phoneBound),
* 页面不感知 UserView 的下划线字段。
*/
import { USE_MOCK_USER } from "@/config/env";
import { STORAGE_KEYS } from "@/constant";
import { clone } from "@/utils";
import loginMock from "@/mock/v1/login.json";
import meMock from "@/mock/v1/me.json";
import erasureMock from "@/mock/v1/erasure.json";
import erasureStatusMock from "@/mock/v1/erasure-status.json";
import * as meApi from "@/request/api/me";
/** 接口 UserView → 页面内部用户模型 */
@@ -42,9 +34,6 @@ const toUserInfo = (v) => {
};
};
/** mock 绑定手机号后的用户视图(真实接口由服务端回吐) */
const MOCK_BOUND_USER = { ...meMock, phone_bound: true, phone_masked: "+86138****8000" };
/** 调 wx.login 拿 code(真实链路的第一步) */
export const getWxCode = () =>
new Promise((resolve, reject) => {
@@ -65,10 +54,6 @@ export const getWxCode = () =>
* @returns {Promise<{ token: string, userInfo: object|null }>}
*/
export const wxLogin = async (consents = []) => {
if (USE_MOCK_USER) {
const res = clone(loginMock);
return { token: res.token, userInfo: toUserInfo(res.user) };
}
const code = await getWxCode();
const body = { code };
if (consents.length) body.consents = consents;
@@ -77,10 +62,7 @@ export const wxLogin = async (consents = []) => {
};
/** 当前用户视图(昵称 / 头像 / 绑定态一次拿全) */
export const fetchMe = async () => {
if (USE_MOCK_USER) return toUserInfo(clone(meMock));
return toUserInfo(await meApi.fetchMe());
};
export const fetchMe = async () => toUserInfo(await meApi.fetchMe());
/**
* 绑定手机号(getPhoneNumber 回调的手机号授权 code)
@@ -88,7 +70,6 @@ export const fetchMe = async () => {
*/
export const bindPhone = async (phoneCode) => {
if (!phoneCode) throw new Error("未获取到手机号授权 code");
if (USE_MOCK_USER) return toUserInfo(clone(MOCK_BOUND_USER));
return toUserInfo(await meApi.bindPhone(phoneCode));
};
@@ -100,12 +81,6 @@ export const updateProfile = async (patch = {}) => {
const body = {};
if (patch.nickName !== undefined) body.nickname = patch.nickName;
if (patch.avatarUrl !== undefined) body.avatar_url = patch.avatarUrl;
if (USE_MOCK_USER) {
const next = {};
if (body.nickname !== undefined) next.nickName = body.nickname;
if (body.avatar_url !== undefined) next.avatarUrl = body.avatar_url;
return next;
}
return toUserInfo(await meApi.updateProfile(body));
};
@@ -115,7 +90,6 @@ export const updateProfile = async (patch = {}) => {
*/
export const uploadAvatar = async (filePath) => {
if (!filePath) throw new Error("未选择头像");
if (USE_MOCK_USER) return { image_url: filePath, object_key: "", mime_type: "", size_bytes: 0 };
const res = await meApi.uploadAvatar(filePath);
const imageUrl = (res && res.image_url) || "";
if (!imageUrl) throw new Error("头像上传失败");
@@ -123,10 +97,7 @@ export const uploadAvatar = async (filePath) => {
};
/** 退出登录:撤销当前 token 对应的一个会话(不影响其它设备) */
export const logout = async () => {
if (USE_MOCK_USER) return { logged_out: true };
return meApi.logout();
};
export const logout = async () => meApi.logout();
/* ---------------- 账户注销:申请 → 回执状态 → 重试清理 ---------------- */
@@ -143,11 +114,6 @@ export const clearReceipt = () => uni.removeStorageSync(STORAGE_KEYS.ERASURE_REC
* @returns {Promise<{ status_token: string, status: string, retryable: boolean }>}
*/
export const submitErasure = async () => {
if (USE_MOCK_USER) {
const res = clone(erasureMock);
saveReceipt(res.status_token);
return res;
}
const res = await meApi.submitErasure();
saveReceipt(res && res.status_token);
return res;
@@ -158,7 +124,7 @@ export const fetchErasureStatus = async () => {
const receipt = readReceipt();
if (!receipt) return null;
try {
const res = USE_MOCK_USER ? clone(erasureStatusMock) : await meApi.fetchErasureStatus(receipt);
const res = await meApi.fetchErasureStatus(receipt);
if (res && res.status === "completed") clearReceipt();
return res;
} catch (e) {
@@ -173,7 +139,7 @@ export const retryErasure = async () => {
const receipt = readReceipt();
if (!receipt) return null;
try {
const res = USE_MOCK_USER ? clone(erasureStatusMock) : await meApi.retryErasure(receipt);
const res = await meApi.retryErasure(receipt);
if (res && res.status === "completed") clearReceipt();
return res;
} catch (e) {

View File

@@ -6,18 +6,15 @@
* 页面禁止硬编码 OSS 域名或拼接路径;本文件只保留兜底占位图与合法域名说明。
* - 接口 baseURL 集中在此,多环境切换只改这一个文件。
*
* 数据源分两个方向独立开关,都指同一个后端(BASE_URL_V1 的 /v1/*):
* - USE_MOCK_CONTENT:内容 / 权益 / 公共内容(协议·隐私·关于·客服)。读 src/mock/v1/*。
* - USE_MOCK_USER :登录 / 本人资料 / 头像 / 退出 / 注销。读 src/mock/v1/*。
* 两条路返回的字段与接口完全一致,接口就绪后逐个置 false 即切真实请求,页面无感。
* 数据源开关只剩内容页链路与权益(同一后端 BASE_URL_V1 的 /v1/*):
* - USE_MOCK_CONTENT:首页 / 分类 / 商家 / 商家详情 / 权益。true 时读 src/mock/v1/*。
* 公共内容(协议·隐私·关于·客服)与用户域(登录 / 资料 / 头像 / 退出 / 注销)
* 已切真实接口,不再有 mock 分支。
*/
/** 内容 / 权益 / 公共内容:仍读本地数据 */
/** 内容页链路 + 权益:仍读本地数据 */
export const USE_MOCK_CONTENT = true;
/** 登录 / 本人资料 / 注销:仍读本地数据 */
export const USE_MOCK_USER = true;
/** 后端接口地址(商家权益平台),全部端点都挂在它的 /v1/* 下 */
export const BASE_URL_V1 = "https://longli.nianxx.cn";

View File

@@ -1,61 +0,0 @@
{
"items": [
{
"key": "terms",
"title": "用户协议",
"body": "欢迎使用龙里文旅小程序。本协议是你与龙里文旅之间就使用本小程序服务所订立的约定,请仔细阅读并同意后使用。\n一、服务内容\n本小程序面向贵州省黔南布依族苗族自治州龙里县,提供景区、旅居、美食、特产的内容导览,以及票根权益的领取与使用服务。\n二、账号与登录\n你可通过微信一键登录使用本小程序,浏览内容无需登录;领取与使用权益需登录并绑定手机号。未注册的手机号将自动创建账号。\n三、权益说明\n权益不绑定具体商品,可多次使用。使用行为仅记录使用次数与时间,不会使权益失效。权益的适用范围与最终解释权归权益提供方所有。\n四、免责声明\n站内营业时间、价格、联系方式等信息可能发生变动,请以商户现场公示为准。\n五、账号注销\n你可以随时在「我的 → 账户注销」中删除个人信息与权益记录,注销完成后账号将无法恢复。\n六、联系我们\n如对本协议有疑问,请致电 0854-5630880。",
"phone": "0854-5630880",
"service_hours": "09:00 - 18:00",
"brand_name": "龙里文旅",
"version": 3,
"enabled": true,
"sort_order": 1,
"valid_from": null,
"valid_until": null,
"updated_at": "2026-09-11T00:00:00Z"
},
{
"key": "privacy",
"title": "隐私政策",
"body": "我们非常重视你的个人信息保护。本政策说明龙里文旅小程序如何收集、使用、存储与保护你的个人信息。\n一、我们收集的信息\n1. 微信授权信息:在你主动点击登录时,我们获取你的微信身份标识,仅用于建立与识别你的账号。\n2. 手机号:在你主动点击「微信一键绑定」时获取,仅用于领取权益、权益使用与账号找回。\n3. 权益记录:你领取与使用权益的时间、权益编号,用于使用校验与纠纷处理。\n二、我们如何使用信息\n仅在实现上述功能所必需的范围内使用,不会用于广告推送或向第三方出售。\n三、信息的存储与保护\n数据存储于中国境内服务器,传输过程采用 HTTPS 加密。我们采取访问控制与最小权限原则保护你的数据。\n四、你的权利\n你可以随时在「我的」页面修改头像与昵称;可以通过「账户注销」删除个人信息与权益记录。\n五、联系我们\n如对本政策有疑问,请致电 0854-5630880。",
"phone": "0854-5630880",
"service_hours": "09:00 - 18:00",
"brand_name": "龙里文旅",
"version": 2,
"enabled": true,
"sort_order": 2,
"valid_from": null,
"valid_until": null,
"updated_at": "2026-09-11T00:00:00Z"
},
{
"key": "about",
"title": "关于我们",
"body": "龙里文旅是面向贵州省黔南布依族苗族自治州龙里县的全域旅游导览小程序,聚合景区、旅居、美食、特产四大板块,并提供票根权益的领取与使用服务。",
"phone": "0854-5630880",
"service_hours": "09:00 - 18:00",
"brand_name": "龙里文旅",
"version": 1,
"enabled": true,
"sort_order": 3,
"valid_from": null,
"valid_until": null,
"updated_at": "2026-09-11T00:00:00Z"
},
{
"key": "customer_service",
"title": "联系客服",
"body": "工作日可拨打以下电话,节假日照常。",
"phone": "0854-5630880",
"service_hours": "09:00 - 18:00",
"brand_name": "龙里文旅",
"version": 1,
"enabled": true,
"sort_order": 4,
"valid_from": null,
"valid_until": null,
"updated_at": "2026-09-11T00:00:00Z"
}
],
"total": 4
}

View File

@@ -1,4 +0,0 @@
{
"status": "completed",
"retryable": false
}

View File

@@ -1,5 +0,0 @@
{
"status_token": "mock-erasure-receipt",
"status": "processing",
"retryable": true
}

View File

@@ -1,11 +0,0 @@
{
"token": "mock-user-session-token",
"user": {
"id": "mock-user-0001",
"nickname": "",
"avatar_url": "",
"phone_bound": false,
"created_at": "2026-09-17T10:00:00Z"
},
"expires_at": "2026-09-24T10:00:00Z"
}

View File

@@ -1,7 +0,0 @@
{
"id": "mock-user-0001",
"nickname": "",
"avatar_url": "",
"phone_bound": false,
"created_at": "2026-09-17T10:00:00Z"
}