Merge branch 'dev' of codeup.aliyun.com:61c14a7efa282c88e103c23f/oak-general-business into dev

This commit is contained in:
Xu Chang 2022-05-31 19:07:18 +08:00
commit 5bb19d3dfb
15 changed files with 455 additions and 356 deletions

View File

@ -1,4 +1,4 @@
import { String, Text } from 'oak-domain/lib/types/DataType';
import { String, Int, Text } from 'oak-domain/lib/types/DataType';
import { FileCarrierEntityShape } from 'oak-domain/lib/types/Entity';
export interface Schema extends FileCarrierEntityShape {
origin: 'qiniu' | 'unknown';
@ -12,4 +12,6 @@ export interface Schema extends FileCarrierEntityShape {
entity: String<32>;
entityId: String<64>;
extra1?: Text;
extension: String<16>;
size?: Int<4>;
}

View File

@ -15,7 +15,9 @@ class ExtraFile extends oak_frontend_base_1.Feature {
}
async upload(extraFile, scene) {
try {
const { origin, extra1: filePath, filename: fileName } = extraFile;
const { origin, extra1: filePath, filename, objectId, extension, entity, } = extraFile;
// 构造文件上传所需的fileName
const fileName = `${entity}/${objectId}.${extension}`;
const uploadInfo = await this.getAspectProxy().getUploadInfo({
origin,
fileName,

View File

@ -11,7 +11,7 @@ export default class qiniuInstance {
bucket: string;
domain: string;
});
getUploadInfo(fileName: string): Promise<{
getUploadInfo(key: string): Promise<{
key: string;
uploadToken: string;
uploadHost: string;

View File

@ -19,10 +19,9 @@ class qiniuInstance {
this.bucket = bucket;
this.domain = domain;
}
async getUploadInfo(fileName) {
async getUploadInfo(key) {
try {
const { uploadHost, domain, bucket } = this;
const key = `${Date.now()}/${fileName}`;
const scope = `${bucket}:${key}`;
const uploadToken = this.getToken(scope);
return {

View File

@ -1,3 +1,3 @@
import { OpSchema as ExtraFile } from 'oak-app-domain/ExtraFile/Schema';
export declare function composeFileUrl(extraFile: Pick<ExtraFile, 'type' | 'bucket' | 'filename' | 'origin' | 'extra1'>): string;
export declare function composeFileUrl(extraFile: Pick<ExtraFile, 'type' | 'bucket' | 'filename' | 'origin' | 'extra1' | 'objectId' | 'extension' | 'entity'>): string;
export declare function decomposeFileUrl(url: string): Pick<ExtraFile, 'bucket' | 'filename' | 'origin' | 'type' | 'extra1'>;

View File

@ -2,12 +2,13 @@
Object.defineProperty(exports, "__esModule", { value: true });
exports.decomposeFileUrl = exports.composeFileUrl = void 0;
function composeFileUrl(extraFile) {
const { type, bucket, filename, origin, extra1 } = extraFile;
const { type, bucket, filename, origin, extra1, objectId, extension, entity } = extraFile;
if (extra1) {
// 有extra1就用extra1
return extra1;
}
return '';
// 缺少https和域名
return `${entity}/${objectId}.${extension}`;
}
exports.composeFileUrl = composeFileUrl;
function decomposeFileUrl(url) {

View File

@ -13,4 +13,6 @@ export interface Schema extends FileCarrierEntityShape {
entity: String<32>;
entityId: String<64>;
extra1?: Text;
extension: String<16>;
size?: Int<4>;
};

View File

@ -15,25 +15,36 @@ export class ExtraFile<
@Action
async upload(extraFile: DeduceCreateOperationData<EntityDict['extraFile']['OpSchema']>, scene: string) {
try {
const { origin, extra1: filePath, filename: fileName } = extraFile;
const uploadInfo =
await this.getAspectProxy().getUploadInfo(
{
origin,
fileName,
},
scene
);
const {
origin,
extra1: filePath,
filename,
objectId,
extension,
entity,
} = extraFile;
// 构造文件上传所需的fileName
const fileName = `${entity}/${objectId}.${extension}`;
const uploadInfo = await this.getAspectProxy().getUploadInfo(
{
origin,
fileName,
},
scene
);
if (process.env.OAK_PLATFORM === 'wechatMp') {
// 微信小程序使用wx.uploadFile, 封装upload上传源为origin
const up = new Upload();
const result = await up.uploadFile(origin, filePath!, uploadInfo);
const up = new Upload();
const result = await up.uploadFile(
origin,
filePath!,
uploadInfo
);
return result;
}
else {
} else {
throw new Error('not implemented yet');
}
}
} catch (err) {
throw err;
}

View File

@ -22,10 +22,9 @@ export default class qiniuInstance {
this.domain = domain;
}
async getUploadInfo(fileName: string) {
async getUploadInfo(key: string) {
try {
const { uploadHost, domain, bucket } = this;
const key = `${Date.now()}/${fileName}`;
const scope = `${bucket}:${key}`;
const uploadToken = this.getToken(scope);
return {
@ -47,10 +46,13 @@ export default class qiniuInstance {
deadline: 3600 + Math.floor(Date.now() / 1000),
};
// 构造凭证
const encodedFlags = this.urlSafeBase64Encode(JSON.stringify(putPolicy));
const encodedFlags = this.urlSafeBase64Encode(
JSON.stringify(putPolicy)
);
const encoded = this.hmacSha1(encodedFlags, this.secretKey);
const encodedSign = this.base64ToUrlSafe(encoded);
const uploadToken = this.accessKey + ':' + encodedSign + ':' + encodedFlags;
const uploadToken =
this.accessKey + ':' + encodedSign + ':' + encodedFlags;
return uploadToken;
}

View File

@ -1,12 +1,26 @@
import { OpSchema as ExtraFile } from 'oak-app-domain/ExtraFile/Schema';
export function composeFileUrl(extraFile: Pick<ExtraFile, 'type' | 'bucket' | 'filename' | 'origin' | 'extra1'>) {
const { type, bucket, filename, origin, extra1 } = extraFile;
export function composeFileUrl(
extraFile: Pick<
ExtraFile,
| 'type'
| 'bucket'
| 'filename'
| 'origin'
| 'extra1'
| 'objectId'
| 'extension'
| 'entity'
>
) {
const { type, bucket, filename, origin, extra1, objectId, extension, entity } =
extraFile;
if (extra1) {
// 有extra1就用extra1
return extra1!;
}
return '';
// 缺少https和域名
return `${entity}/${objectId}.${extension}`;
}
export function decomposeFileUrl(url: string): Pick<ExtraFile, 'bucket' | 'filename' | 'origin' | 'type' | 'extra1'> {

View File

@ -71,6 +71,7 @@ OakComponent({
origin: String,
tag1: String,
tag2: String,
entity: String,
},
methods: {
@ -100,8 +101,16 @@ OakComponent({
return (750 / windowWidth) * px;
},
async onPick() {
const { selectCount, mediaType, sourceType, type, origin, tag1, tag2} =
this.data;
const {
selectCount,
mediaType,
sourceType,
type,
origin,
tag1,
tag2,
entity,
} = this.data;
try {
const { errMsg, tempFiles } = await wx.chooseMedia({
count: selectCount,
@ -116,30 +125,45 @@ OakComponent({
} else {
await Promise.all(tempFiles.map(
async (tempExtraFile) => {
const { tempFilePath, thumbTempFilePath } = tempExtraFile;
const { tempFilePath, thumbTempFilePath, fileType, size } = tempExtraFile;
const filePath = tempFilePath || thumbTempFilePath;
const filename = filePath.match(/[^/]+(?!.*\/)/g)![0];
assert(origin === 'qiniu'); // 目前只支持七牛上传
const ele: Parameters<typeof this['pushNode']>[1] = {
updateData: {
extra1: filePath,
origin,
type,
tag1,
tag2,
objectId: await generateNewId(),
filename: filename,
},
beforeExecute: async (updateData) => {
const { url, bucket } = await this.features.extraFile.upload(
updateData as DeduceCreateOperationData<EntityDict['extraFile']['Schema']>, "extraFile:gallery:upload");
Object.assign(updateData, {
bucket,
extra1: url,
});
},
};
const fileFullName = filePath.match(/[^/]+(?!.*\/)/g)![0];
const extension = fileFullName.substring(
fileFullName.lastIndexOf('.') + 1
);
const filename = fileFullName.substring(
0, fileFullName.lastIndexOf('.')
);
assert(entity, '必须传入entity');
assert(origin === 'qiniu', '目前只支持七牛上传'); // 目前只支持七牛上传
const ele: Parameters<typeof this['pushNode']>[1] =
{
updateData: {
extra1: filePath,
origin,
type: type || fileType,
tag1,
tag2,
objectId: await generateNewId(),
entity,
filename: filename,
size: size,
extension,
},
beforeExecute: async (updateData) => {
const { url, bucket } =
await this.features.extraFile.upload(
updateData as DeduceCreateOperationData<
EntityDict['extraFile']['Schema']
>,
'extraFile:gallery:upload'
);
Object.assign(updateData, {
bucket,
extra1: url,
});
},
};
this.pushNode(undefined, ele);
}

View File

@ -1,118 +1,124 @@
import { ROOT_ROLE_ID } from '../../../../src/constants';
import { composeFileUrl } from '../../../../src/utils/extraFile';
OakPage({
path: 'token:me',
entity: 'token',
isList: true,
projection: {
id: 1,
userId: 1,
playerId: 1,
user: {
OakPage(
{
path: 'token:me',
entity: 'token',
isList: true,
projection: {
id: 1,
nickname: 1,
name: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
id: 1,
tag1: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
type: 1,
userId: 1,
playerId: 1,
user: {
id: 1,
nickname: 1,
name: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
id: 1,
tag1: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
type: 1,
entity: 1,
extension: 1,
},
filter: {
tag1: 'avatar',
},
indexFrom: 0,
count: 1,
},
filter: {
tag1: 'avatar',
mobile$user: {
$entity: 'mobile',
data: {
id: 1,
mobile: 1,
},
},
indexFrom: 0,
count: 1,
},
mobile$user: {
$entity: 'mobile',
data: {
id: 1,
mobile: 1,
player: {
id: 1,
userRole$user: {
$entity: 'userRole',
data: {
id: 1,
userId: 1,
roleId: 1,
},
},
},
},
player: {
id: 1,
userRole$user: {
$entity: 'userRole',
data: {
id: 1,
userId: 1,
roleId: 1,
}
},
}
},
formData: async ({ data: [ token ] }) => {
const user = token?.user;
const player = token?.player;
const avatarFile = user && user.extraFile$entity && user.extraFile$entity[0];
const avatar = avatarFile && composeFileUrl(avatarFile);
const nickname = user && user.nickname;
const mobileData = user && user.mobile$user && user.mobile$user[0];
const { mobile } = mobileData || {};
const mobileCount = user?.mobile$user?.length || 0;
formData: async ({ data: [token] }) => {
const user = token?.user;
const player = token?.player;
const avatarFile =
user && user.extraFile$entity && user.extraFile$entity[0];
const avatar = avatarFile && composeFileUrl(avatarFile);
const nickname = user && user.nickname;
const mobileData = user && user.mobile$user && user.mobile$user[0];
const { mobile } = mobileData || {};
const mobileCount = user?.mobile$user?.length || 0;
const isLoggedIn = !!token;
const isPlayingAnother = token && token.userId !== token.playerId;
const isRoot = player?.userRole$user && player.userRole$user[0].roleId === ROOT_ROLE_ID;
return {
avatar,
nickname,
mobile,
mobileCount,
isLoggedIn,
isPlayingAnother,
isRoot,
};
const isLoggedIn = !!token;
const isPlayingAnother = token && token.userId !== token.playerId;
const isRoot =
player?.userRole$user &&
player.userRole$user[0].roleId === ROOT_ROLE_ID;
return {
avatar,
nickname,
mobile,
mobileCount,
isLoggedIn,
isPlayingAnother,
isRoot,
};
},
},
}, {
methods: {
async onRefresh() {
this.setData({
refreshing: true,
});
try {
await this.features.token.syncUserInfoWechatMp('token:me');
}
catch (err) {
console.error(err);
}
this.setData({
refreshing: false,
});
{
methods: {
async onRefresh() {
this.setData({
refreshing: true,
});
try {
await this.features.token.syncUserInfoWechatMp('token:me');
} catch (err) {
console.error(err);
}
this.setData({
refreshing: false,
});
},
async doLogin() {
this.setData({
refreshing: true,
});
try {
await this.features.token.loginWechatMp('token:me');
} catch (err) {
console.error(err);
}
this.setData({
refreshing: false,
});
},
goMyMobile() {
this.navigateTo({
url: '../../mobile/me/index',
});
},
goUserManage() {
this.navigateTo({
url: '../../user/manage/index',
});
},
},
async doLogin() {
this.setData({
refreshing: true,
});
try {
await this.features.token.loginWechatMp('token:me');
}
catch (err) {
console.error(err);
}
this.setData({
refreshing: false,
});
},
goMyMobile() {
this.navigateTo({
url: '../../mobile/me/index',
});
},
goUserManage() {
this.navigateTo({
url: '../../user/manage/index',
});
}
}
});
);

View File

@ -2,150 +2,175 @@
import { composeFileUrl } from "../../../../../src/utils/extraFile";
OakPage({
path: 'user:manage:detail',
entity: 'user',
projection: {
id: 1,
nickname: 1,
name: 1,
userState: 1,
idState: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
id: 1,
tag1: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
type: 1,
OakPage(
{
path: 'user:manage:detail',
entity: 'user',
projection: {
id: 1,
nickname: 1,
name: 1,
userState: 1,
idState: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
id: 1,
tag1: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
type: 1,
entity: 1,
extension: 1,
},
filter: {
tag1: 'avatar',
},
indexFrom: 0,
count: 1,
},
filter: {
tag1: 'avatar',
},
indexFrom: 0,
count: 1,
},
mobile$user: {
$entity: 'mobile',
data: {
id: 1,
mobile: 1,
mobile$user: {
$entity: 'mobile',
data: {
id: 1,
mobile: 1,
},
},
},
isList: false,
formData: async ({ data: user }) => {
const {
id,
nickname,
idState,
userState,
name,
mobile$user,
extraFile$entity,
} = user || {};
const mobile = mobile$user && mobile$user[0]?.mobile;
const avatar =
extraFile$entity &&
extraFile$entity[0] &&
composeFileUrl(extraFile$entity[0]);
return {
id,
nickname,
name,
mobile,
avatar,
userState,
idState,
};
},
actions: [
'accept',
'activate',
'disable',
'enable',
'remove',
'update',
'verify',
'play',
],
},
isList: false,
formData: async ({ data: user }) => {
const { id, nickname, idState, userState, name, mobile$user, extraFile$entity } = user || {};
const mobile = mobile$user && mobile$user[0]?.mobile;
const avatar = extraFile$entity && extraFile$entity[0] && composeFileUrl(extraFile$entity[0]);
return {
id,
nickname,
name,
mobile,
avatar,
userState,
idState,
};
},
actions: ['accept', 'activate', 'disable', 'enable', 'remove', 'update', 'verify', 'play'],
}, {
data: {
show: false,
actionDescriptions: {
accept: {
icon: {
name: 'pan_tool',
{
data: {
show: false,
actionDescriptions: {
accept: {
icon: {
name: 'pan_tool',
},
label: '通过',
},
activate: {
icon: {
name: 'check',
},
label: '激活',
},
disable: {
icon: {
name: 'flash_off',
},
label: '禁用',
},
enable: {
icon: {
name: 'flash_on',
},
label: '启用',
},
remove: {
icon: {
name: 'clear',
},
label: '删除',
},
update: {
icon: {
name: 'edit',
},
label: '更新',
},
verify: {
icon: {
name: 'how_to_reg',
},
label: '验证',
},
play: {
icon: {
name: 'play_circle',
},
label: '切换',
},
label: '通过',
},
activate: {
icon: {
name: 'check',
},
label: '激活',
},
disable: {
icon: {
name: 'flash_off',
},
label: '禁用',
},
enable: {
icon: {
name: 'flash_on',
},
label: '启用',
},
remove: {
icon: {
name: 'clear',
},
label: '删除',
},
update: {
icon: {
name: 'edit',
},
label: '更新',
},
verify: {
icon: {
name: 'how_to_reg',
},
label: '验证',
},
play: {
icon: {
name: 'play_circle',
},
label: '切换',
}
}
},
methods: {
openDrawer() {
this.setData({
show: true,
});
},
closeDrawer() {
this.setData({
show: false,
});
},
async onActionClick({ detail }: WechatMiniprogram.CustomEvent) {
const { action } = detail;
switch (action) {
case 'update': {
this.navigateTo({
url: '../upsert/index',
oakId: this.data.oakId,
});
return;
}
case 'enable':
case 'disable':
case 'accept':
case 'verify':
case 'activate':
case 'play': {
await this.execute(action);
break;
}
default: {
console.error(`尚未实现的action: ${action}`)
}
}
if (action === 'play') {
wx.navigateBack({
delta: 2,
methods: {
openDrawer() {
this.setData({
show: true,
});
}
}
},
closeDrawer() {
this.setData({
show: false,
});
},
async onActionClick({ detail }: WechatMiniprogram.CustomEvent) {
const { action } = detail;
switch (action) {
case 'update': {
this.navigateTo({
url: '../upsert/index',
oakId: this.data.oakId,
});
return;
}
case 'enable':
case 'disable':
case 'accept':
case 'verify':
case 'activate':
case 'play': {
await this.execute(action);
break;
}
default: {
console.error(`尚未实现的action: ${action}`);
}
}
if (action === 'play') {
wx.navigateBack({
delta: 2,
});
}
},
},
}
});
);

View File

@ -2,47 +2,59 @@
import { composeFileUrl } from "../../../../src/utils/extraFile";
OakPage({
path: 'user:manage',
entity: 'user',
projection: {
id: 1,
nickname: 1,
name: 1,
userState: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
id: 1,
tag1: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
type: 1,
OakPage(
{
path: 'user:manage',
entity: 'user',
projection: {
id: 1,
nickname: 1,
name: 1,
userState: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
id: 1,
tag1: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
type: 1,
entity: 1,
extension: 1,
},
filter: {
tag1: 'avatar',
},
indexFrom: 0,
count: 1,
},
filter: {
tag1: 'avatar',
},
indexFrom: 0,
count: 1,
},
mobile$user: {
$entity: 'mobile',
data: {
id: 1,
mobile: 1,
mobile$user: {
$entity: 'mobile',
data: {
id: 1,
mobile: 1,
},
},
},
},
isList: true,
formData: async ({ data: users }) => {
const userData = users.map(
(user) => {
const { id, nickname, userState, name, mobile$user, extraFile$entity } = user || {};
isList: true,
formData: async ({ data: users }) => {
const userData = users.map((user) => {
const {
id,
nickname,
userState,
name,
mobile$user,
extraFile$entity,
} = user || {};
const mobile = mobile$user && mobile$user[0]?.mobile;
const avatar = extraFile$entity && extraFile$entity[0] && composeFileUrl(extraFile$entity[0]);
const avatar =
extraFile$entity &&
extraFile$entity[0] &&
composeFileUrl(extraFile$entity[0]);
return {
id,
nickname,
@ -51,25 +63,26 @@ OakPage({
avatar,
userState,
};
}
);
return {
userData,
};
},
}, {
methods: {
goUserManageDetail(options: WechatMiniprogram.Touch) {
const { id } = options.currentTarget.dataset;
this.navigateTo({
url: 'detail/index',
oakId: id,
});
return {
userData,
};
},
goNewUser() {
this.navigateTo({
url: 'upsert/index',
});
},
{
methods: {
goUserManageDetail(options: WechatMiniprogram.Touch) {
const { id } = options.currentTarget.dataset;
this.navigateTo({
url: 'detail/index',
oakId: id,
});
},
goNewUser() {
this.navigateTo({
url: 'upsert/index',
});
},
},
}
});
);

View File

@ -32,22 +32,20 @@ OakPage({
},
isList: false,
formData: async ({ data: userEntityGrant }) => {
let qrcodeUrl;
let qrCodeUrl;
const str = userEntityGrant?.wechatQrCode$entity[0]?.buffer;
console.log('str', str);
if (str) {
const buf = new ArrayBuffer(str.length * 2);
const buf2 = new Uint16Array(buf);
for (let i = 0; i < str.length; i++) {
buf2[i] = str.charCodeAt(i);
}
qrcodeUrl = 'data:image/jpeg;base64,' + wx.arrayBufferToBase64(buf2);
console.log('url', qrcodeUrl);
qrCodeUrl = 'data:image/jpeg;base64,' + wx.arrayBufferToBase64(buf2);
}
return {
relation: userEntityGrant?.relation,
entity: userEntityGrant?.entity,
url: qrcodeUrl || userEntityGrant?.wechatQrCode$entity[0]?.url
url: qrCodeUrl || userEntityGrant?.wechatQrCode$entity[0]?.url,
};
},
}, {