Preserve H5 flows while adding Taobao identity, native capture, reports and isolated local development. Fix canceled province selection and expired-session logout. Record passing local regressions, two pre-existing H5 assertion failures and pending platform validation.
90 lines
4.9 KiB
JavaScript
90 lines
4.9 KiB
JavaScript
var config = require('../config'), transport = require('./transport');
|
|
var token = '', user = null, storageKey = 'juzi.taobao.session.v1';
|
|
function assetUrl(path) { return config.assetBase.replace(/\/?$/, '/') + path.replace(/^assets\//, ''); }
|
|
function clear() {
|
|
token = ''; user = null;
|
|
try { my.removeStorageSync({ key: storageKey }); } catch (_) {}
|
|
var app = typeof getApp === 'function' && getApp();
|
|
if (app) { app.globalData.profile = null; app.globalData.photos = null; app.globalData.payload = null; }
|
|
}
|
|
function request(path, body) {
|
|
var requestToken = token;
|
|
return transport.request('/api/taobao/' + path, body || {}, requestToken).then(function (data) {
|
|
if (!data || !data.ok) {
|
|
var err = new Error(data && data.message || '服务暂不可用,请稍后重试');
|
|
err.status = data && data.http_status; err.code = data && data.code;
|
|
if (err.status === 401 && requestToken === token) clear();
|
|
throw err;
|
|
}
|
|
return data;
|
|
});
|
|
}
|
|
function authorize() {
|
|
if (config.transport === 'direct') return Promise.resolve();
|
|
return new Promise(function (resolve, reject) {
|
|
my.authorize({ scopes: 'scope.userInfo', success: resolve, fail: function () { reject(new Error('请授权淘宝账号后继续')); } });
|
|
});
|
|
}
|
|
function login(consent) {
|
|
if (!consent) return Promise.reject(new Error('请先阅读并同意隐私协议'));
|
|
return authorize().then(function () { return request('challenge'); }).then(function (data) {
|
|
return request('session', { consent: true, challenge: data.challenge });
|
|
}).then(function (data) {
|
|
token = data.token; user = data.user;
|
|
my.setStorageSync({ key: storageKey, data: { token: token, user: user } });
|
|
return user;
|
|
});
|
|
}
|
|
function restore() {
|
|
try { var saved = my.getStorageSync({ key: storageKey }).data; if (saved) { token = saved.token || ''; user = saved.user || null; } } catch (_) {}
|
|
if (!token) return Promise.resolve(null);
|
|
var restoredToken = token;
|
|
return request('me').then(function (data) { if (token === restoredToken) user = data.user; return user; });
|
|
}
|
|
function canceled(signal) { if (signal && signal.canceled) { var e = new Error('已取消检测'); e.canceled = true; throw e; } }
|
|
function wait(ms, signal) { return new Promise(function (resolve) { setTimeout(resolve, ms); }).then(function () { canceled(signal); }); }
|
|
function poll(job, signal, deadline) {
|
|
canceled(signal);
|
|
if (Date.now() >= deadline) throw new Error('检测仍在处理中,请稍后从历史报告查看');
|
|
return request('analysis', { job: job }).then(function (data) {
|
|
canceled(signal);
|
|
if (data.status !== 'complete') return wait(1000, signal).then(function () { return poll(job, signal, deadline); });
|
|
if (!data.payload || !data.payload.ok) throw new Error(data.payload && data.payload.message || '检测失败,请重试');
|
|
return data.payload;
|
|
});
|
|
}
|
|
function upload(view, photo, signal) {
|
|
var b64 = photo.replace(/^data:image\/[^;]+;base64,/, ''), size = 64 * 1024;
|
|
if (!b64 || b64.length > 4 * 1024 * 1024) return Promise.reject(new Error('照片过大,请重新拍摄'));
|
|
var id = Date.now().toString(36) + '-' + Math.random().toString(36).slice(2), total = Math.ceil(b64.length / size);
|
|
var chain = Promise.resolve();
|
|
for (var i = 0; i < total; i++) (function (index) {
|
|
chain = chain.then(function () { canceled(signal); return request('photo', { view: view, upload_id: id, index: index, total: total, data: b64.slice(index * size, (index + 1) * size) }); });
|
|
})(i);
|
|
return chain.then(function () { return id; });
|
|
}
|
|
function analyze(photos, profile, signal) {
|
|
if (config.mock) {
|
|
var SS = require('../vendor/skin-score'), UI = require('../vendor/report-ui');
|
|
var vm = SS.buildViewModel(SS.MOCK_RESULT, SS.MOCK_SIDE, { realAge: profile.real_age, skinType: profile.daily_skin_type });
|
|
return Promise.resolve({ ok: true, result: SS.MOCK_RESULT, side: SS.MOCK_SIDE, vm: vm, presentation: UI.build(vm, SS.MOCK_RESULT) });
|
|
}
|
|
var refs = {};
|
|
return ['front', 'left', 'right'].reduce(function (chain, view, i) {
|
|
return chain.then(function () { return upload(view, photos[i], signal); }).then(function (id) { refs[view] = id; });
|
|
}, Promise.resolve()).then(function () {
|
|
canceled(signal); return request('skin-analyze', { photo_refs: refs, profile: profile });
|
|
}).then(function (data) { return poll(data.job, signal, Date.now() + 90000); });
|
|
}
|
|
function wrinkleViews(jobToken, signal) {
|
|
return request('wrinkle-views', { token: jobToken }).then(function (data) { return poll(data.job, signal, Date.now() + 90000); });
|
|
}
|
|
function logout() {
|
|
return request('logout').catch(function (err) {
|
|
// An expired/revoked session is already signed out; network failures still surface.
|
|
if (err.status !== 401) throw err;
|
|
}).then(clear);
|
|
}
|
|
module.exports = { assetUrl: assetUrl, analyze: analyze, login: login, restore: restore, request: request,
|
|
user: function () { return user; }, logout: logout, wrinkleViews: wrinkleViews };
|