import { Body, Controller, Get, Inject, Param, Patch, Post, Query, Req, ValidationPipe, HttpStatus, } from '@nestjs/common'; import { API_ERROR_CODES, PERMISSION_CODES, type ConfirmUploadResponse, type CreateUploadIntentResponse, } from '@lehuo/contracts'; import { RequireSessionChannel } from '../auth/decorators/require-session-channel.decorator'; import { CurrentAuth } from '../common/decorators/current-auth.decorator'; import { ApplicationError } from '../common/application-error'; import { Public } from '../common/decorators/public.decorator'; import { RequirePermissions } from '../common/decorators/require-permissions.decorator'; import type { AuthContext, RequestContext } from '../common/request-context'; import { SessionChannel } from '../generated/prisma/enums'; import { MediaService } from '../participation/media.service'; import { CreateOldItemDonationDto, CreateOldItemMediaUploadIntentDto, CreateOldItemDropOffPointDto, CreateOldItemNeedDto, OldItemDonationQueryDto, OldItemNeedQueryDto, TransitionOldItemDonationDto, TransitionOldItemNeedDto, UpdateOldItemNeedDto, CreateOldItemInventoryDto, ImportOldItemInventoryDto, OldItemInventoryQueryDto, TransitionOldItemInventoryDto, ActivateOldItemRewardConfigDto, AdjustOldItemPointsDto, IssueOldItemCertificateDto, OldItemPointAccountQueryDto, RevokeOldItemCertificateDto, } from './dto/old-items.dto'; import { OldItemInventoryService } from './old-item-inventory.service'; import { OldItemRewardsService } from './old-item-rewards.service'; import { OldItemsService } from './old-items.service'; const strict = (expectedType: new () => object) => new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true, expectedType, }); @Controller('api/v1/old-items') export class OldItemsController { constructor( @Inject(OldItemsService) private readonly service: OldItemsService, @Inject(MediaService) private readonly media: MediaService, @Inject(OldItemRewardsService) private readonly rewards: OldItemRewardsService, ) {} @Get('stats') @Public() stats() { return this.service.stats(); } @Get('needs') @Public() needs(@Query(strict(OldItemNeedQueryDto)) q: OldItemNeedQueryDto) { return this.service.listNeeds(q); } @Get('needs/:publicId') @Public() need(@Param('publicId') id: string) { return this.service.getNeed(id); } @Get('drop-off-points') @Public() points() { return this.service.dropOffPoints(); } @Post('media/upload-intents') @RequireSessionChannel(SessionChannel.WECHAT) mediaIntent( @Body(strict(CreateOldItemMediaUploadIntentDto)) input: CreateOldItemMediaUploadIntentDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ): Promise { if (input.businessType !== 'OLD_ITEM_DONATION_IMAGE') throw new ApplicationError( API_ERROR_CODES.VALIDATION_ERROR, '图片业务类型无效', HttpStatus.BAD_REQUEST, ); return this.media.createIntent(input, actor, req); } @Post('media/:publicId/confirm') @RequireSessionChannel(SessionChannel.WECHAT) confirm( @Param('publicId') id: string, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ): Promise { return this.media.confirm(id, actor, req); } @Post('donations') @RequireSessionChannel(SessionChannel.WECHAT) create( @Body(strict(CreateOldItemDonationDto)) input: CreateOldItemDonationDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.service.createDonation(input, actor, req); } @Get('me/donations') @RequireSessionChannel(SessionChannel.WECHAT) mine( @Query(strict(OldItemDonationQueryDto)) q: OldItemDonationQueryDto, @CurrentAuth() actor: AuthContext, ) { return this.service.myDonations(q, actor); } @Get('me/donations/:publicId') @RequireSessionChannel(SessionChannel.WECHAT) mineOne( @Param('publicId') id: string, @CurrentAuth() actor: AuthContext, ) { return this.service.myDonation(id, actor); } @Get('me/media/:publicId/download') @RequireSessionChannel(SessionChannel.WECHAT) image( @Param('publicId') id: string, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.service.imageDownload(id, actor, req); } @Get('me/rewards') @RequireSessionChannel(SessionChannel.WECHAT) myRewards( @CurrentAuth() actor: AuthContext, ) { return this.rewards.myRewards(actor); } @Get('me/certificates/:publicId') @RequireSessionChannel(SessionChannel.WECHAT) myCertificate(@Param('publicId') id: string, @CurrentAuth() actor: AuthContext) { return this.rewards.myCertificate(id, actor); } } @Controller('api/v1/admin/old-items') @RequireSessionChannel(SessionChannel.ADMIN) export class AdminOldItemsController { constructor( @Inject(OldItemsService) private readonly service: OldItemsService, @Inject(OldItemInventoryService) private readonly inventory: OldItemInventoryService, @Inject(OldItemRewardsService) private readonly rewards: OldItemRewardsService, ) {} @Get('assignees') @RequirePermissions(PERMISSION_CODES.OLD_ITEMS_READ) assignees() { return this.service.assignees(); } @Get('media/:publicId/download') @RequirePermissions(PERMISSION_CODES.OLD_ITEMS_READ) image( @Param('publicId') id: string, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.service.imageDownload(id, actor, req, true); } @Get('donations') @RequirePermissions(PERMISSION_CODES.OLD_ITEMS_READ) donations( @Query(strict(OldItemDonationQueryDto)) q: OldItemDonationQueryDto, ) { return this.service.adminDonations(q); } @Get('donations/:publicId') @RequirePermissions(PERMISSION_CODES.OLD_ITEMS_READ) donation( @Param('publicId') id: string, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.service.adminDonation(id, actor, req); } @Post('donations/:publicId/transition') @RequirePermissions(PERMISSION_CODES.OLD_ITEMS_MANAGE) transition( @Param('publicId') id: string, @Body(strict(TransitionOldItemDonationDto)) input: TransitionOldItemDonationDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.service.transitionDonation(id, input, actor, req); } @Get('needs') @RequirePermissions(PERMISSION_CODES.OLD_ITEMS_READ) needs( @Query(strict(OldItemNeedQueryDto)) q: OldItemNeedQueryDto, ) { return this.service.listNeeds(q, true); } @Get('needs/:publicId') @RequirePermissions(PERMISSION_CODES.OLD_ITEMS_READ) need( @Param('publicId') id: string, ) { return this.service.getNeed(id, true); } @Post('needs') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_NEEDS_MANAGE) createNeed( @Body(strict(CreateOldItemNeedDto)) input: CreateOldItemNeedDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.service.createNeed(input, actor, req); } @Patch('needs/:publicId') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_NEEDS_MANAGE) updateNeed( @Param('publicId') id: string, @Body(strict(UpdateOldItemNeedDto)) input: UpdateOldItemNeedDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.service.updateNeed(id, input, actor, req); } @Post('needs/:publicId/transition') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_NEEDS_MANAGE) transitionNeed( @Param('publicId') id: string, @Body(strict(TransitionOldItemNeedDto)) input: TransitionOldItemNeedDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.service.transitionNeed(id, input, actor, req); } @Post('drop-off-points') @RequirePermissions(PERMISSION_CODES.OLD_ITEMS_MANAGE) createPoint( @Body(strict(CreateOldItemDropOffPointDto)) input: CreateOldItemDropOffPointDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.service.createDropOffPoint(input, actor, req); } @Get('inventory') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_INVENTORY_READ) inventoryList(@Query(strict(OldItemInventoryQueryDto)) q: OldItemInventoryQueryDto) { return this.inventory.list(q); } @Get('inventory/:publicId') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_INVENTORY_READ) inventoryOne( @Param('publicId') id: string, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.inventory.get(id, actor, req); } @Post('inventory') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_INVENTORY_MANAGE) inventoryCreate( @Body(strict(CreateOldItemInventoryDto)) input: CreateOldItemInventoryDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.inventory.create(input, actor, req); } @Post('inventory/import') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_INVENTORY_IMPORT) inventoryImport( @Body(strict(ImportOldItemInventoryDto)) input: ImportOldItemInventoryDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.inventory.import(input, actor, req); } @Post('inventory/:publicId/transition') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_INVENTORY_MANAGE) inventoryTransition( @Param('publicId') id: string, @Body(strict(TransitionOldItemInventoryDto)) input: TransitionOldItemInventoryDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.inventory.transition(id, input, actor, req); } @Get('inventory-export/file') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_INVENTORY_EXPORT) inventoryExport( @Query(strict(OldItemInventoryQueryDto)) q: OldItemInventoryQueryDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.inventory.export(q, actor, req); } @Get('reward-config') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_REWARDS_READ) rewardConfig() { return this.rewards.config(); } @Post('reward-config/activate') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_REWARDS_MANAGE) activateRewardConfig( @Body(strict(ActivateOldItemRewardConfigDto)) input: ActivateOldItemRewardConfigDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.rewards.activateConfig(input, actor, req); } @Get('reward-accounts') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_REWARDS_READ) rewardAccounts(@Query(strict(OldItemPointAccountQueryDto)) q: OldItemPointAccountQueryDto) { return this.rewards.accounts(q); } @Post('reward-accounts/:userPublicId/adjust') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_REWARDS_MANAGE) adjustRewardPoints( @Param('userPublicId') userPublicId: string, @Body(strict(AdjustOldItemPointsDto)) input: AdjustOldItemPointsDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.rewards.adjust(userPublicId, input, actor, req); } @Get('certificates') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_REWARDS_READ) certificates(@Query(strict(OldItemPointAccountQueryDto)) q: OldItemPointAccountQueryDto) { return this.rewards.certificates(q); } @Post('donations/:publicId/certificate') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_REWARDS_MANAGE) issueCertificate( @Param('publicId') publicId: string, @Body(strict(IssueOldItemCertificateDto)) input: IssueOldItemCertificateDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.rewards.issue(publicId, input, actor, req); } @Post('certificates/:publicId/revoke') @RequirePermissions(PERMISSION_CODES.OLD_ITEM_REWARDS_MANAGE) revokeCertificate( @Param('publicId') publicId: string, @Body(strict(RevokeOldItemCertificateDto)) input: RevokeOldItemCertificateDto, @CurrentAuth() actor: AuthContext, @Req() req: RequestContext, ) { return this.rewards.revoke(publicId, input, actor, req); } }