import { createDir, error, firstUpperCase, readFile, rootDir, toCamel } from "../utils";
|
import { join } from "path";
|
import axios from "axios";
|
import { isEmpty, last, values } from "lodash";
|
import { createWriteStream } from "fs";
|
import prettier from "prettier";
|
import { config } from "../config";
|
import type { Eps } from "../../types";
|
|
const service = {};
|
let list: Eps.Entity[] = [];
|
|
// 获取请求地址
|
function getEpsUrl() {
|
let url = config.eps.api;
|
|
if (!url) {
|
url = config.type;
|
}
|
|
switch (url) {
|
case "app":
|
url = "/app/base/comm/eps";
|
break;
|
|
case "admin":
|
url = "/admin/base/open/eps";
|
break;
|
}
|
|
return url;
|
}
|
|
// 获取路径
|
function getEpsPath(filename?: string) {
|
return join(
|
config.type == "admin" ? config.eps.dist : rootDir(config.eps.dist),
|
filename || "",
|
);
|
}
|
|
// 获取方法名
|
function getNames(v: any) {
|
return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
|
}
|
|
// 找字段
|
function findColumns(sources: string[], item: Eps.Entity) {
|
const columns = [item.columns, item.pageColumns].flat().filter(Boolean);
|
return (sources || [])
|
.map((e) => columns.find((c) => c.source == e))
|
.filter(Boolean) as Eps.Column[];
|
}
|
|
// 格式化代码
|
async function formatCode(text: string) {
|
return prettier.format(text, {
|
parser: "typescript",
|
useTabs: true,
|
tabWidth: 4,
|
endOfLine: "lf",
|
semi: true,
|
singleQuote: false,
|
printWidth: 100,
|
trailingComma: "none",
|
});
|
}
|
|
// 获取数据
|
async function getData() {
|
// 读取本地数据
|
list = readFile(getEpsPath("eps.json"), true) || [];
|
|
// 请求地址
|
const url = config.reqUrl + getEpsUrl();
|
|
// 请求数据
|
await axios
|
.get(url, {
|
timeout: 5000,
|
})
|
.then((res) => {
|
const { code, data, message } = res.data;
|
|
if (code === 1000) {
|
if (!isEmpty(data) && data) {
|
list = values(data).flat();
|
}
|
} else {
|
error(`[cool-eps] ${message || "Failed to fetch data"}`);
|
}
|
})
|
.catch(() => {
|
error(`[cool-eps] API service is not running → ${url}`);
|
});
|
|
// 初始化处理
|
list.forEach((e) => {
|
if (!e.namespace) {
|
e.namespace = "";
|
}
|
if (!e.api) {
|
e.api = [];
|
}
|
if (!e.columns) {
|
e.columns = [];
|
}
|
|
if (!e.search) {
|
e.search = {
|
fieldEq: findColumns(e.pageQueryOp?.fieldEq, e),
|
fieldLike: findColumns(e.pageQueryOp?.fieldLike, e),
|
keyWordLikeFields: findColumns(e.pageQueryOp?.keyWordLikeFields, e),
|
};
|
}
|
});
|
}
|
|
// 创建 json 文件
|
function createJson() {
|
const arr = list.map((e) => {
|
return {
|
prefix: e.prefix,
|
name: e.name || "",
|
api: e.api.map((e) => {
|
return {
|
name: e.name,
|
method: e.method,
|
path: e.path,
|
};
|
}),
|
search: e.search,
|
};
|
});
|
|
const content = JSON.stringify(arr);
|
const local_content = readFile(getEpsPath("eps.json"));
|
|
// 是否需要更新
|
const isUpdate = content != local_content;
|
|
if (isUpdate) {
|
createWriteStream(getEpsPath("eps.json"), {
|
flags: "w",
|
}).write(content);
|
}
|
|
return isUpdate;
|
}
|
|
// 创建描述文件
|
async function createDescribe({ list, service }: { list: Eps.Entity[]; service: any }) {
|
// 获取类型
|
function getType({ propertyName, type }: any) {
|
for (const map of config.eps.mapping) {
|
if (map.custom) {
|
const resType = map.custom({ propertyName, type });
|
if (resType) return resType;
|
}
|
if (map.test) {
|
if (map.test.includes(type)) return map.type;
|
}
|
}
|
return type;
|
}
|
|
// 格式化方法名
|
function formatName(name: string) {
|
return (name || "").replace(/[:,\s,\/,-]/g, "");
|
}
|
|
// 创建 Entity
|
function createEntity() {
|
const ignore: string[] = [];
|
|
let t0 = "";
|
|
for (const item of list) {
|
if (!item.name) continue;
|
|
let t = `interface ${formatName(item.name)} {`;
|
|
// 合并多个列
|
const columns: Eps.Column[] = [];
|
|
[item.columns, item.pageColumns]
|
.flat()
|
.filter(Boolean)
|
.forEach((e) => {
|
const d = columns.find((c) => c.source == e.source);
|
|
if (!d) {
|
columns.push(e);
|
}
|
});
|
|
for (const col of columns || []) {
|
t += `
|
/**
|
* ${col.comment}
|
*/
|
${col.propertyName}?: ${getType({
|
propertyName: col.propertyName,
|
type: col.type,
|
})}
|
`;
|
}
|
|
t += `
|
/**
|
* 任意键值
|
*/
|
[key: string]: any;
|
}
|
`;
|
|
if (!ignore.includes(item.name)) {
|
ignore.push(item.name);
|
t0 += t + "\n\n";
|
}
|
}
|
|
return t0;
|
}
|
|
// 创建 Service
|
async function createDts() {
|
let controller = "";
|
let chain = "";
|
|
// 处理数据
|
function deep(d: any, k?: string) {
|
if (!k) k = "";
|
|
for (const i in d) {
|
const name = k + toCamel(firstUpperCase(formatName(i)));
|
|
if (d[i].namespace) {
|
// 查找配置
|
const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
|
|
if (item) {
|
let t = `interface ${name} {`;
|
|
// 插入方法
|
if (item.api) {
|
// 权限列表
|
const permission: string[] = [];
|
|
item.api.forEach((a) => {
|
// 方法名
|
const n = toCamel(
|
formatName(a.name || last(a.path.split("/")) || ""),
|
);
|
|
if (n) {
|
// 参数类型
|
let q: string[] = [];
|
|
// 参数列表
|
const { parameters = [] } = a.dts || {};
|
|
parameters.forEach((p) => {
|
if (p.description) {
|
q.push(`\n/** ${p.description} */\n`);
|
}
|
|
if (p.name.includes(":")) {
|
return false;
|
}
|
|
const a = `${p.name}${p.required ? "" : "?"}`;
|
const b = `${p.schema.type || "string"}`;
|
|
q.push(`${a}: ${b},`);
|
});
|
|
if (isEmpty(q)) {
|
q = ["any"];
|
} else {
|
q.unshift("{");
|
q.push("}");
|
}
|
|
// 返回类型
|
let res = "";
|
|
// 实体名
|
const en = item.name || "any";
|
|
switch (a.path) {
|
case "/page":
|
res = `
|
{
|
pagination: { size: number; page: number; total: number; [key: string]: any };
|
list: ${en} [];
|
[key: string]: any;
|
}
|
`;
|
break;
|
|
case "/list":
|
res = `${en} []`;
|
break;
|
|
case "/info":
|
res = en;
|
break;
|
|
default:
|
res = "any";
|
break;
|
}
|
|
// 描述
|
t += `
|
/**
|
* ${a.summary || n}
|
*/
|
${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<${res}>;
|
`;
|
|
if (!permission.includes(n)) {
|
permission.push(n);
|
}
|
}
|
});
|
|
// 权限标识
|
t += `
|
/**
|
* 权限标识
|
*/
|
permission: { ${permission.map((e) => `${e}: string;`).join("\n")} };
|
`;
|
|
// 权限状态
|
t += `
|
/**
|
* 权限状态
|
*/
|
_permission: { ${permission.map((e) => `${e}: boolean;`).join("\n")} };
|
`;
|
|
t += `
|
request: Service['request']
|
`;
|
}
|
|
t += "}\n\n";
|
|
controller += t;
|
chain += `${formatName(i)}: ${name};`;
|
}
|
} else {
|
chain += `${formatName(i)}: {`;
|
deep(d[i], name);
|
chain += "},";
|
}
|
}
|
}
|
|
// 遍历
|
deep(service);
|
|
return `
|
type json = any;
|
|
${controller}
|
|
type Service = {
|
/**
|
* 基础请求
|
*/
|
request(options?: {
|
url: string;
|
method?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
|
data?: any;
|
params?: any;
|
headers?: {
|
authorization?: string;
|
[key: string]: any;
|
},
|
timeout?: number;
|
proxy?: boolean;
|
[key: string]: any;
|
}): Promise<any>;
|
|
${chain}
|
}
|
|
${await createDict()}
|
`;
|
}
|
|
// 文件内容
|
const text = `
|
declare namespace Eps {
|
${createEntity()}
|
${await createDts()}
|
}
|
`;
|
|
// 文本内容
|
const content = await formatCode(text);
|
|
const local_content = readFile(getEpsPath("eps.d.ts"));
|
|
// 是否需要更新
|
if (content != local_content) {
|
// 创建 eps 描述文件
|
createWriteStream(getEpsPath("eps.d.ts"), {
|
flags: "w",
|
}).write(content);
|
}
|
}
|
|
// 创建 service
|
function createService() {
|
// 路径第一层作为 id 标识
|
const id = getEpsUrl().split("/")[1];
|
|
list.forEach((e) => {
|
// 请求地址
|
const path = e.prefix[0] == "/" ? e.prefix.substring(1, e.prefix.length) : e.prefix;
|
|
// 分隔路径
|
const arr = path.replace(id, "").split("/").filter(Boolean).map(toCamel);
|
|
// 遍历
|
function deep(d: any, i: number) {
|
const k = arr[i];
|
|
if (k) {
|
// 是否最后一个
|
if (arr[i + 1]) {
|
if (!d[k]) {
|
d[k] = {};
|
}
|
|
deep(d[k], i + 1);
|
} else {
|
// 不存在则创建
|
if (!d[k]) {
|
d[k] = {
|
permission: {},
|
};
|
}
|
|
if (!d[k].namespace) {
|
d[k].namespace = path;
|
}
|
|
// 创建权限
|
if (d[k].namespace) {
|
getNames(d[k]).forEach((i) => {
|
d[k].permission[i] =
|
`${d[k].namespace.replace(`${id}/`, "")}/${i}`.replace(/\//g, ":");
|
});
|
}
|
|
// 创建搜索
|
d[k].search = e.search;
|
|
// 创建方法
|
e.api.forEach((a) => {
|
// 方法名
|
const n = a.path.replace("/", "");
|
|
if (n && !/[-:]/g.test(n)) {
|
d[k][n] = a;
|
}
|
});
|
}
|
}
|
}
|
|
deep(service, 0);
|
});
|
}
|
|
// 创建 dict
|
async function createDict() {
|
const url = config.reqUrl + "/" + config.type + "/dict/info/types";
|
|
return axios
|
.get(url)
|
.then((res) => {
|
const { code, data } = res.data as { code: number; data: any[] };
|
|
if (code === 1000) {
|
let v = "string";
|
|
if (!isEmpty(data)) {
|
v = data.map((e) => `"${e.key}"`).join(" | ");
|
}
|
|
return `type DictKey = ${v}`;
|
}
|
})
|
.catch(() => {
|
error(`[cool-eps] Error:${url}`);
|
});
|
}
|
|
// 创建 eps
|
export async function createEps() {
|
if (config.eps.enable) {
|
// 获取数据
|
await getData();
|
|
// 创建 service
|
createService();
|
|
// 创建目录
|
createDir(getEpsPath(), true);
|
|
// 创建 json 文件
|
const isUpdate = createJson();
|
|
// 创建描述文件
|
createDescribe({ service, list });
|
|
return {
|
service,
|
list,
|
isUpdate,
|
};
|
} else {
|
return {
|
service: {},
|
list: [],
|
};
|
}
|
}
|