feat(sdk): 添加鸿蒙SDK核心功能模块

- 实现SDKContext用于配置管理和数据持久化存储
- 定义完整的类型系统包括消息、用户、群组等接口
- 集成更新SDK支持原生应用和RN热更新检查
- 提供统一的XuqmSDK入口类和模块导出
- 编写详细的开发文档和使用示例
这个提交包含在:
XuqmGroup 2026-04-29 19:08:13 +08:00
父节点 470521c3a8
当前提交 e47f510a0b
共有 6 个文件被更改,包括 799 次插入414 次删除

查看文件

@ -15,6 +15,8 @@ declare module 'vue' {
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
@ -29,6 +31,7 @@ declare module 'vue' {
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']

查看文件

@ -39,8 +39,31 @@ updateClient.interceptors.request.use((config) => {
return config
})
export type StoreType = 'HUAWEI' | 'MI' | 'OPPO' | 'VIVO' | 'HONOR' | 'APP_STORE' | 'GOOGLE_PLAY'
export type StoreType = 'HUAWEI' | 'MI' | 'OPPO' | 'VIVO' | 'HONOR' | 'APP_STORE' | 'GOOGLE_PLAY' | 'HARMONY_APP' | 'REVIEW_WEBHOOK'
export type StoreReviewState = 'PENDING' | 'UNDER_REVIEW' | 'APPROVED' | 'REJECTED'
export type PublishMode = 'MANUAL' | 'NOW' | 'SCHEDULED' | 'AUTO_REVIEW'
export type GrayMode = 'PERCENT' | 'MEMBERS'
export type GraySelectionSource = 'LOCAL' | 'CALLBACK'
export interface PublishConfig {
id: string
appId: string
configJson?: string
updatedAt: string
}
export interface GrayMember {
userId: string
name?: string
groupName?: string
extraJson?: string
updatedAt?: string
}
export interface GrayMemberGroup {
groupName: string
members: GrayMember[]
}
export interface StoreConfig {
id: string
@ -71,6 +94,10 @@ export interface AppVersion {
webhookUrl?: string
storeSubmitTargets?: string
storeReviewStatus?: string
storeSubmitMode?: PublishMode
storeSubmitScheduledAt?: string
grayMode?: GrayMode
grayMemberIds?: string
createdAt: string
}
@ -94,8 +121,12 @@ export interface RnBundle {
packageName?: string
note?: string
publishStatus: 'DRAFT' | 'PUBLISHED' | 'DEPRECATED'
publishMode?: PublishMode
scheduledPublishAt?: string
grayEnabled: boolean
grayPercent: number
grayMode?: GrayMode
grayMemberIds?: string
createdAt: string
}
@ -144,16 +175,22 @@ export const updateAdminApi = {
})
},
publishAppVersion(id: string) {
return updateClient.post(`/api/v1/updates/app/${id}/publish`)
publishAppVersion(id: string, body?: { publishImmediately?: boolean; scheduledPublishAt?: string; forceUpdate?: boolean }) {
return updateClient.post(`/api/v1/updates/app/${id}/publish`, body ?? {})
},
unpublishAppVersion(id: string) {
return updateClient.post(`/api/v1/updates/app/${id}/unpublish`)
},
grayAppVersion(id: string, enabled: boolean, percent: number) {
return updateClient.post(`/api/v1/updates/app/${id}/gray`, { enabled, percent })
grayAppVersion(id: string, body: {
enabled: boolean
grayMode: GrayMode
percent?: number
memberIds?: string[]
selectionSource?: GraySelectionSource
}) {
return updateClient.post(`/api/v1/updates/app/${id}/gray`, body)
},
uploadAppVersion(formData: FormData) {
@ -172,16 +209,22 @@ export const updateAdminApi = {
})
},
publishRnBundle(id: string) {
return updateClient.post(`/api/v1/rn/${id}/publish`)
publishRnBundle(id: string, body?: { publishImmediately?: boolean; scheduledPublishAt?: string }) {
return updateClient.post(`/api/v1/rn/${id}/publish`, body ?? {})
},
unpublishRnBundle(id: string) {
return updateClient.post(`/api/v1/rn/${id}/unpublish`)
},
grayRnBundle(id: string, enabled: boolean, percent: number) {
return updateClient.post(`/api/v1/rn/${id}/gray`, { enabled, percent })
grayRnBundle(id: string, body: {
enabled: boolean
grayMode: GrayMode
percent?: number
memberIds?: string[]
selectionSource?: GraySelectionSource
}) {
return updateClient.post(`/api/v1/rn/${id}/gray`, body)
},
uploadRnBundle(formData: FormData) {
@ -214,10 +257,16 @@ export const updateAdminApi = {
return updateClient.delete(`/api/v1/updates/store/configs/${storeType}`, { params: { appId } })
},
executeSubmitToStores(versionId: string, storeTypes: StoreType[]) {
executeSubmitToStores(
versionId: string,
storeTypes: StoreType[],
submitMode: PublishMode = 'MANUAL',
scheduledPublishAt?: string,
autoPublishAfterReview = false,
) {
return updateClient.post<{ data: AppVersion }>(
`/api/v1/updates/store/app/${versionId}/execute-submit`,
{ storeTypes },
{ storeTypes, submitMode, scheduledPublishAt, autoPublishAfterReview },
)
},
@ -227,4 +276,24 @@ export const updateAdminApi = {
{ storeType, state },
)
},
getPublishConfig(appId: string) {
return updateClient.get<{ data: PublishConfig }>('/api/v1/updates/publish/config', { params: { appId } })
},
savePublishConfig(appId: string, config: Record<string, unknown>) {
return updateClient.put<{ data: PublishConfig }>('/api/v1/updates/publish/config', config, { params: { appId } })
},
listGrayMembers(appId: string, keyword?: string, groupName?: string) {
return updateClient.get<{ data: GrayMemberGroup[] }>('/api/v1/updates/gray/members', {
params: { appId, ...(keyword && { keyword }), ...(groupName && { groupName }) },
})
},
syncGrayMembers(appId: string) {
return updateClient.post<{ data: GrayMemberGroup[] }>('/api/v1/updates/gray/members/sync', null, {
params: { appId },
})
},
}

查看文件

@ -53,10 +53,6 @@ const router = createRouter({
path: 'apps/:appId/update',
component: () => import('@/views/update/VersionManagementView.vue'),
},
{
path: 'apps/:appId/update-guide',
component: () => import('@/views/update/StoreGuideView.vue'),
},
{
path: 'accounts',
component: () => import('@/views/accounts/SubAccountView.vue'),

查看文件

@ -60,9 +60,9 @@
<el-card v-for="svcType in ['PUSH', 'UPDATE']" :key="svcType" class="service-card">
<div class="service-header">
<div class="service-title-block">
<span class="service-name">{{ serviceLabel(svcType) }}</span>
<span class="service-help">{{ serviceHelp(svcType) }}</span>
</div>
<span class="service-name">{{ serviceLabel(svcType) }}</span>
<span class="service-help">{{ serviceHelp(svcType) }}</span>
</div>
<el-switch
:model-value="isServiceEnabled(svcType)"
@change="(val: boolean) => onToggleService(svcType, val)"
@ -74,7 +74,7 @@
</el-tag>
<span class="service-status-text">
{{ svcType === 'UPDATE'
? 'Android / iOS / 鸿蒙版本在版本管理页内分别配置。'
? 'Android 整包版本在版本管理页上传;iOS / 鸿蒙仅记录版本号和市场跳转页。商店配置与发布配置都在版本管理页。'
: '推送服务开通后即可在终端接收设备级推送。' }}
</span>
</div>
@ -169,7 +169,7 @@ function serviceHelp(type: string) {
return {
IM: 'IM 服务独立开通后,在管理页配置回调和消息能力。',
PUSH: '一次开通后,推送配置在服务管理页按平台维护。',
UPDATE: '一次开通后,版本管理页按平台维护版本和发布。',
UPDATE: '一次开通后,版本管理页只管理 Android 整包版本,iOS / 鸿蒙仅记录提醒信息。',
}[type] ?? ''
}

查看文件

@ -3,7 +3,7 @@
<el-page-header @back="$router.back()" :content="`应用配置指引 — ${appId}`" />
<el-alert
title="这里是应用商店配置的独立指引页。建议先完成凭据配置,再回到版本管理页提交审核。Harmony 应用仅跳转应用市场,不做本地安装。"
title="这里是应用商店配置的独立指引页。建议先完成凭据配置和市场跳转页,再回到版本管理页提交审核。Harmony 应用仅跳转应用市场,不做本地安装。"
type="info"
show-icon
:closable="false"
@ -117,6 +117,20 @@ const STORE_GUIDES = [
image: honorGuideImage,
enabled: true,
},
{
type: 'HARMONY_APP',
label: '鸿蒙应用',
subtitle: '鸿蒙应用市场独立跳转页',
url: 'https://developer.huawei.com/consumer/cn/',
urlLabel: '查看鸿蒙官方文档',
steps: [
{ title: '确认鸿蒙应用页', description: '准备独立的鸿蒙应用市场详情页。' },
{ title: '复制跳转链接', description: '把市场详情页链接填写到应用商店配置。' },
{ title: '回到版本管理页', description: '鸿蒙版本仅记录版本号和跳转页。' },
],
hint: '鸿蒙应用配置只维护市场跳转页,不参与 Android 审核上传。',
enabled: true,
},
{
type: 'APP_STORE',
label: 'Apple App Store',