API/src/service/site.service.ts

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;
}
}