feat: multi-module iteration

This commit is contained in:
2026-08-04 21:38:49 +08:00
parent 9d96c73246
commit 71f508e02b
1366 changed files with 202004 additions and 0 deletions
@@ -0,0 +1,109 @@
import {
IsEmail,
IsIn,
IsNotEmpty,
IsOptional,
IsString,
MaxLength,
} from 'class-validator';
export class CreateAfterSaleTicketDto {
@IsString()
@IsIn(['REFUND', 'RESHIPMENT', 'DAMAGE_RETURN', 'RETURN_REFUND', 'PACKAGE_DISPUTE'])
ticketType: string;
@IsOptional()
@IsString()
@MaxLength(512)
remark?: string;
@IsOptional()
evidenceUrls?: string[];
}
export class CreatePackageDisputeDto {
@IsString()
@IsNotEmpty()
storeId: string;
@IsOptional()
@IsString()
@MaxLength(512)
remark?: string;
@IsOptional()
@IsString()
redeemRecordId?: string;
}
export class CreateInvoiceDto {
@IsString()
@IsIn(['PERSONAL', 'ENTERPRISE'])
titleType: string;
@IsString()
@IsIn(['NORMAL', 'SPECIAL'])
invoiceKind: string;
@IsString()
@IsNotEmpty()
@MaxLength(128)
titleName: string;
@IsOptional()
@IsString()
@MaxLength(32)
taxNo?: string;
@IsOptional()
@IsString()
@MaxLength(256)
addressPhone?: string;
@IsOptional()
@IsString()
@MaxLength(256)
bankAccount?: string;
@IsEmail()
email: string;
@IsString()
@IsNotEmpty()
@MaxLength(20)
phone: string;
@IsOptional()
@IsString()
@MaxLength(512)
remark?: string;
}
export class AdminCreateInvoiceDto extends CreateInvoiceDto {
@IsString()
@IsNotEmpty()
@MaxLength(32)
orderNo: string;
}
export class IssueInvoiceDto {
@IsString()
@IsNotEmpty()
fileUrl: string;
@IsOptional()
@IsString()
resourceId?: string;
@IsOptional()
@IsString()
@MaxLength(512)
remark?: string;
}
export class RejectInvoiceDto {
@IsOptional()
@IsString()
@MaxLength(512)
remark?: string;
}
@@ -0,0 +1,100 @@
import { Type } from 'class-transformer';
import {
IsBoolean,
IsIn,
IsInt,
IsNotEmpty,
IsOptional,
IsString,
Matches,
MaxLength,
Min,
ValidateIf,
} from 'class-validator';
export class PartnerProxyOrderPreviewDto {
@IsString()
@IsNotEmpty()
productId: string;
@Type(() => Number)
@IsInt()
@Min(1)
quantity: number;
@IsOptional()
@IsIn(['ADDRESS', 'ON_SITE_PICKUP'])
deliveryMode?: 'ADDRESS' | 'ON_SITE_PICKUP';
@IsOptional()
@IsString()
storeId?: string;
@IsOptional()
@IsString()
receiverCity?: string;
@IsOptional()
@IsString()
receiverDistrict?: string;
}
export class PartnerProxyOrderCreateDto {
@IsString()
@Matches(/^1\d{10}$/, { message: '请输入有效手机号' })
phone: string;
@IsIn(['ADDRESS', 'ON_SITE_PICKUP'])
deliveryMode: 'ADDRESS' | 'ON_SITE_PICKUP';
@ValidateIf((o: PartnerProxyOrderCreateDto) => o.deliveryMode === 'ADDRESS')
@IsBoolean()
autoReceive?: boolean;
@IsOptional()
@IsString()
storeId?: string;
@IsOptional()
@IsString()
@MaxLength(32)
receiverName?: string;
@ValidateIf((o: PartnerProxyOrderCreateDto) => o.deliveryMode === 'ADDRESS')
@IsString()
@MaxLength(32)
province?: string;
@ValidateIf((o: PartnerProxyOrderCreateDto) => o.deliveryMode === 'ADDRESS')
@IsString()
@MaxLength(32)
city?: string;
@ValidateIf((o: PartnerProxyOrderCreateDto) => o.deliveryMode === 'ADDRESS')
@IsString()
@MaxLength(32)
district?: string;
@ValidateIf((o: PartnerProxyOrderCreateDto) => o.deliveryMode === 'ADDRESS')
@IsString()
@MaxLength(256)
addressDetail?: string;
@IsString()
@IsNotEmpty()
productId: string;
@Type(() => Number)
@IsInt()
@Min(1)
quantity: number;
@IsOptional()
@IsString()
promoCodeId?: string;
}
export class PartnerProxyOrderPayDto {
@IsIn(['NATIVE', 'JSAPI'])
payMethod: 'NATIVE' | 'JSAPI';
}
@@ -0,0 +1,288 @@
import { Body, Controller, Get, Param, Post, Put, Query, Req, UseGuards } from '@nestjs/common';
import type { Request } from 'express';
import { TradeService } from './trade.service';
import { JwtAuthGuard, AuthUser } from '../../common/guards/jwt-auth.guard';
import { PartnerPrimaryGuard } from '../../common/guards/partner-primary.guard';
import { RequirePartnerPermissions } from '../../common/decorators/partner-permission.decorator';
import { PartnerPermissionGuard } from '../../common/guards/partner-permission.guard';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import {
PartnerProxyOrderCreateDto,
PartnerProxyOrderPayDto,
PartnerProxyOrderPreviewDto,
} from './dto/partner-proxy-order.dto';
import { ManualShipOrderDto } from '../ops/dto/admin-mutate.dto';
import { CreateAfterSaleTicketDto, CreateInvoiceDto, CreatePackageDisputeDto } from './dto/after-sale.dto';
@Controller('trade/orders')
@UseGuards(JwtAuthGuard)
export class TradeController {
constructor(private readonly tradeService: TradeService) {}
@Post('preview')
preview(@CurrentUser() user: AuthUser, @Body() body: Record<string, unknown>) {
return this.tradeService.preview(user.actorId, body as never);
}
@Post()
create(@CurrentUser() user: AuthUser, @Body() body: Record<string, unknown>, @Req() req: Request) {
return this.tradeService.createOrder(user.actorId, body as never, req);
}
@Get()
list(
@CurrentUser() user: AuthUser,
@Query('tab') tab = 'all',
@Query('page') page = '1',
@Query('pageSize') pageSize = '20',
) {
return this.tradeService.listOrders(user.actorId, tab, Number(page), Number(pageSize));
}
@Get(':id')
detail(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.getOrder(user.actorId, BigInt(id));
}
@Get(':id/track')
track(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.getOrderTrack(user.actorId, BigInt(id));
}
@Post(':id/pay')
pay(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.payOrder(user.actorId, BigInt(id), user.clientApp);
}
@Put(':id/address')
updateAddress(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() body: Record<string, unknown>,
) {
return this.tradeService.updateAddress(user.actorId, BigInt(id), body);
}
@Post(':id/confirm-receive')
confirmReceive(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() body?: { onSitePickup?: boolean; source?: 'USER' | 'WECHAT_COMPONENT' },
) {
return this.tradeService.confirmReceive(user.actorId, BigInt(id), {
onSitePickup: !!body?.onSitePickup,
source: body?.source === 'WECHAT_COMPONENT' ? 'WECHAT_COMPONENT' : 'USER',
});
}
@Post(':id/refund-requests')
refundRequest(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() body: { remark?: string },
) {
return this.tradeService.createRefundRequest(user.actorId, BigInt(id), body.remark);
}
@Post(':id/after-sale-tickets')
createAfterSale(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() body: CreateAfterSaleTicketDto,
) {
return this.tradeService.createAfterSaleTicket(user.actorId, BigInt(id), body);
}
@Post(':id/invoices')
createInvoice(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() body: CreateInvoiceDto,
) {
return this.tradeService.createInvoice(user.actorId, BigInt(id), body);
}
}
@Controller('trade/package-disputes')
@UseGuards(JwtAuthGuard)
export class TradePackageDisputeController {
constructor(private readonly tradeService: TradeService) {}
@Post()
create(@CurrentUser() user: AuthUser, @Body() body: CreatePackageDisputeDto) {
return this.tradeService.createPackageDispute(user.actorId, body);
}
}
@Controller('trade/after-sale-tickets')
@UseGuards(JwtAuthGuard)
export class TradeAfterSaleTicketController {
constructor(private readonly tradeService: TradeService) {}
@Get()
list(
@CurrentUser() user: AuthUser,
@Query('page') page = '1',
@Query('pageSize') pageSize = '20',
) {
return this.tradeService.listAfterSaleTickets(user.actorId, Number(page), Number(pageSize));
}
@Get(':id')
detail(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.getAfterSaleTicket(user.actorId, BigInt(id));
}
}
@Controller('trade/invoices')
@UseGuards(JwtAuthGuard)
export class TradeInvoiceController {
constructor(private readonly tradeService: TradeService) {}
@Get()
list(
@CurrentUser() user: AuthUser,
@Query('page') page = '1',
@Query('pageSize') pageSize = '20',
) {
return this.tradeService.listInvoices(user.actorId, Number(page), Number(pageSize));
}
@Get(':id')
detail(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.getInvoice(user.actorId, BigInt(id));
}
}
@Controller('partner/orders')
@UseGuards(JwtAuthGuard, PartnerPermissionGuard)
@RequirePartnerPermissions('order:view', 'warehouse:manage')
export class PartnerOrderController {
constructor(private readonly tradeService: TradeService) {}
@Get()
list(
@CurrentUser() user: AuthUser,
@Query('page') page = '1',
@Query('pageSize') pageSize = '20',
) {
return this.tradeService.listPartnerOrders(user.actorId, Number(page), Number(pageSize));
}
@Get(':id')
detail(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.getPartnerOrder(user.actorId, BigInt(id));
}
@Get(':id/track')
track(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.getPartnerOrderTrack(user.actorId, BigInt(id));
}
@Post(':id/manual-ship')
@RequirePartnerPermissions('warehouse:manage')
manualShip(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() body: ManualShipOrderDto,
) {
return this.tradeService.partnerManualShip(user.actorId, BigInt(id), body);
}
@Post(':id/mock-advance-delivery')
mockAdvance(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() body: { targetStatus: string },
) {
return this.tradeService.advanceDelivery(user.actorId, BigInt(id), body.targetStatus);
}
}
@Controller('partner/reshipments')
@UseGuards(JwtAuthGuard, PartnerPrimaryGuard)
export class PartnerReshipmentController {
constructor(private readonly tradeService: TradeService) {}
@Get()
list(@CurrentUser() user: AuthUser) {
return this.tradeService.listPartnerReshipments(user.actorId);
}
}
@Controller('partner/proxy-orders')
@UseGuards(JwtAuthGuard, PartnerPrimaryGuard)
export class PartnerProxyOrderController {
constructor(private readonly tradeService: TradeService) {}
@Get('options')
options(@CurrentUser() user: AuthUser) {
return this.tradeService.getPartnerProxyOrderOptions(user.actorId);
}
@Get()
list(
@CurrentUser() user: AuthUser,
@Query('page') page = '1',
@Query('pageSize') pageSize = '20',
@Query('keyword') keyword?: string,
) {
return this.tradeService.listPartnerProxyOrders(
user.actorId,
Number(page),
Number(pageSize),
keyword,
);
}
@Post('preview')
preview(@CurrentUser() user: AuthUser, @Body() dto: PartnerProxyOrderPreviewDto) {
return this.tradeService.previewPartnerProxyOrderForPartner(user.actorId, dto);
}
@Post()
create(
@CurrentUser() user: AuthUser,
@Body() dto: PartnerProxyOrderCreateDto,
@Req() req: Request,
) {
return this.tradeService.createPartnerProxyOrder(user.actorId, dto, req);
}
@Post(':id/pay')
pay(
@CurrentUser() user: AuthUser,
@Param('id') id: string,
@Body() dto: PartnerProxyOrderPayDto,
) {
return this.tradeService.payPartnerProxyOrder(user.actorId, BigInt(id), dto.payMethod);
}
@Post(':id/cancel-pay')
cancelPay(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.cancelPartnerProxyOrder(user.actorId, BigInt(id));
}
@Post(':id/pay/mock-confirm')
mockConfirmPay(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.mockConfirmProxyPay(BigInt(id), {
partnerAccountId: user.actorId,
});
}
@Get(':id/pay-status')
async payStatus(@CurrentUser() user: AuthUser, @Param('id') id: string) {
await this.tradeService.getPartnerProxyOrder(user.actorId, BigInt(id));
return this.tradeService.getProxyPayStatus(BigInt(id));
}
@Get(':id/track')
track(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.getPartnerProxyOrderTrack(user.actorId, BigInt(id));
}
@Get(':id')
detail(@CurrentUser() user: AuthUser, @Param('id') id: string) {
return this.tradeService.getPartnerProxyOrder(user.actorId, BigInt(id));
}
}
@@ -0,0 +1,46 @@
import { Module, forwardRef } from '@nestjs/common';
import { AnalyticsModule } from '../analytics/analytics.module';
import { IntegrationsModule } from '../../integrations/integrations.module';
import { IamModule } from '../iam/iam.module';
import { BenefitModule } from '../benefit/benefit.module';
import { CatalogModule } from '../catalog/catalog.module';
import { CommonModule } from '../common/common.module';
import { CityScopeModule } from '../city-scope/city-scope.module';
import { PromoModule } from '../promo/promo.module';
import { FulfillmentModule } from '../fulfillment/fulfillment.module';
import {
TradeController,
PartnerOrderController,
PartnerProxyOrderController,
PartnerReshipmentController,
TradeAfterSaleTicketController,
TradeInvoiceController,
TradePackageDisputeController,
} from './trade.controller';
import { TradeService } from './trade.service';
@Module({
imports: [
IntegrationsModule,
IamModule,
CatalogModule,
AnalyticsModule,
CityScopeModule,
PromoModule,
forwardRef(() => BenefitModule),
forwardRef(() => FulfillmentModule),
CommonModule,
],
controllers: [
TradeController,
TradePackageDisputeController,
TradeAfterSaleTicketController,
TradeInvoiceController,
PartnerOrderController,
PartnerProxyOrderController,
PartnerReshipmentController,
],
providers: [TradeService],
exports: [TradeService],
})
export class TradeModule {}
File diff suppressed because it is too large Load Diff