fix(admin): support 4-segment version in domain layer & make changelog optional
- VersionName value object now accepts x.y.z.w format - changelog field is now optional in upload version DTO - profile page: ensure expired section has full width 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4307d1eb91
commit
586dfda8f7
|
|
@ -27,9 +27,10 @@ export class UploadVersionDto {
|
|||
@IsString()
|
||||
buildNumber?: string
|
||||
|
||||
@ApiProperty({ description: '更新日志' })
|
||||
@ApiPropertyOptional({ description: '更新日志', default: '' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
changelog: string
|
||||
changelog?: string
|
||||
|
||||
@ApiPropertyOptional({ description: '是否强制更新', default: false })
|
||||
@IsOptional()
|
||||
|
|
|
|||
|
|
@ -2,19 +2,21 @@ import { DomainException } from '../../shared/exceptions/domain.exception';
|
|||
|
||||
/**
|
||||
* 版本名称值对象
|
||||
* 格式: major.minor.patch (e.g., 1.0.0, 2.3.1)
|
||||
* 格式: major.minor.patch 或 major.minor.patch.build (e.g., 1.0.0, 1.0.0.4)
|
||||
*/
|
||||
export class VersionName {
|
||||
private readonly _value: string;
|
||||
private readonly _major: number;
|
||||
private readonly _minor: number;
|
||||
private readonly _patch: number;
|
||||
private readonly _build?: number;
|
||||
|
||||
private constructor(value: string, major: number, minor: number, patch: number) {
|
||||
private constructor(value: string, major: number, minor: number, patch: number, build?: number) {
|
||||
this._value = value;
|
||||
this._major = major;
|
||||
this._minor = minor;
|
||||
this._patch = patch;
|
||||
this._build = build;
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
|
|
@ -33,24 +35,30 @@ export class VersionName {
|
|||
return this._patch;
|
||||
}
|
||||
|
||||
get build(): number | undefined {
|
||||
return this._build;
|
||||
}
|
||||
|
||||
static create(value: string): VersionName {
|
||||
if (!value || value.trim() === '') {
|
||||
throw new DomainException('VersionName cannot be empty');
|
||||
}
|
||||
|
||||
const trimmed = value.trim();
|
||||
const versionPattern = /^(\d+)\.(\d+)\.(\d+)$/;
|
||||
// 支持 x.y.z 或 x.y.z.w 格式
|
||||
const versionPattern = /^(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?$/;
|
||||
const match = trimmed.match(versionPattern);
|
||||
|
||||
if (!match) {
|
||||
throw new DomainException('VersionName must follow semantic versioning format (e.g., 1.0.0)');
|
||||
throw new DomainException('VersionName must follow semantic versioning format (e.g., 1.0.0 or 1.0.0.4)');
|
||||
}
|
||||
|
||||
const major = parseInt(match[1], 10);
|
||||
const minor = parseInt(match[2], 10);
|
||||
const patch = parseInt(match[3], 10);
|
||||
const build = match[4] ? parseInt(match[4], 10) : undefined;
|
||||
|
||||
return new VersionName(trimmed, major, minor, patch);
|
||||
return new VersionName(trimmed, major, minor, patch, build);
|
||||
}
|
||||
|
||||
equals(other: VersionName): boolean {
|
||||
|
|
|
|||
|
|
@ -1005,17 +1005,19 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||
|
||||
/// 构建已过期区域
|
||||
Widget _buildExpiredSection() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFF5E6),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: const Color(0x33D4AF37),
|
||||
width: 1,
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFF5E6),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: const Color(0x33D4AF37),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
|
|
@ -1083,6 +1085,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue