fix(mining-admin): 适配 mining-app 版本检查 API 格式
- CheckUpdateDto: current_version_code 改为 versionCode 匹配 mining-app 请求参数
- MobileVersionController: 响应格式改为 { hasUpdate, latestVersion: {...} } 匹配 mining-app 解析
- TransformInterceptor: 添加 @SkipTransform() 装饰器,移动端版本检查接口跳过响应包装
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f595c6f26d
commit
96e1fa4534
|
|
@ -2,12 +2,14 @@ import { Controller, Get, Query } from '@nestjs/common'
|
||||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'
|
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'
|
||||||
import { VersionService } from '../../application/services/version.service'
|
import { VersionService } from '../../application/services/version.service'
|
||||||
import { Platform } from '../../domain/version-management'
|
import { Platform } from '../../domain/version-management'
|
||||||
import { CheckUpdateDto, UpdateCheckResultDto } from '../dto/version'
|
import { CheckUpdateDto } from '../dto/version'
|
||||||
import { Public } from '../../shared/guards/admin-auth.guard'
|
import { Public } from '../../shared/guards/admin-auth.guard'
|
||||||
|
import { SkipTransform } from '../../shared/interceptors/transform.interceptor'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mobile App Version API Controller
|
* Mobile App Version API Controller
|
||||||
* This endpoint is designed to match the mobile app's expected API format
|
* Response format matches mining-app (Flutter) expected format:
|
||||||
|
* { hasUpdate: boolean, latestVersion?: { versionCode, versionName, ... } }
|
||||||
*/
|
*/
|
||||||
@ApiTags('Mobile App Version')
|
@ApiTags('Mobile App Version')
|
||||||
@Controller('api/app/version')
|
@Controller('api/app/version')
|
||||||
|
|
@ -22,29 +24,31 @@ export class MobileVersionController {
|
||||||
|
|
||||||
@Get('check')
|
@Get('check')
|
||||||
@Public()
|
@Public()
|
||||||
@ApiOperation({ summary: '检查更新 (移动端专用)' })
|
@SkipTransform()
|
||||||
@ApiResponse({ status: 200, type: UpdateCheckResultDto })
|
@ApiOperation({ summary: '检查更新 (mining-app 专用)' })
|
||||||
async checkUpdate(@Query() dto: CheckUpdateDto): Promise<UpdateCheckResultDto> {
|
async checkUpdate(@Query() dto: CheckUpdateDto) {
|
||||||
const platform = this.getPlatform(dto.platform)
|
const platform = this.getPlatform(dto.platform)
|
||||||
const result = await this.versionService.checkUpdate(platform, dto.current_version_code)
|
const result = await this.versionService.checkUpdate(platform, dto.versionCode)
|
||||||
|
|
||||||
if (!result.hasUpdate || !result.latestVersion) {
|
if (!result.hasUpdate || !result.latestVersion) {
|
||||||
return {
|
return { hasUpdate: false }
|
||||||
needUpdate: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const v = result.latestVersion
|
||||||
return {
|
return {
|
||||||
needUpdate: true,
|
hasUpdate: true,
|
||||||
version: result.latestVersion.versionName,
|
latestVersion: {
|
||||||
versionCode: result.latestVersion.versionCode,
|
versionCode: v.versionCode,
|
||||||
downloadUrl: result.latestVersion.downloadUrl,
|
versionName: v.versionName,
|
||||||
fileSize: Number(BigInt(result.latestVersion.fileSize)),
|
buildNumber: v.buildNumber,
|
||||||
fileSizeFriendly: result.latestVersion.fileSizeFriendly,
|
downloadUrl: v.downloadUrl,
|
||||||
sha256: result.latestVersion.fileSha256,
|
fileSize: Number(BigInt(v.fileSize)),
|
||||||
forceUpdate: result.isForceUpdate,
|
fileSha256: v.fileSha256,
|
||||||
updateLog: result.latestVersion.changelog,
|
changelog: v.changelog,
|
||||||
releaseDate: result.latestVersion.releaseDate?.toISOString() ?? new Date().toISOString(),
|
isForceUpdate: result.isForceUpdate,
|
||||||
|
minOsVersion: v.minOsVersion ?? null,
|
||||||
|
releaseDate: v.releaseDate?.toISOString() ?? null,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,5 +17,5 @@ export class CheckUpdateDto {
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
current_version_code: number
|
versionCode: number
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,25 @@
|
||||||
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, SetMetadata } from '@nestjs/common';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
export const SKIP_TRANSFORM_KEY = 'skipTransform';
|
||||||
|
export const SkipTransform = () => SetMetadata(SKIP_TRANSFORM_KEY, true);
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TransformInterceptor<T> implements NestInterceptor<T, any> {
|
export class TransformInterceptor<T> implements NestInterceptor<T, any> {
|
||||||
|
constructor(private readonly reflector: Reflector) {}
|
||||||
|
|
||||||
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||||
|
const skip = this.reflector.getAllAndOverride<boolean>(SKIP_TRANSFORM_KEY, [
|
||||||
|
context.getHandler(),
|
||||||
|
context.getClass(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (skip) {
|
||||||
|
return next.handle();
|
||||||
|
}
|
||||||
|
|
||||||
return next.handle().pipe(map((data) => ({ success: true, data, timestamp: new Date().toISOString() })));
|
return next.handle().pipe(map((data) => ({ success: true, data, timestamp: new Date().toISOString() })));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue