create-yss-spec 2.1.6 → 2.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/package.json +1 -1
  2. package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  3. package/template/.agents/skills/yss-domain/SKILL.md +2 -2
  4. package/template/.agents/skills/yss-dto/SKILL.md +19 -4
  5. package/template/.agents/skills/yss-dto/agents/openai.yaml +1 -1
  6. package/template/.agents/skills/yss-dto/references/README.md +9 -2
  7. package/template/.agents/skills/yss-dto/references/openapi-wire-profile.yaml +181 -0
  8. package/template/.agents/skills/yss-mybatis/SKILL.md +1 -1
  9. package/template/.agents/skills/yss-openapi-draft-review/SKILL.md +18 -6
  10. package/template/.agents/skills/yss-openapi-governance/SKILL.md +11 -1
  11. package/template/.agents/skills/yss-repository/SKILL.md +2 -2
  12. package/template/.agents/skills/yss-router/references/boundaries.md +0 -3
  13. package/template/.agents/skills/yss-source-index/references/source-map-config.md +0 -2
  14. package/template/.agents/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +1 -1
  15. package/template/.agents/skills/yss-web-controller/SKILL.md +1 -1
  16. package/template/.claude/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  17. package/template/.claude/skills/yss-domain/SKILL.md +2 -2
  18. package/template/.claude/skills/yss-dto/SKILL.md +19 -4
  19. package/template/.claude/skills/yss-dto/agents/openai.yaml +1 -1
  20. package/template/.claude/skills/yss-dto/references/README.md +9 -2
  21. package/template/.claude/skills/yss-dto/references/openapi-wire-profile.yaml +181 -0
  22. package/template/.claude/skills/yss-mybatis/SKILL.md +1 -1
  23. package/template/.claude/skills/yss-openapi-draft-review/SKILL.md +18 -6
  24. package/template/.claude/skills/yss-openapi-governance/SKILL.md +11 -1
  25. package/template/.claude/skills/yss-repository/SKILL.md +2 -2
  26. package/template/.claude/skills/yss-router/references/boundaries.md +0 -3
  27. package/template/.claude/skills/yss-source-index/references/source-map-config.md +0 -2
  28. package/template/.claude/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +1 -1
  29. package/template/.claude/skills/yss-web-controller/SKILL.md +1 -1
  30. package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  31. package/template/.codex/skills/yss-domain/SKILL.md +2 -2
  32. package/template/.codex/skills/yss-dto/SKILL.md +19 -4
  33. package/template/.codex/skills/yss-dto/agents/openai.yaml +1 -1
  34. package/template/.codex/skills/yss-dto/references/README.md +9 -2
  35. package/template/.codex/skills/yss-dto/references/openapi-wire-profile.yaml +181 -0
  36. package/template/.codex/skills/yss-mybatis/SKILL.md +1 -1
  37. package/template/.codex/skills/yss-openapi-draft-review/SKILL.md +18 -6
  38. package/template/.codex/skills/yss-openapi-governance/SKILL.md +11 -1
  39. package/template/.codex/skills/yss-repository/SKILL.md +2 -2
  40. package/template/.codex/skills/yss-router/references/boundaries.md +0 -3
  41. package/template/.codex/skills/yss-source-index/references/source-map-config.md +0 -2
  42. package/template/.codex/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +1 -1
  43. package/template/.codex/skills/yss-web-controller/SKILL.md +1 -1
  44. package/template/.github/workflows/template-node-tooling.yml +2 -0
  45. package/template/.hermes/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  46. package/template/.hermes/skills/yss-domain/SKILL.md +2 -2
  47. package/template/.hermes/skills/yss-dto/SKILL.md +19 -4
  48. package/template/.hermes/skills/yss-dto/agents/openai.yaml +1 -1
  49. package/template/.hermes/skills/yss-dto/references/README.md +9 -2
  50. package/template/.hermes/skills/yss-dto/references/openapi-wire-profile.yaml +181 -0
  51. package/template/.hermes/skills/yss-mybatis/SKILL.md +1 -1
  52. package/template/.hermes/skills/yss-openapi-draft-review/SKILL.md +18 -6
  53. package/template/.hermes/skills/yss-openapi-governance/SKILL.md +11 -1
  54. package/template/.hermes/skills/yss-repository/SKILL.md +2 -2
  55. package/template/.hermes/skills/yss-router/references/boundaries.md +0 -3
  56. package/template/.hermes/skills/yss-source-index/references/source-map-config.md +0 -2
  57. package/template/.hermes/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +1 -1
  58. package/template/.hermes/skills/yss-web-controller/SKILL.md +1 -1
  59. package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  60. package/template/.pi/skills/yss-domain/SKILL.md +2 -2
  61. package/template/.pi/skills/yss-dto/SKILL.md +19 -4
  62. package/template/.pi/skills/yss-dto/agents/openai.yaml +1 -1
  63. package/template/.pi/skills/yss-dto/references/README.md +9 -2
  64. package/template/.pi/skills/yss-dto/references/openapi-wire-profile.yaml +181 -0
  65. package/template/.pi/skills/yss-mybatis/SKILL.md +1 -1
  66. package/template/.pi/skills/yss-openapi-draft-review/SKILL.md +18 -6
  67. package/template/.pi/skills/yss-openapi-governance/SKILL.md +11 -1
  68. package/template/.pi/skills/yss-repository/SKILL.md +2 -2
  69. package/template/.pi/skills/yss-router/references/boundaries.md +0 -3
  70. package/template/.pi/skills/yss-source-index/references/source-map-config.md +0 -2
  71. package/template/.pi/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +1 -1
  72. package/template/.pi/skills/yss-web-controller/SKILL.md +1 -1
  73. package/template/.qoder/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  74. package/template/.qoder/skills/yss-domain/SKILL.md +2 -2
  75. package/template/.qoder/skills/yss-dto/SKILL.md +19 -4
  76. package/template/.qoder/skills/yss-dto/agents/openai.yaml +1 -1
  77. package/template/.qoder/skills/yss-dto/references/README.md +9 -2
  78. package/template/.qoder/skills/yss-dto/references/openapi-wire-profile.yaml +181 -0
  79. package/template/.qoder/skills/yss-mybatis/SKILL.md +1 -1
  80. package/template/.qoder/skills/yss-openapi-draft-review/SKILL.md +18 -6
  81. package/template/.qoder/skills/yss-openapi-governance/SKILL.md +11 -1
  82. package/template/.qoder/skills/yss-repository/SKILL.md +2 -2
  83. package/template/.qoder/skills/yss-router/references/boundaries.md +0 -3
  84. package/template/.qoder/skills/yss-source-index/references/source-map-config.md +0 -2
  85. package/template/.qoder/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +1 -1
  86. package/template/.qoder/skills/yss-web-controller/SKILL.md +1 -1
  87. package/template/.trae/skills/yss-ddd-scaffold-generator/SKILL.md +1 -1
  88. package/template/.trae/skills/yss-domain/SKILL.md +2 -2
  89. package/template/.trae/skills/yss-dto/SKILL.md +19 -4
  90. package/template/.trae/skills/yss-dto/agents/openai.yaml +1 -1
  91. package/template/.trae/skills/yss-dto/references/README.md +9 -2
  92. package/template/.trae/skills/yss-dto/references/openapi-wire-profile.yaml +181 -0
  93. package/template/.trae/skills/yss-mybatis/SKILL.md +1 -1
  94. package/template/.trae/skills/yss-openapi-draft-review/SKILL.md +18 -6
  95. package/template/.trae/skills/yss-openapi-governance/SKILL.md +11 -1
  96. package/template/.trae/skills/yss-repository/SKILL.md +2 -2
  97. package/template/.trae/skills/yss-router/references/boundaries.md +0 -3
  98. package/template/.trae/skills/yss-source-index/references/source-map-config.md +0 -2
  99. package/template/.trae/skills/yss-source-index/scripts/refresh-yss-skill-index.mjs +1 -1
  100. package/template/.trae/skills/yss-web-controller/SKILL.md +1 -1
  101. package/template/CONTEXT.md +4 -0
  102. package/template/docs/api/templates/openapi-draft-review-checklist.md +6 -0
  103. package/template/scripts/lib/scenario-checks.mjs +21 -0
  104. package/template/scripts/lib/skill-supply-chain.mjs +1 -1
  105. package/template/scripts/verify-template +4 -2
  106. package/template/scripts/verify-yss-dto-openapi-profile +109 -0
  107. package/template/scripts/verify-yss-dto-openapi-scenarios +4 -0
  108. package/template/skills-lock.json +10 -55
  109. package/template/wiki/wiki/YSS/345/267/245/347/250/213/346/212/200/350/203/275/344/275/223/347/263/273.md +1 -1
  110. package/template/yss-public-skills.json +1 -7
  111. package/template.manifest.json +3 -1
  112. package/template.snapshot.json +4 -4
  113. package/template/.agents/skills/yss-db2mybatis/SKILL.md +0 -95
  114. package/template/.agents/skills/yss-db2mybatis/agents/openai.yaml +0 -5
  115. package/template/.agents/skills/yss-db2mybatis/assets/$.java +0 -19
  116. package/template/.agents/skills/yss-db2mybatis/assets/convertor.java.template +0 -22
  117. package/template/.agents/skills/yss-db2mybatis/assets/domain_gateway.java.template +0 -45
  118. package/template/.agents/skills/yss-db2mybatis/assets/domain_model.java.template +0 -16
  119. package/template/.agents/skills/yss-db2mybatis/assets/gateway_impl.java.template +0 -62
  120. package/template/.agents/skills/yss-db2mybatis/assets/po.java.template +0 -18
  121. package/template/.agents/skills/yss-db2mybatis/assets/repository.java.template +0 -7
  122. package/template/.agents/skills/yss-db2mybatis/config/metadata.json +0 -334
  123. package/template/.agents/skills/yss-db2mybatis/config/temp_datasource.json +0 -12
  124. package/template/.agents/skills/yss-db2mybatis/references/datasource-config.example.json +0 -46
  125. package/template/.agents/skills/yss-db2mybatis/references/project-convention.example.json +0 -14
  126. package/template/.agents/skills/yss-db2mybatis/references/type-mapping.json +0 -31
  127. package/template/.agents/skills/yss-db2mybatis/scripts/db2mybatis.mjs +0 -145
  128. package/template/.agents/skills/yss-db2mybatis/scripts/db2mybatis.test.mjs +0 -32
  129. package/template/.agents/skills/yss-valuation/SKILL.md +0 -40
  130. package/template/.agents/skills/yss-valuation/agents/openai.yaml +0 -4
  131. package/template/.agents/skills/yss-valuation/references/source-index.md +0 -37
  132. package/template/.agents/skills/yss-variable/SKILL.md +0 -47
  133. package/template/.agents/skills/yss-variable/agents/openai.yaml +0 -4
  134. package/template/.agents/skills/yss-variable/references/source-index.md +0 -32
  135. package/template/.claude/skills/yss-db2mybatis/SKILL.md +0 -95
  136. package/template/.claude/skills/yss-db2mybatis/agents/openai.yaml +0 -5
  137. package/template/.claude/skills/yss-db2mybatis/assets/$.java +0 -19
  138. package/template/.claude/skills/yss-db2mybatis/assets/convertor.java.template +0 -22
  139. package/template/.claude/skills/yss-db2mybatis/assets/domain_gateway.java.template +0 -45
  140. package/template/.claude/skills/yss-db2mybatis/assets/domain_model.java.template +0 -16
  141. package/template/.claude/skills/yss-db2mybatis/assets/gateway_impl.java.template +0 -62
  142. package/template/.claude/skills/yss-db2mybatis/assets/po.java.template +0 -18
  143. package/template/.claude/skills/yss-db2mybatis/assets/repository.java.template +0 -7
  144. package/template/.claude/skills/yss-db2mybatis/config/metadata.json +0 -334
  145. package/template/.claude/skills/yss-db2mybatis/config/temp_datasource.json +0 -12
  146. package/template/.claude/skills/yss-db2mybatis/references/datasource-config.example.json +0 -46
  147. package/template/.claude/skills/yss-db2mybatis/references/project-convention.example.json +0 -14
  148. package/template/.claude/skills/yss-db2mybatis/references/type-mapping.json +0 -31
  149. package/template/.claude/skills/yss-db2mybatis/scripts/db2mybatis.mjs +0 -145
  150. package/template/.claude/skills/yss-db2mybatis/scripts/db2mybatis.test.mjs +0 -32
  151. package/template/.claude/skills/yss-valuation/SKILL.md +0 -40
  152. package/template/.claude/skills/yss-valuation/agents/openai.yaml +0 -4
  153. package/template/.claude/skills/yss-valuation/references/source-index.md +0 -37
  154. package/template/.claude/skills/yss-variable/SKILL.md +0 -47
  155. package/template/.claude/skills/yss-variable/agents/openai.yaml +0 -4
  156. package/template/.claude/skills/yss-variable/references/source-index.md +0 -32
  157. package/template/.codex/skills/yss-db2mybatis/SKILL.md +0 -95
  158. package/template/.codex/skills/yss-db2mybatis/agents/openai.yaml +0 -5
  159. package/template/.codex/skills/yss-db2mybatis/assets/$.java +0 -19
  160. package/template/.codex/skills/yss-db2mybatis/assets/convertor.java.template +0 -22
  161. package/template/.codex/skills/yss-db2mybatis/assets/domain_gateway.java.template +0 -45
  162. package/template/.codex/skills/yss-db2mybatis/assets/domain_model.java.template +0 -16
  163. package/template/.codex/skills/yss-db2mybatis/assets/gateway_impl.java.template +0 -62
  164. package/template/.codex/skills/yss-db2mybatis/assets/po.java.template +0 -18
  165. package/template/.codex/skills/yss-db2mybatis/assets/repository.java.template +0 -7
  166. package/template/.codex/skills/yss-db2mybatis/config/metadata.json +0 -334
  167. package/template/.codex/skills/yss-db2mybatis/config/temp_datasource.json +0 -12
  168. package/template/.codex/skills/yss-db2mybatis/references/datasource-config.example.json +0 -46
  169. package/template/.codex/skills/yss-db2mybatis/references/project-convention.example.json +0 -14
  170. package/template/.codex/skills/yss-db2mybatis/references/type-mapping.json +0 -31
  171. package/template/.codex/skills/yss-db2mybatis/scripts/db2mybatis.mjs +0 -145
  172. package/template/.codex/skills/yss-db2mybatis/scripts/db2mybatis.test.mjs +0 -32
  173. package/template/.codex/skills/yss-valuation/SKILL.md +0 -40
  174. package/template/.codex/skills/yss-valuation/agents/openai.yaml +0 -4
  175. package/template/.codex/skills/yss-valuation/references/source-index.md +0 -37
  176. package/template/.codex/skills/yss-variable/SKILL.md +0 -47
  177. package/template/.codex/skills/yss-variable/agents/openai.yaml +0 -4
  178. package/template/.codex/skills/yss-variable/references/source-index.md +0 -32
  179. package/template/.hermes/skills/yss-db2mybatis/SKILL.md +0 -95
  180. package/template/.hermes/skills/yss-db2mybatis/agents/openai.yaml +0 -5
  181. package/template/.hermes/skills/yss-db2mybatis/assets/$.java +0 -19
  182. package/template/.hermes/skills/yss-db2mybatis/assets/convertor.java.template +0 -22
  183. package/template/.hermes/skills/yss-db2mybatis/assets/domain_gateway.java.template +0 -45
  184. package/template/.hermes/skills/yss-db2mybatis/assets/domain_model.java.template +0 -16
  185. package/template/.hermes/skills/yss-db2mybatis/assets/gateway_impl.java.template +0 -62
  186. package/template/.hermes/skills/yss-db2mybatis/assets/po.java.template +0 -18
  187. package/template/.hermes/skills/yss-db2mybatis/assets/repository.java.template +0 -7
  188. package/template/.hermes/skills/yss-db2mybatis/config/metadata.json +0 -334
  189. package/template/.hermes/skills/yss-db2mybatis/config/temp_datasource.json +0 -12
  190. package/template/.hermes/skills/yss-db2mybatis/references/datasource-config.example.json +0 -46
  191. package/template/.hermes/skills/yss-db2mybatis/references/project-convention.example.json +0 -14
  192. package/template/.hermes/skills/yss-db2mybatis/references/type-mapping.json +0 -31
  193. package/template/.hermes/skills/yss-db2mybatis/scripts/db2mybatis.mjs +0 -145
  194. package/template/.hermes/skills/yss-db2mybatis/scripts/db2mybatis.test.mjs +0 -32
  195. package/template/.hermes/skills/yss-valuation/SKILL.md +0 -40
  196. package/template/.hermes/skills/yss-valuation/agents/openai.yaml +0 -4
  197. package/template/.hermes/skills/yss-valuation/references/source-index.md +0 -37
  198. package/template/.hermes/skills/yss-variable/SKILL.md +0 -47
  199. package/template/.hermes/skills/yss-variable/agents/openai.yaml +0 -4
  200. package/template/.hermes/skills/yss-variable/references/source-index.md +0 -32
  201. package/template/.pi/skills/yss-db2mybatis/SKILL.md +0 -95
  202. package/template/.pi/skills/yss-db2mybatis/agents/openai.yaml +0 -5
  203. package/template/.pi/skills/yss-db2mybatis/assets/$.java +0 -19
  204. package/template/.pi/skills/yss-db2mybatis/assets/convertor.java.template +0 -22
  205. package/template/.pi/skills/yss-db2mybatis/assets/domain_gateway.java.template +0 -45
  206. package/template/.pi/skills/yss-db2mybatis/assets/domain_model.java.template +0 -16
  207. package/template/.pi/skills/yss-db2mybatis/assets/gateway_impl.java.template +0 -62
  208. package/template/.pi/skills/yss-db2mybatis/assets/po.java.template +0 -18
  209. package/template/.pi/skills/yss-db2mybatis/assets/repository.java.template +0 -7
  210. package/template/.pi/skills/yss-db2mybatis/config/metadata.json +0 -334
  211. package/template/.pi/skills/yss-db2mybatis/config/temp_datasource.json +0 -12
  212. package/template/.pi/skills/yss-db2mybatis/references/datasource-config.example.json +0 -46
  213. package/template/.pi/skills/yss-db2mybatis/references/project-convention.example.json +0 -14
  214. package/template/.pi/skills/yss-db2mybatis/references/type-mapping.json +0 -31
  215. package/template/.pi/skills/yss-db2mybatis/scripts/db2mybatis.mjs +0 -145
  216. package/template/.pi/skills/yss-db2mybatis/scripts/db2mybatis.test.mjs +0 -32
  217. package/template/.pi/skills/yss-valuation/SKILL.md +0 -40
  218. package/template/.pi/skills/yss-valuation/agents/openai.yaml +0 -4
  219. package/template/.pi/skills/yss-valuation/references/source-index.md +0 -37
  220. package/template/.pi/skills/yss-variable/SKILL.md +0 -47
  221. package/template/.pi/skills/yss-variable/agents/openai.yaml +0 -4
  222. package/template/.pi/skills/yss-variable/references/source-index.md +0 -32
  223. package/template/.qoder/skills/yss-db2mybatis/SKILL.md +0 -95
  224. package/template/.qoder/skills/yss-db2mybatis/agents/openai.yaml +0 -5
  225. package/template/.qoder/skills/yss-db2mybatis/assets/$.java +0 -19
  226. package/template/.qoder/skills/yss-db2mybatis/assets/convertor.java.template +0 -22
  227. package/template/.qoder/skills/yss-db2mybatis/assets/domain_gateway.java.template +0 -45
  228. package/template/.qoder/skills/yss-db2mybatis/assets/domain_model.java.template +0 -16
  229. package/template/.qoder/skills/yss-db2mybatis/assets/gateway_impl.java.template +0 -62
  230. package/template/.qoder/skills/yss-db2mybatis/assets/po.java.template +0 -18
  231. package/template/.qoder/skills/yss-db2mybatis/assets/repository.java.template +0 -7
  232. package/template/.qoder/skills/yss-db2mybatis/config/metadata.json +0 -334
  233. package/template/.qoder/skills/yss-db2mybatis/config/temp_datasource.json +0 -12
  234. package/template/.qoder/skills/yss-db2mybatis/references/datasource-config.example.json +0 -46
  235. package/template/.qoder/skills/yss-db2mybatis/references/project-convention.example.json +0 -14
  236. package/template/.qoder/skills/yss-db2mybatis/references/type-mapping.json +0 -31
  237. package/template/.qoder/skills/yss-db2mybatis/scripts/db2mybatis.mjs +0 -145
  238. package/template/.qoder/skills/yss-db2mybatis/scripts/db2mybatis.test.mjs +0 -32
  239. package/template/.qoder/skills/yss-valuation/SKILL.md +0 -40
  240. package/template/.qoder/skills/yss-valuation/agents/openai.yaml +0 -4
  241. package/template/.qoder/skills/yss-valuation/references/source-index.md +0 -37
  242. package/template/.qoder/skills/yss-variable/SKILL.md +0 -47
  243. package/template/.qoder/skills/yss-variable/agents/openai.yaml +0 -4
  244. package/template/.qoder/skills/yss-variable/references/source-index.md +0 -32
  245. package/template/.trae/skills/yss-db2mybatis/SKILL.md +0 -95
  246. package/template/.trae/skills/yss-db2mybatis/agents/openai.yaml +0 -5
  247. package/template/.trae/skills/yss-db2mybatis/assets/$.java +0 -19
  248. package/template/.trae/skills/yss-db2mybatis/assets/convertor.java.template +0 -22
  249. package/template/.trae/skills/yss-db2mybatis/assets/domain_gateway.java.template +0 -45
  250. package/template/.trae/skills/yss-db2mybatis/assets/domain_model.java.template +0 -16
  251. package/template/.trae/skills/yss-db2mybatis/assets/gateway_impl.java.template +0 -62
  252. package/template/.trae/skills/yss-db2mybatis/assets/po.java.template +0 -18
  253. package/template/.trae/skills/yss-db2mybatis/assets/repository.java.template +0 -7
  254. package/template/.trae/skills/yss-db2mybatis/config/metadata.json +0 -334
  255. package/template/.trae/skills/yss-db2mybatis/config/temp_datasource.json +0 -12
  256. package/template/.trae/skills/yss-db2mybatis/references/datasource-config.example.json +0 -46
  257. package/template/.trae/skills/yss-db2mybatis/references/project-convention.example.json +0 -14
  258. package/template/.trae/skills/yss-db2mybatis/references/type-mapping.json +0 -31
  259. package/template/.trae/skills/yss-db2mybatis/scripts/db2mybatis.mjs +0 -145
  260. package/template/.trae/skills/yss-db2mybatis/scripts/db2mybatis.test.mjs +0 -32
  261. package/template/.trae/skills/yss-valuation/SKILL.md +0 -40
  262. package/template/.trae/skills/yss-valuation/agents/openai.yaml +0 -4
  263. package/template/.trae/skills/yss-valuation/references/source-index.md +0 -37
  264. package/template/.trae/skills/yss-variable/SKILL.md +0 -47
  265. package/template/.trae/skills/yss-variable/agents/openai.yaml +0 -4
  266. package/template/.trae/skills/yss-variable/references/source-index.md +0 -32
