import { Provide, Scope, ScopeEnum } from '@midwayjs/core'; import { InjectEntityModel } from '@midwayjs/typeorm'; import { Repository, Like, In } from 'typeorm'; import { Site } from '../entity/site.entity'; import { CreateSiteDTO, UpdateSiteDTO } from '../dto/site.dto'; import { Area } from '../entity/area.entity'; import { StockPoint } from '../entity/stock_point.entity'; import * as countries from 'i18n-iso-countries'; @Provide() @Scope(ScopeEnum.Singleton) export class SiteService { @InjectEntityModel(Site) siteModel: Repository; @InjectEntityModel(Area) areaModel: Repository; @InjectEntityModel(StockPoint) stockPointModel: Repository; async create(data: CreateSiteDTO) { // 从 DTO 中分离出区域代码和其他站点数据 const { areas: areaCodes, stockPointIds, ...restData } = data; const newSite = new Site(); Object.assign(newSite, restData); // 如果传入了区域代码,则查询或创建 Area 实体 if (areaCodes && areaCodes.length > 0) { // 先查询数据库中已存在的 Area 实体 const existingAreas = await this.areaModel.findBy({ code: In(areaCodes), }); const existingCodes = new Set(existingAreas.map(area => area.code)); // 为不存在的 Area 创建新实体 const newAreas = areaCodes .filter(code => !existingCodes.has(code)) .map(areaCode => { const area = new Area(); area.code = areaCode; area.name = countries.getName(areaCode, 'zh') || areaCode; // 使用 countries 获取中文名称,如果获取不到则使用 code return area; }); // 合并已存在和新创建的 Area 实体 newSite.areas = [...existingAreas, ...newAreas]; } else { // 如果没有传入区域,则关联一个空数组,代表"全局" newSite.areas = []; } // 如果传入了仓库点 ID,则查询并关联 StockPoint 实体 if (stockPointIds && stockPointIds.length > 0) { const stockPoints = await this.stockPointModel.findBy({ id: In(stockPointIds.map(Number)), }); newSite.stockPoints = stockPoints; } else { newSite.stockPoints = []; } // 使用 save 方法保存实体及其关联关系 const result = await this.siteModel.save(newSite); console.log('result create siteId',result) return true; } async update(id: string | number, data: UpdateSiteDTO) { const { areas: areaCodes, stockPointIds, isDisabled, ...restData } = data; // 首先,根据 ID 查找要更新的站点实体 const siteToUpdate = await this.siteModel.findOne({ where: { id: Number(id) }, relations: ['areas', 'stockPoints'], }); if (!siteToUpdate) { // 如果找不到站点,则操作失败 return false; } // 更新站点的基本字段 Object.assign(siteToUpdate, restData); if (isDisabled !== undefined) { siteToUpdate.isDisabled = isDisabled; } // 如果 DTO 中传入了 areas 字段(即使是空数组),也要更新关联关系 if (areaCodes !== undefined) { if (areaCodes.length > 0) { // 先查询数据库中已存在的 Area 实体 const existingAreas = await this.areaModel.findBy({ code: In(areaCodes), }); const existingCodes = new Set(existingAreas.map(area => area.code)); // 为不存在的 Area 创建新实体 const newAreas = areaCodes .filter(code => !existingCodes.has(code)) .map(areaCode => { const area = new Area(); area.code = areaCode; // name 使用 i18n-contries 获取 area.name = countries.getName(areaCode, 'zh') || areaCode; // 使用 code 作为 name 的默认值 return area; }); // 合并已存在和新创建的 Area 实体 siteToUpdate.areas = [...existingAreas, ...newAreas]; } else { // 如果传入空数组,则清空所有关联,代表"全局" siteToUpdate.areas = []; } } // 如果 DTO 中传入了 stockPointIds 字段(即使是空数组),也要更新关联关系 if (stockPointIds !== undefined) { if (stockPointIds.length > 0) { const stockPoints = await this.stockPointModel.findBy({ id: In(stockPointIds.map(Number)), }); siteToUpdate.stockPoints = stockPoints; } else { siteToUpdate.stockPoints = []; } } // 使用 save 方法保存实体及其更新后的关联关系 await this.siteModel.save(siteToUpdate); return true; } async get(id: string | number, includeSecret = false):Promise { // 根据主键获取站点,并使用 relations 加载关联的 areas const site = await this.siteModel.findOne({ where: { id: Number(id) }, relations: ['areas', 'stockPoints'], }); if (!site) { return null; } // 如果需要包含密钥,则直接返回 if (includeSecret) { return site; } // 默认不返回密钥,进行字段脱敏 const { consumerKey, consumerSecret, ...rest } = site; return rest; } async list( param: { current?: number; pageSize?: number; keyword?: string; isDisabled?: boolean; ids?: string; }, includeSecret = false ) { // 分页查询站点列表,支持关键字,禁用状态与 ID 列表过滤 const { current = 1, pageSize = 10, keyword, isDisabled, ids } = (param || {}) as any; const where: any = {}; // 按名称模糊查询 if (keyword) { where.name = Like(`%${keyword}%`); } // 按禁用状态过滤(布尔转数值) if (typeof isDisabled === 'boolean') { where.isDisabled = isDisabled ? 1 : 0; } if (ids) { // 解析逗号分隔的 ID 字符串为数字数组,并过滤非法值 const numIds = String(ids) .split(',') .filter(Boolean) .map(i => Number(i)) .filter(v => !Number.isNaN(v)); if (numIds.length > 0) { where.id = In(numIds); } } // 进行分页查询,并使用 relations 加载关联的 areas const [items, total] = await this.siteModel.findAndCount({ where, skip: (current - 1) * pageSize, take: pageSize, relations: ['areas', 'stockPoints'], }); // 根据 includeSecret 决定是否脱敏返回密钥字段 const data = includeSecret ? items : items.map((item: any) => { const { consumerKey, consumerSecret, token, ...rest } = item; return rest; }); return { items: data, total, current, pageSize }; } async disable(id: string | number, disabled: boolean) { // 设置站点禁用状态(true -> 1, false -> 0) await this.siteModel.update({ id: Number(id) }, { isDisabled: disabled }); return true; } }