index.ts 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. import { createDir, error, firstUpperCase, readFile, toCamel } from "../utils";
  2. import { join } from "path";
  3. import { Entity, DistPath } from "./config";
  4. import axios from "axios";
  5. import { isArray, isEmpty, last } from "lodash";
  6. import { createWriteStream } from "fs";
  7. import prettier from "prettier";
  8. import { proxy } from "../../../src/config/proxy";
  9. // 实体类型
  10. type Entity = {
  11. api: {
  12. dts: {
  13. parameters?: {
  14. description: string;
  15. name: string;
  16. required: boolean;
  17. schema: {
  18. type: string;
  19. };
  20. }[];
  21. };
  22. name: string;
  23. method: string;
  24. path: string;
  25. prefix: string;
  26. summary: string;
  27. tag: string;
  28. }[];
  29. columns: {
  30. comment: string;
  31. length: string;
  32. nullable: boolean;
  33. propertyName: string;
  34. type: string;
  35. }[];
  36. module: string;
  37. name: string;
  38. prefix: string;
  39. };
  40. // 获取方法名
  41. function getNames(v: any) {
  42. return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
  43. }
  44. // 获取数据
  45. async function getData(temps: any[]) {
  46. let list: Entity[] = [];
  47. // 本地文件
  48. try {
  49. list = JSON.parse(readFile(join(DistPath, "eps.json")) || "[]");
  50. } catch (err) {
  51. error(`[eps] ${join(DistPath, "eps.json")} 文件异常, ${err.message}`);
  52. }
  53. // 远程地址
  54. const url = proxy["/dev/"].target + "/admin/base/open/eps";
  55. // 请求数据
  56. await axios
  57. .get(url, {
  58. timeout: 5000
  59. })
  60. .then((res) => {
  61. const { code, data, message } = res.data;
  62. if (code === 1000) {
  63. if (!isEmpty(data) && data) {
  64. // @ts-ignore
  65. list = Object.values(data).flat();
  66. }
  67. } else {
  68. error(`[eps] ${message}`);
  69. }
  70. })
  71. .catch(() => {
  72. error(`[eps] 获取失败, ${url} 无法访问!`);
  73. });
  74. // 判断是否重复项
  75. if (isArray(temps)) {
  76. temps.forEach((e) => {
  77. const d = list.find((a) => e.prefix === a.prefix);
  78. if (d) {
  79. Object.assign(d, e);
  80. } else {
  81. list.push(e);
  82. }
  83. });
  84. }
  85. return list;
  86. }
  87. // 创建数据文件
  88. function createJson(eps: Entity[]) {
  89. createWriteStream(join(DistPath, "eps.json"), {
  90. flags: "w"
  91. }).write(
  92. JSON.stringify(
  93. eps.map((e) => {
  94. return {
  95. prefix: e.prefix,
  96. name: e.name || "",
  97. api: e.api.map((e) => {
  98. return {
  99. name: e.name,
  100. method: e.method,
  101. path: e.path
  102. };
  103. })
  104. };
  105. })
  106. )
  107. );
  108. }
  109. // 创建描述文件
  110. async function createDescribe({ list, service }: { list: Entity[]; service: any }) {
  111. // 获取类型
  112. function getType({ propertyName, type }) {
  113. for (const map of Entity.mapping) {
  114. if (map.custom) {
  115. const resType = map.custom({ propertyName, type });
  116. if (resType) return resType;
  117. }
  118. if (map.test) {
  119. if (map.test.includes(type)) return map.type;
  120. }
  121. }
  122. return type;
  123. }
  124. // 创建 Entity
  125. function createEntity() {
  126. const t0: string[][] = [];
  127. for (const item of list) {
  128. if (!item.name) continue;
  129. const t = [`interface ${item.name} {`];
  130. for (const col of item.columns || []) {
  131. // 描述
  132. t.push("\n");
  133. t.push("/**\n");
  134. t.push(` * ${col.comment}\n`);
  135. t.push(" */\n");
  136. t.push(
  137. `${col.propertyName}?: ${getType({
  138. propertyName: col.propertyName,
  139. type: col.type
  140. })};`
  141. );
  142. }
  143. t.push("\n");
  144. t.push("/**\n");
  145. t.push(` * 任意键值\n`);
  146. t.push(" */\n");
  147. t.push(`[key: string]: any;`);
  148. t.push("}");
  149. t0.push(t);
  150. }
  151. return t0.map((e) => e.join("")).join("\n\n");
  152. }
  153. // 创建 Service
  154. function createDts() {
  155. const t0: string[][] = [];
  156. const t1 = [
  157. `
  158. type json = any;
  159. type Service = {
  160. request(options?: {
  161. url: string;
  162. method?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
  163. data?: any;
  164. params?: any;
  165. headers?: {
  166. [key: string]: any;
  167. },
  168. timeout?: number;
  169. proxy?: boolean;
  170. [key: string]: any;
  171. }): Promise<any>;
  172. `
  173. ];
  174. // 处理数据
  175. function deep(d: any, k?: string) {
  176. if (!k) k = "";
  177. for (const i in d) {
  178. const name = k + toCamel(firstUpperCase(i.replace(/[:]/g, "")));
  179. if (d[i].namespace) {
  180. // 查找配置
  181. const item = list.find((e) => (e.prefix || "").includes(d[i].namespace));
  182. if (item) {
  183. const t = [`interface ${name} {`];
  184. t1.push(`${i}: ${name};`);
  185. // 插入方法
  186. if (item.api) {
  187. // 权限列表
  188. const permission: string[] = [];
  189. item.api.forEach((a) => {
  190. // 方法名
  191. const n = toCamel(a.name || last(a.path.split("/")) || "").replace(
  192. /[:\/-]/g,
  193. ""
  194. );
  195. if (n) {
  196. // 参数类型
  197. let q: string[] = [];
  198. // 参数列表
  199. const { parameters = [] } = a.dts || {};
  200. parameters.forEach((p) => {
  201. if (p.description) {
  202. q.push(`\n/** ${p.description} */\n`);
  203. }
  204. if (p.name.includes(":")) {
  205. return false;
  206. }
  207. const a = `${p.name}${p.required ? "" : "?"}`;
  208. const b = `${p.schema.type || "string"}`;
  209. q.push(`${a}: ${b},`);
  210. });
  211. if (isEmpty(q)) {
  212. q = ["any"];
  213. } else {
  214. q.unshift("{");
  215. q.push("}");
  216. }
  217. // 返回类型
  218. let res = "";
  219. // 实体名
  220. const en = item.name || "any";
  221. switch (a.path) {
  222. case "/page":
  223. res = `
  224. {
  225. pagination: { size: number; page: number; total: number; [key: string]: any };
  226. list: ${en} [];
  227. [key: string]: any;
  228. }
  229. `;
  230. break;
  231. case "/list":
  232. res = `${en} []`;
  233. break;
  234. case "/info":
  235. res = en;
  236. break;
  237. default:
  238. res = "any";
  239. break;
  240. }
  241. // 描述
  242. t.push("\n");
  243. t.push("/**\n");
  244. t.push(` * ${a.summary || n}\n`);
  245. t.push(" */\n");
  246. t.push(
  247. `${n}(data${q.length == 1 ? "?" : ""}: ${q.join(
  248. ""
  249. )}): Promise<${res}>;`
  250. );
  251. }
  252. permission.push(n);
  253. });
  254. // 权限标识
  255. t.push("\n");
  256. t.push("/**\n");
  257. t.push(" * 权限标识\n");
  258. t.push(" */\n");
  259. t.push(
  260. `permission: { ${permission
  261. .map((e) => `${e}: string;`)
  262. .join("\n")} };`
  263. );
  264. // 权限状态
  265. t.push("\n");
  266. t.push("/**\n");
  267. t.push(" * 权限状态\n");
  268. t.push(" */\n");
  269. t.push(
  270. `_permission: { ${permission
  271. .map((e) => `${e}: boolean;`)
  272. .join("\n")} };`
  273. );
  274. // 请求
  275. t.push("\n");
  276. t.push("/**\n");
  277. t.push(" * 请求\n");
  278. t.push(" */\n");
  279. t.push(`request: Service['request']`);
  280. }
  281. t.push("}");
  282. t0.push(t);
  283. }
  284. } else {
  285. t1.push(`${i}: {`);
  286. deep(d[i], name);
  287. t1.push(`},`);
  288. }
  289. }
  290. }
  291. // 深度
  292. deep(service);
  293. // 结束
  294. t1.push("}");
  295. // 追加
  296. t0.push(t1);
  297. return t0.map((e) => e.join("")).join("\n\n");
  298. }
  299. // 文件内容
  300. const text = `
  301. declare namespace Eps {
  302. ${createEntity()}
  303. ${createDts()}
  304. }
  305. `;
  306. // 文本内容
  307. const content = await prettier.format(text, {
  308. parser: "typescript",
  309. useTabs: true,
  310. tabWidth: 4,
  311. endOfLine: "lf",
  312. semi: true,
  313. singleQuote: false,
  314. printWidth: 100,
  315. trailingComma: "none"
  316. });
  317. // 创建 eps 描述文件
  318. createWriteStream(join(DistPath, "eps.d.ts"), {
  319. flags: "w"
  320. }).write(content);
  321. }
  322. // 创建服务
  323. function createService(data: Entity[]) {
  324. const list: Entity[] = [];
  325. const service = {};
  326. const d = { data };
  327. for (const i in d) {
  328. if (isArray(d[i])) {
  329. d[i].forEach((e: Entity) => {
  330. // 分隔路径
  331. const arr = e.prefix
  332. .replace(/\//, "")
  333. .replace("admin", "")
  334. .split("/")
  335. .filter(Boolean)
  336. .map(toCamel);
  337. // 遍历
  338. function deep(d: any, i: number) {
  339. const k = arr[i];
  340. if (k) {
  341. // 是否最后一个
  342. if (arr[i + 1]) {
  343. if (!d[k]) {
  344. d[k] = {};
  345. }
  346. deep(d[k], i + 1);
  347. } else {
  348. // 本地不存在则创建实例
  349. if (!d[k]) {
  350. d[k] = {
  351. namespace: e.prefix.substring(1, e.prefix.length),
  352. permission: {}
  353. };
  354. }
  355. // 创建方法
  356. e.api.forEach((a) => {
  357. // 方法名
  358. const n = a.path.replace("/", "");
  359. if (n && !/[-:]/g.test(n)) {
  360. d[k][n] = a;
  361. }
  362. });
  363. // 创建权限
  364. getNames(d[k]).forEach((e) => {
  365. d[k].permission[e] = `${d[k].namespace.replace(
  366. "admin/",
  367. ""
  368. )}/${e}`.replace(/\//g, ":");
  369. });
  370. list.push(e);
  371. }
  372. }
  373. }
  374. deep(service, 0);
  375. });
  376. }
  377. }
  378. return { service, list };
  379. }
  380. // 创建 eps
  381. export async function createEps(query?: { list: any[] }) {
  382. // 获取数据
  383. const data = await getData(query?.list || []);
  384. // 生成数据
  385. const { service, list } = createService(data);
  386. // 创建临时目录
  387. createDir(DistPath);
  388. // 创建数据文件
  389. createJson(data);
  390. // 创建描述文件
  391. createDescribe({ service, list });
  392. return `
  393. export const eps = ${JSON.stringify({ service, list })}
  394. `;
  395. }