Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -16,7 +16,7 @@ import { ClientConfigController } from './client-config.controller';
|
||||
import { WechatLocationService } from './wechat-location.service';
|
||||
|
||||
@Module({
|
||||
imports: [IamModule, IntegrationsModule, SystemConfigModule, forwardRef(() => AnalyticsModule)],
|
||||
imports: [forwardRef(() => IamModule), IntegrationsModule, SystemConfigModule, forwardRef(() => AnalyticsModule)],
|
||||
controllers: [
|
||||
ResourceController,
|
||||
EventController,
|
||||
|
||||
@@ -102,6 +102,9 @@ export class ResourceService {
|
||||
});
|
||||
throw new BadRequestException(message);
|
||||
}
|
||||
if (dto.bizType === 'AVATAR' && !file.mimetype?.startsWith('image/')) {
|
||||
throw new BadRequestException('头像仅支持图片文件');
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.oss.putObject({
|
||||
@@ -125,6 +128,24 @@ export class ResourceService {
|
||||
externalNo: result.ossKey,
|
||||
status: 'SUCCESS',
|
||||
});
|
||||
if (actor?.refType === 'USER' && dto.bizType === 'AVATAR' && dto.mediaType === 'IMAGE') {
|
||||
const resource = await this.prisma.commonResource.create({
|
||||
data: {
|
||||
ownerType: 'USER',
|
||||
ownerId: actor.refId,
|
||||
bizType: 'AVATAR',
|
||||
mediaType: 'IMAGE',
|
||||
ossBucket: result.bucket,
|
||||
ossKey: result.ossKey,
|
||||
url: result.url,
|
||||
fileName: file.originalname || 'avatar',
|
||||
fileSize: BigInt(file.size),
|
||||
mimeType: file.mimetype,
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
});
|
||||
return serializeBigInt({ ...result, resourceId: resource.id });
|
||||
}
|
||||
return result;
|
||||
} catch (err) {
|
||||
await logOssUpload(this.prisma, {
|
||||
@@ -138,6 +159,37 @@ export class ResourceService {
|
||||
}
|
||||
}
|
||||
|
||||
async getOwnedActiveAvatar(resourceId: bigint, userId: bigint) {
|
||||
const resource = await this.prisma.commonResource.findFirst({
|
||||
where: {
|
||||
id: resourceId,
|
||||
ownerType: 'USER',
|
||||
ownerId: userId,
|
||||
bizType: 'AVATAR',
|
||||
mediaType: 'IMAGE',
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
});
|
||||
if (!resource) throw new BadRequestException('头像资源无效或不属于当前用户');
|
||||
return resource;
|
||||
}
|
||||
|
||||
async getOwnedActiveAvatarByUrl(url: string, userId: bigint) {
|
||||
const resource = await this.prisma.commonResource.findFirst({
|
||||
where: {
|
||||
url,
|
||||
ownerType: 'USER',
|
||||
ownerId: userId,
|
||||
bizType: 'AVATAR',
|
||||
mediaType: 'IMAGE',
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
if (!resource) throw new BadRequestException('头像资源无效或不属于当前用户');
|
||||
return resource;
|
||||
}
|
||||
|
||||
async register(dto: RegisterResourceDto) {
|
||||
const resource = await this.prisma.commonResource.create({
|
||||
data: {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { randomUUID } from 'crypto';
|
||||
import {
|
||||
BadRequestException,
|
||||
ForbiddenException,
|
||||
forwardRef,
|
||||
Inject,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
@@ -22,6 +23,7 @@ import { serializeBigInt } from '../../common/decorators/current-user.decorator'
|
||||
import { verifyPassword } from '../../common/crypto/password.util';
|
||||
import { AnalyticsService } from '../analytics/analytics.service';
|
||||
import { UserAddressService } from './user-address.service';
|
||||
import { ResourceService } from '../common/resource.service';
|
||||
|
||||
import type { User } from '@prisma/client';
|
||||
|
||||
@@ -62,6 +64,7 @@ export class AuthService {
|
||||
private readonly analyticsService: AnalyticsService,
|
||||
private readonly smsCodeStore: SmsCodeStore,
|
||||
private readonly userAddressService: UserAddressService,
|
||||
@Inject(forwardRef(() => ResourceService)) private readonly resourceService: ResourceService,
|
||||
) {}
|
||||
|
||||
private assertMobilePhone(phone: string) {
|
||||
@@ -1407,12 +1410,9 @@ export class AuthService {
|
||||
|
||||
async updateMiniWechatProfile(
|
||||
userId: bigint,
|
||||
input: { nickname?: string; avatarUrl?: string },
|
||||
input: { nickname?: string; avatarUrl?: string; avatarResourceId?: string },
|
||||
) {
|
||||
const user = await this.assertActiveUser(userId);
|
||||
if (!user.wxOpenId) {
|
||||
throw new BadRequestException('请先完成微信授权');
|
||||
}
|
||||
|
||||
const data: {
|
||||
nickname?: string;
|
||||
@@ -1425,37 +1425,23 @@ export class AuthService {
|
||||
}
|
||||
|
||||
const avatarUrl = input.avatarUrl?.trim();
|
||||
if (avatarUrl) {
|
||||
if (user.avatarResourceId) {
|
||||
await this.prisma.commonResource.update({
|
||||
where: { id: user.avatarResourceId },
|
||||
data: { url: avatarUrl },
|
||||
});
|
||||
} else {
|
||||
const avatar = await this.prisma.commonResource.create({
|
||||
data: {
|
||||
ownerType: 'USER',
|
||||
ownerId: userId,
|
||||
bizType: 'AVATAR',
|
||||
mediaType: 'IMAGE',
|
||||
ossBucket: 'wechat',
|
||||
ossKey: `wx-avatar/${user.wxOpenId}`,
|
||||
url: avatarUrl,
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
});
|
||||
data.avatarResourceId = avatar.id;
|
||||
const avatarResourceId = input.avatarResourceId?.trim();
|
||||
if (avatarResourceId) {
|
||||
let resourceId: bigint;
|
||||
try {
|
||||
resourceId = BigInt(avatarResourceId);
|
||||
} catch {
|
||||
throw new BadRequestException('头像资源编号无效');
|
||||
}
|
||||
const avatar = await this.resourceService.getOwnedActiveAvatar(resourceId, userId);
|
||||
data.avatarResourceId = avatar.id;
|
||||
} else if (avatarUrl) {
|
||||
// 兼容已发布旧客户端:只接受刚由当前用户上传并登记过的真实资源 URL。
|
||||
const avatar = await this.resourceService.getOwnedActiveAvatarByUrl(avatarUrl, userId);
|
||||
data.avatarResourceId = avatar.id;
|
||||
}
|
||||
|
||||
if (!data.nickname && !data.avatarResourceId) {
|
||||
if (avatarUrl && user.avatarResourceId) {
|
||||
const refreshed = await this.prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
include: { avatar: true },
|
||||
});
|
||||
return this.formatUserProfile(refreshed ?? user);
|
||||
}
|
||||
return this.formatUserProfile(user);
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,10 @@ export class MiniWechatProfileDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
avatarUrl?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
avatarResourceId?: string;
|
||||
}
|
||||
|
||||
export class CheckPartnerPhoneDto {
|
||||
|
||||
@@ -24,10 +24,12 @@ import { PartnerPrimaryGuard } from '../../common/guards/partner-primary.guard';
|
||||
import { PartnerPermissionGuard } from '../../common/guards/partner-permission.guard';
|
||||
import { ShopStoreGuard } from '../../common/guards/shop-store.guard';
|
||||
import { StoreMembershipService } from '../../common/guards/store-membership.service';
|
||||
import { CommonModule } from '../common/common.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
IntegrationsModule,
|
||||
forwardRef(() => CommonModule),
|
||||
forwardRef(() => AnalyticsModule),
|
||||
JwtModule.register({
|
||||
secret: process.env.JWT_SECRET || 'dukang-prev1-dev-secret',
|
||||
|
||||
Reference in New Issue
Block a user