feat(sae): add Java MVP backend and Taobao connectivity page
Spring Boot 3.5.6 / Java 17 JAR with cloud gateway envelope and ping/whoami/echo; Taobao gateway signature verification matching the Node implementation; /pages/mvp page, splash dev entry and local transport contract test; SAE + 聚石塔 runbook and portable JDK/Maven setup; Windows 11 wmic shim for Taobao DevTools.
This commit is contained in:
+2
-1
@@ -4,7 +4,8 @@
|
||||
"pages/profile/profile",
|
||||
"pages/camera/camera",
|
||||
"pages/report/report",
|
||||
"pages/history/history"
|
||||
"pages/history/history",
|
||||
"pages/mvp/mvp"
|
||||
],
|
||||
"window": {
|
||||
"defaultTitle": "1'me在线肌肤测试",
|
||||
|
||||
@@ -8,6 +8,10 @@ module.exports = {
|
||||
cloudAppId: '',
|
||||
cloudEnv: 'online', // External/empty cloud applications use online
|
||||
basePath: '', // E.g. '/state' behind an existing reverse proxy
|
||||
// SAE/Java MVP page only: force the local Java backend without changing the main app config.
|
||||
// Set false and edit transport/baseUrl/cloudAppId above when testing the cloud gateway.
|
||||
mvpLocal: true,
|
||||
mvpBaseUrl: 'http://127.0.0.1:8080',
|
||||
mock: false,
|
||||
productItemIds: {} // Optional real Taobao item IDs keyed by tier 1/2/3
|
||||
};
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
.mvp {
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
padding: 48rpx 32rpx 64rpx;
|
||||
background: #101823;
|
||||
color: #F5F5F5;
|
||||
}
|
||||
|
||||
.mvp-head {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.mvp-kicker {
|
||||
display: block;
|
||||
color: #F5F078;
|
||||
font-size: 24rpx;
|
||||
letter-spacing: 2rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.mvp-title {
|
||||
display: block;
|
||||
font-size: 44rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.mvp-sub {
|
||||
display: block;
|
||||
color: #9AA4B2;
|
||||
font-size: 26rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.mvp-card {
|
||||
background: #1B2634;
|
||||
border: 1rpx solid #2A3748;
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.mvp-label {
|
||||
color: #9AA4B2;
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.mvp-code {
|
||||
display: block;
|
||||
color: #E8EDF4;
|
||||
font-family: Menlo, Consolas, monospace;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.mvp-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.mvp-btn {
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
background: #243244;
|
||||
color: #E8EDF4;
|
||||
font-size: 28rpx;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.mvp-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.mvp-btn-primary {
|
||||
background: #F5F078;
|
||||
color: #101823;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mvp-tip {
|
||||
color: #7F8A99;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.7;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<view class="mvp">
|
||||
<view class="mvp-head">
|
||||
<text class="mvp-kicker">SAE + 淘宝云应用</text>
|
||||
<text class="mvp-title">最小连通性验证</text>
|
||||
<text class="mvp-sub">Java 后端 / 云网关统一响应</text>
|
||||
</view>
|
||||
|
||||
<view class="mvp-card">
|
||||
<view class="mvp-label">当前 config.js</view>
|
||||
<text class="mvp-code">{{configText}}</text>
|
||||
</view>
|
||||
|
||||
<view class="mvp-actions">
|
||||
<button class="mvp-btn mvp-btn-primary" onTap="onPing" loading="{{loading}}">POST /api/taobao/ping</button>
|
||||
<button class="mvp-btn" onTap="onWhoami" loading="{{loading}}">POST /api/taobao/whoami</button>
|
||||
<button class="mvp-btn" onTap="onEcho" loading="{{loading}}">POST /api/taobao/echo</button>
|
||||
<button class="mvp-btn" onTap="onBack">返回首页</button>
|
||||
</view>
|
||||
|
||||
<view class="mvp-card">
|
||||
<view class="mvp-label">返回(已由 transport.unwrap 解包)</view>
|
||||
<text class="mvp-code">{{resultText}}</text>
|
||||
</view>
|
||||
|
||||
<view class="mvp-tip">
|
||||
本地调试:config.transport = direct,baseUrl 指向 127.0.0.1。云端:transport = cloud,baseUrl 必须是 HTTPS,并填写 cloudAppId/cloudEnv。
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,63 @@
|
||||
var config = require('../../config');
|
||||
var transport = require('../../utils/transport');
|
||||
var original = { transport: config.transport, baseUrl: config.baseUrl };
|
||||
|
||||
if (config.mvpLocal) {
|
||||
config.transport = 'direct';
|
||||
config.baseUrl = config.mvpBaseUrl;
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
configText: '',
|
||||
resultText: '',
|
||||
loading: false
|
||||
},
|
||||
|
||||
onLoad: function () {
|
||||
this.setData({
|
||||
configText: JSON.stringify({
|
||||
environment: config.environment,
|
||||
transport: config.transport,
|
||||
baseUrl: config.baseUrl,
|
||||
cloudAppId: config.cloudAppId || '(empty)',
|
||||
cloudEnv: config.cloudEnv
|
||||
}, null, 2)
|
||||
});
|
||||
this.onPing();
|
||||
},
|
||||
|
||||
call: function (path, body) {
|
||||
var that = this;
|
||||
this.setData({ loading: true, resultText: '请求中...' });
|
||||
transport.request(path, body || {}).then(function (data) {
|
||||
that.setData({ loading: false, resultText: JSON.stringify(data, null, 2) });
|
||||
}).catch(function (err) {
|
||||
that.setData({
|
||||
loading: false,
|
||||
resultText: 'ERROR: ' + (err && err.message ? err.message : String(err))
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
onPing: function () {
|
||||
this.call('/api/taobao/ping', {});
|
||||
},
|
||||
|
||||
onWhoami: function () {
|
||||
this.call('/api/taobao/whoami', {});
|
||||
},
|
||||
|
||||
onEcho: function () {
|
||||
this.call('/api/taobao/echo', { hello: 'world', at: Date.now() });
|
||||
},
|
||||
|
||||
onBack: function () {
|
||||
my.navigateBack();
|
||||
},
|
||||
|
||||
onUnload: function () {
|
||||
config.transport = original.transport;
|
||||
config.baseUrl = original.baseUrl;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"defaultTitle": "SAE 连通性 MVP",
|
||||
"titleBarColor": "#101823",
|
||||
"backgroundColor": "#101823"
|
||||
}
|
||||
@@ -142,3 +142,4 @@
|
||||
.brand-row { justify-content: center; }
|
||||
.brand-logo { width: 450rpx; height: 52rpx; margin: 0; }
|
||||
.camera-debug-entry { position: absolute; z-index: 2; top: 24rpx; right: 24rpx; padding: 16rpx 24rpx; border-radius: 16rpx; color: #fff; background: rgba(0,0,0,.65); font-size: 24rpx; }
|
||||
.mvp-debug-entry { top: 100rpx; }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<!-- ═════ 01 启动页 ═════ -->
|
||||
<view class="splash">
|
||||
<view class="camera-debug-entry" a:if="{{showCameraDebug}}" onTap="onCameraDebug">开发调试 · 相机验收</view>
|
||||
<view class="camera-debug-entry mvp-debug-entry" a:if="{{showMvpDebug}}" onTap="onMvpDebug">SAE MVP 连通性</view>
|
||||
<image class="splash-bg" src="{{bgUrl}}" mode="aspectFill" />
|
||||
<view class="splash-slogan">
|
||||
<text>1'me,</text>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* ═════ 启动页:品牌底图 + Slogan + 隐私授权弹窗(对齐 H5 #s-splash + #p-privacy) ═════ */
|
||||
var api = require('../../utils/api.js');
|
||||
var development = require('../../utils/development');
|
||||
var config = require('../../config');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -8,7 +9,8 @@ Page({
|
||||
logoUrl: api.assetUrl('img/ui-20261012/brand-lockup.png'),
|
||||
showPrivacy: false, // 隐私授权弹窗
|
||||
agreed: false, // 隐私协议勾选
|
||||
showPolicy: false, busy: false, signedIn: false, showCameraDebug: development.cameraDebugEnabled()
|
||||
showPolicy: false, busy: false, signedIn: false, showCameraDebug: development.cameraDebugEnabled(),
|
||||
showMvpDebug: config.mvpLocal === true
|
||||
},
|
||||
onShow: function () {
|
||||
var that = this;
|
||||
@@ -65,6 +67,10 @@ Page({
|
||||
if (!development.cameraDebugEnabled()) return;
|
||||
my.navigateTo({ url: '/pages/camera/camera?debug=camera' });
|
||||
},
|
||||
onMvpDebug: function () {
|
||||
if (!config.mvpLocal) return;
|
||||
my.navigateTo({ url: '/pages/mvp/mvp' });
|
||||
},
|
||||
|
||||
noop: function () {} // 弹窗卡片内点击拦截冒泡用
|
||||
});
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
// 不依赖淘宝 IDE 的本地契约测试:
|
||||
// 用 Node 模拟 my.request,加载真实的 utils/transport.js,直连 Java 后端。
|
||||
// 运行前先启动 backend-java,然后:node scripts/mvp-local-check.cjs
|
||||
|
||||
const http = require('node:http');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const vm = require('node:vm');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const BASE = process.env.MVP_BASE_URL || 'http://127.0.0.1:8080';
|
||||
const config = {
|
||||
environment: 'development',
|
||||
transport: 'direct',
|
||||
baseUrl: BASE,
|
||||
assetBase: BASE + '/assets/',
|
||||
cloudAppId: '',
|
||||
cloudEnv: 'test',
|
||||
basePath: '',
|
||||
mock: false
|
||||
};
|
||||
|
||||
function httpRequest(url, options, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = http.request(url, options, (res) => {
|
||||
const chunks = [];
|
||||
res.on('data', (chunk) => chunks.push(chunk));
|
||||
res.on('end', () => {
|
||||
const text = Buffer.concat(chunks).toString('utf8');
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
} catch (e) {
|
||||
reject(new Error('non-JSON response: ' + text.slice(0, 200)));
|
||||
return;
|
||||
}
|
||||
resolve({ status: res.statusCode, statusCode: res.statusCode, data });
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.end(body);
|
||||
});
|
||||
}
|
||||
|
||||
const my = {
|
||||
request(options) {
|
||||
httpRequest(options.url, { method: options.method, headers: options.headers }, options.data)
|
||||
.then((res) => options.success(res))
|
||||
.catch((err) => options.fail(err));
|
||||
}
|
||||
};
|
||||
|
||||
const transportModule = { exports: {} };
|
||||
vm.runInNewContext(fs.readFileSync(path.join(__dirname, '../utils/transport.js'), 'utf8'), {
|
||||
module: transportModule,
|
||||
Promise,
|
||||
my,
|
||||
require(name) {
|
||||
return name === '../config' ? config : {};
|
||||
}
|
||||
});
|
||||
|
||||
const transport = transportModule.exports;
|
||||
|
||||
(async () => {
|
||||
const ping = await transport.request('/api/taobao/ping', {});
|
||||
assert.equal(ping.server, 'java', 'ping.server should be java');
|
||||
assert.equal(ping.pong, true, 'ping.pong should be true');
|
||||
|
||||
const echo = await transport.request('/api/taobao/echo', { hello: 'mvp' });
|
||||
assert.equal(echo.body.hello, 'mvp', 'echo.body.hello should round-trip');
|
||||
|
||||
const whoami = await transport.request('/api/taobao/whoami', {});
|
||||
assert.equal(whoami.ok, true, 'whoami.ok should be true');
|
||||
|
||||
console.log('PASS');
|
||||
console.log(JSON.stringify({ ping, echo, whoami }, null, 2));
|
||||
})().catch((err) => {
|
||||
console.error('FAIL:', err && err.stack ? err.stack : err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user