API/src/service/site.service.ts

181 lines
5.5 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 { WpSite } from '../interface';
import { CreateSiteDTO, UpdateSiteDTO } from '../dto/site.dto';
import { Area } from '../entity/area.entity';
@Provide()
@Scope(ScopeEnum.Singleton)
export class SiteService {
@InjectEntityModel(Site)
siteModel: Repository<Site>;
@InjectEntityModel(Area)
areaModel: Repository<Area>;
async syncFromConfig(sites: WpSite[] = []) {
// 将配置中的 WpSite 同步到数据库 Site 表(用于一次性导入或初始化)
for (const siteConfig of sites) {
// 按站点名称查询是否已存在记录
const exist = await this.siteModel.findOne({
where: { name: siteConfig.name },
});
// 将 WpSite 字段映射为 Site 实体字段
const payload: Partial<Site> = {
name: siteConfig.name,
apiUrl: (siteConfig as any).wpApiUrl,
consumerKey: (siteConfig as any).consumerKey,
consumerSecret: (siteConfig as any).consumerSecret,
type: 'woocommerce',
};
// 存在则更新,不存在则插入新记录
if (exist) {
await this.siteModel.update({ id: exist.id }, payload);
} else {
await this.siteModel.insert(payload as Site);
}
}
}
async create(data: CreateSiteDTO) {
// 从 DTO 中分离出区域代码和其他站点数据
const { areas: areaCodes, ...restData } = data;
const newSite = new Site();
Object.assign(newSite, restData);
// 如果传入了区域代码,则查询并关联 Area 实体
if (areaCodes && areaCodes.length > 0) {
const areas = await this.areaModel.findBy({
code: In(areaCodes),
});
newSite.areas = areas;
} else {
// 如果没有传入区域,则关联一个空数组,代表"全局"
newSite.areas = [];
}
// 使用 save 方法保存实体及其关联关系
await this.siteModel.save(newSite);
return true;
}
async update(id: string | number, data: UpdateSiteDTO) {
// 从 DTO 中分离出区域代码和其他站点数据
const { areas: areaCodes, ...restData } = data;
// 首先,根据 ID 查找要更新的站点实体
const siteToUpdate = await this.siteModel.findOne({
where: { id: Number(id) },
});
if (!siteToUpdate) {
// 如果找不到站点,则操作失败
return false;
}
// 更新站点的基本字段
const payload: Partial<Site> = {
...restData,
isDisabled:
data.isDisabled === undefined
? undefined
: data.isDisabled
? 1
: 0,
} as any;
Object.assign(siteToUpdate, payload);
// 如果 DTO 中传入了 areas 字段(即使是空数组),也要更新关联关系
if (areaCodes !== undefined) {
if (areaCodes.length > 0) {
// 如果区域代码数组不为空,则查找并更新关联
const areas = await this.areaModel.findBy({
code: In(areaCodes),
});
siteToUpdate.areas = areas;
} else {
// 如果传入空数组,则清空所有关联,代表"全局"
siteToUpdate.areas = [];
}
}
// 使用 save 方法保存实体及其更新后的关联关系
await this.siteModel.save(siteToUpdate);
return true;
}
async get(id: string | number, includeSecret = false) {
// 根据主键获取站点,并使用 relations 加载关联的 areas
const site = await this.siteModel.findOne({
where: { id: Number(id) },
relations: ['areas'],
});
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'],
});
// 根据 includeSecret 决定是否脱敏返回密钥字段
const data = includeSecret
? items
: items.map((item: any) => {
const { consumerKey, consumerSecret, ...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;
}
}