feat: 技术支持工单/企微权限/开发版本管理/消息推送等迭代

This commit is contained in:
2026-08-04 21:32:13 +08:00
parent c8ea5a3119
commit 9d96c73246
1341 changed files with 0 additions and 195605 deletions
@@ -1,51 +0,0 @@
import { Type } from 'class-transformer';
import { IsNotEmpty, IsNumber, IsString, Min } from 'class-validator';
import type { RedeemPhoneDirectPrepareRequest } from '@dukang/shared-types';
export class RedeemPhoneSendLookupSmsDto {
@IsString()
@IsNotEmpty()
phone: string;
}
export class RedeemPhoneBalanceDto {
@IsString()
@IsNotEmpty()
phone: string;
@IsString()
@IsNotEmpty()
code: string;
}
export class RedeemPhonePrepareDto {
@IsString()
@IsNotEmpty()
sessionId: string;
@Type(() => Number)
@IsNumber()
@Min(0.01)
amount: number;
}
export class RedeemPhoneDirectPrepareDto implements RedeemPhoneDirectPrepareRequest {
@IsString()
@IsNotEmpty()
phone: string;
@Type(() => Number)
@IsNumber()
@Min(0.01)
amount: number;
}
export class RedeemPhoneConfirmDto {
@IsString()
@IsNotEmpty()
sessionId: string;
@IsString()
@IsNotEmpty()
code: string;
}
@@ -1,40 +0,0 @@
import { Type } from 'class-transformer';
import { IsIn, IsInt, IsNotEmpty, IsOptional, IsString, MaxLength, Min } from 'class-validator';
export class RedeemFailureReportDto {
@IsString()
@IsNotEmpty()
token: string;
@IsIn(['NETWORK', 'BUSINESS'])
errorClass: 'NETWORK' | 'BUSINESS';
@IsOptional()
@IsString()
@MaxLength(256)
message?: string;
@IsIn(['preview', 'confirm'])
step: 'preview' | 'confirm';
}
export class RedeemPendingSubmitDto {
@IsString()
@IsNotEmpty()
token: string;
@IsString()
@IsNotEmpty()
photoResourceId: string;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
failCount?: number;
@IsOptional()
@IsString()
@MaxLength(256)
remark?: string;
}
@@ -1,153 +0,0 @@
import { Body, Controller, Get, Param, Post, Query, UseGuards } from '@nestjs/common';
import { RedeemService } from './redeem.service';
import { JwtAuthGuard, AuthUser } from '../../common/guards/jwt-auth.guard';
import { ShopStoreGuard } from '../../common/guards/shop-store.guard';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import {
RedeemPhoneBalanceDto,
RedeemPhoneConfirmDto,
RedeemPhoneDirectPrepareDto,
RedeemPhonePrepareDto,
RedeemPhoneSendLookupSmsDto,
} from './dto/phone-redeem.dto';
import { RedeemFailureReportDto, RedeemPendingSubmitDto } from './dto/weaknet-redeem.dto';
@Controller('redeem')
@UseGuards(JwtAuthGuard)
export class UserRedeemController {
constructor(private readonly redeemService: RedeemService) {}
@Post('tokens')
createToken(@CurrentUser() user: AuthUser, @Body() body: Record<string, unknown>) {
return this.redeemService.createToken(user.actorId, body as never);
}
@Get('tokens/:token')
getToken(@Param('token') token: string) {
return this.redeemService.getToken(token);
}
@Get('tokens/:token/status')
getTokenStatus(@CurrentUser() user: AuthUser, @Param('token') token: string) {
return this.redeemService.getTokenStatus(token, user.actorId);
}
@Post('ratings')
rating(@CurrentUser() user: AuthUser, @Body() body: Record<string, unknown>) {
return this.redeemService.submitRating(user.actorId, body as never);
}
@Get('records')
records(
@CurrentUser() user: AuthUser,
@Query('page') page = '1',
@Query('pageSize') pageSize = '20',
) {
return this.redeemService.listUserRecords(
user.actorId,
Number(page),
Number(pageSize),
);
}
}
@Controller('shop/redeem')
@UseGuards(JwtAuthGuard, ShopStoreGuard)
export class ShopRedeemController {
constructor(private readonly redeemService: RedeemService) {}
@Post('preview')
preview(@CurrentUser() user: AuthUser, @Body() body: { token: string }) {
return this.redeemService.previewRedeem(user.actorId, user.storeId!, body.token);
}
@Post('confirm')
confirm(@CurrentUser() user: AuthUser, @Body() body: { token: string }) {
return this.redeemService.confirmRedeem(user.actorId, user.storeId!, body);
}
@Post('failures')
reportFailure(
@CurrentUser() user: AuthUser,
@Body() body: RedeemFailureReportDto,
) {
return this.redeemService.reportNetworkFailure(user.actorId, user.storeId!, body);
}
@Post('pending')
submitPending(
@CurrentUser() user: AuthUser,
@Body() body: RedeemPendingSubmitDto,
) {
return this.redeemService.submitPendingRedeem(user.actorId, user.storeId!, body);
}
@Get('records')
records(
@CurrentUser() user: AuthUser,
@Query('page') page = '1',
@Query('pageSize') pageSize = '20',
) {
return this.redeemService.listShopRecords(
user.actorId,
user.storeId!,
Number(page),
Number(pageSize),
);
}
@Get('stats')
stats(
@CurrentUser() user: AuthUser,
@Query('range') range?: string,
) {
const normalized =
range === '7d' || range === '30d' || range === 'today' ? range : 'today';
return this.redeemService.getShopRedeemStats(user.actorId, user.storeId!, normalized);
}
@Post('phone/send-lookup-sms')
sendPhoneLookupSms(@CurrentUser() user: AuthUser, @Body() body: RedeemPhoneSendLookupSmsDto) {
return this.redeemService.sendPhoneLookupSms(user.actorId, user.storeId!, body.phone);
}
@Post('phone/balance')
phoneBalance(@CurrentUser() user: AuthUser, @Body() body: RedeemPhoneBalanceDto) {
return this.redeemService.verifyPhoneAndGetBalance(
user.actorId,
user.storeId!,
body.phone,
body.code,
);
}
@Post('phone/prepare')
phonePrepare(@CurrentUser() user: AuthUser, @Body() body: RedeemPhonePrepareDto) {
return this.redeemService.preparePhoneRedeem(
user.actorId,
user.storeId!,
body.sessionId,
body.amount,
);
}
@Post('phone/prepare-direct')
phonePrepareDirect(@CurrentUser() user: AuthUser, @Body() body: RedeemPhoneDirectPrepareDto) {
return this.redeemService.preparePhoneRedeemDirect(
user.actorId,
user.storeId!,
body.phone,
body.amount,
);
}
@Post('phone/confirm')
phoneConfirm(@CurrentUser() user: AuthUser, @Body() body: RedeemPhoneConfirmDto) {
return this.redeemService.confirmPhoneRedeem(
user.actorId,
user.storeId!,
body.sessionId,
body.code,
);
}
}
@@ -1,15 +0,0 @@
import { Module, forwardRef } from '@nestjs/common';
import { AnalyticsModule } from '../analytics/analytics.module';
import { IamModule } from '../iam/iam.module';
import { BenefitModule } from '../benefit/benefit.module';
import { SettlementModule } from '../settlement/settlement.module';
import { RedeemService } from './redeem.service';
import { ShopRedeemController, UserRedeemController } from './redeem.controller';
@Module({
imports: [IamModule, AnalyticsModule, BenefitModule, forwardRef(() => SettlementModule)],
controllers: [UserRedeemController, ShopRedeemController],
providers: [RedeemService],
exports: [RedeemService],
})
export class RedeemModule {}
File diff suppressed because it is too large Load Diff