feat: 更新作业纸 footer
This commit is contained in:
@@ -60,12 +60,12 @@ await apiRequest('/me');
|
|||||||
|
|
||||||
- `localBaseUrl`:本地开发接口。
|
- `localBaseUrl`:本地开发接口。
|
||||||
- `stagingBaseUrl`:未来测试接口。
|
- `stagingBaseUrl`:未来测试接口。
|
||||||
- `productionBaseUrl`:未来正式接口。
|
- `productionBaseUrl`:线上接口 `https://doodle-api.joeyone.cn/api/v1`。
|
||||||
|
|
||||||
目前按要求,三个地址都暂填 `http://127.0.0.1:8800/api/v1`,客户端缓存和会话按环境隔离,但实际请求仍访问同一个本地后端和数据库。真机的 `127.0.0.1` 指向手机;调试时 local 需改为电脑局域网地址。部署时把 staging/production 占位地址改为对应 HTTPS 地址,不会因地址缺失自动回退 local。
|
开发版默认选择 `production`,体验版和正式版始终连接线上 `https://doodle-api.joeyone.cn/api/v1`。本次启用 v2 环境选择键,旧版保存的 local 选择不会覆盖线上默认值;之后在 Debug 中手动选择的环境仍会持久保存。`local / staging` 暂保留 `http://127.0.0.1:8800/api/v1`,staging 尚非独立测试服务;真机调试 local 时需改为电脑局域网地址。客户端缓存和会话按环境与接口地址隔离,不会因线上连接失败自动回退 local。
|
||||||
|
|
||||||
切换会清空共享用户和内容状态,读取目标环境凭证,并强制从目标接口加载首页;原环境的缓存与会话保留。所有 API 响应按环境版本检查,即使快速 A→B→A,旧请求也不能覆盖当前数据。写请求(含登录等待及上传重试)未完成时禁止切换。目标接口不可用时提示连接失败并保留所选环境,不展示上一环境的内容。打印偏好继续共用。
|
切换会清空共享用户和内容状态,读取目标环境凭证,并强制从目标接口加载首页;原环境的缓存与会话保留。所有 API 响应按环境版本检查,即使快速 A→B→A,旧请求也不能覆盖当前数据。写请求(含登录等待及上传重试)未完成时禁止切换。目标接口不可用时提示连接失败并保留所选环境,不展示上一环境的内容。打印偏好继续共用。
|
||||||
|
|
||||||
本轮仅调整开发版数据来源;体验/正式包原预拉取模式、地址及全部旧云端服务保持原样。
|
本轮调整 HTTP 接口默认环境和线上地址;体验/正式包原预拉取模式及全部旧云端服务保持原样。
|
||||||
|
|
||||||
环境切换回归:`node --import tsx --test tests/environment-switch.test.mjs`。
|
环境切换回归:`node --import tsx --test tests/environment-switch.test.mjs`。
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ export type ApiEnvironment = 'local' | 'staging' | 'production';
|
|||||||
type MiniProgramVersion = 'develop' | 'trial' | 'release';
|
type MiniProgramVersion = 'develop' | 'trial' | 'release';
|
||||||
|
|
||||||
const LOCAL_BASE_URL = 'http://127.0.0.1:8800/api/v1';
|
const LOCAL_BASE_URL = 'http://127.0.0.1:8800/api/v1';
|
||||||
const STORAGE_KEY = 'doodle_develop_api_environment_v1';
|
// 默认来源改为线上时启用新选择键,旧版保存的 local 不覆盖本次默认值。
|
||||||
|
const STORAGE_KEY = 'doodle_develop_api_environment_v2';
|
||||||
let selectedEnvironment: ApiEnvironment | undefined;
|
let selectedEnvironment: ApiEnvironment | undefined;
|
||||||
let revision = 0;
|
let revision = 0;
|
||||||
|
|
||||||
/** 暂时共用本地地址;部署后分别替换 staging/production,真机 local 使用电脑局域网 IP。 */
|
/** 默认访问线上;local 保留调试,staging 暂沿用本机占位地址。 */
|
||||||
export const apiConfig = {
|
export const apiConfig = {
|
||||||
developmentEnvironment: 'local' as ApiEnvironment,
|
developmentEnvironment: 'production' as ApiEnvironment,
|
||||||
localBaseUrl: LOCAL_BASE_URL,
|
localBaseUrl: LOCAL_BASE_URL,
|
||||||
stagingBaseUrl: LOCAL_BASE_URL,
|
stagingBaseUrl: LOCAL_BASE_URL,
|
||||||
productionBaseUrl: LOCAL_BASE_URL,
|
productionBaseUrl: 'https://doodle-api.joeyone.cn/api/v1',
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -174,7 +174,11 @@ export default class PaperDrawService extends BaseDrawService {
|
|||||||
ctx.globalAlpha = 0.35;
|
ctx.globalAlpha = 0.35;
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.textBaseline = 'bottom';
|
ctx.textBaseline = 'bottom';
|
||||||
ctx.fillText('涂鸦丫小程序', canvasWidth / 2, canvasHeight - 16);
|
ctx.fillText(
|
||||||
|
'© 涂鸦丫小程序 | 原创出品',
|
||||||
|
canvasWidth / 2,
|
||||||
|
canvasHeight - 16,
|
||||||
|
);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,12 +106,19 @@ createPage(
|
|||||||
// 草稿在公开详情中固定为 404;开发版使用本地注册配置,不通过失败请求判断预览权限。
|
// 草稿在公开详情中固定为 404;开发版使用本地注册配置,不通过失败请求判断预览权限。
|
||||||
if (!this.data.isDevEnv) {
|
if (!this.data.isDevEnv) {
|
||||||
const remote = await getWorksheet(entry.id);
|
const remote = await getWorksheet(entry.id);
|
||||||
if (
|
if (entry.id !== PAPER_SHEET_WORKSHEET_ID) {
|
||||||
entry.id !== PAPER_SHEET_WORKSHEET_ID &&
|
// 旧服务端可能未下发 rendering。仅对本地已注册 ID、且公开路由一致的条目使用固定配置。
|
||||||
(remote.rendering?.rendererKey !== entry.rendererKey ||
|
// 显式下发的配置仍须匹配;404、网络异常及未知条目不进入此兼容分支。
|
||||||
remote.rendering?.paperType !== paperType)
|
const matches =
|
||||||
) {
|
remote.rendering === undefined
|
||||||
throw new Error('作业纸配置暂不可用,请更新小程序');
|
? remote.path ===
|
||||||
|
getPublishMetaByMode(entry.id)?.path
|
||||||
|
: remote.rendering?.rendererKey ===
|
||||||
|
entry.rendererKey &&
|
||||||
|
remote.rendering?.paperType === paperType;
|
||||||
|
if (!matches) {
|
||||||
|
throw new Error('作业纸配置异常,请稍后重试');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (request !== this._entryRequest) return;
|
if (request !== this._entryRequest) return;
|
||||||
|
|||||||
@@ -117,6 +117,8 @@ function fixture(initialStorage = new Map()) {
|
|||||||
run((name) => load(resolve(dirname(file), name)), exports);
|
run((name) => load(resolve(dirname(file), name)), exports);
|
||||||
return exports;
|
return exports;
|
||||||
}
|
}
|
||||||
|
// 认证回归显式使用本机配置,避免依赖产品默认环境。
|
||||||
|
load('config/api').apiConfig.developmentEnvironment = 'local';
|
||||||
return {
|
return {
|
||||||
auth: load('utils/auth'),
|
auth: load('utils/auth'),
|
||||||
session: load('services/auth-session'),
|
session: load('services/auth-session'),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const deferred = () => {
|
|||||||
const tick = () => new Promise(setImmediate);
|
const tick = () => new Promise(setImmediate);
|
||||||
|
|
||||||
/** 加载真实配置、请求、认证、App 与切换流程,仅模拟微信运行时和服务端响应。 */
|
/** 加载真实配置、请求、认证、App 与切换流程,仅模拟微信运行时和服务端响应。 */
|
||||||
function fixture(storage = new Map()) {
|
function fixture(storage = new Map(), useDefaultEnvironment = false) {
|
||||||
let version = 'develop';
|
let version = 'develop';
|
||||||
let app;
|
let app;
|
||||||
let pages = [];
|
let pages = [];
|
||||||
@@ -131,6 +131,12 @@ function fixture(storage = new Map()) {
|
|||||||
return exports;
|
return exports;
|
||||||
}
|
}
|
||||||
load('app');
|
load('app');
|
||||||
|
const config = load('config/api');
|
||||||
|
if (!useDefaultEnvironment) {
|
||||||
|
// 既有切换场景从 local 开始,并显式模拟三个环境同址。
|
||||||
|
config.apiConfig.developmentEnvironment = 'local';
|
||||||
|
config.apiConfig.productionBaseUrl = config.apiConfig.localBaseUrl;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
config: load('config/api'),
|
config: load('config/api'),
|
||||||
auth: load('utils/auth'),
|
auth: load('utils/auth'),
|
||||||
@@ -423,3 +429,31 @@ test('Debug 页面调用统一切换入口,失败时隐藏 loading 后显示
|
|||||||
});
|
});
|
||||||
assert.equal(toasts.length, 0);
|
assert.equal(toasts.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('所有包默认请求线上;忽略旧 local 选择,新手动选择仍可持久保存', async () => {
|
||||||
|
const online = 'https://doodle-api.joeyone.cn/api/v1';
|
||||||
|
const f = fixture(
|
||||||
|
new Map([['doodle_develop_api_environment_v1', 'local']]),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
for (const version of ['develop', 'trial', 'release', 'unknown']) {
|
||||||
|
f.version(version);
|
||||||
|
assert.equal(f.config.getApiConfig().environment, 'production');
|
||||||
|
assert.equal(f.config.getApiConfig().baseUrl, online);
|
||||||
|
}
|
||||||
|
f.version('develop');
|
||||||
|
await f.api.apiRequest('/categories', { auth: 'none' });
|
||||||
|
assert.equal(f.requests.at(-1).url, `${online}/categories`);
|
||||||
|
f.config.selectApiEnvironment('local');
|
||||||
|
assert.equal(
|
||||||
|
f.config.getApiConfig().baseUrl,
|
||||||
|
'http://127.0.0.1:8800/api/v1',
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
fixture(f.storage, true).config.getApiConfig().environment,
|
||||||
|
'local',
|
||||||
|
);
|
||||||
|
f.version('trial');
|
||||||
|
assert.equal(f.config.getApiConfig().baseUrl, online);
|
||||||
|
assert.equal(f.config.getApiConfig().environment, 'production');
|
||||||
|
});
|
||||||
|
|||||||
@@ -306,3 +306,64 @@ test('体验版与正式版均校验公开详情,404 和网络故障不能降
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('体验/正式版兼容旧接口缺少 rendering,十种纸张保持独立分享、收藏及下载归属', async () => {
|
||||||
|
for (const version of ['trial', 'release']) {
|
||||||
|
const requested = [];
|
||||||
|
const { page, wx, favorites, downloads, toasts } = createPage({
|
||||||
|
getWorksheet: async (id) => {
|
||||||
|
requested.push(id);
|
||||||
|
return { id, path: config.getPublishMetaByMode(id).path };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
wx.getAccountInfoSync = () => ({
|
||||||
|
miniProgram: { envVersion: version },
|
||||||
|
});
|
||||||
|
for (const paper of config.PAPER_TYPE_OPTIONS) {
|
||||||
|
const id = config.getWorksheetIdForPaperType(paper.id);
|
||||||
|
await page.openEntry({ id });
|
||||||
|
assert.equal(page.data.hasContent, true);
|
||||||
|
assert.equal(page.data.selectedPaperType, paper.id);
|
||||||
|
assert.equal(page.data.functionId, id);
|
||||||
|
assert.equal(page.onShareAppMessage().path, `/${route}?id=${id}`);
|
||||||
|
await page.onPreviewFavorite();
|
||||||
|
assert.deepEqual(favorites.at(-1), ['add', id]);
|
||||||
|
await page.exportToPrint();
|
||||||
|
assert.equal(downloads.at(-1).worksheetId, id);
|
||||||
|
}
|
||||||
|
assert.equal(requested.length, 10);
|
||||||
|
assert.equal(
|
||||||
|
toasts.filter((title) => title.includes('配置')).length,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('兼容只允许缺省配置,错误路由、空/残缺/冲突配置仍阻止生成', async () => {
|
||||||
|
const path = config.getPublishMetaByMode('paper-mi').path;
|
||||||
|
for (const version of ['trial', 'release']) {
|
||||||
|
for (const remote of [
|
||||||
|
{ path: `/${route}?id=paper-square` },
|
||||||
|
{ path: '/mathPages/mathDraw/mathDraw?id=paper-mi' },
|
||||||
|
{},
|
||||||
|
{ path, rendering: null },
|
||||||
|
{ path, rendering: {} },
|
||||||
|
{ path, rendering: { rendererKey: 'unknown', paperType: 'mi' } },
|
||||||
|
{
|
||||||
|
path,
|
||||||
|
rendering: { rendererKey: 'paper-sheet', paperType: 'square' },
|
||||||
|
},
|
||||||
|
]) {
|
||||||
|
const { page, wx, toasts } = createPage({
|
||||||
|
getWorksheet: async () => remote,
|
||||||
|
});
|
||||||
|
wx.getAccountInfoSync = () => ({
|
||||||
|
miniProgram: { envVersion: version },
|
||||||
|
});
|
||||||
|
await page.onLoad({ id: 'paper-mi' });
|
||||||
|
assert.equal(page.data.hasContent, false);
|
||||||
|
assert.equal(page.data.isWorksheetReady, false);
|
||||||
|
assert.ok(toasts.includes('作业纸配置异常,请稍后重试'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user