Compare commits
6 Commits
9b8703e192
...
ae32e6d953
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae32e6d953 | ||
|
|
d086ca9f39 | ||
|
|
68e93fe853 | ||
|
|
a402d712de | ||
|
|
4f257cd87c | ||
|
|
87c7e593df |
@ -5,6 +5,7 @@
|
|||||||
<option name="apiProjectIds">
|
<option name="apiProjectIds">
|
||||||
<array>
|
<array>
|
||||||
<option value="<byte-array>rO0ABXNyADZjb20uaXRhbmdjZW50LmlkZWEucGx1Z2luLmFwaS5hY2NvdW50LlByb2plY3RBbmRNb2R1bGUAAAAAAAAAAQIAFVoABmVuYWJsZUwACG1vZHVsZUlkdAASTGphdmEvbGFuZy9TdHJpbmc7TAAGb3RoZXIxcQB+AAFMAAdvdGhlcjEwcQB+AAFMAAdvdGhlcjExcQB+AAFMAAdvdGhlcjEycQB+AAFMAAZvdGhlcjJxAH4AAUwABm90aGVyM3EAfgABTAAGb3RoZXI0cQB+AAFMAAZvdGhlcjVxAH4AAUwABm90aGVyNnEAfgABTAAGb3RoZXI3cQB+AAFMAAZvdGhlcjhxAH4AAUwABm90aGVyOXEAfgABTAAKcGF0aEJlZm9yZXEAfgABTAANcHJvamVjdEZvbGRlcnEAfgABTAAPcHJvamVjdEZvbGRlcklkcQB+AAFMAAlwcm9qZWN0SWRxAH4AAUwAC3Byb2plY3ROYW1lcQB+AAFMAAxzY2hlbWFGb2xkZXJxAH4AAUwACHNjaGVtYUlkcQB+AAF4cAF0ABhjb2Rlci1jb21tb24tdGhpbi1zeXN0ZW10AAc2ODg2NjExcHBwdAAHNjIzOTkwNnQAC2JyYW5jaC1tYWludAAM6buY6K6k5qih5Z2XcHBwcHB0AAB0AAnmoLnnm67lvZV0AAEwdAAHNzE2MjUxMHQAEWNvZGVyLWNvbW1vbi10aGlucQB+AAlxAH4ACg==</byte-array>" />
|
<option value="<byte-array>rO0ABXNyADZjb20uaXRhbmdjZW50LmlkZWEucGx1Z2luLmFwaS5hY2NvdW50LlByb2plY3RBbmRNb2R1bGUAAAAAAAAAAQIAFVoABmVuYWJsZUwACG1vZHVsZUlkdAASTGphdmEvbGFuZy9TdHJpbmc7TAAGb3RoZXIxcQB+AAFMAAdvdGhlcjEwcQB+AAFMAAdvdGhlcjExcQB+AAFMAAdvdGhlcjEycQB+AAFMAAZvdGhlcjJxAH4AAUwABm90aGVyM3EAfgABTAAGb3RoZXI0cQB+AAFMAAZvdGhlcjVxAH4AAUwABm90aGVyNnEAfgABTAAGb3RoZXI3cQB+AAFMAAZvdGhlcjhxAH4AAUwABm90aGVyOXEAfgABTAAKcGF0aEJlZm9yZXEAfgABTAANcHJvamVjdEZvbGRlcnEAfgABTAAPcHJvamVjdEZvbGRlcklkcQB+AAFMAAlwcm9qZWN0SWRxAH4AAUwAC3Byb2plY3ROYW1lcQB+AAFMAAxzY2hlbWFGb2xkZXJxAH4AAUwACHNjaGVtYUlkcQB+AAF4cAF0ABhjb2Rlci1jb21tb24tdGhpbi1zeXN0ZW10AAc2ODg2NjExcHBwdAAHNjIzOTkwNnQAC2JyYW5jaC1tYWludAAM6buY6K6k5qih5Z2XcHBwcHB0AAB0AAnmoLnnm67lvZV0AAEwdAAHNzE2MjUxMHQAEWNvZGVyLWNvbW1vbi10aGlucQB+AAlxAH4ACg==</byte-array>" />
|
||||||
|
<option value="<byte-array>rO0ABXNyADZjb20uaXRhbmdjZW50LmlkZWEucGx1Z2luLmFwaS5hY2NvdW50LlByb2plY3RBbmRNb2R1bGUAAAAAAAAAAQIAFVoABmVuYWJsZUwACG1vZHVsZUlkdAASTGphdmEvbGFuZy9TdHJpbmc7TAAGb3RoZXIxcQB+AAFMAAdvdGhlcjEwcQB+AAFMAAdvdGhlcjExcQB+AAFMAAdvdGhlcjEycQB+AAFMAAZvdGhlcjJxAH4AAUwABm90aGVyM3EAfgABTAAGb3RoZXI0cQB+AAFMAAZvdGhlcjVxAH4AAUwABm90aGVyNnEAfgABTAAGb3RoZXI3cQB+AAFMAAZvdGhlcjhxAH4AAUwABm90aGVyOXEAfgABTAAKcGF0aEJlZm9yZXEAfgABTAANcHJvamVjdEZvbGRlcnEAfgABTAAPcHJvamVjdEZvbGRlcklkcQB+AAFMAAlwcm9qZWN0SWRxAH4AAUwAC3Byb2plY3ROYW1lcQB+AAFMAAxzY2hlbWFGb2xkZXJxAH4AAUwACHNjaGVtYUlkcQB+AAF4cAF0ABVjb2Rlci1jb21tb24tdGhpbi1qb2J0AAc2ODg2NjExcHBwdAAHNjIzOTkwNnQAC2JyYW5jaC1tYWludAAM6buY6K6k5qih5Z2XcHBwcHB0AAB0AAnmoLnnm67lvZV0AAEwdAAHNzE2MjUxMHQAEWNvZGVyLWNvbW1vbi10aGlucQB+AAlxAH4ACg==</byte-array>" />
|
||||||
</array>
|
</array>
|
||||||
</option>
|
</option>
|
||||||
<option name="treeNodes" value="<byte-array>rO0ABXNyABdqYXZhLnV0aWwuTGlua2VkSGFzaE1hcDTATlwQbMD7AgABWgALYWNjZXNzT3JkZXJ4cgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAABHQABzEyOTMxODlzcgAuY29tLml0YW5nY2VudC5pZGVhLnBsdWdpbi5hcGkuYWNjb3VudC5UcmVlTm9kZQAAAAAAAAABAgAQTAAHYWxsUGF0aHQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAFGJyYW5jaEFuZFZlcnNpb25JdGVtdABLW0xjb20vaXRhbmdjZW50L2lkZWEvcGx1Z2luL2RpYWxvZy9jb21wb25lbnQvYWNjb3VudC9BY2NvdW50UmlnaHRQYW5lbEl0ZW07TAAUYnJhbmNoSWRBbmRWZXJzaW9uSWRxAH4ABUwACGNoaWxkcmVudAAPTGphdmEvdXRpbC9NYXA7TAAKZm9sZGVyVHlwZXEAfgAFTAAIZnVsbFBhdGhxAH4ABUwAA2tleXEAfgAFWwAJbW9kZWxJdGVtcQB+AAZMAAhtb2R1bGVJZHEAfgAFTAAEbmFtZXEAfgAFTAAIcGFyZW50SWRxAH4ABUwACXByb2plY3RJZHEAfgAFTAALcHJvamVjdE5hbWVxAH4ABUwABnRlYW1JZHEAfgAFTAAIdGVhbU5hbWVxAH4ABUwABHR5cGV0ADBMY29tL2l0YW5nY2VudC9pZGVhL3BsdWdpbi9hcGkvYWNjb3VudC9Ob2RlVHlwZTt4cHQADOS4quS6uuepuumXtHBwc3EAfgAAP0AAAAAAAAx3CAAAABAAAAAGdAAHNDg1MTE4NnNxAH4ABHQAFOS4quS6uuepuumXtC9jb2RlaHVicHBzcQB+AAA/QAAAAAAAAHcIAAAAEAAAAAB4AHBwcQB+AAxwcHQAEWNvZGVodWIgKDQ4NTExODYpdAAHMTI5MzE4OXEAfgAMdAAHY29kZWh1YnEAfgARcH5yAC5jb20uaXRhbmdjZW50LmlkZWEucGx1Z2luLmFwaS5hY2NvdW50Lk5vZGVUeXBlAAAAAAAAAAASAAB4cgAOamF2YS5sYW5nLkVudW0AAAAAAAAAABIAAHhwdAAHUFJPSkVDVHQABzUwMjI4NDZzcQB+AAR0ACXkuKrkurrnqbrpl7QvU2ltcGxlU3ByaW5nQm9vdFRlbXBsYXRlcHBzcQB+AAA/QAAAAAAAAHcIAAAAEAAAAAB4AHBwcQB+ABdwcHQAIlNpbXBsZVNwcmluZ0Jvb3RUZW1wbGF0ZSAoNTAyMjg0Nil0AAcxMjkzMTg5cQB+ABd0ABhTaW1wbGVTcHJpbmdCb290VGVtcGxhdGVxAH4AHHBxAH4AFXQABzY0OTE0ODBzcQB+AAR0ABfkuKrkurrnqbrpl7Qvcy1wYXktbWFsbHBwc3EAfgAAP0AAAAAAAAB3CAAAABAAAAAAeABwcHEAfgAecHB0ABRzLXBheS1tYWxsICg2NDkxNDgwKXQABzEyOTMxODlxAH4AHnQACnMtcGF5LW1hbGxxAH4AI3BxAH4AFXQABzY1MTkxNzdzcQB+AAR0ABzkuKrkurrnqbrpl7QvY29kZXItc2hvcnRsaW5rcHBzcQB+AAA/QAAAAAAAAHcIAAAAEAAAAAB4AHBwcQB+ACVwcHQAGWNvZGVyLXNob3J0bGluayAoNjUxOTE3Nyl0AAcxMjkzMTg5cQB+ACV0AA9jb2Rlci1zaG9ydGxpbmtxAH4AKnBxAH4AFXQABzY5Mjk1NjNzcQB+AAR0ABnkuKrkurrnqbrpl7QvY29kZXItaGlib29rcHBzcQB+AAA/QAAAAAAAAHcIAAAAEAAAAAB4AHBwcQB+ACxwcHQAFmNvZGVyLWhpYm9vayAoNjkyOTU2Myl0AAcxMjkzMTg5cQB+ACx0AAxjb2Rlci1oaWJvb2txAH4AMXBxAH4AFXQABzcxNjI1MTBzcQB+AAR0AB7kuKrkurrnqbrpl7QvY29kZXItY29tbW9uLXRoaW5wcHNxAH4AAD9AAAAAAAAAdwgAAAAQAAAAAHgAcHBxAH4AM3BwdAAbY29kZXItY29tbW9uLXRoaW4gKDcxNjI1MTApdAAHMTI5MzE4OXEAfgAzdAARY29kZXItY29tbW9uLXRoaW5xAH4AOHBxAH4AFXgAcHBxAH4AA3BwcQB+AApwcHBxAH4AA3EAfgAKfnEAfgATdAAEVEVBTXQABzIyNDA3MzdzcQB+AAR0ABLmtYvor5XnlKjkvovpobnnm65wcHNxAH4AAD9AAAAAAAAAdwgAAAAQAAAAAHgAcHBxAH4APHBwcQB+AD5wcHBxAH4APHEAfgA+cQB+ADp0AAcyMjQwNzQ0c3EAfgAEdAAJTGVv5rWL6K+VcHBzcQB+AAA/QAAAAAAADHcIAAAAEAAAAAF0AAczODU3NDk4c3EAfgAEdAAcTGVv5rWL6K+VL0FwaWZveOeugOWNlea1i+ivlXBwc3EAfgAAP0AAAAAAAAB3CAAAABAAAAAAeABwcHEAfgBEcHB0ABxBcGlmb3jnroDljZXmtYvor5UgKDM4NTc0OTgpdAAHMjI0MDc0NHEAfgBEdAASQXBpZm94566A5Y2V5rWL6K+VcQB+AElwcQB+ABV4AHBwcQB+AEBwcHEAfgBCcHBwcQB+AEBxAH4AQnEAfgA6dAAHMzU3NjE2NXNxAH4ABHQABuWFrOWPuHBwc3EAfgAAP0AAAAAAAAx3CAAAABAAAAABdAAHNjU3MDQzM3NxAH4ABHQAE+WFrOWPuC/lhbbku5blhaXlupNwcHNxAH4AAD9AAAAAAAAAdwgAAAAQAAAAAHgAcHBxAH4AT3BwdAAW5YW25LuW5YWl5bqTICg2NTcwNDMzKXQABzM1NzYxNjVxAH4AT3QADOWFtuS7luWFpeW6k3EAfgBUcHEAfgAVeABwcHEAfgBLcHBxAH4ATXBwcHEAfgBLcQB+AE1xAH4AOngA</byte-array>" />
|
<option name="treeNodes" value="<byte-array>rO0ABXNyABdqYXZhLnV0aWwuTGlua2VkSGFzaE1hcDTATlwQbMD7AgABWgALYWNjZXNzT3JkZXJ4cgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAABHQABzEyOTMxODlzcgAuY29tLml0YW5nY2VudC5pZGVhLnBsdWdpbi5hcGkuYWNjb3VudC5UcmVlTm9kZQAAAAAAAAABAgAQTAAHYWxsUGF0aHQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAFGJyYW5jaEFuZFZlcnNpb25JdGVtdABLW0xjb20vaXRhbmdjZW50L2lkZWEvcGx1Z2luL2RpYWxvZy9jb21wb25lbnQvYWNjb3VudC9BY2NvdW50UmlnaHRQYW5lbEl0ZW07TAAUYnJhbmNoSWRBbmRWZXJzaW9uSWRxAH4ABUwACGNoaWxkcmVudAAPTGphdmEvdXRpbC9NYXA7TAAKZm9sZGVyVHlwZXEAfgAFTAAIZnVsbFBhdGhxAH4ABUwAA2tleXEAfgAFWwAJbW9kZWxJdGVtcQB+AAZMAAhtb2R1bGVJZHEAfgAFTAAEbmFtZXEAfgAFTAAIcGFyZW50SWRxAH4ABUwACXByb2plY3RJZHEAfgAFTAALcHJvamVjdE5hbWVxAH4ABUwABnRlYW1JZHEAfgAFTAAIdGVhbU5hbWVxAH4ABUwABHR5cGV0ADBMY29tL2l0YW5nY2VudC9pZGVhL3BsdWdpbi9hcGkvYWNjb3VudC9Ob2RlVHlwZTt4cHQADOS4quS6uuepuumXtHBwc3EAfgAAP0AAAAAAAAx3CAAAABAAAAAGdAAHNDg1MTE4NnNxAH4ABHQAFOS4quS6uuepuumXtC9jb2RlaHVicHBzcQB+AAA/QAAAAAAAAHcIAAAAEAAAAAB4AHBwcQB+AAxwcHQAEWNvZGVodWIgKDQ4NTExODYpdAAHMTI5MzE4OXEAfgAMdAAHY29kZWh1YnEAfgARcH5yAC5jb20uaXRhbmdjZW50LmlkZWEucGx1Z2luLmFwaS5hY2NvdW50Lk5vZGVUeXBlAAAAAAAAAAASAAB4cgAOamF2YS5sYW5nLkVudW0AAAAAAAAAABIAAHhwdAAHUFJPSkVDVHQABzUwMjI4NDZzcQB+AAR0ACXkuKrkurrnqbrpl7QvU2ltcGxlU3ByaW5nQm9vdFRlbXBsYXRlcHBzcQB+AAA/QAAAAAAAAHcIAAAAEAAAAAB4AHBwcQB+ABdwcHQAIlNpbXBsZVNwcmluZ0Jvb3RUZW1wbGF0ZSAoNTAyMjg0Nil0AAcxMjkzMTg5cQB+ABd0ABhTaW1wbGVTcHJpbmdCb290VGVtcGxhdGVxAH4AHHBxAH4AFXQABzY0OTE0ODBzcQB+AAR0ABfkuKrkurrnqbrpl7Qvcy1wYXktbWFsbHBwc3EAfgAAP0AAAAAAAAB3CAAAABAAAAAAeABwcHEAfgAecHB0ABRzLXBheS1tYWxsICg2NDkxNDgwKXQABzEyOTMxODlxAH4AHnQACnMtcGF5LW1hbGxxAH4AI3BxAH4AFXQABzY1MTkxNzdzcQB+AAR0ABzkuKrkurrnqbrpl7QvY29kZXItc2hvcnRsaW5rcHBzcQB+AAA/QAAAAAAAAHcIAAAAEAAAAAB4AHBwcQB+ACVwcHQAGWNvZGVyLXNob3J0bGluayAoNjUxOTE3Nyl0AAcxMjkzMTg5cQB+ACV0AA9jb2Rlci1zaG9ydGxpbmtxAH4AKnBxAH4AFXQABzY5Mjk1NjNzcQB+AAR0ABnkuKrkurrnqbrpl7QvY29kZXItaGlib29rcHBzcQB+AAA/QAAAAAAAAHcIAAAAEAAAAAB4AHBwcQB+ACxwcHQAFmNvZGVyLWhpYm9vayAoNjkyOTU2Myl0AAcxMjkzMTg5cQB+ACx0AAxjb2Rlci1oaWJvb2txAH4AMXBxAH4AFXQABzcxNjI1MTBzcQB+AAR0AB7kuKrkurrnqbrpl7QvY29kZXItY29tbW9uLXRoaW5wcHNxAH4AAD9AAAAAAAAAdwgAAAAQAAAAAHgAcHBxAH4AM3BwdAAbY29kZXItY29tbW9uLXRoaW4gKDcxNjI1MTApdAAHMTI5MzE4OXEAfgAzdAARY29kZXItY29tbW9uLXRoaW5xAH4AOHBxAH4AFXgAcHBxAH4AA3BwcQB+AApwcHBxAH4AA3EAfgAKfnEAfgATdAAEVEVBTXQABzIyNDA3MzdzcQB+AAR0ABLmtYvor5XnlKjkvovpobnnm65wcHNxAH4AAD9AAAAAAAAAdwgAAAAQAAAAAHgAcHBxAH4APHBwcQB+AD5wcHBxAH4APHEAfgA+cQB+ADp0AAcyMjQwNzQ0c3EAfgAEdAAJTGVv5rWL6K+VcHBzcQB+AAA/QAAAAAAADHcIAAAAEAAAAAF0AAczODU3NDk4c3EAfgAEdAAcTGVv5rWL6K+VL0FwaWZveOeugOWNlea1i+ivlXBwc3EAfgAAP0AAAAAAAAB3CAAAABAAAAAAeABwcHEAfgBEcHB0ABxBcGlmb3jnroDljZXmtYvor5UgKDM4NTc0OTgpdAAHMjI0MDc0NHEAfgBEdAASQXBpZm94566A5Y2V5rWL6K+VcQB+AElwcQB+ABV4AHBwcQB+AEBwcHEAfgBCcHBwcQB+AEBxAH4AQnEAfgA6dAAHMzU3NjE2NXNxAH4ABHQABuWFrOWPuHBwc3EAfgAAP0AAAAAAAAx3CAAAABAAAAABdAAHNjU3MDQzM3NxAH4ABHQAE+WFrOWPuC/lhbbku5blhaXlupNwcHNxAH4AAD9AAAAAAAAAdwgAAAAQAAAAAHgAcHBxAH4AT3BwdAAW5YW25LuW5YWl5bqTICg2NTcwNDMzKXQABzM1NzYxNjVxAH4AT3QADOWFtuS7luWFpeW6k3EAfgBUcHEAfgAVeABwcHEAfgBLcHBxAH4ATXBwcHEAfgBLcQB+AE1xAH4AOngA</byte-array>" />
|
||||||
|
|||||||
@ -17,6 +17,8 @@
|
|||||||
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-dict/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-dict/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-easyexcel/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-easyexcel/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-easyexcel/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-easyexcel/src/main/resources" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-job/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-job/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-limit/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-limit/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-limit/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-limit/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-oper-logs/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-oper-logs/src/main/java" charset="UTF-8" />
|
||||||
|
|||||||
55
CLAUDE.md
55
CLAUDE.md
@ -206,6 +206,44 @@ mvn test -pl coder-common-thin-web
|
|||||||
- 严禁在Sa-Token配置中添加业务接口的排除路径
|
- 严禁在Sa-Token配置中添加业务接口的排除路径
|
||||||
- 严禁通过其他方式绕过权限验证
|
- 严禁通过其他方式绕过权限验证
|
||||||
|
|
||||||
|
5. **Controller接口文档规范**: 所有Controller必须配置Swagger注解以生成规范的中文接口文档
|
||||||
|
- **强制要求**: 每个Controller类和方法都必须添加完整的Swagger注解
|
||||||
|
- **类级别注解**: 必须添加`@Tag(name = "中文模块名", description = "详细描述")`
|
||||||
|
- **方法级别注解**: 必须添加`@Operation(summary = "中文接口名", description = "详细功能描述")`
|
||||||
|
- **必要导入**: 在Controller类中导入:
|
||||||
|
```java
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
```
|
||||||
|
- **依赖配置**: 使用Swagger注解的模块必须在pom.xml中添加SpringDoc依赖:
|
||||||
|
```xml
|
||||||
|
<!-- SpringDoc OpenAPI 3.0 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
- **标准示例**:
|
||||||
|
```java
|
||||||
|
@Tag(name = "定时任务管理", description = "系统定时任务的增删改查和执行控制")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/coder")
|
||||||
|
public class SysJobController {
|
||||||
|
|
||||||
|
@Operation(summary = "分页查询定时任务", description = "根据查询条件分页获取系统定时任务列表")
|
||||||
|
@SaCheckPermission("monitor:job:list")
|
||||||
|
@GetMapping("/sysJob/listPage")
|
||||||
|
public IPage<SysJob> listPage(SysJobVo vo) {
|
||||||
|
// 业务逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **接口文档效果**: 正确配置后,接口文档将显示中文模块名和接口名,而不是英文类名和方法名
|
||||||
|
- **禁止行为**:
|
||||||
|
- 严禁创建没有Swagger注解的Controller
|
||||||
|
- 严禁使用英文summary和description
|
||||||
|
- 严禁在缺少SpringDoc依赖的模块中使用Swagger注解
|
||||||
|
|
||||||
### 工具类使用
|
### 工具类使用
|
||||||
项目提供了丰富的工具类,位于`coder-common-thin-common/utils`:
|
项目提供了丰富的工具类,位于`coder-common-thin-common/utils`:
|
||||||
- **缓存工具**: `RedisUtil`, `LocalCacheUtil`
|
- **缓存工具**: `RedisUtil`, `LocalCacheUtil`
|
||||||
@ -264,4 +302,19 @@ mvn test -pl coder-common-thin-web
|
|||||||
6. **禁止自动编译**: Claude在完成新功能代码或修改代码后,严禁自动执行任何编译操作(如mvn compile、mvn package等)
|
6. **禁止自动编译**: Claude在完成新功能代码或修改代码后,严禁自动执行任何编译操作(如mvn compile、mvn package等)
|
||||||
- **禁止行为**: 严禁在代码修改完成后自动运行编译命令
|
- **禁止行为**: 严禁在代码修改完成后自动运行编译命令
|
||||||
- **原因说明**: 编译操作应由开发者根据实际需要手动执行,避免不必要的编译开销和潜在的环境冲突
|
- **原因说明**: 编译操作应由开发者根据实际需要手动执行,避免不必要的编译开销和潜在的环境冲突
|
||||||
- **允许行为**: 仅在用户明确要求时才执行编译操作
|
- **允许行为**: 仅在用户明确要求时才执行编译操作
|
||||||
|
|
||||||
|
7. **Swagger注解强制要求**: 所有Controller必须配置完整的Swagger注解,确保接口文档显示中文
|
||||||
|
- **强制配置**: 新建Controller时必须同时添加`@Tag`和`@Operation`注解
|
||||||
|
- **依赖检查**: 使用Swagger注解前必须确保模块pom.xml包含SpringDoc依赖
|
||||||
|
- **中文规范**: 所有summary和description必须使用中文描述
|
||||||
|
- **编译验证**: 添加Swagger注解后必须确保项目能正常编译,如遇到"Cannot resolve symbol"错误,立即检查依赖配置
|
||||||
|
- **示例配置**:
|
||||||
|
```xml
|
||||||
|
<!-- 在模块pom.xml中添加 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
- **验证标准**: 接口文档中必须显示中文模块名和接口名,而非英文类名和方法名
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
package org.leocoder.thin.domain.model.bo.system;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务业务对象]
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SysJobBo implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
private Long jobId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "任务名称不能为空")
|
||||||
|
private String jobName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务类型[1-管理平台 2-小程序 3-App]
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "任务类型不能为空")
|
||||||
|
private String jobType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类路径
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "类路径不能为空")
|
||||||
|
private String classPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "方法名称不能为空")
|
||||||
|
private String methodName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cron执行表达式
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "cron执行表达式不能为空")
|
||||||
|
private String cronExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cron计划策略[1-立即执行 2-执行一次 3-放弃执行]
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "cron计划策略不能为空")
|
||||||
|
private String policyStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务状态 [0正常 1暂停]
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "任务状态不能为空")
|
||||||
|
private String jobStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务参数
|
||||||
|
*/
|
||||||
|
private String jobParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package org.leocoder.thin.domain.model.vo.system;
|
||||||
|
|
||||||
|
import org.leocoder.thin.domain.model.vo.base.BaseVo;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务查询视图对象]
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
public class SysJobVo extends BaseVo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
private Long jobId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务名称
|
||||||
|
*/
|
||||||
|
private String jobName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务类型[1-管理平台 2-小程序 3-App]
|
||||||
|
*/
|
||||||
|
private String jobType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cron执行表达式
|
||||||
|
*/
|
||||||
|
private String cronExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cron计划策略[1-立即执行 2-执行一次 3-放弃执行]
|
||||||
|
*/
|
||||||
|
private String policyStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务状态 [0正常 1暂停]
|
||||||
|
*/
|
||||||
|
private String jobStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务参数
|
||||||
|
*/
|
||||||
|
private String jobParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类路径
|
||||||
|
*/
|
||||||
|
private String classPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法名称
|
||||||
|
*/
|
||||||
|
private String methodName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,149 @@
|
|||||||
|
package org.leocoder.thin.domain.pojo.system;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务-模型]
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@TableName("sys_job")
|
||||||
|
public class SysJob implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "job_id", type = IdType.ASSIGN_ID)
|
||||||
|
private Long jobId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "任务名称不能为空")
|
||||||
|
@TableField("job_name")
|
||||||
|
private String jobName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务类型[1-管理平台 2-小程序 3-App]
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "任务类型不能为空")
|
||||||
|
@TableField("job_type")
|
||||||
|
private String jobType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类路径
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "类路径不能为空")
|
||||||
|
@TableField("class_path")
|
||||||
|
private String classPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "方法名称不能为空")
|
||||||
|
@TableField("method_name")
|
||||||
|
private String methodName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cron执行表达式
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "cron执行表达式不能为空")
|
||||||
|
@TableField("cron_expression")
|
||||||
|
private String cronExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cron计划策略[1-立即执行 2-执行一次 3-放弃执行]
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "cron计划策略不能为空")
|
||||||
|
@TableField("policy_status")
|
||||||
|
private String policyStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务状态 [0正常 1暂停]
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "任务状态不能为空")
|
||||||
|
@TableField("job_status")
|
||||||
|
private String jobStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务参数
|
||||||
|
*/
|
||||||
|
@TableField("job_params")
|
||||||
|
private String jobParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务备注
|
||||||
|
*/
|
||||||
|
@TableField("remark")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
@TableField("create_by")
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(value = "create_time", fill = FieldFill.INSERT)
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
@TableField("update_by")
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [获取任务状态文本]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
public String getJobStatusText() {
|
||||||
|
return "0".equals(jobStatus) ? "正常" : "暂停";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [获取策略状态文本]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
public String getPolicyStatusText() {
|
||||||
|
switch (policyStatus) {
|
||||||
|
case "1": return "立即执行";
|
||||||
|
case "2": return "执行一次";
|
||||||
|
case "3": return "放弃执行";
|
||||||
|
default: return "未知";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [获取任务类型文本]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
public String getJobTypeText() {
|
||||||
|
switch (jobType) {
|
||||||
|
case "1": return "管理平台";
|
||||||
|
case "2": return "小程序";
|
||||||
|
case "3": return "App";
|
||||||
|
default: return "其他";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package org.leocoder.thin.mybatisplus.mapper.system;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.leocoder.thin.domain.pojo.system.SysJob;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务数据访问层]
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface SysJobMapper extends BaseMapper<SysJob> {
|
||||||
|
|
||||||
|
}
|
||||||
85
coder-common-thin-plugins/coder-common-thin-job/pom.xml
Normal file
85
coder-common-thin-plugins/coder-common-thin-job/pom.xml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.leocoder.thin</groupId>
|
||||||
|
<artifactId>coder-common-thin-plugins</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>coder-common-thin-job</artifactId>
|
||||||
|
<description>定时任务插件模块</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Boot Starter Web -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Boot Starter Validation -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis Plus -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Sa-Token 权限认证 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Hutool 工具类 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Commons Lang3 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringDoc OpenAPI 3.0 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 项目内部依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.leocoder.thin</groupId>
|
||||||
|
<artifactId>coder-common-thin-common</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.leocoder.thin</groupId>
|
||||||
|
<artifactId>coder-common-thin-model</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.leocoder.thin</groupId>
|
||||||
|
<artifactId>coder-common-thin-mybatisplus</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package org.leocoder.thin.job.anno;
|
||||||
|
|
||||||
|
import org.leocoder.thin.job.config.JobConfiguration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [启用定时任务插件注解]
|
||||||
|
*/
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Inherited
|
||||||
|
@Import({JobConfiguration.class})
|
||||||
|
public @interface EnableCoderJob {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package org.leocoder.thin.job.config;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务插件配置类]
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackages = "org.leocoder.thin.job")
|
||||||
|
public class JobConfiguration {
|
||||||
|
|
||||||
|
public JobConfiguration() {
|
||||||
|
log.info("定时任务插件已启用");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,201 @@
|
|||||||
|
package org.leocoder.thin.job.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import cn.hutool.cron.CronUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.leocoder.thin.domain.model.vo.system.SysJobVo;
|
||||||
|
import org.leocoder.thin.domain.pojo.system.SysJob;
|
||||||
|
import org.leocoder.thin.job.service.SysJobService;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务管理控制器]
|
||||||
|
*/
|
||||||
|
@Tag(name = "定时任务管理", description = "系统定时任务的增删改查和执行控制")
|
||||||
|
@Slf4j
|
||||||
|
@RequestMapping("/coder")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
public class SysJobController {
|
||||||
|
|
||||||
|
private final SysJobService sysJobService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [多条件分页查询]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(summary = "分页查询定时任务", description = "根据查询条件分页获取系统定时任务列表")
|
||||||
|
@SaCheckPermission("monitor:job:list")
|
||||||
|
@GetMapping("/sysJob/listPage")
|
||||||
|
public IPage<SysJob> listPage(SysJobVo vo) {
|
||||||
|
// 分页构造器
|
||||||
|
Page<SysJob> page = new Page<>(vo.getPageNo(), vo.getPageSize());
|
||||||
|
// 条件构造器
|
||||||
|
LambdaQueryWrapper<SysJob> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.like(StringUtils.isNotBlank(vo.getJobName()), SysJob::getJobName, vo.getJobName());
|
||||||
|
wrapper.eq(StringUtils.isNotBlank(vo.getJobType()), SysJob::getJobType, vo.getJobType());
|
||||||
|
wrapper.eq(StringUtils.isNotBlank(vo.getJobStatus()), SysJob::getJobStatus, vo.getJobStatus());
|
||||||
|
wrapper.orderByDesc(SysJob::getCreateTime);
|
||||||
|
// 进行分页查询
|
||||||
|
page = sysJobService.page(page, wrapper);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [查询所有]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(summary = "查询所有定时任务", description = "获取系统中所有定时任务信息")
|
||||||
|
@SaCheckPermission("monitor:job:list")
|
||||||
|
@GetMapping("/sysJob/list")
|
||||||
|
public List<SysJob> list(SysJobVo vo) {
|
||||||
|
LambdaQueryWrapper<SysJob> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.like(StringUtils.isNotBlank(vo.getJobName()), SysJob::getJobName, vo.getJobName());
|
||||||
|
wrapper.eq(StringUtils.isNotBlank(vo.getJobType()), SysJob::getJobType, vo.getJobType());
|
||||||
|
wrapper.eq(StringUtils.isNotBlank(vo.getJobStatus()), SysJob::getJobStatus, vo.getJobStatus());
|
||||||
|
wrapper.orderByDesc(SysJob::getCreateTime);
|
||||||
|
return sysJobService.list(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [根据ID查询数据]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(summary = "根据ID查询任务", description = "根据任务ID获取定时任务详细信息")
|
||||||
|
@SaCheckPermission("monitor:job:list")
|
||||||
|
@GetMapping("/sysJob/getById/{id}")
|
||||||
|
public SysJob getById(@PathVariable("id") Long id) {
|
||||||
|
return sysJobService.getById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [删除任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(summary = "删除定时任务", description = "根据任务ID删除指定的定时任务")
|
||||||
|
@SaCheckPermission("monitor:job:delete")
|
||||||
|
@PostMapping("/sysJob/deleteById/{id}")
|
||||||
|
public String deleteById(@PathVariable("id") Long id) {
|
||||||
|
try {
|
||||||
|
// 1、停止定时任务
|
||||||
|
CronUtil.remove(id + "");
|
||||||
|
// 2、进行删除
|
||||||
|
boolean remove = sysJobService.removeById(id);
|
||||||
|
if (!remove) {
|
||||||
|
return "删除任务失败,请重试";
|
||||||
|
}
|
||||||
|
return "删除成功";
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除定时任务失败", e);
|
||||||
|
return "删除任务失败:" + e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [批量删除]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(summary = "批量删除定时任务", description = "根据任务ID列表批量删除定时任务")
|
||||||
|
@SaCheckPermission("monitor:job:delete")
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@PostMapping("/sysJob/batchDelete")
|
||||||
|
public String batchDelete(@RequestBody List<Long> jobIds) {
|
||||||
|
try {
|
||||||
|
// 1、停止定时任务
|
||||||
|
for (Long jobId : jobIds) {
|
||||||
|
CronUtil.remove(jobId + "");
|
||||||
|
}
|
||||||
|
// 2、批量删除
|
||||||
|
boolean batch = sysJobService.removeBatchByIds(jobIds);
|
||||||
|
if (!batch) {
|
||||||
|
return "删除任务失败,请重试";
|
||||||
|
}
|
||||||
|
return "批量删除成功";
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("批量删除定时任务失败", e);
|
||||||
|
return "删除任务失败:" + e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [任务调度状态修改]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(summary = "修改任务状态", description = "修改定时任务的运行状态和执行策略")
|
||||||
|
@SaCheckPermission("monitor:job:update")
|
||||||
|
@PostMapping("/sysJob/updateStatus/{id}/{jobStatus}/{policyStatus}")
|
||||||
|
public String updateStatus(@PathVariable("id") Long id,
|
||||||
|
@PathVariable("jobStatus") String jobStatus,
|
||||||
|
@PathVariable("policyStatus") String policyStatus) {
|
||||||
|
try {
|
||||||
|
sysJobService.updateStatus(id, jobStatus, policyStatus);
|
||||||
|
return "操作成功";
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("修改任务状态失败", e);
|
||||||
|
return "操作失败:" + e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [立即运行任务-执行一次]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(summary = "立即执行任务", description = "手动立即执行指定的定时任务")
|
||||||
|
@SaCheckPermission("monitor:job:run")
|
||||||
|
@GetMapping("/sysJob/runNow/{id}")
|
||||||
|
public String runNow(@PathVariable Long id) {
|
||||||
|
try {
|
||||||
|
sysJobService.runNow(id);
|
||||||
|
return "任务执行成功";
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("立即执行任务失败", e);
|
||||||
|
return "任务执行失败:" + e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [添加定时任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(summary = "新增定时任务", description = "创建新的定时任务配置")
|
||||||
|
@SaCheckPermission("monitor:job:add")
|
||||||
|
@PostMapping("/sysJob/add")
|
||||||
|
public String addJob(@RequestBody SysJob job) {
|
||||||
|
try {
|
||||||
|
sysJobService.addJob(job);
|
||||||
|
return "添加成功";
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("添加定时任务失败", e);
|
||||||
|
return "添加失败:" + e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [修改定时任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Operation(summary = "修改定时任务", description = "更新现有定时任务的配置信息")
|
||||||
|
@SaCheckPermission("monitor:job:update")
|
||||||
|
@PostMapping("/sysJob/update")
|
||||||
|
public String updateJob(@RequestBody SysJob job) {
|
||||||
|
try {
|
||||||
|
sysJobService.updateJob(job);
|
||||||
|
return "修改成功";
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("修改定时任务失败", e);
|
||||||
|
return "修改失败:" + e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package org.leocoder.thin.job.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import org.leocoder.thin.domain.pojo.system.SysJob;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务服务接口]
|
||||||
|
*/
|
||||||
|
public interface SysJobService extends IService<SysJob> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [停止任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
void pauseJob(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [启动定时任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
void resumeJob(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [任务调度状态修改]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
void updateStatus(Long id, String jobStatus, String policyStatus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [立即运行任务-执行一次]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
void runNow(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [添加定时任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
void addJob(SysJob job);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [修改定时任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
void updateJob(SysJob job);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,392 @@
|
|||||||
|
package org.leocoder.thin.job.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.cron.CronUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.leocoder.thin.common.satoken.CoderLoginUtil;
|
||||||
|
import org.leocoder.thin.domain.pojo.system.SysJob;
|
||||||
|
import org.leocoder.thin.job.service.SysJobService;
|
||||||
|
import org.leocoder.thin.job.task.CommonTimerTaskRunner;
|
||||||
|
import org.leocoder.thin.mybatisplus.mapper.system.SysJobMapper;
|
||||||
|
import org.springframework.scheduling.support.CronExpression;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务服务实现类]
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {
|
||||||
|
|
||||||
|
private final SysJobMapper sysJobMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [停止任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void pauseJob(Long id) {
|
||||||
|
SysJob byId = this.getById(id);
|
||||||
|
if (byId == null) {
|
||||||
|
throw new RuntimeException("任务不存在");
|
||||||
|
}
|
||||||
|
if ("1".equals(byId.getJobStatus())) {
|
||||||
|
throw new RuntimeException("该任务已处于停止状态");
|
||||||
|
}
|
||||||
|
|
||||||
|
CronUtil.remove(id + "");
|
||||||
|
|
||||||
|
LambdaUpdateWrapper<SysJob> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.set(SysJob::getJobStatus, "1");
|
||||||
|
updateWrapper.eq(SysJob::getJobId, id);
|
||||||
|
boolean update = this.update(updateWrapper);
|
||||||
|
|
||||||
|
if (!update) {
|
||||||
|
throw new RuntimeException("暂停任务失败,请重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [启动定时任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void resumeJob(Long id) {
|
||||||
|
if (null == id) {
|
||||||
|
throw new RuntimeException("该任务未查询到");
|
||||||
|
}
|
||||||
|
|
||||||
|
SysJob job = this.getById(id);
|
||||||
|
if (job == null) {
|
||||||
|
throw new RuntimeException("任务不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先停止定时任务
|
||||||
|
CronUtil.remove(id + "");
|
||||||
|
|
||||||
|
// 再注册定时任务
|
||||||
|
CronUtil.schedule(job.getJobId() + "", job.getCronExpression(), () -> {
|
||||||
|
executeMethod(job.getClassPath(), job.getMethodName(), job.getJobParams());
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新任务状态为正常
|
||||||
|
LambdaUpdateWrapper<SysJob> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.set(SysJob::getJobStatus, "0");
|
||||||
|
updateWrapper.eq(SysJob::getJobId, id);
|
||||||
|
this.update(updateWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [执行方法,多个参数传递,必须使用逗号分割,一个一个对应]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
private void executeMethod(String classPath, String methodName, String params) {
|
||||||
|
if (StringUtils.isBlank(classPath)) {
|
||||||
|
throw new RuntimeException("类绝对路径不能为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(methodName)) {
|
||||||
|
throw new RuntimeException("方法名称不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用SpringUtil获取类的实例
|
||||||
|
CommonTimerTaskRunner runner = (CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(classPath));
|
||||||
|
|
||||||
|
Method[] methods = runner.getClass().getMethods();
|
||||||
|
Method targetMethod = null;
|
||||||
|
|
||||||
|
for (Method method : methods) {
|
||||||
|
if (method.getName().equals(methodName)) {
|
||||||
|
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||||
|
if (parameterTypes.length > 0 && StringUtils.isBlank(params)) {
|
||||||
|
throw new IllegalArgumentException("缺少参数");
|
||||||
|
}
|
||||||
|
if (parameterTypes.length == 0 && StringUtils.isNotBlank(params)) {
|
||||||
|
throw new IllegalArgumentException("不应传递参数");
|
||||||
|
}
|
||||||
|
if (parameterTypes.length > 0 && StringUtils.isNotBlank(params)) {
|
||||||
|
String[] paramArray = params.split(",");
|
||||||
|
if (paramArray.length != parameterTypes.length) {
|
||||||
|
throw new IllegalArgumentException("参数数量不匹配");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < paramArray.length; i++) {
|
||||||
|
Object convertedValue = convertToType(parameterTypes[i], paramArray[i]);
|
||||||
|
if (convertedValue == null) {
|
||||||
|
throw new IllegalArgumentException("参数类型不匹配");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMethod = method;
|
||||||
|
} else {
|
||||||
|
targetMethod = method;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetMethod != null) {
|
||||||
|
// 调用方法
|
||||||
|
if (StringUtils.isNotBlank(params)) {
|
||||||
|
targetMethod.invoke(runner, extractTypedParams(targetMethod.getParameterTypes(), params));
|
||||||
|
} else {
|
||||||
|
targetMethod.invoke(runner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("指定方法不存在");
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("执行定时任务出现异常", e);
|
||||||
|
throw new RuntimeException("执行定时任务出现异常:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [多参数使用逗号分隔开]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
private Object[] extractTypedParams(Class<?>[] parameterTypes, String params) {
|
||||||
|
String[] paramArray = params.split(",");
|
||||||
|
Object[] typedParams = new Object[paramArray.length];
|
||||||
|
for (int i = 0; i < paramArray.length; i++) {
|
||||||
|
typedParams[i] = convertToType(parameterTypes[i], paramArray[i].trim());
|
||||||
|
}
|
||||||
|
return typedParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [类型转换]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
private Object convertToType(Class<?> targetType, String value) {
|
||||||
|
if (targetType.equals(String.class)) {
|
||||||
|
return value;
|
||||||
|
} else if (targetType.equals(Integer.class) || targetType.equals(int.class)) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (targetType.equals(Boolean.class) || targetType.equals(boolean.class)) {
|
||||||
|
return Boolean.parseBoolean(value);
|
||||||
|
} else if (targetType.equals(Long.class) || targetType.equals(long.class)) {
|
||||||
|
try {
|
||||||
|
return Long.parseLong(value);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 根据需要添加更多类型转换
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [任务调度状态修改]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void updateStatus(Long id, String jobStatus, String policyStatus) {
|
||||||
|
if (StringUtils.isBlank(jobStatus) || id == null) {
|
||||||
|
throw new RuntimeException("请传递相关信息");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("0".equals(jobStatus)) {
|
||||||
|
if ("1".equals(policyStatus)) {
|
||||||
|
resumeJob(id); // 启动定时任务
|
||||||
|
}
|
||||||
|
} else if ("1".equals(jobStatus)) {
|
||||||
|
pauseJob(id); // 停止定时任务
|
||||||
|
}
|
||||||
|
|
||||||
|
LambdaUpdateWrapper<SysJob> wrapper = new LambdaUpdateWrapper<>();
|
||||||
|
wrapper.set(SysJob::getJobStatus, jobStatus);
|
||||||
|
wrapper.set(SysJob::getPolicyStatus, policyStatus);
|
||||||
|
wrapper.eq(SysJob::getJobId, id);
|
||||||
|
boolean update = this.update(wrapper);
|
||||||
|
|
||||||
|
if (!update) {
|
||||||
|
throw new RuntimeException("操作失败,请重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [立即运行任务-执行一次,不影响定时调度]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void runNow(Long id) {
|
||||||
|
SysJob job = this.getById(id);
|
||||||
|
if (ObjectUtils.isEmpty(job) || job.getJobId() == null) {
|
||||||
|
throw new RuntimeException("未查到当前任务");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接执行一次任务,不影响现有的定时调度
|
||||||
|
executeMethod(job.getClassPath(), job.getMethodName(), job.getJobParams());
|
||||||
|
|
||||||
|
// 注意:这里不移除定时任务,让定时调度继续按计划运行
|
||||||
|
log.info("立即执行任务完成,任务ID:{},定时调度继续保持运行", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [添加定时任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void addJob(SysJob job) {
|
||||||
|
// 1、参数校验
|
||||||
|
checkParams(job);
|
||||||
|
|
||||||
|
// 2、是否添加重复的定时任务
|
||||||
|
LambdaQueryWrapper<SysJob> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SysJob::getClassPath, job.getClassPath());
|
||||||
|
wrapper.eq(SysJob::getMethodName, job.getMethodName());
|
||||||
|
wrapper.eq(SysJob::getCronExpression, job.getCronExpression());
|
||||||
|
long count = this.count(wrapper);
|
||||||
|
if (count > 0) {
|
||||||
|
throw new RuntimeException("存在重复执行的定时任务,名称为:" + job.getJobName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3、添加定时任务
|
||||||
|
boolean save = this.save(job);
|
||||||
|
if (!save) {
|
||||||
|
throw new RuntimeException("添加失败,请重试");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4、根据任务状态,进行执行定时策略
|
||||||
|
if ("0".equals(job.getJobStatus())) {
|
||||||
|
if ("1".equals(job.getPolicyStatus())) {
|
||||||
|
// 开启定时任务
|
||||||
|
resumeJob(job.getJobId());
|
||||||
|
} else if ("2".equals(job.getPolicyStatus())) {
|
||||||
|
// 先停止定时任务
|
||||||
|
CronUtil.remove(job.getJobId() + "");
|
||||||
|
// 执行一次
|
||||||
|
executeMethod(job.getClassPath(), job.getMethodName(), job.getJobParams());
|
||||||
|
} else {
|
||||||
|
// 停止任务
|
||||||
|
CronUtil.remove(job.getJobId() + "");
|
||||||
|
LambdaUpdateWrapper<SysJob> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.set(SysJob::getPolicyStatus, "3");
|
||||||
|
updateWrapper.eq(SysJob::getJobId, job.getJobId());
|
||||||
|
boolean update = this.update(updateWrapper);
|
||||||
|
if (!update) {
|
||||||
|
throw new RuntimeException("操作失败,请重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 停止任务,计划策略并改为放弃执行
|
||||||
|
CronUtil.remove(job.getJobId() + "");
|
||||||
|
LambdaUpdateWrapper<SysJob> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.set(SysJob::getPolicyStatus, "3");
|
||||||
|
updateWrapper.eq(SysJob::getJobId, job.getJobId());
|
||||||
|
boolean update = this.update(updateWrapper);
|
||||||
|
if (!update) {
|
||||||
|
throw new RuntimeException("操作失败,请重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [参数校验]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
private void checkParams(SysJob job) {
|
||||||
|
// 校验表达式
|
||||||
|
if (!CronExpression.isValidExpression(job.getCronExpression())) {
|
||||||
|
throw new RuntimeException("cron表达式:" + job.getCronExpression() + "格式不正确");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验定时任务类
|
||||||
|
try {
|
||||||
|
Class<?> actionClass = Class.forName(job.getClassPath());
|
||||||
|
if (!CommonTimerTaskRunner.class.isAssignableFrom(actionClass)) {
|
||||||
|
throw new RuntimeException("定时任务对应的类:" + job.getClassPath() + "不符合要求");
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException("定时任务找不到对应的类,名称为:" + job.getClassPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [修改定时任务]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void updateJob(SysJob job) {
|
||||||
|
// 1、参数校验
|
||||||
|
checkParams(job);
|
||||||
|
if (job.getJobId() == null) {
|
||||||
|
throw new RuntimeException("请选择需要修改的任务");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2、是否修改为数据库已经存在的定时任务(保持与添加任务相同的检查逻辑)
|
||||||
|
LambdaQueryWrapper<SysJob> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SysJob::getClassPath, job.getClassPath());
|
||||||
|
wrapper.eq(SysJob::getMethodName, job.getMethodName());
|
||||||
|
wrapper.eq(SysJob::getCronExpression, job.getCronExpression());
|
||||||
|
wrapper.ne(SysJob::getJobId, job.getJobId()); // 排除当前任务
|
||||||
|
long count = this.count(wrapper);
|
||||||
|
if (count > 0) {
|
||||||
|
throw new RuntimeException("存在重复执行的定时任务,名称为:" + job.getJobName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3、修改任务
|
||||||
|
// 这里应该从登录用户上下文获取
|
||||||
|
job.setUpdateBy(CoderLoginUtil.getLoginName());
|
||||||
|
boolean update = this.updateById(job);
|
||||||
|
if (!update) {
|
||||||
|
throw new RuntimeException("修改失败,请重试");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4、根据任务状态,进行执行定时策略
|
||||||
|
if ("0".equals(job.getJobStatus())) {
|
||||||
|
if ("1".equals(job.getPolicyStatus())) {
|
||||||
|
// 先停止定时任务,再开启定时任务
|
||||||
|
resumeJob(job.getJobId());
|
||||||
|
} else if ("2".equals(job.getPolicyStatus())) {
|
||||||
|
// 先停止定时任务
|
||||||
|
CronUtil.remove(job.getJobId() + "");
|
||||||
|
// 再开始执行一次定时任务
|
||||||
|
executeMethod(job.getClassPath(), job.getMethodName(), job.getJobParams());
|
||||||
|
} else {
|
||||||
|
// 停止任务,计划策略并改为放弃执行
|
||||||
|
CronUtil.remove(job.getJobId() + "");
|
||||||
|
LambdaUpdateWrapper<SysJob> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.set(SysJob::getPolicyStatus, "3");
|
||||||
|
updateWrapper.eq(SysJob::getJobId, job.getJobId());
|
||||||
|
boolean updateBoolean = this.update(updateWrapper);
|
||||||
|
if (!updateBoolean) {
|
||||||
|
throw new RuntimeException("操作失败,请重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 停止任务,计划策略并改为放弃执行
|
||||||
|
CronUtil.remove(job.getJobId() + "");
|
||||||
|
LambdaUpdateWrapper<SysJob> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.set(SysJob::getPolicyStatus, "3");
|
||||||
|
updateWrapper.eq(SysJob::getJobId, job.getJobId());
|
||||||
|
boolean updateBoolean = this.update(updateWrapper);
|
||||||
|
if (!updateBoolean) {
|
||||||
|
throw new RuntimeException("操作失败,请重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
package org.leocoder.thin.job.task;
|
||||||
|
|
||||||
|
import cn.hutool.cron.CronUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.leocoder.thin.domain.pojo.system.SysJob;
|
||||||
|
import org.leocoder.thin.job.service.SysJobService;
|
||||||
|
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务监听器,系统启动时将定时任务启动]
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
public class CoderJobListener implements ApplicationListener<ApplicationStartedEvent>, Ordered {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SysJobService sysJobService;
|
||||||
|
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
|
||||||
|
try {
|
||||||
|
// 查询状态正常的定时任务 AND 执行策略是立即执行类型的数据,筛选后进行开始定时任务
|
||||||
|
LambdaQueryWrapper<SysJob> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SysJob::getJobStatus, "0"); // 正常状态
|
||||||
|
wrapper.eq(SysJob::getPolicyStatus, "1"); // 立即执行
|
||||||
|
|
||||||
|
List<SysJob> jobList = sysJobService.list(wrapper);
|
||||||
|
if(CollectionUtils.isNotEmpty(jobList)){
|
||||||
|
for (SysJob sysJob : jobList) {
|
||||||
|
try {
|
||||||
|
// 启动定时任务
|
||||||
|
sysJobService.resumeJob(sysJob.getJobId());
|
||||||
|
log.info("自动启动定时任务:{},表达式:{}", sysJob.getJobName(), sysJob.getCronExpression());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("自动启动定时任务失败:{},错误:{}", sysJob.getJobName(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置秒级别的启用
|
||||||
|
CronUtil.setMatchSecond(true);
|
||||||
|
log.info("启动定时器执行器,支持秒级精度");
|
||||||
|
CronUtil.restart();
|
||||||
|
|
||||||
|
log.info("定时任务系统初始化完成,共启动{}个任务", jobList != null ? jobList.size() : 0);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("定时任务系统初始化失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return LOWEST_PRECEDENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package org.leocoder.thin.job.task;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.leocoder.thin.job.service.SysJobService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务执行器示例实现]
|
||||||
|
* 注意:仅支持字符串、数字、布尔值进行传递参数。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CoderJobTimerTaskRunner implements CommonTimerTaskRunner {
|
||||||
|
|
||||||
|
private final SysJobService sysJobService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [单个参数,param可以传递参数,通过数据库的job_params]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void paramAction(String param) {
|
||||||
|
log.info("执行单参数定时任务,参数:{}", param);
|
||||||
|
// 这里可以执行具体的业务逻辑
|
||||||
|
// 示例:发送邮件、清理缓存、同步数据等
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [无参数执行]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void noParamAction() {
|
||||||
|
log.info("执行无参数定时任务");
|
||||||
|
// 示例:查询所有定时任务并输出
|
||||||
|
sysJobService.list().forEach(job ->
|
||||||
|
log.info("任务:{},状态:{}", job.getJobName(), job.getJobStatusText())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [多参数执行]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void manyParamsAction(String param1, Integer param2) {
|
||||||
|
log.info("执行多参数定时任务,参数1:{},参数2:{}", param1, param2);
|
||||||
|
// 这里可以执行具体的业务逻辑
|
||||||
|
// 示例:根据参数处理不同类型的数据
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package org.leocoder.thin.job.task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Leocoder
|
||||||
|
* @description [定时任务执行器通用接口 - 所有定时任务都实现这个接口,方便我们遍历所有可用的定时任务类]
|
||||||
|
*/
|
||||||
|
public interface CommonTimerTaskRunner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [单参数执行]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
void paramAction(String param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [无参数执行]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
void noParamAction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description [多参数执行,多个参数传递,必须使用逗号分割,一个一个对应]
|
||||||
|
* @author Leocoder
|
||||||
|
*/
|
||||||
|
void manyParamsAction(String param1, Integer param2);
|
||||||
|
|
||||||
|
}
|
||||||
@ -24,6 +24,7 @@
|
|||||||
<module>coder-common-thin-oper-logs</module>
|
<module>coder-common-thin-oper-logs</module>
|
||||||
<module>coder-common-thin-oss</module>
|
<module>coder-common-thin-oss</module>
|
||||||
<module>coder-common-thin-dict</module>
|
<module>coder-common-thin-dict</module>
|
||||||
|
<module>coder-common-thin-job</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@ -44,11 +44,11 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- 定时任务 -->
|
<!-- 定时任务 -->
|
||||||
<!-- <dependency> -->
|
<dependency>
|
||||||
<!-- <groupId>org.leocoder.thin</groupId> -->
|
<groupId>org.leocoder.thin</groupId>
|
||||||
<!-- <artifactId>coder-common-thin-job</artifactId> -->
|
<artifactId>coder-common-thin-job</artifactId>
|
||||||
<!-- <version>${revision}</version> -->
|
<version>${revision}</version>
|
||||||
<!-- </dependency> -->
|
</dependency>
|
||||||
<!-- 代码生成器 -->
|
<!-- 代码生成器 -->
|
||||||
<!-- <dependency> -->
|
<!-- <dependency> -->
|
||||||
<!-- <groupId>org.leocoder.thin</groupId> -->
|
<!-- <groupId>org.leocoder.thin</groupId> -->
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import org.leocoder.thin.repect.anno.EnableCoderRepeatSubmit;
|
|||||||
import org.leocoder.thin.resultex.anno.EnableResultEx;
|
import org.leocoder.thin.resultex.anno.EnableResultEx;
|
||||||
import org.leocoder.thin.satoken.anno.EnableCoderSaToken;
|
import org.leocoder.thin.satoken.anno.EnableCoderSaToken;
|
||||||
import org.leocoder.thin.dict.anno.EnableCoderDict;
|
import org.leocoder.thin.dict.anno.EnableCoderDict;
|
||||||
|
import org.leocoder.thin.job.anno.EnableCoderJob;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
@ -31,6 +32,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
|||||||
@EnableOperLog
|
@EnableOperLog
|
||||||
@EnableCoderOss
|
@EnableCoderOss
|
||||||
@EnableCoderDict
|
@EnableCoderDict
|
||||||
|
@EnableCoderJob
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@SpringBootApplication(scanBasePackages = "org.leocoder.thin")
|
@SpringBootApplication(scanBasePackages = "org.leocoder.thin")
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
-- 字典管理按钮权限补充数据
|
|
||||||
-- 时间:2025-09-26
|
|
||||||
-- 功能:为字典管理添加操作按钮和子菜单
|
|
||||||
|
|
||||||
-- 为字典管理主菜单(ID:24)添加操作按钮
|
|
||||||
-- 搜索按钮
|
|
||||||
INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `en_name`, `parent_id`, `menu_type`, `path`, `name`, `component`, `icon`, `auth`, `menu_status`, `active_menu`, `is_hide`, `is_link`, `is_keep_alive`, `is_full`, `is_affix`, `is_spread`, `sorted`, `create_by`, `create_time`, `update_by`, `update_time`)
|
|
||||||
VALUES (25, '搜索', NULL, 24, '3', '', '', '', '', 'system:dict:search', '0', '', '0', '', '0', '1', '1', '1', 1, 'admin', NOW(), 'admin', NOW());
|
|
||||||
|
|
||||||
-- 新增按钮
|
|
||||||
INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `en_name`, `parent_id`, `menu_type`, `path`, `name`, `component`, `icon`, `auth`, `menu_status`, `active_menu`, `is_hide`, `is_link`, `is_keep_alive`, `is_full`, `is_affix`, `is_spread`, `sorted`, `create_by`, `create_time`, `update_by`, `update_time`)
|
|
||||||
VALUES (26, '新增', NULL, 24, '3', '', '', '', '', 'system:dict:add', '0', '', '0', '', '0', '1', '1', '1', 2, 'admin', NOW(), 'admin', NOW());
|
|
||||||
|
|
||||||
-- 修改按钮
|
|
||||||
INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `en_name`, `parent_id`, `menu_type`, `path`, `name`, `component`, `icon`, `auth`, `menu_status`, `active_menu`, `is_hide`, `is_link`, `is_keep_alive`, `is_full`, `is_affix`, `is_spread`, `sorted`, `create_by`, `create_time`, `update_by`, `update_time`)
|
|
||||||
VALUES (27, '修改', NULL, 24, '3', '', '', '', '', 'system:dict:update', '0', '', '0', '', '0', '1', '1', '1', 3, 'admin', NOW(), 'admin', NOW());
|
|
||||||
|
|
||||||
-- 删除按钮
|
|
||||||
INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `en_name`, `parent_id`, `menu_type`, `path`, `name`, `component`, `icon`, `auth`, `menu_status`, `active_menu`, `is_hide`, `is_link`, `is_keep_alive`, `is_full`, `is_affix`, `is_spread`, `sorted`, `create_by`, `create_time`, `update_by`, `update_time`)
|
|
||||||
VALUES (28, '删除', NULL, 24, '3', '', '', '', '', 'system:dict:delete', '0', '', '0', '', '0', '1', '1', '1', 4, 'admin', NOW(), 'admin', NOW());
|
|
||||||
|
|
||||||
-- 为超级管理员角色添加这些新权限(假设角色ID为1)
|
|
||||||
INSERT INTO `sys_role_menu` VALUES (1, 25);
|
|
||||||
INSERT INTO `sys_role_menu` VALUES (1, 26);
|
|
||||||
INSERT INTO `sys_role_menu` VALUES (1, 27);
|
|
||||||
INSERT INTO `sys_role_menu` VALUES (1, 28);
|
|
||||||
111
sql/20250927-sys_job.sql
Normal file
111
sql/20250927-sys_job.sql
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- 定时任务管理功能 SQL 脚本
|
||||||
|
-- 作者: Leocoder
|
||||||
|
-- 创建时间: 2025-09-27
|
||||||
|
-- 描述: 支持动态配置和管理的定时任务系统表结构
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sys_job
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sys_job`;
|
||||||
|
CREATE TABLE `sys_job` (
|
||||||
|
`job_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务ID',
|
||||||
|
`job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '任务名称',
|
||||||
|
`job_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '任务类型[1-管理平台 2-小程序 3-App]',
|
||||||
|
`class_path` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '类路径',
|
||||||
|
`method_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '方法名称',
|
||||||
|
`cron_expression` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'cron表达式',
|
||||||
|
`policy_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT 'cron计划策略[1-立即执行 2-执行一次 3-放弃执行]',
|
||||||
|
`job_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '任务状态[0-正常 1-暂停]',
|
||||||
|
`job_params` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '任务参数',
|
||||||
|
`remark` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '备注',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`create_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||||
|
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '修改人',
|
||||||
|
PRIMARY KEY (`job_id`) USING BTREE,
|
||||||
|
KEY `idx_job_status` (`job_status`),
|
||||||
|
KEY `idx_job_type` (`job_type`),
|
||||||
|
KEY `idx_create_time` (`create_time`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='定时任务管理表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of sys_job - 初始化示例数据
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO `sys_job` (`job_id`, `job_name`, `job_type`, `class_path`, `method_name`, `cron_expression`, `policy_status`, `job_status`, `job_params`, `remark`, `create_by`) VALUES
|
||||||
|
(1, '测试任务-单参数', '1', 'org.leocoder.thin.job.task.CoderJobTimerTaskRunner', 'paramAction', '0 0/5 * * * ?', '1', '1', 'CODER-THIN-TEST', '每5分钟执行一次的测试任务', 'Leocoder'),
|
||||||
|
(2, '测试任务-无参数', '1', 'org.leocoder.thin.job.task.CoderJobTimerTaskRunner', 'noParamAction', '0 0/10 * * * ?', '3', '1', '', '每10分钟执行一次的无参数测试任务', 'Leocoder'),
|
||||||
|
(3, '测试任务-多参数', '1', 'org.leocoder.thin.job.task.CoderJobTimerTaskRunner', 'manyParamsAction', '0 0 2 * * ?', '1', '1', 'CODER-THIN,999', '每天凌晨2点执行的多参数测试任务', 'Leocoder');
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sys_job_log (可选,用于任务执行日志记录)
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sys_job_log`;
|
||||||
|
CREATE TABLE `sys_job_log` (
|
||||||
|
`job_log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务日志ID',
|
||||||
|
`job_id` bigint NOT NULL COMMENT '任务ID',
|
||||||
|
`job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '任务名称',
|
||||||
|
`job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名',
|
||||||
|
`invoke_target` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调用目标字符串',
|
||||||
|
`job_message` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '日志信息',
|
||||||
|
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '执行状态[0-正常 1-失败]',
|
||||||
|
`exception_info` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '异常信息',
|
||||||
|
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
|
||||||
|
`stop_time` datetime DEFAULT NULL COMMENT '结束时间',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
PRIMARY KEY (`job_log_id`) USING BTREE,
|
||||||
|
KEY `idx_job_id` (`job_id`),
|
||||||
|
KEY `idx_status` (`status`),
|
||||||
|
KEY `idx_create_time` (`create_time`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='定时任务执行日志表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 添加菜单权限数据 (可选,用于前端菜单展示)
|
||||||
|
-- ----------------------------
|
||||||
|
-- 注意:以下SQL需要根据实际的菜单权限表结构进行调整
|
||||||
|
|
||||||
|
-- 定时任务管理菜单
|
||||||
|
-- INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `url`, `menu_type`, `visible`, `perms`, `icon`, `create_by`, `remark`) VALUES
|
||||||
|
-- ('定时任务', 2, 6, '/monitor/job', 'C', '0', 'monitor:job:view', 'job', 'Leocoder', '定时任务菜单');
|
||||||
|
|
||||||
|
-- 定时任务按钮权限
|
||||||
|
-- INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `url`, `menu_type`, `visible`, `perms`, `create_by`, `remark`) VALUES
|
||||||
|
-- ('任务查询', 最新菜单ID, 1, '#', 'F', '0', 'monitor:job:list', 'Leocoder', ''),
|
||||||
|
-- ('任务新增', 最新菜单ID, 2, '#', 'F', '0', 'monitor:job:add', 'Leocoder', ''),
|
||||||
|
-- ('任务修改', 最新菜单ID, 3, '#', 'F', '0', 'monitor:job:edit', 'Leocoder', ''),
|
||||||
|
-- ('任务删除', 最新菜单ID, 4, '#', 'F', '0', 'monitor:job:delete', 'Leocoder', ''),
|
||||||
|
-- ('任务执行', 最新菜单ID, 5, '#', 'F', '0', 'monitor:job:run', 'Leocoder', ''),
|
||||||
|
-- ('状态修改', 最新菜单ID, 6, '#', 'F', '0', 'monitor:job:update', 'Leocoder', '');
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- 说明文档
|
||||||
|
-- ============================================================================
|
||||||
|
/*
|
||||||
|
1. 表结构说明:
|
||||||
|
- sys_job: 定时任务配置表,存储任务的基本信息和调度配置
|
||||||
|
- sys_job_log: 任务执行日志表,记录每次任务的执行情况(可选)
|
||||||
|
|
||||||
|
2. 字段说明:
|
||||||
|
- job_id: 任务唯一标识,雪花算法生成
|
||||||
|
- class_path: 任务执行类的完整路径
|
||||||
|
- method_name: 要执行的方法名
|
||||||
|
- cron_expression: 标准cron表达式
|
||||||
|
- policy_status: 执行策略(1立即执行/2执行一次/3放弃执行)
|
||||||
|
- job_status: 任务状态(0正常/1暂停)
|
||||||
|
- job_params: 任务参数,多个参数用逗号分隔
|
||||||
|
- is_delete: 逻辑删除标识
|
||||||
|
|
||||||
|
3. 使用说明:
|
||||||
|
- 支持多种参数类型:String, Integer, Boolean
|
||||||
|
- 支持动态启停任务
|
||||||
|
- 支持任务执行日志记录
|
||||||
|
- 集成权限控制机制
|
||||||
|
|
||||||
|
4. 注意事项:
|
||||||
|
- 执行类必须实现 CommonTimerTaskRunner 接口
|
||||||
|
- cron表达式支持秒级精度(6位)
|
||||||
|
- 建议生产环境定期清理执行日志
|
||||||
|
*/
|
||||||
Loading…
Reference in New Issue
Block a user