import { Injectable, NotFoundException, ConflictException, Logger, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, FindOptionsWhere } from 'typeorm'; import { Broker } from './broker.entity'; import { CreateBrokerDto } from './dto/create-broker.dto'; import { UpdateBrokerDto } from './dto/update-broker.dto'; import { QueryBrokerDto } from './dto/query-broker.dto'; import { BatchCreateBrokerDto } from './dto/batch-create-broker.dto'; import { PaginationInfo } from '@/common/dto/pagination.dto'; import { StorageService } from '../storage/storage.service'; @Injectable() export class BrokerService { private readonly logger = new Logger(BrokerService.name); constructor( @InjectRepository(Broker) private readonly brokerRepository: Repository, private readonly storageService: StorageService, ) {} /** * 单独创建 broker */ async create(createBrokerDto: CreateBrokerDto): Promise { // 检查同一地区的 broker_code 是否已存在 const existingByCode = await this.brokerRepository.findOne({ where: { brokerCode: createBrokerDto.brokerCode, region: createBrokerDto.region, }, }); if (existingByCode) { throw new ConflictException( `地区 "${createBrokerDto.region}" 中已存在代码为 "${createBrokerDto.brokerCode}" 的券商`, ); } // 检查同一地区的 broker_name 是否已存在 const existingByName = await this.brokerRepository.findOne({ where: { brokerName: createBrokerDto.brokerName, region: createBrokerDto.region, }, }); if (existingByName) { throw new ConflictException( `地区 "${createBrokerDto.region}" 中已存在名称为 "${createBrokerDto.brokerName}" 的券商`, ); } const broker = this.brokerRepository.create({ ...createBrokerDto, sortOrder: createBrokerDto.sortOrder ?? 0, isActive: createBrokerDto.isActive ?? true, }); return this.brokerRepository.save(broker); } /** * 批量创建 broker */ async batchCreate( batchCreateBrokerDto: BatchCreateBrokerDto, ): Promise { const brokers = batchCreateBrokerDto.brokers.map((dto) => this.brokerRepository.create({ ...dto, sortOrder: dto.sortOrder ?? 0, isActive: dto.isActive ?? true, }), ); // 检查是否有重复的 broker_code + region 组合 const codeRegionPairs = brokers.map((b) => ({ brokerCode: b.brokerCode, region: b.region, })); const existingBrokers = await this.brokerRepository.find({ where: codeRegionPairs.map((pair) => ({ brokerCode: pair.brokerCode, region: pair.region, })), }); if (existingBrokers.length > 0) { const conflicts = existingBrokers.map( (b) => `${b.brokerCode} (${b.region})`, ); throw new ConflictException( `以下券商已存在:${conflicts.join('、')}`, ); } // 检查批量数据内部是否有重复 const uniquePairs = new Set( codeRegionPairs.map((p) => `${p.brokerCode}-${p.region}`), ); if (uniquePairs.size !== codeRegionPairs.length) { throw new ConflictException( '批量数据中存在重复的券商代码和地区组合', ); } return this.brokerRepository.save(brokers); } /** * 查询 broker(支持多种查询条件和分页) */ async findAll(queryDto: QueryBrokerDto): Promise<{ list: Broker[]; pagination: PaginationInfo; }> { const where: FindOptionsWhere = {}; if (queryDto.brokerId) { where.brokerId = queryDto.brokerId; } if (queryDto.brokerCode) { where.brokerCode = queryDto.brokerCode; } if (queryDto.brokerName) { where.brokerName = queryDto.brokerName; } if (queryDto.region) { where.region = queryDto.region; } if (queryDto.isActive !== undefined) { where.isActive = queryDto.isActive; } // 分页参数 const page = queryDto.page || 1; const limit = queryDto.limit || 10; const skip = (page - 1) * limit; // 排序字段映射 const sortBy = queryDto.sortBy || 'createdAt'; const sortOrder = queryDto.sortOrder || 'DESC'; // 构建排序对象 const order: Record = {}; if (sortBy === 'createdAt') { order.createdAt = sortOrder; } else if (sortBy === 'sortOrder') { order.sortOrder = sortOrder; } else { order.createdAt = 'DESC'; } // 添加默认排序 if (sortBy !== 'sortOrder') { order.sortOrder = 'ASC'; } order.brokerId = 'ASC'; // 查询总数 const total = await this.brokerRepository.count({ where }); // 查询分页数据 const list = await this.brokerRepository.find({ where, order, skip, take: limit, }); // 计算总页数 const total_page = Math.ceil(total / limit); return { list, pagination: { total, total_page, page_size: limit, current_page: page, }, }; } /** * 根据 ID 查询单个 broker */ async findOne(id: number): Promise { const broker = await this.brokerRepository.findOne({ where: { brokerId: id }, }); if (!broker) { throw new NotFoundException(`未找到ID为 ${id} 的券商`); } return broker; } /** * 根据条件查询单个 broker(返回第一个匹配的) */ async findOneByCondition(queryDto: QueryBrokerDto): Promise { const where: FindOptionsWhere = {}; if (queryDto.brokerId) { where.brokerId = queryDto.brokerId; } if (queryDto.brokerCode) { where.brokerCode = queryDto.brokerCode; } if (queryDto.brokerName) { where.brokerName = queryDto.brokerName; } if (queryDto.region) { where.region = queryDto.region; } if (queryDto.isActive !== undefined) { where.isActive = queryDto.isActive; } const broker = await this.brokerRepository.findOne({ where }); if (!broker) { throw new NotFoundException('未找到符合给定条件的券商'); } return broker; } /** * 更新 broker */ async update( id: number, updateBrokerDto: UpdateBrokerDto, ): Promise { const broker = await this.findOne(id); // 如果更新 broker_code 或 region,检查是否冲突 if ('brokerCode' in updateBrokerDto || 'region' in updateBrokerDto) { const newCode = updateBrokerDto.brokerCode ?? broker.brokerCode; const newRegion = updateBrokerDto.region ?? broker.region; const existing = await this.brokerRepository.findOne({ where: { brokerCode: newCode, region: newRegion, }, }); if (existing && existing.brokerId !== id) { throw new ConflictException( `地区 "${newRegion}" 中已存在代码为 "${newCode}" 的券商`, ); } } // 如果更新 broker_name 或 region,检查是否冲突 if ('brokerName' in updateBrokerDto || 'region' in updateBrokerDto) { const newName = updateBrokerDto.brokerName ?? broker.brokerName; const newRegion = updateBrokerDto.region ?? broker.region; const existing = await this.brokerRepository.findOne({ where: { brokerName: newName, region: newRegion, }, }); if (existing && existing.brokerId !== id) { throw new ConflictException( `地区 "${newRegion}" 中已存在名称为 "${newName}" 的券商`, ); } } Object.assign(broker, updateBrokerDto); return this.brokerRepository.save(broker); } /** * 删除 broker * 同时删除券商Logo图片 */ async remove(id: number): Promise { const broker = await this.findOne(id); // 删除券商Logo图片 if (broker.brokerImage) { try { const imagePath = this.storageService.extractStoragePath( broker.brokerImage, ); if (imagePath) { await this.storageService.delete(imagePath); this.logger.log(`已删除券商Logo: ${imagePath}`); } } catch (error) { // 图片删除失败不影响券商删除操作 this.logger.warn( `删除券商Logo失败: ${broker.brokerImage}`, error, ); } } await this.brokerRepository.remove(broker); } }