v3.5.8和v3.5.9版本更新
CI / verify (pull_request) Has been cancelled

This commit is contained in:
2026-08-25 09:20:32 +08:00
parent 7304c7a8e1
commit 5935024ea8
101 changed files with 7640 additions and 5364 deletions
@@ -0,0 +1,21 @@
import { Body, Controller, Param, Put, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { SaveHqListColumnPrefsDto } from './dto/auth.dto';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import { AuthUser } from '../../common/guards/jwt-auth.guard';
@Controller('admin/me')
@UseGuards(JwtAuthGuard)
export class AdminMeController {
constructor(private readonly authService: AuthService) {}
@Put('list-columns/:listKey')
saveListColumns(
@CurrentUser() user: AuthUser,
@Param('listKey') listKey: string,
@Body() dto: SaveHqListColumnPrefsDto,
) {
return this.authService.updateMyListColumnPrefs(user.actorType, user.actorId, listKey, dto);
}
}
@@ -10,7 +10,7 @@ import {
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ClientApp, SmsScene } from '@dukang/shared-types';
import { ClientApp, SmsScene, isHqListColumnKey, type HqListColumnPrefsMap } from '@dukang/shared-types';
import { generateUserNo } from '@dukang/domain';
import { PrismaService } from '../../common/prisma/prisma.module';
import { RedisService } from '../../common/redis/redis.service';
@@ -55,6 +55,20 @@ type UserRow = Pick<
avatar?: { url: string } | null;
};
function parseListColumnPrefs(raw: unknown): HqListColumnPrefsMap {
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {};
const out: HqListColumnPrefsMap = {};
for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {
if (!isHqListColumnKey(key) || !value || typeof value !== 'object' || Array.isArray(value)) continue;
const row = value as { order?: unknown; hidden?: unknown };
out[key] = {
order: Array.isArray(row.order) ? row.order.filter((v): v is string => typeof v === 'string') : [],
hidden: Array.isArray(row.hidden) ? row.hidden.filter((v): v is string => typeof v === 'string') : [],
};
}
return out;
}
@Injectable()
export class AuthService {
constructor(
@@ -1207,11 +1221,48 @@ export class AuthService {
const account = await this.prisma.hqAccount.findUnique({ where: { id: actorId } });
if (!account) return null;
const permissionKeys = await this.hqPermissions.resolveEffectiveKeys(actorId);
return serializeBigInt({ ...account, permissionKeys });
const cityScope = await this.hqPermissions.resolveCityScope(actorId);
const { passwordHash: _passwordHash, ...safeAccount } = account;
return serializeBigInt({
...safeAccount,
listColumnPrefs: parseListColumnPrefs(account.listColumnPrefs),
permissionKeys,
cityIds: (cityScope ?? []).map((id) => id.toString()),
cityScoped: cityScope !== null,
});
}
return null;
}
async updateMyListColumnPrefs(
actorType: string,
actorId: bigint,
listKey: string,
dto: { reset?: boolean; order?: string[]; hidden?: string[] },
) {
if (actorType !== 'HQ') throw new ForbiddenException('仅总部账号可保存列表列设置');
if (!isHqListColumnKey(listKey)) throw new BadRequestException('未知列表');
const account = await this.prisma.hqAccount.findUnique({
where: { id: actorId },
select: { id: true, listColumnPrefs: true },
});
if (!account) throw new NotFoundException('账号不存在');
const current = parseListColumnPrefs(account.listColumnPrefs);
if (dto.reset) {
delete current[listKey];
} else {
current[listKey] = {
order: (dto.order ?? []).filter((k) => typeof k === 'string' && k.trim()),
hidden: (dto.hidden ?? []).filter((k) => typeof k === 'string' && k.trim()),
};
}
const updated = await this.prisma.hqAccount.update({
where: { id: actorId },
data: { listColumnPrefs: current },
});
return { listColumnPrefs: parseListColumnPrefs(updated.listColumnPrefs) };
}
wechatDisabled() {
throw new NotImplementedException('FEATURE_DISABLED');
}
@@ -1,4 +1,4 @@
import { IsIn, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { IsArray, IsBoolean, IsIn, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { SmsScene } from '@dukang/shared-types';
export class SendSmsDto {
@@ -129,3 +129,19 @@ export class CheckPartnerPhoneDto {
@IsNotEmpty()
phone: string;
}
export class SaveHqListColumnPrefsDto {
@IsOptional()
@IsBoolean()
reset?: boolean;
@IsOptional()
@IsArray()
@IsString({ each: true })
order?: string[];
@IsOptional()
@IsArray()
@IsString({ each: true })
hidden?: string[];
}
@@ -16,6 +16,7 @@ import { PartnerStaffService } from './partner-staff.service';
import { StoreStaffController } from './store-staff.controller';
import { StoreStaffService } from './store-staff.service';
import { AdminAuthController } from './admin-auth.controller';
import { AdminMeController } from './admin-me.controller';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { PhoneVerifiedGuard } from '../../common/guards/phone-verified.guard';
import { OptionalJwtAuthGuard } from '../../common/guards/optional-jwt-auth.guard';
@@ -50,6 +51,7 @@ import { CommonModule } from '../common/common.module';
UserProfileController,
UserAddressController,
AdminAuthController,
AdminMeController,
],
providers: [
AuthService,