Files
dukang/server/dukang-api/src/modules/ops/dto/admin-mutate.dto.ts
T
2026-07-26 09:51:33 +08:00

1280 lines
20 KiB
TypeScript

import { Type } from 'class-transformer';
import {
IsArray,
IsBoolean,
IsIn,
IsNotEmpty,
IsNumber,
IsObject,
IsOptional,
IsString,
Min,
Max,
MinLength,
ValidateIf,
} from 'class-validator';
export class UpdateStoreStatusDto {
@IsString()
@IsIn(['OPEN', 'PAUSED', 'CLOSED'])
status: string;
}
export class CreateStoreDto {
@IsString()
@IsNotEmpty()
partnerAccountId: string;
@IsString()
@IsNotEmpty()
cityId: string;
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@IsNotEmpty()
phone: string;
@IsString()
@IsNotEmpty()
categoryId: string;
@IsOptional()
@IsString()
province?: string;
@IsOptional()
@IsString()
city?: string;
@IsOptional()
@IsString()
district?: string;
@IsString()
@IsNotEmpty()
address: string;
@IsOptional()
@IsString()
intro?: string;
@IsOptional()
@IsString()
coverUrl?: string;
@IsOptional()
@IsString()
accountName?: string;
@IsOptional()
@IsString()
bankAccountName?: string;
@IsOptional()
@IsString()
bankAccountNo?: string;
@IsOptional()
@IsString()
bankBranch?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
envPhotoUrls?: string[];
@IsOptional()
@IsString()
contractUrl?: string;
@IsOptional()
@IsNumber()
@Min(0)
settlementRate?: number;
}
export class UpdateStoreDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
phone?: string;
@IsOptional()
@IsString()
intro?: string;
@IsOptional()
@IsString()
coverUrl?: string;
@IsOptional()
@IsString()
address?: string;
@IsOptional()
@IsString()
district?: string;
@IsOptional()
@IsNumber()
@Min(0)
settlementRate?: number;
}
export class CreateStoreAccountDto {
@IsString()
@IsNotEmpty()
storeId: string;
@IsString()
@IsNotEmpty()
phone: string;
@IsString()
@IsNotEmpty()
name: string;
}
export class UpdateStoreAccountDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
phone?: string;
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: string;
}
export class CreatePartnerDto {
@IsString()
@IsNotEmpty()
cityId: string;
@IsString()
@IsNotEmpty()
phone: string;
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@IsNotEmpty()
companyName: string;
@IsString()
@IsNotEmpty()
address: string;
@IsOptional()
@IsString()
contactPhone?: string;
@IsString()
@IsIn(['CITY_WIDE', 'DISTRICT'])
scopeType: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
districtCodes?: string[];
@IsOptional()
@IsNumber()
@Min(0)
orderCommissionRate?: number;
@IsOptional()
@IsNumber()
@Min(0)
redeemCommissionRate?: number;
@IsOptional()
@IsIn(['ACTIVE', 'PAUSED'])
bindingStatus?: string;
@IsOptional()
@IsString()
managedWarehouseId?: string;
@IsOptional()
@IsString()
contractNo?: string;
@IsOptional()
@IsString()
bankAccountName?: string;
@IsOptional()
@IsString()
bankAccountNo?: string;
@IsOptional()
@IsString()
bankBranch?: string;
@IsOptional()
@IsNumber()
weeklyStoreTarget?: number;
}
export class UpdatePartnerDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
phone?: string;
@IsOptional()
@IsString()
companyName?: string;
@IsOptional()
@IsString()
address?: string;
@IsOptional()
@IsString()
contactPhone?: string;
@IsOptional()
@IsIn(['CITY_WIDE', 'DISTRICT'])
scopeType?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
districtCodes?: string[];
@IsOptional()
@IsNumber()
@Min(0)
orderCommissionRate?: number;
@IsOptional()
@IsNumber()
@Min(0)
redeemCommissionRate?: number;
@IsOptional()
@IsIn(['ACTIVE', 'PAUSED'])
bindingStatus?: string;
@IsOptional()
@ValidateIf((_, v) => v !== null)
@IsString()
managedWarehouseId?: string | null;
@IsOptional()
@IsString()
contractNo?: string;
@IsOptional()
@IsString()
bankAccountName?: string;
@IsOptional()
@IsString()
bankAccountNo?: string;
@IsOptional()
@IsString()
bankBranch?: string;
@IsOptional()
@IsNumber()
weeklyStoreTarget?: number;
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: string;
}
export class UpdatePartnerAccountDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
phone?: string;
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: string;
@IsOptional()
@IsIn(['PARTNER', 'INTERNAL', 'PROMOTER'])
staffRole?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
permissions?: string[];
}
export class CreatePartnerAccountDto {
@IsString()
@IsNotEmpty()
parentAccountId: string;
@IsString()
@IsNotEmpty()
phone: string;
@IsString()
@IsNotEmpty()
name: string;
@IsOptional()
@IsIn(['PARTNER', 'INTERNAL', 'PROMOTER'])
staffRole?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
permissions?: string[];
}
export class CreateCityDto {
@IsString()
@IsNotEmpty()
code: string;
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@IsNotEmpty()
province: string;
@IsOptional()
@IsIn(['PENDING', 'ACTIVE', 'PAUSED'])
status?: string;
}
export class UpdateCityDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
province?: string;
@IsOptional()
@IsIn(['PENDING', 'ACTIVE', 'PAUSED'])
status?: string;
@IsOptional()
localMinQty?: number;
@IsOptional()
crossMinQty?: number;
@IsOptional()
@IsNumber()
@Min(0)
@Max(1)
maxPartnerCommissionRate?: number;
}
export class BindCityPartnerDto {
@IsString()
@IsNotEmpty()
partnerId: string;
@IsString()
@IsIn(['CITY_WIDE', 'DISTRICT'])
scopeType: 'CITY_WIDE' | 'DISTRICT';
@IsOptional()
@IsArray()
@IsString({ each: true })
districtCodes?: string[];
@IsOptional()
@IsNumber()
@Min(0)
orderCommissionRate?: number;
@IsOptional()
@IsNumber()
@Min(0)
redeemCommissionRate?: number;
@IsOptional()
@IsIn(['ACTIVE', 'PAUSED'])
status?: 'ACTIVE' | 'PAUSED';
}
export class UpdateCityPartnerDto {
@IsOptional()
@IsIn(['CITY_WIDE', 'DISTRICT'])
scopeType?: 'CITY_WIDE' | 'DISTRICT';
@IsOptional()
@IsArray()
@IsString({ each: true })
districtCodes?: string[];
@IsOptional()
@IsNumber()
@Min(0)
orderCommissionRate?: number;
@IsOptional()
@IsNumber()
@Min(0)
redeemCommissionRate?: number;
@IsOptional()
@IsIn(['ACTIVE', 'PAUSED'])
status?: 'ACTIVE' | 'PAUSED';
}
export class CreateCityWarehouseDto {
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@IsNotEmpty()
address: string;
@IsString()
@IsNotEmpty()
contactName: string;
@IsString()
@IsNotEmpty()
contactPhone: string;
@IsString()
@IsIn(['HQ', 'PARTNER'])
managerType: 'HQ' | 'PARTNER';
@IsOptional()
@IsString()
partnerAccountId?: string;
@IsOptional()
@IsIn(['ACTIVE', 'PAUSED'])
status?: 'ACTIVE' | 'PAUSED';
@IsOptional()
@IsIn(['API_AUTO', 'MANUAL'])
fulfillmentMode?: 'API_AUTO' | 'MANUAL';
@IsOptional()
@IsString()
fulfillmentProviderId?: string;
@IsOptional()
@IsString()
manualCarrierLabel?: string;
@IsOptional()
@IsString()
manualQueryUrlTemplate?: string;
@IsOptional()
@IsNumber()
lng?: number;
@IsOptional()
@IsNumber()
lat?: number;
}
export class UpdateCityWarehouseDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
address?: string;
@IsOptional()
@IsString()
contactName?: string;
@IsOptional()
@IsString()
contactPhone?: string;
@IsOptional()
@IsIn(['HQ', 'PARTNER'])
managerType?: 'HQ' | 'PARTNER';
@IsOptional()
@ValidateIf((_, v) => v !== null)
@IsString()
partnerAccountId?: string | null;
@IsOptional()
@IsIn(['ACTIVE', 'PAUSED'])
status?: 'ACTIVE' | 'PAUSED';
@IsOptional()
@IsIn(['API_AUTO', 'MANUAL'])
fulfillmentMode?: 'API_AUTO' | 'MANUAL';
@IsOptional()
@ValidateIf((_, v) => v !== null)
@IsString()
fulfillmentProviderId?: string | null;
@IsOptional()
@ValidateIf((_, v) => v !== null)
@IsString()
manualCarrierLabel?: string | null;
@IsOptional()
@ValidateIf((_, v) => v !== null)
@IsString()
manualQueryUrlTemplate?: string | null;
@IsOptional()
@ValidateIf((_, v) => v !== null)
@IsNumber()
lng?: number | null;
@IsOptional()
@ValidateIf((_, v) => v !== null)
@IsNumber()
lat?: number | null;
}
export class CreateFulfillmentProviderDto {
@IsString()
@IsNotEmpty()
code: string;
@IsString()
@IsNotEmpty()
name: string;
@IsIn(['API', 'MANUAL'])
type: 'API' | 'MANUAL';
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: 'ACTIVE' | 'DISABLED';
@IsOptional()
@IsString()
configJson?: string;
@IsOptional()
@IsString()
capabilitiesJson?: string;
@IsOptional()
@IsObject()
xiaofeixiaConfig?: {
apiUrl?: string;
mchId?: string;
apiKey?: string;
signType?: 'MD5' | 'HMAC-SHA256';
appId?: string;
};
@IsOptional()
@IsString()
bankAccountName?: string | null;
@IsOptional()
@IsString()
bankName?: string | null;
@IsOptional()
@IsString()
bankBranch?: string | null;
@IsOptional()
@IsString()
bankAccountNo?: string | null;
@IsOptional()
@IsIn(['PREPAID', 'MONTHLY_CREDIT'])
settlementMethod?: 'PREPAID' | 'MONTHLY_CREDIT';
@IsOptional()
@IsObject()
pricingRules?: {
baseBottles: number;
baseFee: number;
extraBottleFee: number;
boxBottles?: number;
boxFee?: number;
} | null;
}
export class UpdateFulfillmentProviderDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsIn(['API', 'MANUAL'])
type?: 'API' | 'MANUAL';
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: 'ACTIVE' | 'DISABLED';
@IsOptional()
@IsString()
configJson?: string;
@IsOptional()
@IsString()
capabilitiesJson?: string;
@IsOptional()
@IsObject()
xiaofeixiaConfig?: {
apiUrl?: string;
mchId?: string;
apiKey?: string;
signType?: 'MD5' | 'HMAC-SHA256';
appId?: string;
};
@IsOptional()
@IsString()
bankAccountName?: string | null;
@IsOptional()
@IsString()
bankName?: string | null;
@IsOptional()
@IsString()
bankBranch?: string | null;
@IsOptional()
@IsString()
bankAccountNo?: string | null;
@IsOptional()
@IsIn(['PREPAID', 'MONTHLY_CREDIT'])
settlementMethod?: 'PREPAID' | 'MONTHLY_CREDIT';
@IsOptional()
@IsObject()
pricingRules?: {
baseBottles: number;
baseFee: number;
extraBottleFee: number;
boxBottles?: number;
boxFee?: number;
} | null;
}
export class RechargeFulfillmentProviderDto {
@IsNumber()
amount: number;
@IsOptional()
@IsString()
remark?: string;
}
export class ManualShipOrderDto {
@IsString()
@IsNotEmpty()
logisticsCompany: string;
@IsString()
@IsNotEmpty()
trackingNo: string;
@IsOptional()
@IsString()
manualQueryUrl?: string;
}
export class HqLogisticsShipDto extends ManualShipOrderDto {}
export class CreateStoreMediaDto {
@IsString()
@IsNotEmpty()
storeId: string;
@IsString()
@IsIn(['IMAGE', 'VIDEO'])
mediaType: string;
@IsString()
@IsNotEmpty()
url: string;
@IsOptional()
sortOrder?: number;
}
export class UpdateStoreMediaDto {
@IsOptional()
@IsString()
url?: string;
@IsOptional()
@IsIn(['IMAGE', 'VIDEO'])
mediaType?: string;
@IsOptional()
sortOrder?: number;
}
export class UpdateOrderStatusDto {
@IsString()
@IsIn([
'PENDING_PAY',
'PENDING_SHIP',
'OUT_WAREHOUSE',
'SHIPPING',
'PENDING_RECEIVE',
'COMPLETED',
'CANCELLED',
'REFUNDING',
'REFUNDED',
])
status: string;
}
export class BatchDeleteOrdersDto {
@IsArray()
@IsString({ each: true })
ids: string[];
}
export class BatchDeleteUsersDto {
@IsArray()
@IsString({ each: true })
ids: string[];
}
export class BatchDeleteUsersConfirmDto extends BatchDeleteUsersDto {
@IsBoolean()
confirmRisk: boolean;
}
/** HQ 订单发货(目前仅小飞侠 XFX) */
export class AdminShipOrderDto {
@IsIn(['XFX'])
provider: string;
/** 可选:指定/改派履约仓后再推仓配 */
@IsOptional()
@IsString()
warehouseId?: string;
@IsOptional()
@IsString()
fromName?: string;
@IsOptional()
@IsString()
fromMobile?: string;
@IsOptional()
@IsString()
fromAddress?: string;
@IsOptional()
@IsString()
fromAddressDetail?: string;
@IsOptional()
@IsNumber()
fromLng?: number;
@IsOptional()
@IsNumber()
fromLat?: number;
@IsOptional()
@Type(() => Number)
@IsNumber()
@Min(0.01)
weight?: number;
@IsOptional()
@IsIn(['1', '2'])
payMode?: string;
@IsOptional()
@IsString()
remark?: string;
}
export class AdminRedeemDebugCreateTokenDto {
@IsString()
@IsNotEmpty()
userId: string;
@Type(() => Number)
@IsNumber()
@Min(0.01)
amount: number;
@IsOptional()
@IsString()
couponId?: string;
@IsOptional()
@IsString()
storeId?: string;
}
export class AdminRedeemDebugStoreTokenDto {
@IsString()
@IsNotEmpty()
storeId: string;
@IsString()
@IsNotEmpty()
token: string;
}
export class AdminRedeemDebugPhoneStoreDto {
@IsString()
@IsNotEmpty()
storeId: string;
@IsString()
@IsNotEmpty()
phone: string;
}
export class AdminRedeemDebugPhoneBalanceDto extends AdminRedeemDebugPhoneStoreDto {
@IsString()
@IsNotEmpty()
code: string;
}
export class AdminRedeemDebugPhonePrepareDto {
@IsString()
@IsNotEmpty()
storeId: string;
@IsString()
@IsNotEmpty()
sessionId: string;
@Type(() => Number)
@IsNumber()
@Min(0.01)
amount: number;
}
export class AdminRedeemDebugPhoneConfirmDto {
@IsString()
@IsNotEmpty()
storeId: string;
@IsString()
@IsNotEmpty()
sessionId: string;
@IsString()
@IsNotEmpty()
code: string;
}
export class UpdateDeliveryDto {
@IsOptional()
@IsString()
provider?: string;
@IsOptional()
@IsString()
providerOrderNo?: string;
@IsOptional()
@IsString()
trackingNo?: string;
}
export class CreateHqAccountDto {
@IsIn(['phone', 'password'])
credentialType: 'phone' | 'password';
@IsOptional()
@IsString()
phone?: string;
@ValidateIf((o: CreateHqAccountDto) => o.credentialType === 'password')
@IsString()
@IsNotEmpty()
loginName?: string;
@ValidateIf((o: CreateHqAccountDto) => o.credentialType === 'password')
@IsString()
@MinLength(6)
password?: string;
@IsString()
@IsNotEmpty()
name: string;
@IsOptional()
@IsIn(['SUPER_ADMIN', 'OPS', 'FINANCE', 'CUSTOMER_SERVICE'])
adminRole?: string;
}
export class UpdateHqAccountDto {
@IsOptional()
@IsString()
loginName?: string;
@IsOptional()
@IsString()
phone?: string;
@IsOptional()
@IsString()
@MinLength(6)
password?: string;
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsIn(['SUPER_ADMIN', 'OPS', 'FINANCE', 'CUSTOMER_SERVICE'])
adminRole?: string;
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: string;
}
export class SaveHqRolePermissionsDto {
@IsArray()
@IsString({ each: true })
permissionKeys: string[];
}
export class SaveHqAccountPermissionsDto {
@IsArray()
@IsString({ each: true })
permissionKeys: string[];
}
export class CreateProductDto {
@IsString()
@IsNotEmpty()
skuCode: string;
@IsString()
@IsNotEmpty()
barcode69: string;
@IsString()
@IsNotEmpty()
name: string;
@IsOptional()
@IsString()
subtitle?: string;
@IsIn(['QINGXIANG', 'JIANGXIANG', 'NONGXIANG'])
aromaType: string;
@IsString()
@IsNotEmpty()
spec: string;
@IsNumber()
price: number;
@IsOptional()
@IsNumber()
benefitAmount?: number;
@IsOptional()
@IsIn(['DRAFT', 'ON_SALE', 'OFF_SALE'])
status?: string;
@IsOptional()
@IsNumber()
sortOrder?: number;
@IsOptional()
@IsBoolean()
allowOnSitePickup?: boolean;
@IsOptional()
@IsString()
coverUrl?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
carouselUrls?: string[];
@IsOptional()
@IsArray()
@IsString({ each: true })
detailImageUrls?: string[];
@IsOptional()
@IsObject()
detailContent?: Record<string, unknown>;
}
export class UpdateProductDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
subtitle?: string;
@IsOptional()
@IsString()
spec?: string;
@IsOptional()
@IsNumber()
price?: number;
@IsOptional()
@IsNumber()
benefitAmount?: number;
@IsOptional()
@IsIn(['DRAFT', 'ON_SALE', 'OFF_SALE'])
status?: string;
@IsOptional()
@IsNumber()
sortOrder?: number;
@IsOptional()
@IsBoolean()
allowOnSitePickup?: boolean;
@IsOptional()
@IsString()
coverUrl?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
carouselUrls?: string[];
@IsOptional()
@IsArray()
@IsString({ each: true })
detailImageUrls?: string[];
@IsOptional()
@IsObject()
detailContent?: Record<string, unknown>;
}
class ProductDetailFeatureInputDto {
@IsString()
icon: string;
@IsString()
title: string;
@IsString()
desc: string;
}
export class CreateProductDetailTemplateDto {
@IsString()
@IsNotEmpty()
code: string;
@IsString()
@IsNotEmpty()
name: string;
@IsOptional()
@IsString()
description?: string;
@IsOptional()
@IsIn(['QINGXIANG', 'JIANGXIANG', 'NONGXIANG'])
aromaType?: string;
@IsOptional()
@IsString()
storyTitle?: string;
@IsOptional()
@IsString()
storyText?: string;
@IsOptional()
@IsArray()
features?: ProductDetailFeatureInputDto[];
@IsOptional()
@IsArray()
@IsString({ each: true })
detailImageUrls?: string[];
@IsOptional()
@IsNumber()
suggestedDetailImageCount?: number;
@IsOptional()
@IsNumber()
sortOrder?: number;
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: string;
}
export class UpdateProductDetailTemplateDto {
@IsOptional()
@IsString()
code?: string;
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
description?: string;
@IsOptional()
@IsIn(['QINGXIANG', 'JIANGXIANG', 'NONGXIANG', null])
aromaType?: string | null;
@IsOptional()
@IsString()
storyTitle?: string;
@IsOptional()
@IsString()
storyText?: string;
@IsOptional()
@IsArray()
features?: ProductDetailFeatureInputDto[];
@IsOptional()
@IsArray()
@IsString({ each: true })
detailImageUrls?: string[];
@IsOptional()
@IsNumber()
suggestedDetailImageCount?: number;
@IsOptional()
@IsNumber()
sortOrder?: number;
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: string;
}
export class CreatePromoCodeDto {
@IsString()
@IsNotEmpty()
name: string;
@IsOptional()
@IsString()
code?: string;
}
export class UpdatePromoCodeStatusDto {
@IsString()
@IsIn(['ACTIVE', 'DISABLED'])
status: 'ACTIVE' | 'DISABLED';
}
export class AdminBenefitGrantDto {
@IsString()
@IsNotEmpty()
phone: string;
@Type(() => Number)
@IsNumber()
@Min(0.01)
@Max(999999.99)
amount: number;
@IsOptional()
@IsString()
remark?: string;
}
export class CreateStoreCategoryDto {
@IsString()
@IsNotEmpty()
code: string;
@IsString()
@IsNotEmpty()
name: string;
@IsOptional()
@Type(() => Number)
@IsNumber()
sort?: number;
/** 空/不传 = 一级分类;传入一级 id = 二级分类 */
@IsOptional()
@IsString()
parentId?: string | null;
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: string;
}
export class UpdateStoreCategoryDto {
@IsOptional()
@IsString()
@IsNotEmpty()
code?: string;
@IsOptional()
@IsString()
@IsNotEmpty()
name?: string;
@IsOptional()
@Type(() => Number)
@IsNumber()
sort?: number;
@IsOptional()
@ValidateIf((_, v) => v !== null && v !== undefined)
@IsString()
parentId?: string | null;
@IsOptional()
@IsIn(['ACTIVE', 'DISABLED'])
status?: string;
}