181 lines
5.5 KiB
TypeScript
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;
|
|
}
|
|
} |