@@ -1,334 +0,0 @@
1
- {
2
- "db_type": "mysql",
3
- "database": "yss_metadata",
4
- "schema": "yss_metadata",
5
- "tables": [
6
- {
7
- "table_name": "dbs",
8
- "table_comment": "dbs",
9
- "primary_keys": [
10
- "db_id"
11
- ],
12
- "columns": [
13
- {
14
- "name": "db_id",
15
- "sql_type": "bigint",
16
- "nullable": false,
17
- "primary": true,
18
- "auto_increment": false,
19
- "comment": "id"
20
- },
21
- {
22
- "name": "desc",
23
- "sql_type": "varchar(4000)",
24
- "nullable": true,
25
- "primary": false,
26
- "auto_increment": false,
27
- "comment": ""
28
- },
29
- {
30
- "name": "db_location",
31
- "sql_type": "varchar(4000)",
32
- "nullable": false,
33
- "primary": false,
34
- "auto_increment": false,
35
- "comment": ""
36
- },
37
- {
38
- "name": "name",
39
- "sql_type": "varchar(128)",
40
- "nullable": true,
41
- "primary": false,
42
- "auto_increment": false,
43
- "comment": ""
44
- },
45
- {
46
- "name": "owner_name",
47
- "sql_type": "varchar(128)",
48
- "nullable": true,
49
- "primary": false,
50
- "auto_increment": false,
51
- "comment": ""
52
- },
53
- {
54
- "name": "owner_type",
55
- "sql_type": "varchar(10)",
56
- "nullable": true,
57
- "primary": false,
58
- "auto_increment": false,
59
- "comment": ""
60
- },
61
- {
62
- "name": "db_type",
63
- "sql_type": "varchar(256)",
64
- "nullable": false,
65
- "primary": false,
66
- "auto_increment": false,
67
- "comment": ""
68
- }
69
- ],
70
- "indexes": [
71
- {
72
- "name": "PRIMARY",
73
- "columns": [
74
- "db_id"
75
- ],
76
- "unique": 1,
77
- "primary": 1
78
- }
79
- ],
80
- "foreign_keys": []
81
- },
82
- {
83
- "table_name": "sds",
84
- "table_comment": "sds",
85
- "primary_keys": [],
86
- "columns": [
87
- {
88
- "name": "sd_id",
89
- "sql_type": "bigint",
90
- "nullable": false,
91
- "primary": false,
92
- "auto_increment": false,
93
- "comment": ""
94
- },
95
- {
96
- "name": "cd_id",
97
- "sql_type": "bigint",
98
- "nullable": true,
99
- "primary": false,
100
- "auto_increment": false,
101
- "comment": ""
102
- },
103
- {
104
- "name": "input_format",
105
- "sql_type": "varchar(4000)",
106
- "nullable": true,
107
- "primary": false,
108
- "auto_increment": false,
109
- "comment": ""
110
- },
111
- {
112
- "name": "is_compressed",
113
- "sql_type": "bit(1)",
114
- "nullable": false,
115
- "primary": false,
116
- "auto_increment": false,
117
- "comment": ""
118
- },
119
- {
120
- "name": "is_storedassubdirectories",
121
- "sql_type": "bit(1)",
122
- "nullable": false,
123
- "primary": false,
124
- "auto_increment": false,
125
- "comment": ""
126
- },
127
- {
128
- "name": "location",
129
- "sql_type": "varchar(4000)",
130
- "nullable": true,
131
- "primary": false,
132
- "auto_increment": false,
133
- "comment": ""
134
- },
135
- {
136
- "name": "num_buckets",
137
- "sql_type": "int",
138
- "nullable": false,
139
- "primary": false,
140
- "auto_increment": false,
141
- "comment": ""
142
- },
143
- {
144
- "name": "output_format",
145
- "sql_type": "varchar(4000)",
146
- "nullable": true,
147
- "primary": false,
148
- "auto_increment": false,
149
- "comment": ""
150
- },
151
- {
152
- "name": "serde_id",
153
- "sql_type": "bigint",
154
- "nullable": true,
155
- "primary": false,
156
- "auto_increment": false,
157
- "comment": ""
158
- }
159
- ],
160
- "indexes": [],
161
- "foreign_keys": []
162
- },
163
- {
164
- "table_name": "tbl_lineage",
165
- "table_comment": "tbl_lineage",
166
- "primary_keys": [],
167
- "columns": [
168
- {
169
- "name": "db_id",
170
- "sql_type": "int",
171
- "nullable": true,
172
- "primary": false,
173
- "auto_increment": false,
174
- "comment": "数据库id"
175
- },
176
- {
177
- "name": "tbl_id",
178
- "sql_type": "varchar(255)",
179
- "nullable": true,
180
- "primary": false,
181
- "auto_increment": false,
182
- "comment": "表id"
183
- },
184
- {
185
- "name": "tbl_name",
186
- "sql_type": "varchar(255)",
187
- "nullable": true,
188
- "primary": false,
189
- "auto_increment": false,
190
- "comment": "表名称"
191
- },
192
- {
193
- "name": "up_tbl_id",
194
- "sql_type": "varchar(255)",
195
- "nullable": true,
196
- "primary": false,
197
- "auto_increment": false,
198
- "comment": "上游id"
199
- },
200
- {
201
- "name": "down_tbl_id",
202
- "sql_type": "int",
203
- "nullable": true,
204
- "primary": false,
205
- "auto_increment": false,
206
- "comment": "下游表id"
207
- },
208
- {
209
- "name": "add_type",
210
- "sql_type": "varchar(255)",
211
- "nullable": true,
212
- "primary": false,
213
- "auto_increment": false,
214
- "comment": "添加方式"
215
- },
216
- {
217
- "name": "create_time",
218
- "sql_type": "datetime",
219
- "nullable": true,
220
- "primary": false,
221
- "auto_increment": false,
222
- "comment": "创建时间"
223
- },
224
- {
225
- "name": "tbl_type",
226
- "sql_type": "varchar(255)",
227
- "nullable": true,
228
- "primary": false,
229
- "auto_increment": false,
230
- "comment": "表类型(mysql,hive)"
231
- }
232
- ],
233
- "indexes": [],
234
- "foreign_keys": []
235
- },
236
- {
237
- "table_name": "tbls",
238
- "table_comment": "tbls",
239
- "primary_keys": [],
240
- "columns": [
241
- {
242
- "name": "tbl_id",
243
- "sql_type": "bigint",
244
- "nullable": false,
245
- "primary": false,
246
- "auto_increment": false,
247
- "comment": ""
248
- },
249
- {
250
- "name": "create_time",
251
- "sql_type": "int",
252
- "nullable": false,
253
- "primary": false,
254
- "auto_increment": false,
255
- "comment": ""
256
- },
257
- {
258
- "name": "db_id",
259
- "sql_type": "bigint",
260
- "nullable": true,
261
- "primary": false,
262
- "auto_increment": false,
263
- "comment": ""
264
- },
265
- {
266
- "name": "last_access_time",
267
- "sql_type": "int",
268
- "nullable": false,
269
- "primary": false,
270
- "auto_increment": false,
271
- "comment": ""
272
- },
273
- {
274
- "name": "owner",
275
- "sql_type": "varchar(767)",
276
- "nullable": true,
277
- "primary": false,
278
- "auto_increment": false,
279
- "comment": ""
280
- },
281
- {
282
- "name": "owner_type",
283
- "sql_type": "varchar(10)",
284
- "nullable": true,
285
- "primary": false,
286
- "auto_increment": false,
287
- "comment": ""
288
- },
289
- {
290
- "name": "retention",
291
- "sql_type": "int",
292
- "nullable": false,
293
- "primary": false,
294
- "auto_increment": false,
295
- "comment": ""
296
- },
297
- {
298
- "name": "sd_id",
299
- "sql_type": "bigint",
300
- "nullable": true,
301
- "primary": false,
302
- "auto_increment": false,
303
- "comment": ""
304
- },
305
- {
306
- "name": "tbl_name",
307
- "sql_type": "varchar(256)",
308
- "nullable": true,
309
- "primary": false,
310
- "auto_increment": false,
311
- "comment": ""
312
- },
313
- {
314
- "name": "tbl_type",
315
- "sql_type": "varchar(128)",
316
- "nullable": true,
317
- "primary": false,
318
- "auto_increment": false,
319
- "comment": ""
320
- },
321
- {
322
- "name": "is_rewrite_enabled",
323
- "sql_type": "bit(1)",
324
- "nullable": false,
325
- "primary": false,
326
- "auto_increment": false,
327
- "comment": ""
328
- }
329
- ],
330
- "indexes": [],
331
- "foreign_keys": []
332
- }
333
- ]
334
- }
@@ -1,12 +0,0 @@
1
- {
2
- "metadata_mysql": {
3
- "db_type": "mysql",
4
- "host": "<host>",
5
- "port": 3306,
6
- "user": "<user>",
7
- "password": "<password>",
8
- "database": "<database>",
9
- "schema": null,
10
- "service_name": null
11
- }
12
- }
@@ -1,46 +0,0 @@
1
- {
2
- "datasources": {
3
- "quality-mysql": {
4
- "db_type": "mysql",
5
- "host": "127.0.0.1",
6
- "port": 3306,
7
- "user": "root",
8
- "password": "root",
9
- "database": "yss_quality"
10
- },
11
- "quality-postgres": {
12
- "db_type": "postgres",
13
- "host": "127.0.0.1",
14
- "port": 5432,
15
- "user": "postgres",
16
- "password": "postgres",
17
- "database": "yss_quality",
18
- "schema": "public"
19
- },
20
- "quality-oracle": {
21
- "db_type": "oracle",
22
- "host": "127.0.0.1",
23
- "port": 1521,
24
- "user": "quality",
25
- "password": "quality",
26
- "database": "ORCLPDB1"
27
- },
28
- "quality-opengauss": {
29
- "db_type": "opengauss",
30
- "host": "127.0.0.1",
31
- "port": 5432,
32
- "user": "opengauss",
33
- "password": "password",
34
- "database": "yss_quality",
35
- "schema": "public"
36
- },
37
- "quality-oceanbase": {
38
- "db_type": "oceanbase",
39
- "host": "127.0.0.1",
40
- "port": 2881,
41
- "user": "root@mysql",
42
- "password": "root",
43
- "database": "yss_quality"
44
- }
45
- }
46
- }
@@ -1,14 +0,0 @@
1
- {
2
- "audit_columns": [
3
- "created_by",
4
- "created_date",
5
- "last_modified_by",
6
- "last_modified_date"
7
- ],
8
- "logic_delete_fields": [
9
- "deleted",
10
- "is_deleted"
11
- ],
12
- "pk_strategy": "error",
13
- "page_method_name": "page"
14
- }
@@ -1,31 +0,0 @@
1
- {
2
- "bigint": "Long",
3
- "int": "Integer",
4
- "integer": "Integer",
5
- "smallint": "Integer",
6
- "tinyint": "Integer",
7
- "number": "Long",
8
- "numeric": "BigDecimal",
9
- "decimal": "BigDecimal",
10
- "float": "Float",
11
- "double": "Double",
12
- "real": "Float",
13
- "varchar": "String",
14
- "varchar2": "String",
15
- "nvarchar2": "String",
16
- "char": "String",
17
- "nchar": "String",
18
- "text": "String",
19
- "clob": "String",
20
- "nclob": "String",
21
- "date": "LocalDateTime",
22
- "datetime": "LocalDateTime",
23
- "timestamp": "LocalDateTime",
24
- "timestamp with time zone": "LocalDateTime",
25
- "boolean": "Boolean",
26
- "bool": "Boolean",
27
- "bit": "Boolean",
28
- "json": "String",
29
- "jsonb": "String",
30
- "uuid": "String"
31
- }
@@ -1,145 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Offline-first database metadata and YSS MyBatis scaffold generator.
4
- * Node >=22 is required. Live database drivers are deliberately resolved only
5
- * from YSS_DB2MYBATIS_DRIVER_ROOT (or the caller's cwd); this tool never installs
6
- * packages or modifies the driver directory.
7
- */
8
- import fs from 'node:fs';
9
- import path from 'node:path';
10
- import os from 'node:os';
11
- import readline from 'node:readline/promises';
12
- import { stdin as input, stdout as output } from 'node:process';
13
- import { createRequire } from 'node:module';
14
-
15
- const AUDIT_COLUMNS = new Set(['created_by', 'created_date', 'last_modified_by', 'last_modified_date']);
16
- const LOGIC_DELETE_FIELDS = ['deleted', 'is_deleted'];
17
- const JAVA_IMPORTS = { BigDecimal: 'import java.math.BigDecimal;', LocalDateTime: 'import java.time.LocalDateTime;', LocalDate: 'import java.time.LocalDate;', LocalTime: 'import java.time.LocalTime;' };
18
-
19
- class SkillError extends Error {}
20
- const readText = (file) => fs.readFileSync(file, 'utf8');
21
- const writeText = (file, text) => { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, text, 'utf8'); };
22
- const loadJson = (file) => JSON.parse(readText(file));
23
- const pascal = (value) => String(value || '').split(/[_\-\s]+/).filter(Boolean).map((x) => x[0].toUpperCase() + x.slice(1).toLowerCase()).join('');
24
- const camel = (value) => { const x = pascal(value); return x ? x[0].toLowerCase() + x.slice(1) : x; };
25
- const upperFirst = (value) => value ? value[0].toUpperCase() + value.slice(1) : value;
26
- const lowerFirst = (value) => value ? value[0].toLowerCase() + value.slice(1) : value;
27
- const normalizeSqlType = (value) => String(value || '').trim().toLowerCase().replace(/\(.*\)/, '').replace(/\s+/g, ' ');
28
- const stripSqlType = (value) => normalizeSqlType(value).split(' ')[0] || '';
29
- const replaceTemplate = (text, values) => text.replace(/\$\{([^}]+)\}/g, (all, key) => Object.hasOwn(values, key) ? String(values[key]) : all);
30
- const normalizeTables = (value) => value ? value.split(',').map((x) => x.trim()).filter(Boolean) : null;
31
- const unquoteIdentifier = (value) => { let x = String(value).trim(); if ((x.startsWith('`') && x.endsWith('`')) || (x.startsWith('"') && x.endsWith('"'))) x = x.slice(1, -1); return x.includes('.') ? x.split('.').at(-1) : x; };
32
- const defaultPath = (root, relative) => path.resolve(root, relative);
33
-
34
- function filterTables(tables, include, exclude) {
35
- const inc = include ? new RegExp(include) : null; const exc = exclude ? new RegExp(exclude) : null;
36
- return tables.filter((table) => (!inc || inc.test(table.table_name)) && (!exc || !exc.test(table.table_name)));
37
- }
38
- function splitTopLevel(text, delimiter = ',') {
39
- const values = []; let buffer = ''; let depth = 0; let quote = '';
40
- for (let i = 0; i < text.length; i += 1) { const ch = text[i];
41
- if (quote) { buffer += ch; if (ch === quote && text[i - 1] !== '\\') quote = ''; continue; }
42
- if (["'", '"', '`'].includes(ch)) { quote = ch; buffer += ch; }
43
- else if (ch === '(') { depth += 1; buffer += ch; }
44
- else if (ch === ')') { depth = Math.max(0, depth - 1); buffer += ch; }
45
- else if (ch === delimiter && depth === 0) { if (buffer.trim()) values.push(buffer.trim()); buffer = ''; }
46
- else buffer += ch;
47
- }
48
- if (buffer.trim()) values.push(buffer.trim()); return values;
49
- }
50
- function parseSqlType(definition) {
51
- let end = definition.length; const lower = definition.toLowerCase();
52
- for (const pattern of [/\bnot\s+null\b/i, /\bnull\b/i, /\bdefault\b/i, /\bcomment\b/i, /\bprimary\s+key\b/i, /\bauto_increment\b/i, /\bgenerated\b/i, /\bunique\b/i, /\breferences\b/i, /\bconstraint\b/i, /\bcheck\b/i]) { const match = lower.match(pattern); if (match?.index !== undefined) end = Math.min(end, match.index); }
53
- return definition.slice(0, end).trim();
54
- }
55
- function parseDdlTable(statement) {
56
- const found = statement.match(/create\s+table\s+(?:if\s+not\s+exists\s+)?([`".\w]+)/i);
57
- if (!found) throw new SkillError('DDL 解析失败:未识别 CREATE TABLE 语句');
58
- const tableName = unquoteIdentifier(found[1]); const left = statement.indexOf('(', found.index + found[0].length);
59
- if (left < 0) throw new SkillError(`DDL 解析失败:表 ${tableName} 缺少字段定义`);
60
- let depth = 1; let quote = ''; let right = -1;
61
- for (let i = left + 1; i < statement.length; i += 1) { const ch = statement[i]; if (quote) { if (ch === quote && statement[i - 1] !== '\\') quote = ''; } else if (["'", '"', '`'].includes(ch)) quote = ch; else if (ch === '(') depth += 1; else if (ch === ')' && --depth === 0) { right = i; break; } }
62
- if (right < 0) throw new SkillError(`DDL 解析失败:表 ${tableName} 字段括号未闭合`);
63
- const comment = statement.slice(right + 1).match(/comment\s*=\s*'([^']*)'/i)?.[1] || '';
64
- const columns = []; const primaryKeys = [];
65
- for (const raw of splitTopLevel(statement.slice(left + 1, right))) { const item = raw.trim().replace(/,+$/, ''); const lower = item.toLowerCase(); if (!item) continue;
66
- if (lower.startsWith('primary key') || (lower.includes('constraint') && lower.includes('primary key'))) { const pkMatch = item.match(/\((.*?)\)/is); if (pkMatch) primaryKeys.push(...splitTopLevel(pkMatch[1]).map(unquoteIdentifier)); continue; }
67
- if (/^(key|index|unique key|unique index|constraint|foreign key)\b/i.test(lower)) continue;
68
- const colMatch = item.match(/^\s*([`".\w]+)\s+(.+)$/is); if (!colMatch) continue;
69
- const name = unquoteIdentifier(colMatch[1]); const definition = colMatch[2].trim(); const inlinePk = /\bprimary\s+key\b/i.test(definition);
70
- columns.push({ name, sql_type: parseSqlType(definition), nullable: !/\bnot\s+null\b/i.test(definition), primary: inlinePk, auto_increment: /\bauto_increment\b|\bidentity\b/i.test(definition), comment: definition.match(/comment\s+'([^']*)'/i)?.[1] || '' });
71
- if (inlinePk && !primaryKeys.includes(name)) primaryKeys.push(name);
72
- }
73
- const pkSet = new Set(primaryKeys); for (const column of columns) if (pkSet.has(column.name)) { column.primary = true; column.nullable = false; }
74
- return { table_name: tableName, table_comment: comment || tableName, primary_keys: primaryKeys, columns, indexes: [], foreign_keys: [] };
75
- }
76
- function metadataFromDdl(text, dbType = 'mysql', database = 'ddl', schema = null) {
77
- const matches = [...text.matchAll(/\bcreate\s+table\b/ig)]; const tables = matches.map((match, index) => parseDdlTable(text.slice(match.index, matches[index + 1]?.index).trim()));
78
- return { db_type: dbType, database, schema: schema || database, tables };
79
- }
80
- function readDdl(args) { if (args['ddl-file'] && args['ddl-sql']) throw new SkillError('不能同时使用 --ddl-file 和 --ddl-sql'); if (args['ddl-file']) return readText(args['ddl-file']); if (args['ddl-sql']) return args['ddl-sql']; throw new SkillError('请提供 --ddl-file 或 --ddl-sql'); }
81
- function javaType(type, mapping) { const normalized = normalizeSqlType(type); return mapping[normalized] || mapping[stripSqlType(type)] || 'String'; }
82
- function inferDomain(table, prefix) { return pascal(prefix && table.startsWith(prefix) ? table.slice(prefix.length) : table); }
83
-
84
- function parseArgs(argv) {
85
- const [command, ...tokens] = argv; if (!command || command === '--help' || command === '-h') return { command: 'help', args: {} };
86
- const args = {}; for (let i = 0; i < tokens.length; i += 1) { const token = tokens[i]; if (!token.startsWith('--')) throw new SkillError(`未知参数: ${token}`); const key = token.slice(2); if (['dry-run', 'overwrite', 'verbose', 'keep-audit-columns'].includes(key)) args[key] = true; else { if (tokens[i + 1] === undefined || tokens[i + 1].startsWith('--')) throw new SkillError(`参数 --${key} 需要值`); args[key] = tokens[++i]; } }
87
- return { command, args };
88
- }
89
- const CONNECTION_OPTIONS = ['datasource-config', 'datasource-name', 'db-type', 'host', 'port', 'user', 'password', 'database', 'schema', 'tables', 'include-tables-regex', 'exclude-tables-regex'];
90
- const SCAFFOLD_OPTIONS = ['skill-root', 'base-package', 'domain-segment', 'domain-java-root', 'infra-java-root', 'table-prefix', 'po-template', 'domain-model-template', 'repository-template', 'domain-gateway-template', 'gateway-impl-template', 'convertor-template', 'type-mapping', 'convention-file', 'pk-strategy', 'keep-audit-columns', 'overwrite', 'dry-run', 'verbose', 'ddl-file', 'ddl-sql'];
91
- const COMMAND_OPTIONS = {
92
- init: ['skill-root'],
93
- extract: [...CONNECTION_OPTIONS, 'output'],
94
- ddl2metadata: ['ddl-file', 'ddl-sql', 'db-type', 'database', 'schema', 'output'],
95
- scaffold: [...CONNECTION_OPTIONS, 'metadata-file', ...SCAFFOLD_OPTIONS],
96
- validate: ['datasource-config', ...SCAFFOLD_OPTIONS],
97
- };
98
- function validateOptionNames(command, args) {
99
- if (!COMMAND_OPTIONS[command]) throw new SkillError(`不支持的命令: ${command}`);
100
- const allowed = new Set(COMMAND_OPTIONS[command]);
101
- for (const name of Object.keys(args)) if (!allowed.has(name)) throw new SkillError(`未知参数: --${name}`);
102
- }
103
- function requireArgs(args, names) { for (const name of names) if (args[name] === undefined || args[name] === '') throw new SkillError(`缺少 --${name}`); }
104
- function help() { console.log(`usage: node scripts/db2mybatis.mjs <init|extract|ddl2metadata|scaffold|validate> [options]\n\ncommands:\n init 交互式配置向导\n extract 提取数据库元数据\n ddl2metadata 根据 DDL 文本生成 metadata.json\n scaffold 根据元数据生成代码\n validate 校验配置和模板`); }
105
-
106
- function mergeDatasource(args) { if (!args['datasource-config']) return args; const config = loadJson(args['datasource-config']); if (!args['datasource-name']) throw new SkillError('使用 --datasource-config 时必须传 --datasource-name'); const source = (config.datasources || {})[args['datasource-name']] || config[args['datasource-name']]; if (!source) throw new SkillError(`datasource 未找到: ${args['datasource-name']}`); for (const key of ['db-type', 'host', 'port', 'user', 'password', 'database', 'schema']) { const inputKey = key.replace('-', '_'); if ((args[key] === undefined || args[key] === '') && source[inputKey] !== undefined && source[inputKey] !== '') args[key] = source[inputKey]; } return args; }
107
- function requireConnection(args) { if (!args['db-type']) throw new SkillError('缺少 --db-type'); const missing = ['host', 'port', 'user', 'password', 'database'].filter((key) => !args[key]); if (missing.length) throw new SkillError(`缺少连接参数: ${missing.join(', ')}`); }
108
- function driver(name, dbType) { const root = process.env.YSS_DB2MYBATIS_DRIVER_ROOT || process.cwd(); try { return createRequire(path.join(path.resolve(root), 'package.json'))(name); } catch (error) { throw new SkillError(`${dbType} 需要 Node 驱动 ${name};请在 YSS_DB2MYBATIS_DRIVER_ROOT(未设置时为当前工作目录)提供该依赖。本工具不会自动安装依赖。`); } }
109
- const mysqlQuery = async (connection, sql, values = []) => { const [rows] = await connection.execute(sql, values); return rows; };
110
- async function mysqlMetadata(args, dbType = 'mysql') {
111
- const mysql = driver('mysql2/promise', dbType); let connection;
112
- try { connection = await mysql.createConnection({ host: args.host, port: Number(args.port), user: args.user, password: args.password, database: args.database, charset: 'utf8mb4' }); const wanted = normalizeTables(args.tables); const tableRows = await mysqlQuery(connection, `SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.tables WHERE table_schema=? AND table_type='BASE TABLE'${wanted ? ` AND table_name IN (${wanted.map(() => '?').join(',')})` : ''} ORDER BY table_name`, [args.database, ...(wanted || [])]); const tables = [];
113
- for (const row of tableRows) { const tableName = row.TABLE_NAME; const cols = await mysqlQuery(connection, `SELECT COLUMN_NAME, DATA_TYPE, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, EXTRA, COLUMN_COMMENT FROM information_schema.columns WHERE table_schema=? AND table_name=? ORDER BY ORDINAL_POSITION`, [args.database, tableName]); const indexRows = await mysqlQuery(connection, `SELECT INDEX_NAME AS index_name, COLUMN_NAME AS column_name, NON_UNIQUE = 0 AS is_unique, INDEX_NAME = 'PRIMARY' AS is_primary FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=? AND TABLE_NAME=? ORDER BY INDEX_NAME, SEQ_IN_INDEX`, [args.database, tableName]); const fkRows = await mysqlQuery(connection, `SELECT CONSTRAINT_NAME AS fk_name, COLUMN_NAME AS column_name, REFERENCED_TABLE_NAME AS referenced_table, REFERENCED_COLUMN_NAME AS referenced_column FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA=? AND TABLE_NAME=? AND REFERENCED_TABLE_NAME IS NOT NULL ORDER BY CONSTRAINT_NAME, ORDINAL_POSITION`, [args.database, tableName]); const indexes = groupRows(indexRows, 'index_name', (x) => ({ name: x.index_name, columns: [], unique: Boolean(x.is_unique), primary: Boolean(x.is_primary) }), (result, x) => result.columns.push(x.column_name)); const foreignKeys = groupRows(fkRows, 'fk_name', (x) => ({ name: x.fk_name, columns: [], referenced_table: x.referenced_table, referenced_columns: [] }), (result, x) => { result.columns.push(x.column_name); result.referenced_columns.push(x.referenced_column); }); const columns = cols.map((col) => ({ name: col.COLUMN_NAME, sql_type: col.COLUMN_TYPE || col.DATA_TYPE, nullable: col.IS_NULLABLE === 'YES', primary: col.COLUMN_KEY === 'PRI', auto_increment: String(col.EXTRA || '').toLowerCase().includes('auto_increment'), comment: col.COLUMN_COMMENT || '' })); tables.push({ table_name: tableName, table_comment: row.TABLE_COMMENT || tableName, primary_keys: columns.filter((x) => x.primary).map((x) => x.name), columns, indexes, foreign_keys: foreignKeys }); }
114
- return { db_type: dbType, database: args.database, schema: args.database, tables };
115
- } finally { if (connection) await connection.end(); }
116
- }
117
- function groupRows(rows, key, factory, add) { const map = new Map(); for (const row of rows) { const value = row[key]; if (!map.has(value)) map.set(value, factory(row)); add(map.get(value), row); } return [...map.values()]; }
118
- async function postgresMetadata(args, dbType = 'postgres') {
119
- const pg = driver('pg', dbType); const schema = args.schema || 'public'; const client = new pg.Client({ host: args.host, port: Number(args.port), user: args.user, password: args.password, database: args.database }); await client.connect(); try { const wanted = normalizeTables(args.tables); const tableResult = await client.query(`SELECT t.table_name, pgd.description AS table_comment FROM information_schema.tables t LEFT JOIN pg_catalog.pg_statio_all_tables st ON st.schemaname=t.table_schema AND st.relname=t.table_name LEFT JOIN pg_catalog.pg_description pgd ON pgd.objoid=st.relid AND pgd.objsubid=0 WHERE t.table_schema=$1 AND t.table_type='BASE TABLE'${wanted ? ' AND t.table_name = ANY($2)' : ''} ORDER BY t.table_name`, wanted ? [schema, wanted] : [schema]); const tables = [];
120
- for (const tableRow of tableResult.rows) { const tableName = tableRow.table_name; const cols = (await client.query(`SELECT c.column_name,c.data_type,c.udt_name,c.is_nullable,pgd.description AS column_comment FROM information_schema.columns c LEFT JOIN pg_catalog.pg_statio_all_tables st ON st.schemaname=c.table_schema AND st.relname=c.table_name LEFT JOIN pg_catalog.pg_description pgd ON pgd.objoid=st.relid AND pgd.objsubid=c.ordinal_position WHERE c.table_schema=$1 AND c.table_name=$2 ORDER BY c.ordinal_position`, [schema, tableName])).rows; const pks = (await client.query(`SELECT kcu.column_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name=kcu.constraint_name AND tc.table_schema=kcu.table_schema AND tc.table_name=kcu.table_name WHERE tc.table_schema=$1 AND tc.table_name=$2 AND tc.constraint_type='PRIMARY KEY' ORDER BY kcu.ordinal_position`, [schema, tableName])).rows.map((x) => x.column_name); const indexRows = (await client.query(`SELECT ic.relname AS index_name,a.attname AS column_name,idx.indisunique AS is_unique,idx.indisprimary AS is_primary FROM pg_index idx JOIN pg_class ic ON idx.indexrelid=ic.oid JOIN pg_class t ON idx.indrelid=t.oid JOIN pg_namespace n ON t.relnamespace=n.oid JOIN pg_attribute a ON a.attrelid=t.oid AND a.attnum=ANY(idx.indkey) WHERE n.nspname=$1 AND t.relname=$2 ORDER BY ic.relname,a.attnum`, [schema, tableName])).rows; const fkRows = (await client.query(`SELECT tc.constraint_name AS fk_name,kcu.column_name AS column_name,ccu.table_name AS referenced_table,ccu.column_name AS referenced_column FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name=kcu.constraint_name JOIN information_schema.constraint_column_usage ccu ON ccu.constraint_name=tc.constraint_name WHERE tc.table_schema=$1 AND tc.table_name=$2 AND tc.constraint_type='FOREIGN KEY' ORDER BY tc.constraint_name,kcu.ordinal_position`, [schema, tableName])).rows; const pkSet = new Set(pks); tables.push({ table_name: tableName, table_comment: tableRow.table_comment || tableName, primary_keys: pks, columns: cols.map((x) => ({ name: x.column_name, sql_type: x.udt_name || x.data_type, nullable: x.is_nullable === 'YES', primary: pkSet.has(x.column_name), auto_increment: ['serial', 'bigserial'].includes(x.udt_name), comment: x.column_comment || '' })), indexes: groupRows(indexRows, 'index_name', (x) => ({ name: x.index_name, columns: [], unique: x.is_unique, primary: x.is_primary }), (x, row) => x.columns.push(row.column_name)), foreign_keys: groupRows(fkRows, 'fk_name', (x) => ({ name: x.fk_name, columns: [], referenced_table: x.referenced_table, referenced_columns: [] }), (x, row) => { x.columns.push(row.column_name); x.referenced_columns.push(row.referenced_column); }) }); }
121
- return { db_type: dbType, database: args.database, schema, tables };
122
- } finally { await client.end(); }
123
- }
124
- async function oracleMetadata(args) {
125
- const oracle = driver('oracledb', 'oracle'); const connection = await oracle.getConnection({ user: args.user, password: args.password, connectString: `${args.host}:${Number(args.port)}/${args.database}` }); try { const wanted = normalizeTables(args.tables); const binds = {}; const queryNames = wanted?.map((x, i) => { binds[`t${i}`] = x.toUpperCase(); return `:t${i}`; }); const tableRows = (await connection.execute(`SELECT table_name FROM user_tables${queryNames ? ` WHERE table_name IN (${queryNames.join(',')})` : ''} ORDER BY table_name`, binds)).rows; const tables = [];
126
- for (const row of tableRows) { const tableName = row[0]; const run = async (sql) => (await connection.execute(sql, { table_name: tableName })).rows; const colRows = await run(`SELECT utc.column_name,utc.data_type,utc.data_precision,utc.data_scale,utc.nullable,ucc.comments FROM user_tab_columns utc LEFT JOIN user_col_comments ucc ON utc.table_name=ucc.table_name AND utc.column_name=ucc.column_name WHERE utc.table_name=:table_name ORDER BY utc.column_id`); const pks = (await run(`SELECT cols.column_name FROM user_constraints cons JOIN user_cons_columns cols ON cons.constraint_name=cols.constraint_name WHERE cons.constraint_type='P' AND cons.table_name=:table_name ORDER BY cols.position`)).map((x) => x[0].toLowerCase()); const identity = new Set((await run(`SELECT column_name FROM user_tab_identity_cols WHERE table_name=:table_name`)).map((x) => x[0].toLowerCase())); const indexRows = await run(`SELECT ui.index_name,uic.column_name,ui.uniqueness='UNIQUE' AS is_unique,ui.index_name IN (SELECT uc.constraint_name FROM user_constraints uc WHERE uc.table_name=:table_name AND uc.constraint_type='P') AS is_primary FROM user_indexes ui JOIN user_ind_columns uic ON ui.index_name=uic.index_name WHERE ui.table_name=:table_name ORDER BY ui.index_name,uic.column_position`); const fkRows = await run(`SELECT uc.constraint_name,ucc.column_name,ruc.table_name,rucc.column_name FROM user_constraints uc JOIN user_cons_columns ucc ON uc.constraint_name=ucc.constraint_name JOIN user_constraints ruc ON uc.r_constraint_name=ruc.constraint_name JOIN user_cons_columns rucc ON ruc.constraint_name=rucc.constraint_name AND ucc.position=rucc.position WHERE uc.table_name=:table_name AND uc.constraint_type='R' ORDER BY uc.constraint_name,ucc.position`); const tableComment = (await run(`SELECT comments FROM user_tab_comments WHERE table_name=:table_name`))[0]?.[0] || tableName.toLowerCase(); const pkSet = new Set(pks); const typeFor = (x) => { const type = String(x[1] || '').toLowerCase(); if (type !== 'number') return type; return Number(x[3]) > 0 ? 'numeric' : Number(x[2]) <= 9 ? 'integer' : 'bigint'; }; tables.push({ table_name: tableName.toLowerCase(), table_comment: tableComment, primary_keys: pks, columns: colRows.map((x) => { const name = x[0].toLowerCase(); return { name, sql_type: typeFor(x), nullable: x[4] === 'Y', primary: pkSet.has(name), auto_increment: identity.has(name), comment: x[5] || '' }; }), indexes: groupRows(indexRows.map((x) => ({ index_name: x[0].toLowerCase(), column_name: x[1].toLowerCase(), is_unique: x[2], is_primary: x[3] })), 'index_name', (x) => ({ name: x.index_name, columns: [], unique: x.is_unique, primary: x.is_primary }), (x, row2) => x.columns.push(row2.column_name)), foreign_keys: groupRows(fkRows.map((x) => ({ fk_name: x[0].toLowerCase(), column_name: x[1].toLowerCase(), referenced_table: x[2].toLowerCase(), referenced_column: x[3].toLowerCase() })), 'fk_name', (x) => ({ name: x.fk_name, columns: [], referenced_table: x.referenced_table, referenced_columns: [] }), (x, row2) => { x.columns.push(row2.column_name); x.referenced_columns.push(row2.referenced_column); }) }); }
127
- return { db_type: 'oracle', database: args.database, schema: args.user, tables };
128
- } finally { await connection.close(); }
129
- }
130
- async function extract(args) { mergeDatasource(args); requireConnection(args); let metadata; try { if (['mysql', 'oceanbase'].includes(args['db-type'])) metadata = await mysqlMetadata(args, args['db-type']); else if (['postgres', 'opengauss'].includes(args['db-type'])) metadata = await postgresMetadata(args, args['db-type']); else if (args['db-type'] === 'oracle') metadata = await oracleMetadata(args); else throw new SkillError(`不支持的 db_type: ${args['db-type']}`); } catch (error) { if (error instanceof SkillError) throw error; throw new SkillError(`数据库连接失败: ${error.message}\n\n排查建议:\n 1. 检查数据库服务是否启动\n 2. 检查主机地址和端口是否正确\n 3. 检查用户名和密码是否正确\n 4. 检查数据库名是否正确\n 5. 检查网络连接是否正常\n`); }
131
- const original = metadata.tables; metadata.tables = filterTables(original, args['include-tables-regex'], args['exclude-tables-regex']); if (!metadata.tables.length && original.length) throw new SkillError(`没有匹配到表。可用表:${original.map((x) => x.table_name).join(', ')}`); return metadata; }
132
-
133
- function normaliseScaffold(args) { const root = args['skill-root']; for (const [name, target] of Object.entries({ 'type-mapping': 'references/type-mapping.json', 'po-template': 'assets/po.java.template', 'domain-model-template': 'assets/domain_model.java.template', 'repository-template': 'assets/repository.java.template', 'domain-gateway-template': 'assets/domain_gateway.java.template', 'gateway-impl-template': 'assets/gateway_impl.java.template', 'convertor-template': 'assets/convertor.java.template' })) if (!args[name]) args[name] = defaultPath(root, target); const convention = { audit_columns: [...AUDIT_COLUMNS], logic_delete_fields: [...LOGIC_DELETE_FIELDS], pk_strategy: args['pk-strategy'] || 'error', page_method_name: 'page' }; if (args['convention-file']) Object.assign(convention, loadJson(args['convention-file'])); if (args['keep-audit-columns']) convention.audit_columns = []; args._convention = convention; }
134
- function validateTemplates(args) { const expected = { [args['po-template']]: ['${domain_name}', '${fields_block}', '${table_name}'], [args['domain-model-template']]: ['${domain_name}'], [args['repository-template']]: ['${domain_name}'], [args['domain-gateway-template']]: ['${domain_name}', '${pk_java_type}'], [args['gateway-impl-template']]: ['${domain_name}', '${repository_field_name}'], [args['convertor-template']]: ['${domain_name}'] }; for (const [file, tokens] of Object.entries(expected)) for (const token of tokens) if (!readText(file).includes(token)) throw new SkillError(`模板 ${file} 缺少占位符 ${token}`); }
135
- function buildFields(columns, mapping, skipped, forPo) { const lines = []; const usable = []; for (const column of columns) { if (skipped.has(column.name)) continue; usable.push(column); const field = camel(column.name); if (column.comment) lines.push(`/** ${column.comment} */`); if (forPo) lines.push(column.primary ? `@TableId(value = "${column.name}", type = IdType.${column.auto_increment ? 'AUTO' : 'ASSIGN_ID'})` : ` @TableField("${column.name}")`); lines.push(` private ${javaType(column.sql_type, mapping)} ${field};`, ''); } while (lines.at(-1) === '') lines.pop(); return [lines.join('\n'), usable]; }
136
- function pickPrimary(table, columns, strategy) { const keys = table.primary_keys || []; if (keys.length > 1 && strategy === 'error') throw new SkillError(`表 ${table.table_name} 为联合主键 ${JSON.stringify(keys)},当前 pk_strategy=error。可改用 --pk-strategy first`); if (keys.length) { const hit = columns.find((x) => x.name === keys[0]); if (hit) return hit; } return columns.find((x) => x.primary) || columns.find((x) => x.name === 'id') || { name: 'id', sql_type: 'bigint', primary: true, auto_increment: false }; }
137
- function importsFor(columns, mapping) { return [...new Set(columns.map((x) => JAVA_IMPORTS[javaType(x.sql_type, mapping)]).filter(Boolean))].sort().join('\n'); }
138
- function filePaths(args, domain) { const base = args['base-package'].replaceAll('.', '/'); const segment = args['domain-segment'] || domain.toLowerCase(); const domainRoot = path.join(args['domain-java-root'], base, 'domain', segment); const infra = path.join(args['infra-java-root'], base, 'repository'); return { domain_model: path.join(domainRoot, 'model', `${domain}.java`), domain_gateway: path.join(domainRoot, 'gateway', `${domain}Gateway.java`), po: path.join(infra, 'entity', `${domain}PO.java`), repo: path.join(infra, `${domain}Repository.java`), gateway_impl: path.join(infra, 'gateway', 'impl', `${domain}GatewayImpl.java`), convertor: path.join(infra, 'convertor', `${domain}Convertor.java`) }; }
139
- function writeJava(file, content, args) { if (fs.existsSync(file) && !args.overwrite) throw new SkillError(`文件已存在: ${file}. 如需覆盖请添加 --overwrite`); if (!args['dry-run']) writeText(file, `${content.trim()}\n`); }
140
- function generateTable(table, args, mapping) { const convention = args._convention; const domain = inferDomain(table.table_name, args['table-prefix'] || 't_'); const segment = args['domain-segment']; const modelPkg = `${args['base-package']}.domain.${segment || domain.toLowerCase()}.model`; const gatewayPkg = `${args['base-package']}.domain.${segment || domain.toLowerCase()}.gateway`; const [poFields, poColumns] = buildFields(table.columns || [], mapping, new Set(convention.audit_columns || []), true); const [domainFields, domainColumns] = buildFields(table.columns || [], mapping, new Set(convention.audit_columns || []), false); const primary = pickPrimary(table, domainColumns, convention.pk_strategy || 'error'); const pkName = camel(primary.name); const values = { base_package: args['base-package'], gateway_pkg: gatewayPkg, model_pkg: modelPkg, domain_segment: segment, table_name: table.table_name, table_comment: String(table.table_comment || table.table_name).replaceAll('*/', ''), domain_name: domain, fields_block: poFields, extra_imports: importsFor(poColumns, mapping), pk_java_type: javaType(primary.sql_type, mapping), pk_field_name: pkName, pk_getter: `get${upperFirst(pkName)}`, repository_field_name: `${lowerFirst(domain)}Repository`, method_add: `add${domain}`, method_update: `update${domain}`, method_delete: `delete${domain}`, method_get: `get${domain}ById`, method_page: `page${domain}`, logic_delete_condition: '' }; const logical = (convention.logic_delete_fields || []).find((x) => domainColumns.some((col) => col.name === x)); if (logical) values.logic_delete_condition = ` wrapper.eq(${domain}PO::get${upperFirst(camel(logical))}, 0);`; const templates = { domain_model: args['domain-model-template'], domain_gateway: args['domain-gateway-template'], po: args['po-template'], repo: args['repository-template'], gateway_impl: args['gateway-impl-template'], convertor: args['convertor-template'] }; const paths = filePaths(args, domain); const outputValues = { ...values, fields_block: domainFields, extra_imports: importsFor(domainColumns, mapping) }; const generated = []; for (const [kind, file] of Object.entries(paths)) { const templateValues = kind === 'domain_model' ? outputValues : values; writeJava(file, replaceTemplate(readText(templates[kind]), templateValues), args); generated.push(file); } return generated; }
141
-
142
- async function runInit(args) { requireArgs(args, ['skill-root']); const rl = readline.createInterface({ input, output }); const ask = async (label, fallback) => (await rl.question(`${label}${fallback ? ` [${fallback}]` : ''}: `)).trim() || fallback || ''; try { console.log('='.repeat(60) + '\n yss-db2mybatis 配置向导\n' + '='.repeat(60)); const dbType = await ask('数据库类型(mysql/postgres/oracle)', 'postgres'); if (!['mysql', 'postgres', 'oracle'].includes(dbType)) throw new SkillError('数据库类型必须是 mysql、postgres 或 oracle'); const host = await ask('数据库主机地址', 'localhost'); const port = await ask('数据库端口', ({ mysql: '3306', postgres: '5432', oracle: '1521' })[dbType]); const user = await ask('数据库用户名', 'dmz'); const password = await ask('数据库密码', 'dmz'); const database = await ask('数据库名', 'yss_metadata'); const schema = dbType === 'postgres' ? await ask('Schema 名称', 'public') : null; const datasourceName = await ask('数据源名称', 'my-datasource'); const basePackage = await ask('基础包名', 'com.yss.metadata'); const segment = await ask('领域分段名', 'metadata'); const confirm = (await ask('确认生成配置文件? (y/n)', 'y')).toLowerCase(); if (!['y', 'yes', ''].includes(confirm)) { console.log('已取消'); return; } const file = path.join(os.tmpdir(), 'yss-db2mybatis', 'datasource-config.json'); const source = { db_type: dbType, host, port: Number(port), user, password, database }; if (schema) source.schema = schema; writeText(file, `${JSON.stringify({ datasources: { [datasourceName]: source } }, null, 2)}\n`); const metadata = path.join(os.tmpdir(), 'yss-db2mybatis', 'metadata.json'); console.log(`\n✅ 数据源配置文件已生成: ${file}\n\n1. 提取元数据:\n node ${args['skill-root']}/scripts/db2mybatis.mjs extract \\\n --datasource-config ${file} \\\n --datasource-name ${datasourceName} \\\n --include-tables-regex '.*' \\\n --output ${metadata}\n\n2. 生成代码:\n node ${args['skill-root']}/scripts/db2mybatis.mjs scaffold \\\n --skill-root ${args['skill-root']} \\\n --metadata-file ${metadata} \\\n --base-package ${basePackage} \\\n --domain-segment ${segment} \\\n --domain-java-root ./domain \\\n --infra-java-root ./infrastructure \\\n --dry-run`); } finally { rl.close(); } }
143
- async function main() { let parsed; try { parsed = parseArgs(process.argv.slice(2)); if (parsed.command === 'help') return help(); const { command, args } = parsed; validateOptionNames(command, args); if (command === 'init') return runInit(args); if (command === 'ddl2metadata') { requireArgs(args, ['output']); const metadata = metadataFromDdl(readDdl(args), args['db-type'] || 'mysql', args.database || 'ddl', args.schema); if (!metadata.tables.length) throw new SkillError('DDL 中未识别到 CREATE TABLE'); writeText(args.output, `${JSON.stringify(metadata, null, 2)}\n`); console.log(`metadata written: ${args.output}\ntables: ${metadata.tables.length}`); return; } if (command === 'extract') { requireArgs(args, ['output']); const metadata = await extract(args); writeText(args.output, `${JSON.stringify(metadata, null, 2)}\n`); console.log(`metadata written: ${args.output}\ntables: ${metadata.tables.length}`); return; } if (!['scaffold', 'validate'].includes(command)) throw new SkillError(`不支持的命令: ${command}`); requireArgs(args, ['skill-root', 'base-package', 'domain-segment', 'domain-java-root', 'infra-java-root']); normaliseScaffold(args); validateTemplates(args); if (command === 'validate') { const checks = []; if (args['datasource-config']) { const config = loadJson(args['datasource-config']); if (!config.datasources || typeof config.datasources !== 'object' || Array.isArray(config.datasources)) throw new SkillError('datasource 配置文件缺少 datasources 对象'); checks.push('datasource-config'); } if (args['convention-file']) { const convention = loadJson(args['convention-file']); if (!convention || typeof convention !== 'object' || Array.isArray(convention)) throw new SkillError('convention 文件必须为 JSON 对象'); checks.push('convention-file'); } console.log(`validate passed\nchecks: ${[...checks, 'templates'].join(', ')}`); return; } let metadata; if (args['metadata-file']) { metadata = loadJson(args['metadata-file']); } else if (args['ddl-file'] || args['ddl-sql']) { metadata = metadataFromDdl(readDdl(args), args['db-type'] || 'mysql', args.database || 'ddl', args.schema); } else metadata = await extract(args); const tables = filterTables(metadata.tables || [], args['include-tables-regex'], args['exclude-tables-regex']); if (!tables.length) throw new SkillError('没有匹配到可生成的表'); const mapping = loadJson(args['type-mapping']); const files = tables.flatMap((table) => generateTable(table, args, mapping)); for (const file of files) console.log(`${args['dry-run'] ? 'planned' : 'generated'}: ${file}`); console.log(`done. file count: ${files.length}`); } catch (error) { console.error(`error: ${error.message}`); process.exitCode = 2; } }
144
-
145
- await main();