ai-engineering-init 1.1.0

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 (313) hide show
  1. package/.claude/agents/code-reviewer.md +139 -0
  2. package/.claude/agents/project-manager.md +159 -0
  3. package/.claude/audio/completed.wav +0 -0
  4. package/.claude/commands/add-todo.md +255 -0
  5. package/.claude/commands/check.md +210 -0
  6. package/.claude/commands/crud.md +454 -0
  7. package/.claude/commands/dev.md +503 -0
  8. package/.claude/commands/init-docs.md +681 -0
  9. package/.claude/commands/next.md +251 -0
  10. package/.claude/commands/progress.md +242 -0
  11. package/.claude/commands/start.md +199 -0
  12. package/.claude/commands/sync.md +307 -0
  13. package/.claude/commands/update-status.md +428 -0
  14. package/.claude/docs/Mixin/344/275/277/347/224/250/346/214/207/345/215/227.md +299 -0
  15. package/.claude/docs/README.md +167 -0
  16. package/.claude/docs//345/211/215/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +599 -0
  17. package/.claude/docs//345/220/216/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +726 -0
  18. package/.claude/docs//345/267/245/344/275/234/346/265/201/345/274/200/345/217/221/346/214/207/345/215/227.md +714 -0
  19. package/.claude/docs//345/267/245/345/205/267/347/261/273/344/275/277/347/224/250/346/214/207/345/215/227.md +463 -0
  20. package/.claude/docs//346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/350/247/204/350/214/203.md +390 -0
  21. package/.claude/docs//346/226/260/345/212/237/350/203/275/345/274/200/345/217/221/346/265/201/347/250/213/350/247/204/350/214/203.md +688 -0
  22. package/.claude/docs//346/226/260/351/241/271/347/233/256/345/274/200/345/217/221/346/265/201/347/250/213.md +365 -0
  23. package/.claude/docs//346/241/206/346/236/266/350/257/264/346/230/216.md +393 -0
  24. package/.claude/docs//350/267/257/347/224/261/351/205/215/347/275/256/346/214/207/345/215/227.md +246 -0
  25. package/.claude/framework-config.json +73 -0
  26. package/.claude/hooks/pre-tool-use.js +117 -0
  27. package/.claude/hooks/skill-forced-eval.js +167 -0
  28. package/.claude/hooks/stop.js +58 -0
  29. package/.claude/settings.json +41 -0
  30. package/.claude/skills/add-skill/SKILL.md +352 -0
  31. package/.claude/skills/api-development/SKILL.md +560 -0
  32. package/.claude/skills/architecture-design/SKILL.md +756 -0
  33. package/.claude/skills/backend-annotations/SKILL.md +674 -0
  34. package/.claude/skills/banana-image/CHANGELOG.md +37 -0
  35. package/.claude/skills/banana-image/README.md +146 -0
  36. package/.claude/skills/banana-image/SKILL.md +164 -0
  37. package/.claude/skills/banana-image/assets/logo.png +0 -0
  38. package/.claude/skills/banana-image/references/advanced-usage.md +189 -0
  39. package/.claude/skills/banana-image/scripts/apply_template.py +125 -0
  40. package/.claude/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  41. package/.claude/skills/banana-image/scripts/batch_prep.py +82 -0
  42. package/.claude/skills/banana-image/scripts/package-lock.json +1437 -0
  43. package/.claude/skills/banana-image/scripts/package.json +18 -0
  44. package/.claude/skills/banana-image/scripts/requirements.txt +10 -0
  45. package/.claude/skills/banana-image/templates/poster.json +22 -0
  46. package/.claude/skills/banana-image/templates/product.json +17 -0
  47. package/.claude/skills/banana-image/templates/social.json +22 -0
  48. package/.claude/skills/banana-image/templates/thumbnail.json +17 -0
  49. package/.claude/skills/brainstorm/SKILL.md +648 -0
  50. package/.claude/skills/bug-detective/SKILL.md +1206 -0
  51. package/.claude/skills/code-patterns/SKILL.md +590 -0
  52. package/.claude/skills/collaborating-with-codex/SKILL.md +174 -0
  53. package/.claude/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  54. package/.claude/skills/collaborating-with-gemini/SKILL.md +194 -0
  55. package/.claude/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  56. package/.claude/skills/crud-development/SKILL.md +649 -0
  57. package/.claude/skills/data-permission/SKILL.md +599 -0
  58. package/.claude/skills/database-ops/SKILL.md +407 -0
  59. package/.claude/skills/error-handler/SKILL.md +371 -0
  60. package/.claude/skills/file-oss-management/SKILL.md +863 -0
  61. package/.claude/skills/git-workflow/SKILL.md +375 -0
  62. package/.claude/skills/json-serialization/SKILL.md +357 -0
  63. package/.claude/skills/leniu-api-development/SKILL.md +803 -0
  64. package/.claude/skills/leniu-architecture-design/SKILL.md +598 -0
  65. package/.claude/skills/leniu-backend-annotations/SKILL.md +664 -0
  66. package/.claude/skills/leniu-code-patterns/SKILL.md +365 -0
  67. package/.claude/skills/leniu-crud-development/SKILL.md +1110 -0
  68. package/.claude/skills/leniu-data-permission/SKILL.md +256 -0
  69. package/.claude/skills/leniu-database-ops/SKILL.md +426 -0
  70. package/.claude/skills/leniu-error-handler/SKILL.md +462 -0
  71. package/.claude/skills/leniu-java-amount-handling/SKILL.md +461 -0
  72. package/.claude/skills/leniu-java-code-style/SKILL.md +510 -0
  73. package/.claude/skills/leniu-java-concurrent/SKILL.md +400 -0
  74. package/.claude/skills/leniu-java-entity/SKILL.md +751 -0
  75. package/.claude/skills/leniu-java-export/SKILL.md +560 -0
  76. package/.claude/skills/leniu-java-logging/SKILL.md +832 -0
  77. package/.claude/skills/leniu-java-mq/SKILL.md +338 -0
  78. package/.claude/skills/leniu-java-mybatis/SKILL.md +640 -0
  79. package/.claude/skills/leniu-java-report-query-param/SKILL.md +291 -0
  80. package/.claude/skills/leniu-java-task/SKILL.md +367 -0
  81. package/.claude/skills/leniu-java-total-line/SKILL.md +195 -0
  82. package/.claude/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  83. package/.claude/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  84. package/.claude/skills/leniu-mealtime/SKILL.md +215 -0
  85. package/.claude/skills/leniu-redis-cache/SKILL.md +316 -0
  86. package/.claude/skills/leniu-security-guard/SKILL.md +520 -0
  87. package/.claude/skills/leniu-utils-toolkit/SKILL.md +380 -0
  88. package/.claude/skills/openspec-apply-change/SKILL.md +156 -0
  89. package/.claude/skills/openspec-archive-change/SKILL.md +114 -0
  90. package/.claude/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  91. package/.claude/skills/openspec-continue-change/SKILL.md +118 -0
  92. package/.claude/skills/openspec-explore/SKILL.md +290 -0
  93. package/.claude/skills/openspec-ff-change/SKILL.md +101 -0
  94. package/.claude/skills/openspec-new-change/SKILL.md +74 -0
  95. package/.claude/skills/openspec-onboard/SKILL.md +529 -0
  96. package/.claude/skills/openspec-sync-specs/SKILL.md +138 -0
  97. package/.claude/skills/openspec-verify-change/SKILL.md +168 -0
  98. package/.claude/skills/performance-doctor/SKILL.md +627 -0
  99. package/.claude/skills/project-navigator/SKILL.md +305 -0
  100. package/.claude/skills/redis-cache/SKILL.md +839 -0
  101. package/.claude/skills/scheduled-jobs/SKILL.md +633 -0
  102. package/.claude/skills/security-guard/SKILL.md +748 -0
  103. package/.claude/skills/sms-mail/SKILL.md +766 -0
  104. package/.claude/skills/social-login/SKILL.md +668 -0
  105. package/.claude/skills/store-pc/SKILL.md +366 -0
  106. package/.claude/skills/task-tracker/SKILL.md +307 -0
  107. package/.claude/skills/tech-decision/SKILL.md +393 -0
  108. package/.claude/skills/tenant-management/SKILL.md +603 -0
  109. package/.claude/skills/test-development/SKILL.md +755 -0
  110. package/.claude/skills/ui-pc/SKILL.md +438 -0
  111. package/.claude/skills/utils-toolkit/SKILL.md +615 -0
  112. package/.claude/skills/websocket-sse/SKILL.md +716 -0
  113. package/.claude/skills/workflow-engine/SKILL.md +676 -0
  114. package/.claude/templates//345/276/205/345/212/236/346/270/205/345/215/225/346/250/241/346/235/277.md +56 -0
  115. package/.claude/templates//351/234/200/346/261/202/346/226/207/346/241/243/346/250/241/346/235/277.md +85 -0
  116. package/.claude/templates//351/241/271/347/233/256/347/212/266/346/200/201/346/250/241/346/235/277.md +43 -0
  117. package/.codex/skills/add-skill/SKILL.md +352 -0
  118. package/.codex/skills/add-todo/SKILL.md +269 -0
  119. package/.codex/skills/api-development/SKILL.md +693 -0
  120. package/.codex/skills/architecture-design/SKILL.md +628 -0
  121. package/.codex/skills/backend-annotations/SKILL.md +664 -0
  122. package/.codex/skills/banana-image/CHANGELOG.md +37 -0
  123. package/.codex/skills/banana-image/README.md +146 -0
  124. package/.codex/skills/banana-image/SKILL.md +164 -0
  125. package/.codex/skills/banana-image/assets/logo.png +0 -0
  126. package/.codex/skills/banana-image/references/advanced-usage.md +189 -0
  127. package/.codex/skills/banana-image/scripts/apply_template.py +125 -0
  128. package/.codex/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  129. package/.codex/skills/banana-image/scripts/batch_prep.py +82 -0
  130. package/.codex/skills/banana-image/scripts/package-lock.json +1437 -0
  131. package/.codex/skills/banana-image/scripts/package.json +18 -0
  132. package/.codex/skills/banana-image/scripts/requirements.txt +10 -0
  133. package/.codex/skills/banana-image/templates/poster.json +22 -0
  134. package/.codex/skills/banana-image/templates/product.json +17 -0
  135. package/.codex/skills/banana-image/templates/social.json +22 -0
  136. package/.codex/skills/banana-image/templates/thumbnail.json +17 -0
  137. package/.codex/skills/brainstorm/SKILL.md +648 -0
  138. package/.codex/skills/bug-detective/SKILL.md +1206 -0
  139. package/.codex/skills/check/SKILL.md +367 -0
  140. package/.codex/skills/code-patterns/SKILL.md +442 -0
  141. package/.codex/skills/collaborating-with-codex/SKILL.md +174 -0
  142. package/.codex/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  143. package/.codex/skills/collaborating-with-gemini/SKILL.md +194 -0
  144. package/.codex/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  145. package/.codex/skills/crud/SKILL.md +265 -0
  146. package/.codex/skills/crud-development/SKILL.md +637 -0
  147. package/.codex/skills/data-permission/SKILL.md +591 -0
  148. package/.codex/skills/database-ops/SKILL.md +553 -0
  149. package/.codex/skills/dev/SKILL.md +187 -0
  150. package/.codex/skills/error-handler/SKILL.md +361 -0
  151. package/.codex/skills/file-oss-management/SKILL.md +863 -0
  152. package/.codex/skills/git-workflow/SKILL.md +375 -0
  153. package/.codex/skills/init-docs/SKILL.md +194 -0
  154. package/.codex/skills/json-serialization/SKILL.md +357 -0
  155. package/.codex/skills/leniu-api-development/SKILL.md +803 -0
  156. package/.codex/skills/leniu-architecture-design/SKILL.md +594 -0
  157. package/.codex/skills/leniu-backend-annotations/SKILL.md +662 -0
  158. package/.codex/skills/leniu-code-patterns/SKILL.md +365 -0
  159. package/.codex/skills/leniu-crud-development/SKILL.md +1110 -0
  160. package/.codex/skills/leniu-data-permission/SKILL.md +256 -0
  161. package/.codex/skills/leniu-database-ops/SKILL.md +426 -0
  162. package/.codex/skills/leniu-error-handler/SKILL.md +462 -0
  163. package/.codex/skills/leniu-java-amount-handling/SKILL.md +461 -0
  164. package/.codex/skills/leniu-java-code-style/SKILL.md +510 -0
  165. package/.codex/skills/leniu-java-concurrent/SKILL.md +400 -0
  166. package/.codex/skills/leniu-java-entity/SKILL.md +751 -0
  167. package/.codex/skills/leniu-java-export/SKILL.md +560 -0
  168. package/.codex/skills/leniu-java-logging/SKILL.md +832 -0
  169. package/.codex/skills/leniu-java-mq/SKILL.md +338 -0
  170. package/.codex/skills/leniu-java-mybatis/SKILL.md +640 -0
  171. package/.codex/skills/leniu-java-report-query-param/SKILL.md +291 -0
  172. package/.codex/skills/leniu-java-task/SKILL.md +367 -0
  173. package/.codex/skills/leniu-java-total-line/SKILL.md +195 -0
  174. package/.codex/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  175. package/.codex/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  176. package/.codex/skills/leniu-mealtime/SKILL.md +215 -0
  177. package/.codex/skills/leniu-redis-cache/SKILL.md +316 -0
  178. package/.codex/skills/leniu-security-guard/SKILL.md +520 -0
  179. package/.codex/skills/leniu-utils-toolkit/SKILL.md +378 -0
  180. package/.codex/skills/next/SKILL.md +137 -0
  181. package/.codex/skills/openspec-apply-change/SKILL.md +156 -0
  182. package/.codex/skills/openspec-archive-change/SKILL.md +114 -0
  183. package/.codex/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  184. package/.codex/skills/openspec-continue-change/SKILL.md +118 -0
  185. package/.codex/skills/openspec-explore/SKILL.md +290 -0
  186. package/.codex/skills/openspec-ff-change/SKILL.md +101 -0
  187. package/.codex/skills/openspec-new-change/SKILL.md +74 -0
  188. package/.codex/skills/openspec-onboard/SKILL.md +529 -0
  189. package/.codex/skills/openspec-sync-specs/SKILL.md +138 -0
  190. package/.codex/skills/openspec-verify-change/SKILL.md +168 -0
  191. package/.codex/skills/performance-doctor/SKILL.md +627 -0
  192. package/.codex/skills/progress/SKILL.md +193 -0
  193. package/.codex/skills/project-navigator/SKILL.md +286 -0
  194. package/.codex/skills/redis-cache/SKILL.md +829 -0
  195. package/.codex/skills/scheduled-jobs/SKILL.md +633 -0
  196. package/.codex/skills/security-guard/SKILL.md +739 -0
  197. package/.codex/skills/sms-mail/SKILL.md +766 -0
  198. package/.codex/skills/social-login/SKILL.md +668 -0
  199. package/.codex/skills/start/SKILL.md +154 -0
  200. package/.codex/skills/store-pc/SKILL.md +491 -0
  201. package/.codex/skills/sync/SKILL.md +149 -0
  202. package/.codex/skills/task-tracker/SKILL.md +307 -0
  203. package/.codex/skills/tech-decision/SKILL.md +393 -0
  204. package/.codex/skills/tenant-management/SKILL.md +603 -0
  205. package/.codex/skills/test-development/SKILL.md +755 -0
  206. package/.codex/skills/ui-pc/SKILL.md +475 -0
  207. package/.codex/skills/update-status/SKILL.md +159 -0
  208. package/.codex/skills/utils-toolkit/SKILL.md +593 -0
  209. package/.codex/skills/websocket-sse/SKILL.md +716 -0
  210. package/.codex/skills/workflow-engine/SKILL.md +676 -0
  211. package/.cursor/agents/code-reviewer.md +139 -0
  212. package/.cursor/agents/project-manager.md +159 -0
  213. package/.cursor/commands/opsx-apply.md +152 -0
  214. package/.cursor/commands/opsx-archive.md +157 -0
  215. package/.cursor/commands/opsx-bulk-archive.md +242 -0
  216. package/.cursor/commands/opsx-continue.md +114 -0
  217. package/.cursor/commands/opsx-explore.md +174 -0
  218. package/.cursor/commands/opsx-ff.md +94 -0
  219. package/.cursor/commands/opsx-new.md +69 -0
  220. package/.cursor/commands/opsx-onboard.md +525 -0
  221. package/.cursor/commands/opsx-sync.md +134 -0
  222. package/.cursor/commands/opsx-verify.md +164 -0
  223. package/.cursor/mcp.json +22 -0
  224. package/.cursor/skills/add-skill/SKILL.md +352 -0
  225. package/.cursor/skills/api-development/SKILL.md +560 -0
  226. package/.cursor/skills/architecture-design/SKILL.md +756 -0
  227. package/.cursor/skills/backend-annotations/SKILL.md +674 -0
  228. package/.cursor/skills/banana-image/CHANGELOG.md +37 -0
  229. package/.cursor/skills/banana-image/README.md +146 -0
  230. package/.cursor/skills/banana-image/SKILL.md +164 -0
  231. package/.cursor/skills/banana-image/assets/logo.png +0 -0
  232. package/.cursor/skills/banana-image/references/advanced-usage.md +189 -0
  233. package/.cursor/skills/banana-image/scripts/apply_template.py +125 -0
  234. package/.cursor/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  235. package/.cursor/skills/banana-image/scripts/batch_prep.py +82 -0
  236. package/.cursor/skills/banana-image/scripts/package-lock.json +1437 -0
  237. package/.cursor/skills/banana-image/scripts/package.json +18 -0
  238. package/.cursor/skills/banana-image/scripts/requirements.txt +10 -0
  239. package/.cursor/skills/banana-image/templates/poster.json +22 -0
  240. package/.cursor/skills/banana-image/templates/product.json +17 -0
  241. package/.cursor/skills/banana-image/templates/social.json +22 -0
  242. package/.cursor/skills/banana-image/templates/thumbnail.json +17 -0
  243. package/.cursor/skills/brainstorm/SKILL.md +648 -0
  244. package/.cursor/skills/bug-detective/SKILL.md +1206 -0
  245. package/.cursor/skills/code-patterns/SKILL.md +590 -0
  246. package/.cursor/skills/collaborating-with-codex/SKILL.md +174 -0
  247. package/.cursor/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  248. package/.cursor/skills/collaborating-with-gemini/SKILL.md +194 -0
  249. package/.cursor/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  250. package/.cursor/skills/crud-development/SKILL.md +649 -0
  251. package/.cursor/skills/data-permission/SKILL.md +599 -0
  252. package/.cursor/skills/database-ops/SKILL.md +407 -0
  253. package/.cursor/skills/error-handler/SKILL.md +371 -0
  254. package/.cursor/skills/file-oss-management/SKILL.md +863 -0
  255. package/.cursor/skills/git-workflow/SKILL.md +375 -0
  256. package/.cursor/skills/json-serialization/SKILL.md +357 -0
  257. package/.cursor/skills/leniu-api-development/SKILL.md +803 -0
  258. package/.cursor/skills/leniu-architecture-design/SKILL.md +598 -0
  259. package/.cursor/skills/leniu-backend-annotations/SKILL.md +664 -0
  260. package/.cursor/skills/leniu-code-patterns/SKILL.md +365 -0
  261. package/.cursor/skills/leniu-crud-development/SKILL.md +1110 -0
  262. package/.cursor/skills/leniu-data-permission/SKILL.md +256 -0
  263. package/.cursor/skills/leniu-database-ops/SKILL.md +426 -0
  264. package/.cursor/skills/leniu-error-handler/SKILL.md +462 -0
  265. package/.cursor/skills/leniu-java-amount-handling/SKILL.md +461 -0
  266. package/.cursor/skills/leniu-java-code-style/SKILL.md +510 -0
  267. package/.cursor/skills/leniu-java-concurrent/SKILL.md +400 -0
  268. package/.cursor/skills/leniu-java-entity/SKILL.md +751 -0
  269. package/.cursor/skills/leniu-java-export/SKILL.md +560 -0
  270. package/.cursor/skills/leniu-java-logging/SKILL.md +832 -0
  271. package/.cursor/skills/leniu-java-mq/SKILL.md +338 -0
  272. package/.cursor/skills/leniu-java-mybatis/SKILL.md +640 -0
  273. package/.cursor/skills/leniu-java-report-query-param/SKILL.md +291 -0
  274. package/.cursor/skills/leniu-java-task/SKILL.md +367 -0
  275. package/.cursor/skills/leniu-java-total-line/SKILL.md +195 -0
  276. package/.cursor/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  277. package/.cursor/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  278. package/.cursor/skills/leniu-mealtime/SKILL.md +215 -0
  279. package/.cursor/skills/leniu-redis-cache/SKILL.md +316 -0
  280. package/.cursor/skills/leniu-security-guard/SKILL.md +520 -0
  281. package/.cursor/skills/leniu-utils-toolkit/SKILL.md +380 -0
  282. package/.cursor/skills/openspec-apply-change/SKILL.md +156 -0
  283. package/.cursor/skills/openspec-archive-change/SKILL.md +114 -0
  284. package/.cursor/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  285. package/.cursor/skills/openspec-continue-change/SKILL.md +118 -0
  286. package/.cursor/skills/openspec-explore/SKILL.md +290 -0
  287. package/.cursor/skills/openspec-ff-change/SKILL.md +101 -0
  288. package/.cursor/skills/openspec-new-change/SKILL.md +74 -0
  289. package/.cursor/skills/openspec-onboard/SKILL.md +529 -0
  290. package/.cursor/skills/openspec-sync-specs/SKILL.md +138 -0
  291. package/.cursor/skills/openspec-verify-change/SKILL.md +168 -0
  292. package/.cursor/skills/performance-doctor/SKILL.md +627 -0
  293. package/.cursor/skills/project-navigator/SKILL.md +305 -0
  294. package/.cursor/skills/redis-cache/SKILL.md +839 -0
  295. package/.cursor/skills/scheduled-jobs/SKILL.md +633 -0
  296. package/.cursor/skills/security-guard/SKILL.md +748 -0
  297. package/.cursor/skills/sms-mail/SKILL.md +766 -0
  298. package/.cursor/skills/social-login/SKILL.md +668 -0
  299. package/.cursor/skills/store-pc/SKILL.md +366 -0
  300. package/.cursor/skills/task-tracker/SKILL.md +307 -0
  301. package/.cursor/skills/tech-decision/SKILL.md +393 -0
  302. package/.cursor/skills/tenant-management/SKILL.md +603 -0
  303. package/.cursor/skills/test-development/SKILL.md +755 -0
  304. package/.cursor/skills/ui-pc/SKILL.md +438 -0
  305. package/.cursor/skills/utils-toolkit/SKILL.md +615 -0
  306. package/.cursor/skills/websocket-sse/SKILL.md +716 -0
  307. package/.cursor/skills/workflow-engine/SKILL.md +676 -0
  308. package/AGENTS.md +669 -0
  309. package/CLAUDE.md +205 -0
  310. package/README.md +205 -0
  311. package/bin/index.js +179 -0
  312. package/init.sh +178 -0
  313. package/package.json +27 -0
