207 lines
6.8 KiB
TypeScript
207 lines
6.8 KiB
TypeScript
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<Site>;
|
|
|
|
@InjectEntityModel(Area)
|
|
areaModel: Repository<Area>;
|
|
|
|
@InjectEntityModel(StockPoint)
|
|
stockPointModel: Repository<StockPoint>;
|
|
|
|
|
|
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<Site> {
|
|
// 根据主键获取站点,并使用 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;
|
|
}
|
|
} |