退役旧创作模板业务并保留数据壳

保留 SpacetimeDB 历史表、迁移白名单与最小兼容读取定义
移除旧创作前后端、worker、业务过程及纯业务 crate 的编译依赖
恢复现役创作、项目、我的入口及桌面移动导航
收紧 Vite、TypeScript、ESLint、Vitest 与静态资源退役边界
补齐开发栈、网关、原生壳和文档退役约束
This commit is contained in:
2026-07-18 22:02:22 +08:00
parent 9c9c8f468a
commit 7ea463ed08
776 changed files with 2073 additions and 27260 deletions
+1 -2
View File
@@ -2,8 +2,7 @@
"pages": [
"pages/web-view/index",
"pages/share-grid/index",
"pages/wechat-pay/index",
"pages/subscribe-message/index"
"pages/wechat-pay/index"
],
"window": {
"navigationBarTitleText": "陶泥儿",
-5
View File
@@ -15,10 +15,6 @@ const MINI_PROGRAM_APP_ID = 'wx3da23ea14ca66b65';
// 中文注释:仅作为运行时环境识别失败时的兜底;正常情况下由 wx.getAccountInfoSync 自动判断。
const MINI_PROGRAM_ENV = 'release';
// 中文注释:AI 创作生成结果订阅消息模板,需与微信公众平台后台的模板 ID 保持一致。
const GENERATION_RESULT_SUBSCRIBE_TEMPLATE_ID =
'm5z7BkkBhJGbcH0cdDeHaeRU2tViDEguP38XdrRRCdU';
// 中文注释:给 H5 加一个来源标记,用于识别微信小程序 web-view 宿主。
const WEB_VIEW_SOURCE_QUERY = {
clientType: 'mini_program',
@@ -29,7 +25,6 @@ module.exports = {
API_BASE_URL,
DEV_API_BASE_URL,
DEV_WEB_VIEW_ENTRY_URL,
GENERATION_RESULT_SUBSCRIBE_TEMPLATE_ID,
MINI_PROGRAM_APP_ID,
MINI_PROGRAM_ENV,
WEB_VIEW_ENTRY_URL,
-2
View File
@@ -1,13 +1,11 @@
const payment = require('./payment');
const protocol = require('./protocol');
const shareGrid = require('./shareGrid');
const subscribeMessage = require('./subscribeMessage');
const webView = require('./webView');
module.exports = {
payment,
protocol,
shareGrid,
subscribeMessage,
webView,
};
-8
View File
@@ -3,7 +3,6 @@ const WECHAT_HOST_CAPABILITIES = [
'payment.request',
'share.setTarget',
'share.open',
'navigation.openNativePage',
];
const WECHAT_WEB_VIEW_PAGE_URL = '/pages/web-view/index';
@@ -11,12 +10,8 @@ const WECHAT_AUTH_PAGE_URL =
`${WECHAT_WEB_VIEW_PAGE_URL}?authAction=login&returnTo=previous`;
const WECHAT_PAY_PAGE_URL = '/pages/wechat-pay/index';
const WECHAT_SHARE_GRID_PAGE_URL = '/pages/share-grid/index';
const WECHAT_SUBSCRIBE_MESSAGE_PAGE_URL = '/pages/subscribe-message/index';
const WECHAT_PAY_RESULT_HASH_KEY = 'wx_pay_result';
const WECHAT_SUBSCRIBE_RESULT_HASH_KEY = 'wx_subscribe_result';
const WECHAT_PAY_RESULT_STORAGE_KEY = 'genarrative:wechat-pay-result';
const WECHAT_SUBSCRIBE_RESULT_STORAGE_KEY =
'genarrative:wechat-subscribe-result';
const WECHAT_SHARE_TARGET_MESSAGE_TYPE = 'genarrative:share-target';
module.exports = {
@@ -27,8 +22,5 @@ module.exports = {
WECHAT_PAY_RESULT_STORAGE_KEY,
WECHAT_SHARE_GRID_PAGE_URL,
WECHAT_SHARE_TARGET_MESSAGE_TYPE,
WECHAT_SUBSCRIBE_MESSAGE_PAGE_URL,
WECHAT_SUBSCRIBE_RESULT_HASH_KEY,
WECHAT_SUBSCRIBE_RESULT_STORAGE_KEY,
WECHAT_WEB_VIEW_PAGE_URL,
};
-15
View File
@@ -21,10 +21,6 @@ const shareGridPath = path.resolve(
process.cwd(),
'miniprogram/host-bridge/shareGrid.js',
);
const subscribeMessagePath = path.resolve(
process.cwd(),
'miniprogram/host-bridge/subscribeMessage.js',
);
const webViewPath = path.resolve(
process.cwd(),
'miniprogram/host-bridge/webView.js',
@@ -42,26 +38,18 @@ describe('wechat mini program host bridge protocol index', () => {
);
expect(protocol.WECHAT_PAY_PAGE_URL).toBe('/pages/wechat-pay/index');
expect(protocol.WECHAT_SHARE_GRID_PAGE_URL).toBe('/pages/share-grid/index');
expect(protocol.WECHAT_SUBSCRIBE_MESSAGE_PAGE_URL).toBe(
'/pages/subscribe-message/index',
);
expect(protocol.WECHAT_PAY_RESULT_HASH_KEY).toBe('wx_pay_result');
expect(protocol.WECHAT_SUBSCRIBE_RESULT_HASH_KEY).toBe(
'wx_subscribe_result',
);
});
test('dispatch index exposes current bridge ability modules', () => {
const payment = loadCommonJsModule(paymentPath);
const protocol = loadCommonJsModule(protocolPath);
const shareGrid = loadCommonJsModule(shareGridPath);
const subscribeMessage = loadCommonJsModule(subscribeMessagePath);
const webView = loadCommonJsModule(webViewPath);
const dispatch = loadCommonJsModule(dispatchPath, {
'./payment': payment,
'./protocol': protocol,
'./shareGrid': shareGrid,
'./subscribeMessage': subscribeMessage,
'./webView': webView,
});
@@ -71,8 +59,5 @@ describe('wechat mini program host bridge protocol index', () => {
);
expect(typeof dispatch.payment.requestWechatPayment).toBe('function');
expect(typeof dispatch.shareGrid.buildShareGridTilePlan).toBe('function');
expect(typeof dispatch.subscribeMessage.buildSubscribeResultValue).toBe(
'function',
);
});
});
-135
View File
@@ -1,135 +0,0 @@
/* global wx */
const SUBSCRIBE_RESULT_STORAGE_KEY = 'genarrative:wechat-subscribe-result';
const WECHAT_SUBSCRIBE_UNAVAILABLE_REASON = 'wechat subscribe unavailable';
function logWechatSubscribeFailure(label, _error) {
console.error(`[subscribe-message] ${label}`);
}
function appendSubscribeResult(url, result) {
const hashIndex = String(url || '').indexOf('#');
const baseUrl =
hashIndex >= 0 ? String(url).slice(0, hashIndex) : String(url || '');
const rawHash = hashIndex >= 0 ? String(url).slice(hashIndex + 1) : '';
const nextHash = rawHash
.split('&')
.filter((part) => part && !part.startsWith('wx_subscribe_result='))
.concat(`wx_subscribe_result=${encodeURIComponent(result)}`)
.join('&');
return `${baseUrl}#${nextHash}`;
}
function buildSubscribeResultValue(requestId, status, reason) {
const segments = [requestId, status];
if (reason) {
segments.push(encodeURIComponent(reason));
}
return segments.join(':');
}
function notifyPreviousWebView(requestId, status, reason) {
const result = buildSubscribeResultValue(requestId, status, reason);
wx.setStorageSync(SUBSCRIBE_RESULT_STORAGE_KEY, result);
}
function resolveSubscribeStatus(result, templateId) {
return result && result[templateId] === 'accept'
? 'success'
: 'skip';
}
function createSubscribeMessagePageController(pageContext, options = {}) {
const templateId = String(options.templateId || '').trim();
const notifyPageResult = (methodThis, status, reason) => {
const page = pageContext ?? methodThis;
const requestId = page.requestId || '';
if (!requestId || page.hasNotifiedSubscribeResult) {
return;
}
page.hasNotifiedSubscribeResult = true;
notifyPreviousWebView(requestId, status, reason);
};
return {
data: {
title: '接收生成结果通知',
errorMessage: '',
requesting: false,
},
onLoad(query) {
const page = pageContext ?? this;
page.requestId = String(query.requestId || '');
page.hasNotifiedSubscribeResult = false;
},
notifyResult(status, reason) {
notifyPageResult(this, status, reason);
},
requestSubscribe() {
const page = pageContext ?? this;
const requestId = page.requestId || '';
if (!requestId) {
page.setData({
errorMessage: '缺少订阅请求参数。',
});
return;
}
if (!templateId) {
notifyPageResult(this, 'skip', 'missing_template_id');
wx.navigateBack();
return;
}
if (typeof wx.requestSubscribeMessage !== 'function') {
notifyPageResult(this, 'skip', 'unsupported');
wx.navigateBack();
return;
}
page.setData({
requesting: true,
errorMessage: '',
});
wx.requestSubscribeMessage({
tmplIds: [templateId],
success(result) {
notifyPageResult(
page,
resolveSubscribeStatus(result, templateId),
'',
);
wx.navigateBack();
},
fail(error) {
logWechatSubscribeFailure('request failed', error);
notifyPageResult(
page,
'skip',
WECHAT_SUBSCRIBE_UNAVAILABLE_REASON,
);
wx.navigateBack();
},
});
},
handleSkip() {
notifyPageResult(this, 'skip', 'user_skip');
wx.navigateBack();
},
onUnload() {
notifyPageResult(this, 'skip', 'page_unload');
},
};
}
module.exports = {
SUBSCRIBE_RESULT_STORAGE_KEY,
WECHAT_SUBSCRIBE_UNAVAILABLE_REASON,
appendSubscribeResult,
buildSubscribeResultValue,
createSubscribeMessagePageController,
resolveSubscribeStatus,
};
@@ -1,137 +0,0 @@
import path from 'node:path';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { loadCommonJsModule } from '../test-utils/loadCommonJsModule.js';
const TEST_TEMPLATE_ID = 'm5z7BkkBhJGbcH0cdDeHaeRU2tViDEguP38XdrRRCdU';
const subscribeBridgePath = path.resolve(
process.cwd(),
'miniprogram/host-bridge/subscribeMessage.js',
);
describe('subscribe-message mini program bridge', () => {
let subscribeMessageBridge;
beforeEach(() => {
vi.spyOn(console, 'error').mockImplementation(() => {});
globalThis.wx = {
requestSubscribeMessage: vi.fn(),
setStorageSync: vi.fn(),
navigateBack: vi.fn(),
};
globalThis.getCurrentPages = vi.fn(() => []);
subscribeMessageBridge = loadCommonJsModule(subscribeBridgePath);
});
test('requests subscribe message and stores result before returning', () => {
const {
SUBSCRIBE_RESULT_STORAGE_KEY,
createSubscribeMessagePageController,
} = subscribeMessageBridge;
const previousPage = {
data: { webViewUrl: 'https://web.test/#tab=create' },
setData: vi.fn(),
};
globalThis.getCurrentPages = vi.fn(() => [previousPage, {}]);
globalThis.wx.requestSubscribeMessage.mockImplementationOnce((options) => {
options.success?.({
m5z7BkkBhJGbcH0cdDeHaeRU2tViDEguP38XdrRRCdU: 'accept',
});
});
const page = createSubscribeMessagePageController(
{
setData: vi.fn(),
},
{ templateId: TEST_TEMPLATE_ID },
);
page.onLoad({ requestId: 'request-1' });
page.requestSubscribe();
expect(globalThis.wx.requestSubscribeMessage).toHaveBeenCalledWith({
tmplIds: [TEST_TEMPLATE_ID],
success: expect.any(Function),
fail: expect.any(Function),
});
expect(globalThis.wx.setStorageSync).toHaveBeenCalledWith(
SUBSCRIBE_RESULT_STORAGE_KEY,
'request-1:success',
);
expect(previousPage.setData).not.toHaveBeenCalled();
expect(globalThis.wx.navigateBack).toHaveBeenCalled();
});
test('hides requestSubscribeMessage native failure details from H5 result', () => {
const {
SUBSCRIBE_RESULT_STORAGE_KEY,
createSubscribeMessagePageController,
} = subscribeMessageBridge;
const subscribeError = {
errMsg: 'requestSubscribeMessage:fail private native detail',
};
globalThis.wx.requestSubscribeMessage.mockImplementationOnce((options) => {
options.fail?.(subscribeError);
});
const page = createSubscribeMessagePageController(
{
setData: vi.fn(),
},
{ templateId: TEST_TEMPLATE_ID },
);
page.onLoad({ requestId: 'request-fail' });
page.requestSubscribe();
expect(globalThis.wx.setStorageSync).toHaveBeenCalledWith(
SUBSCRIBE_RESULT_STORAGE_KEY,
'request-fail:skip:wechat%20subscribe%20unavailable',
);
expect(console.error).toHaveBeenCalledWith('[subscribe-message] request failed');
expect(console.error.mock.calls.flat()).not.toContain(subscribeError);
expect(globalThis.wx.navigateBack).toHaveBeenCalled();
});
test('skip action notifies previous web-view', () => {
const {
SUBSCRIBE_RESULT_STORAGE_KEY,
createSubscribeMessagePageController,
} = subscribeMessageBridge;
const previousPage = {
data: { webViewUrl: 'https://web.test/' },
setData: vi.fn(),
};
globalThis.getCurrentPages = vi.fn(() => [previousPage, {}]);
const page = createSubscribeMessagePageController(
{
setData: vi.fn(),
},
{ templateId: TEST_TEMPLATE_ID },
);
page.onLoad({ requestId: 'request-skip' });
page.handleSkip();
expect(globalThis.wx.setStorageSync).toHaveBeenCalledWith(
SUBSCRIBE_RESULT_STORAGE_KEY,
'request-skip:skip:user_skip',
);
expect(previousPage.setData).not.toHaveBeenCalled();
expect(globalThis.wx.navigateBack).toHaveBeenCalled();
});
test('appendSubscribeResult replaces stale subscribe hash', () => {
const { appendSubscribeResult, buildSubscribeResultValue } =
subscribeMessageBridge;
expect(
appendSubscribeResult(
'https://web.test/#old=1&wx_subscribe_result=old',
'req:skip',
),
).toBe('https://web.test/#old=1&wx_subscribe_result=req%3Askip');
expect(buildSubscribeResultValue('req-1', 'skip', 'user_cancel')).toBe(
'req-1:skip:user_cancel',
);
});
});
@@ -1,12 +0,0 @@
/* global Page */
const { GENERATION_RESULT_SUBSCRIBE_TEMPLATE_ID } = require('../../config');
const {
createSubscribeMessagePage,
} = require('../../shell/subscribeMessage');
Page(
createSubscribeMessagePage(null, {
templateId: GENERATION_RESULT_SUBSCRIBE_TEMPLATE_ID,
}),
);
@@ -1,3 +0,0 @@
{
"navigationBarTitleText": "生成通知"
}
@@ -1,19 +0,0 @@
<view class="subscribe-screen">
<view class="subscribe-card">
<view class="subscribe-title">{{title}}</view>
<view wx:if="{{errorMessage}}" class="subscribe-text subscribe-text--danger">
{{errorMessage}}
</view>
<button
class="primary-button"
loading="{{requesting}}"
disabled="{{requesting}}"
bindtap="requestSubscribe"
>
继续并接收通知
</button>
<button class="ghost-button" disabled="{{requesting}}" bindtap="handleSkip">
仅继续生成
</button>
</view>
</view>
@@ -1,58 +0,0 @@
.subscribe-screen {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48rpx;
background: #0b0f14;
box-sizing: border-box;
}
.subscribe-card {
width: 100%;
max-width: 560rpx;
padding: 36rpx;
border: 1rpx solid rgba(255, 255, 255, 0.14);
border-radius: 12rpx;
background: rgba(255, 255, 255, 0.06);
box-sizing: border-box;
}
.subscribe-title {
font-size: 34rpx;
font-weight: 600;
line-height: 1.35;
color: #f5f7fb;
}
.subscribe-text {
margin-top: 16rpx;
font-size: 26rpx;
line-height: 1.55;
color: rgba(245, 247, 251, 0.72);
}
.subscribe-text--danger {
color: #ffb4a9;
}
.primary-button,
.ghost-button {
margin-top: 28rpx;
width: 100%;
border-radius: 8rpx;
font-size: 26rpx;
line-height: 2.6;
}
.primary-button {
background: #f5f7fb;
color: #0b0f14;
}
.ghost-button {
margin-top: 20rpx;
border: 1rpx solid rgba(255, 255, 255, 0.24);
background: transparent;
color: rgba(245, 247, 251, 0.86);
}
-11
View File
@@ -1,11 +0,0 @@
const {
createSubscribeMessagePageController,
} = require('../host-bridge/subscribeMessage');
function createSubscribeMessagePage(pageContext, options = {}) {
return createSubscribeMessagePageController(pageContext, options);
}
module.exports = {
createSubscribeMessagePage,
};
@@ -1,60 +0,0 @@
import path from 'node:path';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { loadCommonJsModule } from '../test-utils/loadCommonJsModule.js';
const TEST_TEMPLATE_ID = 'm5z7BkkBhJGbcH0cdDeHaeRU2tViDEguP38XdrRRCdU';
const subscribeBridgePath = path.resolve(
process.cwd(),
'miniprogram/host-bridge/subscribeMessage.js',
);
const subscribeShellPath = path.resolve(
process.cwd(),
'miniprogram/shell/subscribeMessage.js',
);
describe('wechat subscribe-message shell page', () => {
let createSubscribeMessagePage;
beforeEach(() => {
globalThis.wx = {
navigateBack: vi.fn(),
requestSubscribeMessage: vi.fn(),
setStorageSync: vi.fn(),
};
const subscribeMessageBridge = loadCommonJsModule(subscribeBridgePath);
const subscribeMessageShell = loadCommonJsModule(subscribeShellPath, {
'../host-bridge/subscribeMessage': subscribeMessageBridge,
});
createSubscribeMessagePage =
subscribeMessageShell.createSubscribeMessagePage;
});
test('requests generation-result subscribe template and stores result', () => {
globalThis.wx.requestSubscribeMessage.mockImplementationOnce((options) => {
options.success?.({
[TEST_TEMPLATE_ID]: 'accept',
});
});
const page = createSubscribeMessagePage(
{ setData: vi.fn() },
{ templateId: TEST_TEMPLATE_ID },
);
page.onLoad({ requestId: 'request-1' });
page.requestSubscribe();
expect(globalThis.wx.requestSubscribeMessage).toHaveBeenCalledWith({
tmplIds: [TEST_TEMPLATE_ID],
success: expect.any(Function),
fail: expect.any(Function),
});
expect(globalThis.wx.setStorageSync).toHaveBeenCalledWith(
'genarrative:wechat-subscribe-result',
'request-1:success',
);
expect(globalThis.wx.navigateBack).toHaveBeenCalled();
});
});