@@ -0,0 +1,1110 @@
1
+ ---
2
+ name: leniu-crud-development
3
+ description: |
4
+ leniu 项目 CRUD 开发规范。基于 pigx-framework 三层架构(Controller → Service → Mapper),无独立 DAO 层。
5
+ 涵盖 Service 层事务管理、分页查询模式、并发处理、代码质量要点。
6
+
7
+ 触发场景:
8
+ - 新建 leniu 业务模块的 CRUD 功能
9
+ - 创建 Entity、DTO、VO、Service、Mapper、Controller
10
+ - 分页查询、新增、修改、删除、导出
11
+ - 查询条件构建(LambdaQueryWrapper)
12
+ - Service 层事务管理(多表操作、事务回滚)
13
+ - 并发处理(CompletableFuture、分布式锁)
14
+ - 代码质量:空指针防护、返回值兜底、集合参数防御
15
+
16
+ 适用项目:
17
+ - leniu-tengyun-core(云食堂核心服务)
18
+ - leniu-yunshitang(云食堂业务服务)
19
+
20
+ 触发词:leniu-CRUD、leniu-增删改查、leniu-新建模块、leniu-Entity、leniu-DTO、leniu-VO、leniu-Service、leniu-Mapper、leniu-Controller、leniu-分页查询、LeRequest、PageDTO、net.xnzn、leniu-yunshitang、云食堂CRUD、leniu-事务、leniu-并发、leniu-代码质量、leniu-空指针、leniu-ServiceImpl
21
+ ---
22
+
23
+ # leniu CRUD 开发规范
24
+
25
+ ## 项目定位
26
+
27
+ 本技能专用于 **leniu 云食堂项目** 的 CRUD 功能开发。
28
+
29
+ | 项目 | 路径 |
30
+ |------|------|
31
+ | **leniu-tengyun-core** | `/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core` |
32
+ | **leniu-yunshitang** | `/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang` |
33
+ | **包名前缀** | `net.xnzn.core.*` |
34
+
35
+ ---
36
+
37
+ ## 核心架构差异
38
+
39
+ | 对比项 | leniu-tengyun-core |
40
+ |--------|-------------------|
41
+ | **包名前缀** | `net.xnzn.*` |
42
+ | **架构** | 三层:Controller → Service → Mapper |
43
+ | **DAO 层** | 不存在,Service 直接注入 Mapper |
44
+ | **查询构建** | Service 层 LambdaQueryWrapper |
45
+ | **Mapper 继承** | `BaseMapper<Entity>` |
46
+ | **对象转换** | `BeanUtil.copyProperties()` (Hutool) |
47
+ | **Entity 基类** | 无基类(自定义审计字段) |
48
+ | **请求封装** | `LeRequest<T>` 包装 |
49
+ | **响应封装** | `Page<T>`, `LeResponse<T>`, `void` |
50
+ | **分组校验** | `InsertGroup`, `UpdateGroup` |
51
+ | **认证注解** | `@RequiresAuthentication`, `@RequiresGuest` |
52
+ | **异常类** | `LeException` |
53
+ | **审计字段** | `crby/crtime/upby/uptime` |
54
+ | **逻辑删除** | `del_flag` (1=删除, 2=正常) |
55
+ | **主键策略** | 雪花ID 或 自增ID |
56
+ | **Mapper XML** | 与 Java 同目录(非 resources/mapper) |
57
+
58
+ ---
59
+
60
+ ## 1. Entity 实体类
61
+
62
+ ```java
63
+ package net.xnzn.core.xxx.model;
64
+
65
+ import com.baomidou.mybatisplus.annotation.*;
66
+ import io.swagger.annotations.ApiModel;
67
+ import io.swagger.annotations.ApiModelProperty;
68
+ import lombok.Data;
69
+ import lombok.experimental.Accessors;
70
+
71
+ import java.io.Serializable;
72
+ import java.time.LocalDateTime;
73
+
74
+ /**
75
+ * XXX 对象
76
+ */
77
+ @Data
78
+ @Accessors(chain = true)
79
+ @TableName("xxx_table")
80
+ @ApiModel(value = "XXX对象", description = "XXX表")
81
+ public class XxxEntity implements Serializable {
82
+
83
+ private static final long serialVersionUID = 1L;
84
+
85
+ /**
86
+ * 主键 ID
87
+ */
88
+ @ApiModelProperty("主键ID")
89
+ @TableId(value = "id", type = IdType.AUTO)
90
+ private Long id;
91
+
92
+ /**
93
+ * 名称
94
+ */
95
+ @ApiModelProperty("名称")
96
+ @TableField("name")
97
+ private String name;
98
+
99
+ /**
100
+ * 状态
101
+ */
102
+ @ApiModelProperty("状态")
103
+ @TableField("status")
104
+ private Integer status;
105
+
106
+ /**
107
+ * 删除标识(1删除,2正常)
108
+ */
109
+ @ApiModelProperty("删除标识(1删除,2正常)")
110
+ @TableField("del_flag")
111
+ private Integer delFlag;
112
+
113
+ /**
114
+ * 乐观锁
115
+ */
116
+ @ApiModelProperty("乐观锁")
117
+ @TableField("revision")
118
+ private Integer revision;
119
+
120
+ /**
121
+ * 创建人
122
+ */
123
+ @ApiModelProperty("创建人")
124
+ @TableField(value = "crby", fill = FieldFill.INSERT)
125
+ private String crby;
126
+
127
+ /**
128
+ * 创建时间
129
+ */
130
+ @ApiModelProperty("创建时间")
131
+ @TableField(value = "crtime", fill = FieldFill.INSERT)
132
+ private LocalDateTime crtime;
133
+
134
+ /**
135
+ * 更新人
136
+ */
137
+ @ApiModelProperty("更新人")
138
+ @TableField(value = "upby", fill = FieldFill.INSERT_UPDATE)
139
+ private String upby;
140
+
141
+ /**
142
+ * 更新时间
143
+ */
144
+ @ApiModelProperty("更新时间")
145
+ @TableField(value = "uptime", fill = FieldFill.INSERT_UPDATE)
146
+ private LocalDateTime uptime;
147
+ }
148
+ ```
149
+
150
+ ---
151
+
152
+ ## 2. DTO 数据传输对象
153
+
154
+ ```java
155
+ package net.xnzn.core.xxx.dto;
156
+
157
+ import io.swagger.annotations.ApiModel;
158
+ import io.swagger.annotations.ApiModelProperty;
159
+ import lombok.Data;
160
+
161
+ import jakarta.validation.constraints.*;
162
+ import java.io.Serializable;
163
+ import java.util.Date;
164
+
165
+ /**
166
+ * XXX DTO
167
+ */
168
+ @Data
169
+ @ApiModel("XXX DTO")
170
+ public class XxxDTO implements Serializable {
171
+
172
+ private static final long serialVersionUID = 1L;
173
+
174
+ @ApiModelProperty(value = "主键ID")
175
+ @NotNull(message = "主键ID不能为空", groups = {UpdateGroup.class})
176
+ private Long id;
177
+
178
+ @ApiModelProperty(value = "名称", required = true)
179
+ @NotBlank(message = "名称不能为空", groups = {InsertGroup.class, UpdateGroup.class})
180
+ @Size(max = 100, message = "名称长度不能超过100个字符")
181
+ private String name;
182
+
183
+ @ApiModelProperty(value = "状态")
184
+ private Integer status;
185
+
186
+ @ApiModelProperty(value = "开始时间", required = true)
187
+ @NotNull(message = "开始时间不能为空", groups = {InsertGroup.class, UpdateGroup.class})
188
+ private Date startTime;
189
+
190
+ @ApiModelProperty(value = "结束时间", required = true)
191
+ @NotNull(message = "结束时间不能为空", groups = {InsertGroup.class, UpdateGroup.class})
192
+ private Date endTime;
193
+ }
194
+ ```
195
+
196
+ ---
197
+
198
+ ## 3. VO 视图对象
199
+
200
+ ```java
201
+ package net.xnzn.core.xxx.vo;
202
+
203
+ import com.fasterxml.jackson.annotation.JsonFormat;
204
+ import io.swagger.annotations.ApiModel;
205
+ import io.swagger.annotations.ApiModelProperty;
206
+ import lombok.Data;
207
+
208
+ import java.io.Serializable;
209
+ import java.util.Date;
210
+
211
+ /**
212
+ * XXX VO
213
+ */
214
+ @Data
215
+ @ApiModel("XXX VO")
216
+ public class XxxVO implements Serializable {
217
+
218
+ private static final long serialVersionUID = 1L;
219
+
220
+ @ApiModelProperty("主键ID")
221
+ private Long id;
222
+
223
+ @ApiModelProperty("名称")
224
+ private String name;
225
+
226
+ @ApiModelProperty("状态")
227
+ private String status;
228
+
229
+ @ApiModelProperty("状态描述")
230
+ private String statusDesc;
231
+
232
+ @ApiModelProperty(value = "创建时间")
233
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
234
+ private Date crtime;
235
+
236
+ @ApiModelProperty(value = "更新时间")
237
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
238
+ private Date uptime;
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ## 4. Service 接口
245
+
246
+ ```java
247
+ package net.xnzn.core.xxx.service;
248
+
249
+ import net.xnzn.core.xxx.dto.XxxDTO;
250
+ import net.xnzn.core.xxx.vo.XxxVO;
251
+ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
252
+
253
+ import java.util.List;
254
+
255
+ /**
256
+ * XXX 服务接口
257
+ */
258
+ public interface XxxService {
259
+
260
+ /**
261
+ * 新增
262
+ */
263
+ Long add(XxxDTO dto);
264
+
265
+ /**
266
+ * 修改
267
+ */
268
+ void update(XxxDTO dto);
269
+
270
+ /**
271
+ * 删除
272
+ */
273
+ void delete(Long id);
274
+
275
+ /**
276
+ * 根据ID查询
277
+ */
278
+ XxxVO getById(Long id);
279
+
280
+ /**
281
+ * 分页查询
282
+ */
283
+ Page<XxxVO> page(XxxDTO dto);
284
+
285
+ /**
286
+ * 查询列表
287
+ */
288
+ List<XxxVO> list(XxxDTO dto);
289
+ }
290
+ ```
291
+
292
+ ---
293
+
294
+ ## 5. Service 实现类
295
+
296
+ ```java
297
+ package net.xnzn.core.xxx.service.impl;
298
+
299
+ import cn.hutool.core.bean.BeanUtil;
300
+ import cn.hutool.core.collection.CollUtil;
301
+ import cn.hutool.core.util.ObjectUtil;
302
+ import cn.hutool.core.util.StrUtil;
303
+ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
304
+ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
305
+ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
306
+ import com.pig4cloud.pigx.common.core.exception.LeException;
307
+ import lombok.extern.slf4j.Slf4j;
308
+ import net.xnzn.core.xxx.dto.XxxDTO;
309
+ import net.xnzn.core.xxx.mapper.XxxMapper;
310
+ import net.xnzn.core.xxx.model.XxxEntity;
311
+ import net.xnzn.core.xxx.service.XxxService;
312
+ import net.xnzn.core.xxx.vo.XxxVO;
313
+ import org.springframework.stereotype.Service;
314
+ import org.springframework.transaction.annotation.Transactional;
315
+
316
+ import javax.annotation.Resource;
317
+ import java.util.Collections;
318
+ import java.util.List;
319
+ import java.util.Optional;
320
+ import java.util.stream.Collectors;
321
+
322
+ /**
323
+ * XXX 服务实现
324
+ */
325
+ @Slf4j
326
+ @Service
327
+ public class XxxServiceImpl implements XxxService {
328
+
329
+ @Resource
330
+ private XxxMapper xxxMapper;
331
+
332
+ @Override
333
+ @Transactional(rollbackFor = Exception.class)
334
+ public Long add(XxxDTO dto) {
335
+ log.info("开始新增XXX,名称: {}", dto.getName());
336
+
337
+ // 参数校验
338
+ if (StrUtil.isBlank(dto.getName())) {
339
+ throw new LeException("名称不能为空");
340
+ }
341
+
342
+ // 业务校验:唯一性检查
343
+ LambdaQueryWrapper<XxxEntity> wrapper = Wrappers.lambdaQuery();
344
+ wrapper.eq(XxxEntity::getName, dto.getName());
345
+ wrapper.eq(XxxEntity::getDelFlag, 2);
346
+ Long count = xxxMapper.selectCount(wrapper);
347
+ if (count > 0) {
348
+ throw new LeException("名称已存在");
349
+ }
350
+
351
+ // 转换并保存
352
+ XxxEntity entity = BeanUtil.copyProperties(dto, XxxEntity.class);
353
+ entity.setDelFlag(2); // 正常状态
354
+
355
+ xxxMapper.insert(entity);
356
+
357
+ log.info("新增XXX成功,ID: {}", entity.getId());
358
+ return entity.getId();
359
+ }
360
+
361
+ @Override
362
+ @Transactional(rollbackFor = Exception.class)
363
+ public void update(XxxDTO dto) {
364
+ if (ObjectUtil.isNull(dto.getId())) {
365
+ throw new LeException("ID不能为空");
366
+ }
367
+
368
+ // 查询并校验记录存在
369
+ XxxEntity exist = Optional.ofNullable(xxxMapper.selectById(dto.getId()))
370
+ .orElseThrow(() -> new LeException("记录不存在"));
371
+
372
+ XxxEntity entity = BeanUtil.copyProperties(dto, XxxEntity.class);
373
+ xxxMapper.updateById(entity);
374
+
375
+ log.info("更新XXX成功,ID: {}", dto.getId());
376
+ }
377
+
378
+ @Override
379
+ @Transactional(rollbackFor = Exception.class)
380
+ public void delete(Long id) {
381
+ if (ObjectUtil.isNull(id)) {
382
+ throw new LeException("ID不能为空");
383
+ }
384
+
385
+ Optional.ofNullable(xxxMapper.selectById(id))
386
+ .orElseThrow(() -> new LeException("记录不存在"));
387
+
388
+ // 逻辑删除
389
+ XxxEntity entity = new XxxEntity();
390
+ entity.setId(id);
391
+ entity.setDelFlag(1); // 删除状态
392
+
393
+ xxxMapper.updateById(entity);
394
+
395
+ log.info("删除XXX成功,ID: {}", id);
396
+ }
397
+
398
+ @Override
399
+ public XxxVO getById(Long id) {
400
+ if (ObjectUtil.isNull(id)) {
401
+ throw new LeException("ID不能为空");
402
+ }
403
+
404
+ XxxEntity entity = Optional.ofNullable(xxxMapper.selectById(id))
405
+ .orElseThrow(() -> new LeException("记录不存在"));
406
+
407
+ return BeanUtil.copyProperties(entity, XxxVO.class);
408
+ }
409
+
410
+ @Override
411
+ public Page<XxxVO> page(XxxDTO dto) {
412
+ LambdaQueryWrapper<XxxEntity> wrapper = buildWrapper(dto);
413
+
414
+ // 分页
415
+ Page<XxxEntity> page = new Page<>(dto.getPageNum(), dto.getPageSize());
416
+ Page<XxxEntity> result = xxxMapper.selectPage(page, wrapper);
417
+
418
+ // 转换为 VO
419
+ Page<XxxVO> voPage = new Page<>();
420
+ BeanUtil.copyProperties(result, voPage, "records");
421
+ List<XxxVO> voList = result.getRecords().stream()
422
+ .map(entity -> BeanUtil.copyProperties(entity, XxxVO.class))
423
+ .collect(Collectors.toList());
424
+ voPage.setRecords(voList);
425
+
426
+ return voPage;
427
+ }
428
+
429
+ @Override
430
+ public List<XxxVO> list(XxxDTO dto) {
431
+ LambdaQueryWrapper<XxxEntity> wrapper = buildWrapper(dto);
432
+
433
+ List<XxxEntity> list = xxxMapper.selectList(wrapper);
434
+
435
+ // 空集合兜底
436
+ if (CollUtil.isEmpty(list)) {
437
+ return Collections.emptyList();
438
+ }
439
+
440
+ return list.stream()
441
+ .map(entity -> BeanUtil.copyProperties(entity, XxxVO.class))
442
+ .collect(Collectors.toList());
443
+ }
444
+
445
+ /**
446
+ * 构建查询条件
447
+ */
448
+ private LambdaQueryWrapper<XxxEntity> buildWrapper(XxxDTO dto) {
449
+ LambdaQueryWrapper<XxxEntity> wrapper = Wrappers.lambdaQuery();
450
+
451
+ // 只查询正常数据
452
+ wrapper.eq(XxxEntity::getDelFlag, 2);
453
+
454
+ // 名称模糊查询
455
+ if (StrUtil.isNotBlank(dto.getName())) {
456
+ wrapper.like(XxxEntity::getName, dto.getName());
457
+ }
458
+
459
+ // 状态精确查询
460
+ if (ObjectUtil.isNotNull(dto.getStatus())) {
461
+ wrapper.eq(XxxEntity::getStatus, dto.getStatus());
462
+ }
463
+
464
+ // 按创建时间倒序
465
+ wrapper.orderByDesc(XxxEntity::getCrtime);
466
+
467
+ return wrapper;
468
+ }
469
+ }
470
+ ```
471
+
472
+ ---
473
+
474
+ ## 6. Mapper 接口
475
+
476
+ ```java
477
+ package net.xnzn.core.xxx.mapper;
478
+
479
+ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
480
+ import net.xnzn.core.xxx.model.XxxEntity;
481
+ import org.apache.ibatis.annotations.Mapper;
482
+
483
+ /**
484
+ * XXX Mapper 接口
485
+ */
486
+ @Mapper
487
+ public interface XxxMapper extends BaseMapper<XxxEntity> {
488
+
489
+ // 继承 BaseMapper,已提供常用 CRUD 方法
490
+ // 如需自定义 SQL,在此添加方法并在对应的 XML 中实现
491
+ }
492
+ ```
493
+
494
+ ---
495
+
496
+ ## 7. Mapper XML(与 Java 同目录)
497
+
498
+ ```xml
499
+ <?xml version="1.0" encoding="UTF-8"?>
500
+ <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
501
+ <mapper namespace="net.xnzn.core.xxx.mapper.XxxMapper">
502
+
503
+ <!-- 禁止使用 SELECT *,明确指定查询字段 -->
504
+ <select id="selectCustom" resultType="net.xnzn.core.xxx.model.XxxEntity">
505
+ SELECT id, name, status, del_flag, crby, crtime, upby, uptime
506
+ FROM xxx_table
507
+ WHERE del_flag = 2
508
+ </select>
509
+
510
+ </mapper>
511
+ ```
512
+
513
+ ---
514
+
515
+ ## 8. Controller 控制器
516
+
517
+ ```java
518
+ package net.xnzn.core.xxx.controller;
519
+
520
+ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
521
+ import com.pig4cloud.pigx.common.core.util.LeRequest;
522
+ import io.swagger.annotations.Api;
523
+ import io.swagger.annotations.ApiOperation;
524
+ import lombok.extern.slf4j.Slf4j;
525
+ import net.xnzn.core.xxx.dto.XxxDTO;
526
+ import net.xnzn.core.xxx.service.XxxService;
527
+ import net.xnzn.core.xxx.vo.XxxVO;
528
+ import net.xnzn.framework.secure.filter.annotation.RequiresAuthentication;
529
+ import net.xnzn.framework.secure.filter.annotation.RequiresGuest;
530
+ import org.springframework.validation.annotation.Validated;
531
+ import org.springframework.web.bind.annotation.*;
532
+
533
+ import javax.annotation.Resource;
534
+ import java.util.List;
535
+
536
+ /**
537
+ * XXX 管理控制器
538
+ */
539
+ @Slf4j
540
+ @RestController
541
+ @RequestMapping("/api/xxx")
542
+ @Api(tags = "XXX管理")
543
+ public class XxxController {
544
+
545
+ @Resource
546
+ private XxxService xxxService;
547
+
548
+ @PostMapping("/add")
549
+ @ApiOperation(value = "XXX-新增")
550
+ @RequiresAuthentication
551
+ public Long add(@Validated(InsertGroup.class) @RequestBody LeRequest<XxxDTO> request) {
552
+ return xxxService.add(request.getContent());
553
+ }
554
+
555
+ @PostMapping("/update")
556
+ @ApiOperation(value = "XXX-修改")
557
+ @RequiresAuthentication
558
+ public void update(@Validated(UpdateGroup.class) @RequestBody LeRequest<XxxDTO> request) {
559
+ xxxService.update(request.getContent());
560
+ }
561
+
562
+ @PostMapping("/delete")
563
+ @ApiOperation(value = "XXX-删除")
564
+ @RequiresAuthentication
565
+ public void delete(@RequestBody LeRequest<Long> request) {
566
+ xxxService.delete(request.getContent());
567
+ }
568
+
569
+ @GetMapping("/get/{id}")
570
+ @ApiOperation(value = "XXX-获取详情")
571
+ @RequiresGuest
572
+ public XxxVO getById(@PathVariable Long id) {
573
+ return xxxService.getById(id);
574
+ }
575
+
576
+ @PostMapping("/page")
577
+ @ApiOperation(value = "XXX-分页查询")
578
+ @RequiresAuthentication
579
+ public Page<XxxVO> page(@Validated @RequestBody LeRequest<XxxDTO> request) {
580
+ return xxxService.page(request.getContent());
581
+ }
582
+
583
+ @PostMapping("/list")
584
+ @ApiOperation(value = "XXX-查询列表")
585
+ @RequiresGuest
586
+ public List<XxxVO> list(@RequestBody LeRequest<XxxDTO> request) {
587
+ return xxxService.list(request.getContent());
588
+ }
589
+ }
590
+ ```
591
+
592
+ ---
593
+
594
+ ## 9. 数据库建表(SQL)
595
+
596
+ ```sql
597
+ -- 表名:xxx_table(根据业务命名,格式: {模块}_{业务名})
598
+ CREATE TABLE `xxx_table` (
599
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
600
+
601
+ -- 业务字段
602
+ `name` VARCHAR(100) NOT NULL COMMENT '名称',
603
+ `status` TINYINT(1) DEFAULT 1 COMMENT '状态(0停用 1启用)',
604
+
605
+ -- 删除标识(注意:1=删除,2=正常)
606
+ `del_flag` TINYINT(1) DEFAULT 2 COMMENT '删除标识(1删除 2正常)',
607
+ `revision` INT DEFAULT 0 COMMENT '乐观锁版本号',
608
+
609
+ -- 审计字段
610
+ `crby` VARCHAR(64) DEFAULT NULL COMMENT '创建人',
611
+ `crtime` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
612
+ `upby` VARCHAR(64) DEFAULT NULL COMMENT '更新人',
613
+ `uptime` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
614
+
615
+ PRIMARY KEY (`id`),
616
+ KEY `idx_status` (`status`),
617
+ KEY `idx_crtime` (`crtime`),
618
+ KEY `idx_del_flag` (`del_flag`)
619
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='XXX表';
620
+ ```
621
+
622
+ ---
623
+
624
+ ## 10. 事务管理规范
625
+
626
+ ### 多表操作必须加事务
627
+
628
+ ```java
629
+ /**
630
+ * 多表操作示例:创建订单同时更新库存
631
+ */
632
+ @Transactional(rollbackFor = Exception.class)
633
+ public void createOrderWithStock(OrderDTO dto) {
634
+ // 操作订单表
635
+ OrderEntity order = BeanUtil.copyProperties(dto, OrderEntity.class);
636
+ orderMapper.insert(order);
637
+
638
+ // 操作订单明细表
639
+ List<OrderDetailEntity> details = dto.getDetails().stream()
640
+ .map(d -> BeanUtil.copyProperties(d, OrderDetailEntity.class))
641
+ .collect(Collectors.toList());
642
+ orderDetailMapper.insert(details);
643
+
644
+ // 操作库存表
645
+ stockMapper.deduct(dto.getStockId(), dto.getQuantity());
646
+ }
647
+ ```
648
+
649
+ ### 查询方法不加事务
650
+
651
+ ```java
652
+ // 查询方法不需要事务,避免不必要的事务开销
653
+ public XxxVO getById(Long id) {
654
+ // 无需 @Transactional
655
+ XxxEntity entity = xxxMapper.selectById(id);
656
+ ...
657
+ }
658
+ ```
659
+
660
+ ### 手动回滚(特殊场景)
661
+
662
+ ```java
663
+ @Transactional(rollbackFor = Exception.class)
664
+ public void businessMethod() {
665
+ try {
666
+ // 业务逻辑
667
+ } catch (Exception e) {
668
+ log.error("处理失败", e);
669
+ // 手动标记回滚
670
+ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
671
+ throw new LeException("处理失败");
672
+ }
673
+ }
674
+ ```
675
+
676
+ ### Self 自注入(同类事务调用)
677
+
678
+ 当 Service/Business 中的方法 A 需要调用同类的带 `@Transactional` 方法 B 时,必须通过 `self` 代理调用,否则事务不生效:
679
+
680
+ ```java
681
+ @Slf4j
682
+ @Service
683
+ public class OrderPlaceBusiness {
684
+
685
+ @Autowired
686
+ @Lazy
687
+ private OrderPlaceBusiness self; // ⚠️ 自注入,用于触发 Spring AOP 事务代理
688
+
689
+ /**
690
+ * 外部调用入口(可以不加事务)
691
+ */
692
+ public void doSave(OrderSavePO orderSavePO) {
693
+ // ✅ 通过 self 调用,保证 @Transactional 生效
694
+ self.save(orderSavePO, false, false);
695
+ }
696
+
697
+ /**
698
+ * 实际保存逻辑
699
+ */
700
+ @Transactional(rollbackFor = Exception.class)
701
+ public void save(OrderSavePO orderSavePO, boolean orderExists, boolean removeDetails) {
702
+ // 多表操作...
703
+ }
704
+ }
705
+ ```
706
+
707
+ **规则**:
708
+ - 同类方法互调,被调用方有 `@Transactional` → 必须用 `self.xxx()` 而非 `this.xxx()`
709
+ - `self` 字段必须配合 `@Autowired @Lazy` 避免循环依赖
710
+ - 纯方法重载(无 `@Transactional`)的情况无需 `self`
711
+
712
+ ---
713
+
714
+ ## 11. 分页查询模式
715
+
716
+ ### 标准 MyBatis-Plus 分页
717
+
718
+ ```java
719
+ @Override
720
+ public Page<XxxVO> page(XxxDTO dto) {
721
+ LambdaQueryWrapper<XxxEntity> wrapper = buildWrapper(dto);
722
+
723
+ // 创建分页对象
724
+ Page<XxxEntity> page = new Page<>(dto.getPageNum(), dto.getPageSize());
725
+ Page<XxxEntity> result = xxxMapper.selectPage(page, wrapper);
726
+
727
+ // 转换 VO
728
+ Page<XxxVO> voPage = new Page<>();
729
+ BeanUtil.copyProperties(result, voPage, "records");
730
+ voPage.setRecords(BeanUtil.copyToList(result.getRecords(), XxxVO.class));
731
+
732
+ return voPage;
733
+ }
734
+ ```
735
+
736
+ ### PageHelper 分页(报表场景)
737
+
738
+ ```java
739
+ public PageVO<XxxVO> pageList(XxxPageParam param) {
740
+ // 1. 开启分页:必须传入 param.getPage()(PageDTO对象),不能传整个 param
741
+ if (Objects.nonNull(param.getPage())) {
742
+ PageMethod.startPage(param.getPage());
743
+ }
744
+
745
+ // 2. 执行查询
746
+ List<XxxVO> records = xxxMapper.pageList(param);
747
+
748
+ // 3. 封装分页结果(自动提取 total 等信息)
749
+ return PageVO.of(records);
750
+ }
751
+ ```
752
+
753
+ **分页关键规则**:
754
+ 1. `PageMethod.startPage(param.getPage())` 必须紧接在查询前调用,传 `PageDTO` 对象
755
+ 2. 调用 startPage 和查询之间不能插入其他查询,否则分页失效
756
+ 3. `PageVO.of()` 自动从 Page 对象提取分页信息
757
+ 4. Mapper 方法返回 `List` 即可,PageHelper 自动转换
758
+
759
+ ### 带合计行的分页(报表场景)
760
+
761
+ ```java
762
+ public ReportBaseTotalVO<XxxVO> pageWithTotal(XxxPageParam param) {
763
+ MgrUserAuthPO authPO = mgrAuthApi.getUserAuthPO();
764
+ ReportDataPermissionParam dataPermission = reportDataPermissionService.getDataPermission(authPO);
765
+
766
+ if (Objects.nonNull(param.getPage())) {
767
+ PageMethod.startPage(param.getPage());
768
+ }
769
+ List<XxxVO> list = xxxMapper.getSummaryList(param, authPO, dataPermission);
770
+ XxxVO totalLine = Optional.ofNullable(xxxMapper.getSummaryTotal(param, authPO, dataPermission))
771
+ .orElse(new XxxVO());
772
+ return new ReportBaseTotalVO<XxxVO>()
773
+ .setResultPage(PageVO.of(list))
774
+ .setTotalLine(totalLine);
775
+ }
776
+ ```
777
+
778
+ ---
779
+
780
+ ## 12. 报表 Service 模式(含数据权限)
781
+
782
+ 报表模块 Service 直接作为 `@Service` 类(**无接口**),标准模式如下:
783
+
784
+ ```java
785
+ @Slf4j
786
+ @Service
787
+ public class ReportXxxService {
788
+
789
+ @Autowired
790
+ private ReportXxxMapper reportXxxMapper;
791
+ @Autowired
792
+ private MgrAuthV2Api mgrAuthApi;
793
+ @Autowired
794
+ private ReportDataPermissionService reportDataPermissionService;
795
+ @Resource(name = "yunshitangTaskExecutor")
796
+ private AsyncTaskExecutor asyncTaskExecutor;
797
+
798
+ /**
799
+ * 分页查询(含合计行)
800
+ */
801
+ public ReportBaseTotalVO<XxxVO> pageXxx(XxxParam param) {
802
+ long start = System.currentTimeMillis();
803
+ MgrUserAuthPO authPO = mgrAuthApi.getUserAuthPO();
804
+ ReportDataPermissionParam dataPermission = reportDataPermissionService.getDataPermission(authPO);
805
+
806
+ if (Objects.nonNull(param.getPage())) {
807
+ PageMethod.startPage(param.getPage());
808
+ }
809
+ List<XxxVO> list = reportXxxMapper.pageXxx(param, authPO, dataPermission);
810
+ XxxVO totalLine = Optional.ofNullable(reportXxxMapper.sumXxx(param, authPO, dataPermission))
811
+ .orElse(new XxxVO());
812
+ log.info("pageXxx耗时:{}", System.currentTimeMillis() - start);
813
+ return new ReportBaseTotalVO<XxxVO>()
814
+ .setResultPage(PageVO.of(list))
815
+ .setTotalLine(totalLine);
816
+ }
817
+ }
818
+ ```
819
+
820
+ **报表 Service 关键规则**:
821
+ - 通过 `mgrAuthApi.getUserAuthPO()` 获取当前用户权限
822
+ - 通过 `reportDataPermissionService.getDataPermission(authPO)` 获取数据权限
823
+ - 线程池名称为 `yunshitangTaskExecutor`(用 `@Resource(name = "yunshitangTaskExecutor")`)
824
+ - 性能日志:`log.info("方法名耗时:{}", System.currentTimeMillis() - start)`
825
+
826
+ ---
827
+
828
+ ## 13. 并发处理
829
+
830
+ ### CompletableFuture 并行查询(报表场景)
831
+
832
+ ```java
833
+ @Resource(name = "yunshitangTaskExecutor")
834
+ private AsyncTaskExecutor asyncTaskExecutor;
835
+
836
+ public ReportXxxVO getXxx(XxxParam param) {
837
+ long start = System.currentTimeMillis();
838
+ MgrUserAuthPO authPO = mgrAuthApi.getUserAuthPO();
839
+ ReportDataPermissionParam dataPermission = reportDataPermissionService.getDataPermission(authPO);
840
+
841
+ // 并发查询多维度数据
842
+ CompletableFuture<List<TypeA>> futureA = CompletableFuture
843
+ .supplyAsync(() -> reportXxxMapper.getTypeA(param, authPO, dataPermission), asyncTaskExecutor);
844
+ CompletableFuture<List<TypeB>> futureB = CompletableFuture
845
+ .supplyAsync(() -> reportXxxMapper.getTypeB(param, authPO, dataPermission), asyncTaskExecutor);
846
+ CompletableFuture<TypeC> futureC = CompletableFuture
847
+ .supplyAsync(() -> reportXxxMapper.getTotal(param, authPO, dataPermission), asyncTaskExecutor);
848
+
849
+ // 等待所有完成
850
+ CompletableFuture.allOf(futureA, futureB, futureC).join();
851
+
852
+ log.info("getXxx耗时:{}", System.currentTimeMillis() - start);
853
+ return new ReportXxxVO(futureC.join(), futureA.join(), futureB.join());
854
+ }
855
+ ```
856
+
857
+ ### Redisson 分布式锁(导入/并发写操作)
858
+
859
+ ```java
860
+ @Resource
861
+ private RedissonClient redissonClient;
862
+
863
+ public void importExcel(MultipartFile file) {
864
+ RLock lock = redissonClient.getLock("import:lock:" + TenantContextHolder.getTenantId());
865
+ if (!lock.tryLock(5, 60, TimeUnit.SECONDS)) {
866
+ throw new LeException("正在处理中,请稍后再试");
867
+ }
868
+ try {
869
+ // 业务逻辑
870
+ doImport(file);
871
+ } finally {
872
+ // 必须在 finally 中释放锁
873
+ if (lock.isLocked() && lock.isHeldByCurrentThread()) {
874
+ lock.unlock();
875
+ }
876
+ }
877
+ }
878
+ ```
879
+
880
+ ### 异步执行(不等待结果)
881
+
882
+ ```java
883
+ CompletableFuture.runAsync(() -> {
884
+ // 异步操作(如发送通知、记录日志等)
885
+ }, asyncTaskExecutor);
886
+ ```
887
+
888
+ ---
889
+
890
+ ## 13. 代码质量要点
891
+
892
+ ### 空指针防护
893
+
894
+ ```java
895
+ // ❌ 错误:selectOne 返回值未判空
896
+ XxxEntity entity = xxxMapper.selectOne(query);
897
+ entity.getName(); // 可能 NPE
898
+
899
+ // ✅ 正确:ObjectUtil 判空
900
+ XxxEntity entity = xxxMapper.selectOne(query);
901
+ if (ObjectUtil.isNull(entity)) {
902
+ throw new LeException("数据不存在");
903
+ }
904
+
905
+ // ✅ 推荐:Optional 链式处理
906
+ XxxEntity entity = Optional.ofNullable(xxxMapper.selectById(id))
907
+ .orElseThrow(() -> new LeException("数据不存在"));
908
+ ```
909
+
910
+ ### 返回值兜底
911
+
912
+ ```java
913
+ // ❌ 错误:返回 null 导致前端 NPE
914
+ public List<XxxVO> listByParam(XxxDTO dto) {
915
+ return xxxMapper.selectList(buildWrapper(dto)); // 可能返回 null
916
+ }
917
+
918
+ // ✅ 正确:空集合兜底
919
+ public List<XxxVO> listByParam(XxxDTO dto) {
920
+ List<XxxEntity> list = xxxMapper.selectList(buildWrapper(dto));
921
+ if (CollUtil.isEmpty(list)) {
922
+ return Collections.emptyList();
923
+ }
924
+ return BeanUtil.copyToList(list, XxxVO.class);
925
+ }
926
+ ```
927
+
928
+ ### 集合参数防御
929
+
930
+ ```java
931
+ // ❌ 错误:空集合导致 SQL 异常 WHERE id IN ()
932
+ public List<XxxVO> selectByIds(List<Long> ids) {
933
+ return xxxMapper.selectByIds(ids);
934
+ }
935
+
936
+ // ✅ 正确:集合判空提前返回
937
+ public List<XxxVO> selectByIds(List<Long> ids) {
938
+ if (CollUtil.isEmpty(ids)) {
939
+ return Collections.emptyList();
940
+ }
941
+ return BeanUtil.copyToList(xxxMapper.selectBatchIds(ids), XxxVO.class);
942
+ }
943
+ ```
944
+
945
+ ### 级联调用防护
946
+
947
+ ```java
948
+ // ❌ 错误:级联调用易产生 NPE
949
+ String city = user.getAddress().getCity().getName();
950
+
951
+ // ✅ 正确:使用 Optional 链式处理
952
+ String city = Optional.ofNullable(user)
953
+ .map(User::getAddress)
954
+ .map(Address::getCity)
955
+ .map(City::getName)
956
+ .orElse("未知");
957
+ ```
958
+
959
+ ### Stream 操作规范
960
+
961
+ ```java
962
+ // Java 21 使用 toList()
963
+ List<Long> ids = list.stream()
964
+ .map(XxxVO::getId)
965
+ .distinct()
966
+ .toList();
967
+
968
+ // 转 Map(注意 key 重复问题)
969
+ Map<Long, XxxVO> map = list.stream()
970
+ .collect(Collectors.toMap(XxxVO::getId, Function.identity()));
971
+
972
+ // 分组
973
+ Map<Integer, List<XxxVO>> grouped = list.stream()
974
+ .collect(Collectors.groupingBy(XxxVO::getStatus));
975
+
976
+ // 过滤空元素
977
+ list.stream()
978
+ .filter(Objects::nonNull)
979
+ .collect(Collectors.toList());
980
+ ```
981
+
982
+ ---
983
+
984
+ ## 14. 分组校验定义
985
+
986
+ ```java
987
+ // InsertGroup.java
988
+ public interface InsertGroup {}
989
+
990
+ // UpdateGroup.java
991
+ public interface UpdateGroup {}
992
+ ```
993
+
994
+ ---
995
+
996
+ ## 15. 常见错误速查
997
+
998
+ ### 不要做
999
+
1000
+ ```java
1001
+ // 错误 1: 在 Service 层注入 DAO
1002
+ @Resource
1003
+ private XxxDao xxxDao; // leniu 没有 DAO 层!
1004
+
1005
+ // 错误 2: 使用错误的包名
1006
+ package org.dromara.xxx; // 必须是 net.xnzn.*
1007
+
1008
+ // 错误 3: 使用 javax.validation(JDK 21 应用 jakarta.validation)
1009
+ import javax.validation.Valid; // 错误
1010
+
1011
+ // 错误 4: 使用 AddGroup/EditGroup(leniu 使用 InsertGroup/UpdateGroup)
1012
+ @Validated(AddGroup.class) // 应该用 InsertGroup.class
1013
+
1014
+ // 错误 5: 使用错误的审计字段
1015
+ private String createBy; // 应该用 crby
1016
+
1017
+ // 错误 6: 使用错误的逻辑删除值
1018
+ entity.setDelFlag(0); // leniu 中 0=错误,1=删除,2=正常
1019
+
1020
+ // 错误 7: Mapper XML 放在 resources/mapper 目录
1021
+ // src/main/resources/mapper/XxxMapper.xml // 错误位置
1022
+
1023
+ // 错误 8: 使用错误的异常类
1024
+ throw new ServiceException("错误"); // 应该用 LeException
1025
+
1026
+ // 错误 9: 多表操作不加事务
1027
+ public void multiTableOp() { ... } // 应该加 @Transactional(rollbackFor = Exception.class)
1028
+
1029
+ // 错误 10: 返回 null(应返回空集合)
1030
+ return null; // 应返回 Collections.emptyList()
1031
+ ```
1032
+
1033
+ ### 正确做法
1034
+
1035
+ ```java
1036
+ // 正确 1: 直接在 Service 中注入 Mapper
1037
+ @Resource
1038
+ private XxxMapper xxxMapper;
1039
+
1040
+ // 正确 2: 使用正确的包名
1041
+ package net.xnzn.core.xxx;
1042
+
1043
+ // 正确 3: 使用 Jakarta Validation
1044
+ import jakarta.validation.Valid;
1045
+
1046
+ // 正确 4: 使用正确的分组
1047
+ @Validated(InsertGroup.class)
1048
+
1049
+ // 正确 5: 使用 leniu 审计字段
1050
+ private String crby;
1051
+
1052
+ // 正确 6: 使用正确的逻辑删除值
1053
+ entity.setDelFlag(2); // 2=正常
1054
+
1055
+ // 正确 7: Mapper XML 与 Java 同目录
1056
+ // src/main/java/net/xnzn/core/xxx/mapper/XxxMapper.xml
1057
+
1058
+ // 正确 8: 使用 LeException
1059
+ throw new LeException("错误");
1060
+
1061
+ // 正确 9: 多表操作加事务
1062
+ @Transactional(rollbackFor = Exception.class)
1063
+ public void multiTableOp() { ... }
1064
+
1065
+ // 正确 10: 返回空集合
1066
+ return Collections.emptyList();
1067
+ ```
1068
+
1069
+ ---
1070
+
1071
+ ## 检查清单
1072
+
1073
+ 生成代码前必须检查:
1074
+
1075
+ - [ ] **包名是否是 `net.xnzn.core.*`**?
1076
+ - [ ] **Service 是否只实现接口,不继承任何基类**?
1077
+ - [ ] **Service 是否直接注入 Mapper(无 DAO 层)**?
1078
+ - [ ] **查询条件是否在 Service 层构建**?
1079
+ - [ ] **Entity 是否包含审计字段**(crby/crtime/upby/uptime)?
1080
+ - [ ] **delFlag 是否正确使用**(1=删除,2=正常)?
1081
+ - [ ] **是否使用 `BeanUtil.copyProperties()` 转换对象**?
1082
+ - [ ] **是否使用 Jakarta Validation**(jakarta.validation)?
1083
+ - [ ] **是否使用正确的分组**(InsertGroup/UpdateGroup)?
1084
+ - [ ] **Mapper XML 是否与 Java 文件同目录**?
1085
+ - [ ] **异常是否使用 `LeException`**?
1086
+ - [ ] **Controller 是否添加认证注解**(@RequiresAuthentication/@RequiresGuest)?
1087
+ - [ ] **请求是否使用 `LeRequest<T>` 封装**?
1088
+ - [ ] **响应是否使用 `Page<T>` 或 `LeResponse<T>`**?
1089
+ - [ ] **多表操作是否添加 `@Transactional(rollbackFor = Exception.class)`**?
1090
+ - [ ] **查询方法是否未添加不必要的事务**?
1091
+ - [ ] **返回 List 是否有空集合兜底**?
1092
+ - [ ] **集合参数是否有判空保护**?
1093
+ - [ ] **selectOne/selectById 结果是否有判空处理**?
1094
+ - [ ] **所有代码注释是否使用中文**?
1095
+
1096
+ ---
1097
+
1098
+ ## 参考代码位置
1099
+
1100
+ | 类型 | 路径 |
1101
+ |------|------|
1102
+ | Controller 示例 | `core-attendance/.../controller/AttendanceLeaveInfoController.java` |
1103
+ | Service 示例 | `core-attendance/.../service/impl/AttendanceLeaveInfoServiceImpl.java` |
1104
+ | Mapper 示例 | `core-attendance/.../mapper/AttendanceLeaveInfoMapper.java` |
1105
+ | Entity 示例 | `core-attendance/.../model/AttendanceLeaveInfo.java` |
1106
+
1107
+ | 项目 | 路径 |
1108
+ |------|------|
1109
+ | **leniu-tengyun-core** | `/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core` |
1110
+ | **leniu-yunshitang** | `/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang` |