init
This commit is contained in:
commit
3cdddc4533
|
|
@ -0,0 +1,8 @@
|
||||||
|
# 营销模块目录
|
||||||
|
marketing-module-*/
|
||||||
|
marketing-framework/
|
||||||
|
marketing-client-sdk/
|
||||||
|
marketing-dependencies/
|
||||||
|
# 前端依赖
|
||||||
|
marketing-ui/admin-ui/node_modules/
|
||||||
|
**/node_modules/
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-admin-all</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>marketing-admin-all</name>
|
||||||
|
<description>marketing-admin基础脚手架</description>
|
||||||
|
<url>https://github.com/makemyownlife/marketing-admin</url>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>huaweicloud</id>
|
||||||
|
<name>huawei</name>
|
||||||
|
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>aliyunmaven</id>
|
||||||
|
<name>aliyun</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
.idea
|
||||||
|
# bin
|
||||||
|
pkg
|
||||||
|
*.iml
|
||||||
|
target
|
||||||
|
.vscode/*
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
.svn/
|
||||||
|
target/
|
||||||
|
test-output/
|
||||||
|
*.class
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings/
|
||||||
|
tmp
|
||||||
|
temp
|
||||||
|
*.log
|
||||||
|
antx.properties
|
||||||
|
otter.properties
|
||||||
|
jtester.properties
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
.DS_Store
|
||||||
|
*.tar.gz
|
||||||
|
*.rpm
|
||||||
|
*.dat
|
||||||
|
conf/
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
target/
|
||||||
|
marketing-ui/admin-ui/dist-prod/favicon.ico
|
||||||
|
marketing-ui/admin-ui/dist-prod/home.png
|
||||||
|
marketing-ui/admin-ui/dist-prod/index.html
|
||||||
|
marketing-ui/admin-ui/dist-prod/logo.gif
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/403---x72x_k.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/403-RqeqO19C.svg
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/404-B3JyPfEa.svg
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/404-TFng1Qog.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/500-BGu8fdSB.svg
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/500-DHh5Q027.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ApiAccessLogDetail-CAT7ipXo.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ApiAccessLogDetail.vue_vue_type_script_setup_true_lang-DRe7aG98.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ApiErrorLogDetail-BG7lkl1-.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ApiErrorLogDetail.vue_vue_type_script_setup_true_lang-B2lrJ1Mf.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/AreaForm-C2IAMT7d.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/AreaForm.vue_vue_type_script_setup_true_lang-Ug8Jk3ra.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/avatar-5EZc-Rgm.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/avatar-Ca9gIz_i.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/avatar-Dcbh69co.gif
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/BasicInfo-BS4wKhiV.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/BasicInfo-BwbMfmj0.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/BasicInfo-BwbMfmj0.css.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/BasicInfo.vue_vue_type_script_setup_true_lang-Dz3kyAPJ.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/BasicInfo.vue_vue_type_script_setup_true_lang-Dz3kyAPJ.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/BasicInfoForm-Bgrql1U2.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/BasicInfoForm.vue_vue_type_script_setup_true_lang-Bfm0Ur-D.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ChannelAccountForm-DfmrksCV.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ChannelAccountForm.vue_vue_type_script_setup_true_lang-D-3WI5tz.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/color-xEzN0Pg5.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ColumInfoForm-JtfCXPEo.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ColumInfoForm.vue_vue_type_script_setup_true_lang-4Crsas99.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ConfigForm-C8FfNkXj.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ConfigForm.vue_vue_type_script_setup_true_lang-DH4PLCMr.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/constants-D0aoFN3l.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ContentWrap.vue_vue_type_script_setup_true_lang-DnXdNlYy.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DataSourceConfigForm-D3JMl6Br.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DataSourceConfigForm.vue_vue_type_script_setup_true_lang-Be-KP8WD.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo01ContactForm-Dtx3ls76.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo01ContactForm.vue_vue_type_script_setup_true_lang-DEGK_ELx.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo02CategoryForm-BQIY2jfy.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo02CategoryForm.vue_vue_type_script_setup_true_lang-CHyxtO5c.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseForm-CWrhNLo9.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseForm-D5jay_pz.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseForm-DVQTOoi2.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseForm.vue_vue_type_script_setup_true_lang-Cp1KjWMi.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseForm.vue_vue_type_script_setup_true_lang-CZOYW_Iv.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseForm.vue_vue_type_script_setup_true_lang-DeICqiOB.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseList-B8Mknwmz.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseList-C8fONkDN.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseList.vue_vue_type_script_setup_true_lang-D_0z4z15.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03CourseList.vue_vue_type_script_setup_true_lang-DfO2DiX2.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeForm-BlHpRrRC.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeForm-CAdT3vrv.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeForm-Om44l9YU.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeForm.vue_vue_type_script_setup_true_lang-Bl1acwtg.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeForm.vue_vue_type_script_setup_true_lang-BygOcUvf.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeForm.vue_vue_type_script_setup_true_lang-thuOcCHX.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeList-BCHOWl40.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeList-DuGtbLQ6.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeList.vue_vue_type_script_setup_true_lang-BY2JEaqy.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03GradeList.vue_vue_type_script_setup_true_lang-nUFrI-rU.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03StudentForm-4xkVEPe3.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03StudentForm-Cg9TdrhZ.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03StudentForm-Drb-41jz.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03StudentForm.vue_vue_type_script_setup_true_lang-D3dl_ynA.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03StudentForm.vue_vue_type_script_setup_true_lang-pesN_ZQ0.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Demo03StudentForm.vue_vue_type_script_setup_true_lang-PugoSPnh.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DeptForm-C2T255is.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DeptForm.vue_vue_type_script_setup_true_lang-B0N6L4Ga.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DeptTree-T7XtdGkx.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DeptTree.vue_vue_type_script_setup_true_lang-4sL964g8.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Dialog-BdewL7YE.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Dialog.vue_vue_type_style_index_0_lang-DvBbpV3D.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/dict.type-C0qRJc_H.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DictDataForm-B4V6YkHi.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DictDataForm.vue_vue_type_script_setup_true_lang-BmhRM8Sx.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DictTag.vue_vue_type_script_lang-CPtIjj0S.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DictTypeForm-CdX27RA_.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/DictTypeForm.vue_vue_type_script_setup_true_lang-BLltR3Rv.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/download-oWiM5xVU.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Echart.vue_vue_type_script_setup_true_lang-BcsVFKzI.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Echart.vue_vue_type_script_setup_true_lang-BcsVFKzI.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/echarts-B3Xnk3T1.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/echarts-B3Xnk3T1.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/echarts-data-D_oXWXkg.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/echarts-data-ODRhKdw_.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/EditTable-dVZgKaiI.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/el-descriptions-item-CdyfzhGA.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/el-descriptions-item-D_IHWRLM.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/el-table-v2-Bd4f-86g.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/el-table-v2-Bd4f-86g.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/el-table-v2-UcpT72VY.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/el-tree-select-BC9u6a6J.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/el-tree-select-CcwX-1RK.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/el-virtual-list-a11W3tEv.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/el-virtual-list-gqT3UWYL.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/en-QtiwgkPu.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/en-QtiwgkPu.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Error.vue_vue_type_script_setup_true_lang-D0IaN0n3.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/FileConfigForm-DJUQGYt5.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/FileConfigForm.vue_vue_type_script_setup_true_lang-TsCgwK-I.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/FileForm-BtrdpNoR.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/FileForm.vue_vue_type_script_setup_true_lang-mUeQvY6A.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/fontello-BTAxVWQW.woff
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ForgetPasswordForm-aob2xmWK.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ForgetPasswordForm-CBdI5LV8.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/formatTime-DV6z3HJ9.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/formRules-6BHLt0eT.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/GenerateInfoForm-Cm3zoIJc.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/GenerateInfoForm.vue_vue_type_script_setup_true_lang-fuLOFsp0.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/IFrame.vue_vue_type_script_setup_true_lang-CtOnZAt0.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ImportTable-CZA2ZW-V.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ImportTable.vue_vue_type_script_setup_true_lang-CMHaYpHQ.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-_V6zJOg3.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-2D27ydj1.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Index-4jkUOdzG.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-b1eB6pGg.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-B5nujwuD.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-B9wl4o4u.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-Ba38QfZu.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-BAJLB_-R.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-Bb3kBxCv.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-BGeiC94L.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-Bl_EnnHt.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-BOTr1eeH.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-BrUZgf8Q.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-BRxBcAHj.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-BSp__PPC.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-C0LA7-Yf.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Index-C3L8yP93.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-C03T_KSS.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-C4BENW8H.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-C4yChNuY.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-C8yW75XH.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CB0lTtlY.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Index-CbZesan5.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-ChsGs3cI.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CHvI6XKA.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CIuBqTRP.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CjOFW6_i.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-COMmfAhY.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-Cpuairgj.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-Cpuairgj.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CrfTg3rd.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CRU_VxN8.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CTY-1tgs.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CVFYqYIZ.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CxH2vw_3.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CYLhsPln.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-CYLhsPln.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-D-nrPQ6r.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-D03I7QA5.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-D5gOqURp.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-D8lsdqED.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-Df26oTqT.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-DFCTHbPg.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-DG6XYd9E.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-DiAZs2K6.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-Dqp_pGU3.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-Dqp_pGU3.css.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-DUKJZojM.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-DUKJZojM.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-DVSGTC-P.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-DzSQo7NT.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-iEdnegTT.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-IvNnt4fu.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-KhhTxVBR.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-N6cZ28Pp.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-oLYpNB-Q.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-PGQ0TbxF.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-raJoS0ux.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index-Xmn4R73-.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/index.vue_vue_type_script_setup_true_lang-X_OdiC1e.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Index2-3gfXcXef.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Index2-DeC7rtYf.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/InputPassword-BggAj9L0.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/InputPassword-BggAj9L0.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/InputPassword-CRqODEyh.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/java-CGk0_04g.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/java-CJPVIdx6.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/java-CJPVIdx6.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/JobDetail-CS5C_fMx.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/JobDetail-uzx6ttP-.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/JobDetail.vue_vue_type_script_setup_true_lang-B0U48_wX.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/JobForm-COxR787v.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/JobForm-DylK8ism.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/JobForm.vue_vue_type_script_setup_true_lang-Bfp5x_DV.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/JobForm.vue_vue_type_script_setup_true_lang-Bfp5x_DV.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/JobLogDetail-CA9ZpktQ.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/JobLogDetail.vue_vue_type_script_setup_true_lang-BSLezxhx.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Layout-CCSDB6Ru.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Layout-CCSDB6Ru.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Layout-Cq32ah7f.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Layout-Cq32ah7f.css.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/LocaleDropdown-CWX4f097.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/LocaleDropdown.vue_vue_type_script_setup_true_lang-CVflBqTQ.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Login-7fQCP0wX.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/login-box-bg-CgotIC_L.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/login-box-bg-CL6i7T2F.svg
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Login-brbWnuhl.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/LoginForm-CtKx8Pcd.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/LoginForm-TBteuj-9.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/LoginFormTitle-DX-bWpHn.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/LoginFormTitle.vue_vue_type_script_setup_true_lang-CDZVRR3_.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/LoginLogDetail-Das6Ml15.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/LoginLogDetail.vue_vue_type_script_setup_true_lang-BDFGUZIu.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/logo-D8fXwB4F.png
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/logo-DTOYuZRW.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/MenuForm-gfdt_aAm.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/MenuForm.vue_vue_type_script_setup_true_lang-BptOOqry.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/MenuForm.vue_vue_type_script_setup_true_lang-BptOOqry.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/MobileForm-B-MCaqQz.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/MobileForm-V-i_1bow.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/NoticeForm-DEbMVWaa.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/NoticeForm.vue_vue_type_script_setup_true_lang-DzFaDTTC.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/OperateLogDetail-BlRTWTXj.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/OperateLogDetail.vue_vue_type_script_setup_true_lang-PrMQIlQi.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/permission-BlaWMjPj.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/PostForm-Cj1dTKmx.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/PostForm.vue_vue_type_script_setup_true_lang-CeqSF7hD.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/PreviewCode-Dwx5-jFd.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/PreviewCode-xvKbIAPl.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/PreviewCode.vue_vue_type_style_index_0_lang-DovLP5zf.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/PreviewCode.vue_vue_type_style_index_0_lang-DovLP5zf.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/profile-BjePXVCo.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ProfileUser-Ba_t_siW.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ProfileUser-D993dKCS.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/QrCodeForm-CDj06kWG.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/QrCodeForm-hfCjyDin.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/QrCodeForm.vue_vue_type_script_setup_true_lang-s18LUGA1.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/QrCodeForm.vue_vue_type_script_setup_true_lang-s18LUGA1.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Redirect-C4EIex3a.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/RegisterForm-D-gnI4Ka.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/RegisterForm-DjdIS0aw.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ResetPwd-DNDkM6-q.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/ResetPwd.vue_vue_type_script_setup_true_lang-Cvg0L-Ee.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/RoleAssignMenuForm-D9NxKBl2.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/RoleAssignMenuForm.vue_vue_type_script_setup_true_lang-DxO9MYpz.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/RoleDataPermissionForm-cAMH0-3Y.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/RoleDataPermissionForm.vue_vue_type_script_setup_true_lang-3jyeMQFq.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/RoleForm-G2Rm_hhh.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/RoleForm.vue_vue_type_script_setup_true_lang-C5mP5XPA.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/SmsChannelInfoForm-CoS3bVBZ.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/SmsChannelInfoForm-Un21-ad5.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/SocialLogin-6dLikcbD.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/SocialLogin-CJ7S1maZ.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/SSOLogin-DdHKSaVH.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/SSOLogin.vue_vue_type_script_setup_true_lang-GiaibmFM.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/tagsView-Bckid-dM.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/tree-DAv-QQbh.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/useIcon-CsZ8KiuC.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserAssignRoleForm-CGwwcqKX.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserAssignRoleForm.vue_vue_type_script_setup_true_lang-WhjG6V3W.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserAvatar-CLPjOoSV.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserAvatar-CLPjOoSV.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserAvatar-DZGzQns6.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserForm-6b8EkzNj.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserForm.vue_vue_type_script_setup_true_lang-BUXYeilv.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserImportForm-YCI3iyeS.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserImportForm.vue_vue_type_script_setup_true_lang-DJuTEnKX.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserSocial-CAOV7Vhf.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/UserSocial-DB_gu1EO.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/useWatermark-BmKapRf9.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Verify-BvbYQthm.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Verify-BvbYQthm.css.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Verify-NjODIIBB.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/Verify-NjODIIBB.js.gz
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/XButton-CrMSGTmM.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/XButton-ohXGS2Na.css
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/zh-CN-BGT-CEDR.js
|
||||||
|
marketing-ui/admin-ui/dist-prod/assets/zh-CN-BGT-CEDR.js.gz
|
||||||
|
/marketing-ui/admin-ui/dist-prod/
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
短信平台 Pro 版本:
|
||||||
|
http://14.103.184.174:81/login?redirect=/index
|
||||||
|
用户名:admin
|
||||||
|
密码:gaohui0601
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,392 @@
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sms_channel_account
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sms_channel_account`;
|
||||||
|
CREATE TABLE `sms_channel_account` (
|
||||||
|
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`channel_id` int NOT NULL COMMENT '逻辑关联渠道ID',
|
||||||
|
`channel_code` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '渠道标识(逻辑关联channel_info.code)',
|
||||||
|
`account_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '账号名称',
|
||||||
|
`app_key` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'API密钥',
|
||||||
|
`app_secret` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'API密钥',
|
||||||
|
`md5_value` char(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关键字段MD5值(算法:MD5(channel_id|account_name|app_key|app_secret))',
|
||||||
|
`support_sms_type` json NOT NULL COMMENT '支持的短信类型[0:验证码,1:短信通知,2:推广短信,3:国际/港澳台消息]',
|
||||||
|
`ext_params` json DEFAULT NULL COMMENT '扩展参数',
|
||||||
|
`remark` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0-正常 1-禁用',
|
||||||
|
`creator` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_md5_value` (`md5_value`) COMMENT '防重复提交',
|
||||||
|
KEY `idx_channel_id` (`channel_id`),
|
||||||
|
KEY `idx_channel_code` (`channel_code`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='短信渠道账号表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of sms_channel_account
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO `sms_channel_account` (`id`, `channel_id`, `channel_code`, `account_name`, `app_key`, `app_secret`, `md5_value`, `support_sms_type`, `ext_params`, `remark`, `deleted`, `creator`, `create_time`, `updater`, `update_time`) VALUES (1, 1, 'aliyun', '阿里云勇哥账号', '22333333aaaadafsadf', '23232aadafsfsdaaaa', '8de8d3023dc91845be26c49f0c926955', '[0, 1, 2, 3]', '{}', '', 0, '1', '2025-07-06 21:52:04', '1', '2025-07-07 11:22:09');
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sms_channel_info
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sms_channel_info`;
|
||||||
|
CREATE TABLE `sms_channel_info` (
|
||||||
|
`id` int NOT NULL AUTO_INCREMENT COMMENT '渠道ID',
|
||||||
|
`channel_name` varchar(30) COLLATE utf8mb4_general_ci NOT NULL COMMENT '渠道名称',
|
||||||
|
`channel_code` varchar(30) COLLATE utf8mb4_general_ci NOT NULL COMMENT '渠道标识(如:aliyun/tencent等)',
|
||||||
|
`domain_url` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求地址',
|
||||||
|
`label` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '渠道标签(逗号分隔)',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0-正常 1-禁用',
|
||||||
|
`support_apply_template` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否支持申请模板(1-支持 0-不支持)',
|
||||||
|
`support_apply_sign` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否支持申请签名(1-支持 0-不支持)',
|
||||||
|
`support_batch_send` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否支持批量发送(1-支持 0-不支持)',
|
||||||
|
`support_international` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否支持国际短信(1-支持 0-不支持)',
|
||||||
|
`support_query_template` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否支持查询模板信息(1-支持 0-不支持)',
|
||||||
|
`support_reply` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否支持上行回复(1-支持 0-不支持)',
|
||||||
|
`channel_properties` text COLLATE utf8mb4_general_ci COMMENT '渠道扩展属性(JSON格式配置/动态参数)',
|
||||||
|
`creator` varchar(64) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='短信渠道配置表(含扩展属性字段)';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of sms_channel_info
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO `sms_channel_info` (`id`, `channel_name`, `channel_code`, `domain_url`, `label`, `deleted`, `support_apply_template`, `support_apply_sign`, `support_batch_send`, `support_international`, `support_query_template`, `support_reply`, `channel_properties`, `creator`, `create_time`, `updater`, `update_time`) VALUES (1, '阿里云', 'aliyun', 'https://dysmsapi.aliyuncs.com', '内置', 0, 0, 0, 0, 1, 0, 0, '{}', '1', '2025-07-03 19:37:14', '1', '2025-07-04 22:06:38');
|
||||||
|
INSERT INTO `sms_channel_info` (`id`, `channel_name`, `channel_code`, `domain_url`, `label`, `deleted`, `support_apply_template`, `support_apply_sign`, `support_batch_send`, `support_international`, `support_query_template`, `support_reply`, `channel_properties`, `creator`, `create_time`, `updater`, `update_time`) VALUES (2, '腾讯云', 'tencent', 'https://sms.tencentcloudapi.com', '内置', 0, 1, 1, 1, 1, 1, 1, '{\"appId\":\"应用编号\"}', '1', '2025-07-03 21:10:35', '1', '2025-07-03 21:22:25');
|
||||||
|
INSERT INTO `sms_channel_info` (`id`, `channel_name`, `channel_code`, `domain_url`, `label`, `deleted`, `support_apply_template`, `support_apply_sign`, `support_batch_send`, `support_international`, `support_query_template`, `support_reply`, `channel_properties`, `creator`, `create_time`, `updater`, `update_time`) VALUES (3, '合一短信', 'unisms', 'https://uni.apistd.com/', '内置', 0, 0, 0, 1, 1, 0, 0, '{\"signType\":\"签名方式:easy/sha256\"}', '1', '2025-07-03 21:13:43', '1', '2025-07-03 21:13:43');
|
||||||
|
INSERT INTO `sms_channel_info` (`id`, `channel_name`, `channel_code`, `domain_url`, `label`, `deleted`, `support_apply_template`, `support_apply_sign`, `support_batch_send`, `support_international`, `support_query_template`, `support_reply`, `channel_properties`, `creator`, `create_time`, `updater`, `update_time`) VALUES (4, '亿美短信', 'emay', 'http://www.btom.cn', '内置', 0, 1, 0, 1, 1, 1, 0, NULL, '1', '2025-07-03 21:41:20', '1', '2025-07-03 21:41:20');
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
|
||||||
|
-- 渠道管理 --
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3000, '短信渠道', '', 1, 30, 0, '/smschannel', 'fa:address-card', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2025-07-02 21:03:22', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5000, '渠道配置', '', 2, 0, 3000, 'sms-channel-info', 'fa:creative-commons', 'smschannel/smschannelinfo/index', 'SmsChannelInfo', 0, b'1', b'1', b'1', '', '2025-07-02 20:54:30', '1', '2025-07-02 21:03:41', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5001, '渠道配置查询', 't:sms-channel-info:query', 3, 1, 5000, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-07-02 20:54:30', '1', '2025-07-02 20:58:34', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5002, '创建渠道配置', 't:sms-channel-info:create', 3, 2, 5000, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-07-02 20:54:30', '1', '2025-07-02 20:58:46', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5003, '更新渠道配置', 't:sms-channel-info:update', 3, 3, 5000, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-07-02 20:54:30', '1', '2025-07-02 20:59:01', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5004, '删除渠道配置', 't:sms-channel-info:delete', 3, 4, 5000, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-07-02 20:54:30', '1', '2025-07-02 20:59:08', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5005, '导出渠道配置', 't:sms-channel-info:export', 3, 5, 5000, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-07-02 20:54:30', '1', '2025-07-02 20:59:17', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5006, '渠道账号', '', 2, 0, 3000, 'channel-account', 'ep:film', 'smschannel/channelaccount/index', 'ChannelAccount', 0, b'1', b'1', b'1', '', '2025-07-05 23:46:12', '1', '2025-07-05 23:47:25', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5007, '短信渠道账号查询', 'smschannel:channel-account:query', 3, 1, 5006, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-05 23:46:12', '', '2025-07-05 23:46:12', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5008, '短信渠道账号创建', 'smschannel:channel-account:create', 3, 2, 5006, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-05 23:46:12', '', '2025-07-05 23:46:12', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5009, '短信渠道账号更新', 'smschannel:channel-account:update', 3, 3, 5006, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-05 23:46:12', '', '2025-07-05 23:46:12', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5010, '短信渠道账号删除', 'smschannel:channel-account:delete', 3, 4, 5006, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-05 23:46:12', '', '2025-07-05 23:46:12', b'0');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `sms_sign_info`;
|
||||||
|
CREATE TABLE `sms_sign_info` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`sign_name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '签名名称',
|
||||||
|
`tenant_id` bigint(20) NOT NULL DEFAULT '1' COMMENT '租户ID',
|
||||||
|
`sign_type` tinyint(3) NOT NULL DEFAULT '0' COMMENT '签名类型:0-企业,1-APP,2-商标,3-政府/事业单位/其他企业',
|
||||||
|
`international` tinyint(3) NOT NULL DEFAULT '0' COMMENT '短信区域:0-国内短信 1-国际/港澳台短信',
|
||||||
|
`binding_account_ids` json DEFAULT NULL COMMENT '绑定的账号ID集合(JSON数组格式,如 ["1001","1002"])',
|
||||||
|
`remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '备注说明',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态:0-正常,1-禁用',
|
||||||
|
`creator` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_sign_name` (`sign_name`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='短信签名信息表';
|
||||||
|
|
||||||
|
-- 国内短信 --
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6000, '国内短信', '', 1, 30, 0, '/domesticsms', 'fa:archive', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2025-07-08 22:18:44', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6002, '签名管理', '', 2, 0, 6000, 'sms-sign-info', 'fa:creative-commons', 'domesticsms/signinfo/index', 'SmsSignInfo', 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '1', '2025-07-08 17:05:16', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6003, '短信签名信息查询', 'sms:sign-info:query', 3, 1, 6002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6004, '短信签名信息创建', 'sms:sign-info:create', 3, 2, 6002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6005, '短信签名信息更新', 'sms:sign-info:update', 3, 3, 6002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6006, '短信签名信息删除', 'sms:sign-info:delete', 3, 4, 6002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6007, '短信签名信息导出', 'sms:sign-info:export', 3, 5, 6002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6008, '模板信息', '', 2, 0, 6000, 'templateinfo', 'ep:eleme-filled', 'domesticsms/templateinfo/index', 'TemplateInfo', 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '1', '2025-07-14 11:02:47', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6009, '短信模板信息查询', 'smschannel:template-info:query', 3, 1, 6008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6010, '短信模板信息创建', 'smschannel:template-info:create', 3, 2, 6008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6011, '短信模板信息更新', 'smschannel:template-info:update', 3, 3, 6008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6012, '短信模板信息删除', 'smschannel:template-info:delete', 3, 4, 6008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6013, '短信模板信息导出', 'smschannel:template-info:export', 3, 5, 6008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6020, '短信记录', '', 2, 0, 6000, 'record', 'ep:message', 'domesticsms/record/index', 'Record', 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '1', '2025-09-13 22:01:28', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6021, '短信记录查询', 'sms:record:query', 3, 1, 6020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6022, '短信记录创建', 'sms:record:create', 3, 2, 6020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6023, '短信记录更新', 'sms:record:update', 3, 3, 6020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6024, '短信记录删除', 'sms:record:delete', 3, 4, 6020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6025, '短信记录导出', 'sms:record:export', 3, 5, 6020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6040, '发送任务', '', 2, 0, 6000, 'domesticsms/sendtask/index', 'fa:tasks', 'domesticsms/sendtask/index', 'SmsTask', 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '1', '2025-09-19 10:06:55', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6041, '短信任务查询', 'record:sms-task:query', 3, 1, 6040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6042, '短信任务创建', 'record:sms-task:create', 3, 2, 6040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6043, '短信任务更新', 'record:sms-task:update', 3, 3, 6040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6044, '短信任务删除', 'record:sms-task:delete', 3, 4, 6040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6045, '短信任务导出', 'record:sms-task:export', 3, 5, 6040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
|
||||||
|
-- 国际短信 --
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7000, '国际短信', '', 1, 31, 0, '/internationalsms', 'fa:globe', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2025-07-08 22:18:44', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7002, '签名管理', '', 2, 0, 7000, 'sms-sign-info', 'fa:creative-commons', 'internationalsms/signinfo/index', 'SmsSignInfo1', 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '1', '2025-07-08 17:05:16', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7003, '短信签名信息查询', 'sms:sign-info:query', 3, 1, 7002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7004, '短信签名信息创建', 'sms:sign-info:create', 3, 2, 7002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7005, '短信签名信息更新', 'sms:sign-info:update', 3, 3, 7002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7006, '短信签名信息删除', 'sms:sign-info:delete', 3, 4, 7002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7007, '短信签名信息导出', 'sms:sign-info:export', 3, 5, 7002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-08 17:00:51', '', '2025-07-08 17:00:51', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7008, '模板信息', '', 2, 0, 7000, 'templateinfo', 'ep:eleme-filled', 'internationalsms/templateinfo/index', 'TemplateInfo1', 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '1', '2025-07-14 11:02:47', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7009, '短信模板信息查询', 'smschannel:template-info:query', 3, 1, 7008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7010, '短信模板信息创建', 'smschannel:template-info:create', 3, 2, 7008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7011, '短信模板信息更新', 'smschannel:template-info:update', 3, 3, 7008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7012, '短信模板信息删除', 'smschannel:template-info:delete', 3, 4, 7008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7013, '短信模板信息导出', 'smschannel:template-info:export', 3, 5, 7008, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-07-09 10:50:59', '', '2025-07-09 10:50:59', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7020, '短信记录', '', 2, 0, 7000, 'record', 'ep:message', 'internationalsms/record/index', 'Record1', 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '1', '2025-09-13 22:01:28', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7021, '短信记录查询', 'sms:record:query', 3, 1, 7020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7022, '短信记录创建', 'sms:record:create', 3, 2, 7020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7023, '短信记录更新', 'sms:record:update', 3, 3, 7020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7024, '短信记录删除', 'sms:record:delete', 3, 4, 7020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7025, '短信记录导出', 'sms:record:export', 3, 5, 7020, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-13 21:58:45', '', '2025-09-13 21:58:45', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7040, '发送任务', '', 2, 0, 7000, 'internationalsms/sendtask/index', 'fa:tasks', 'internationalsms/sendtask/index', 'SmsTask1', 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '1', '2025-09-19 10:06:55', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7041, '短信任务查询', 'record:sms-task:query', 3, 1, 7040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7042, '短信任务创建', 'record:sms-task:create', 3, 2, 7040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7043, '短信任务更新', 'record:sms-task:update', 3, 3, 7040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7044, '短信任务删除', 'record:sms-task:delete', 3, 4, 7040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7045, '短信任务导出', 'record:sms-task:export', 3, 5, 7040, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-19 10:02:14', '', '2025-09-19 10:02:14', b'0');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `sms_template_info`;
|
||||||
|
CREATE TABLE `sms_template_info` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '模板ID',
|
||||||
|
`template_name` varchar(64) NOT NULL COMMENT '模板名称',
|
||||||
|
`content` varchar(500) NOT NULL COMMENT '模板内容',
|
||||||
|
`sign_id` bigint(20) NOT NULL COMMENT '签名ID(关联签名表)',
|
||||||
|
`sign_name` varchar(32) NOT NULL COMMENT '签名名称(冗余存储)',
|
||||||
|
`template_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '模板类型:0-验证码 1-短信通知 2-推广短信',
|
||||||
|
`international` tinyint(3) NOT NULL DEFAULT '0' COMMENT '短信区域:0-国内短信 1-国际/港澳台短信',
|
||||||
|
`switch_open` tinyint(1) NOT NULL DEFAULT '1' COMMENT '启用开关:1-开启 0-关闭',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除状态:0-正常 1-删除',
|
||||||
|
`creator` varchar(64) DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`remark` varchar(200) DEFAULT '' COMMENT '备注',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_template_name` (`template_name`),
|
||||||
|
KEY `idx_sign_name` (`sign_name`),
|
||||||
|
KEY `idx_sign_id` (`sign_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='短信模板信息表';
|
||||||
|
|
||||||
|
-- 1. 删除原表(如果存在)
|
||||||
|
DROP TABLE IF EXISTS `sms_template_binding_info`;
|
||||||
|
|
||||||
|
-- 2. 创建新表(包含 account_id 字段)
|
||||||
|
CREATE TABLE `sms_template_binding_info` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '绑定ID',
|
||||||
|
`template_id` bigint(20) NOT NULL COMMENT '模板ID',
|
||||||
|
`template_code` varchar(64) NOT NULL COMMENT '三方模板编码',
|
||||||
|
`template_content` varchar(500) NOT NULL COMMENT '三方模板内容',
|
||||||
|
`channel_id` bigint(20) NOT NULL COMMENT '渠道ID',
|
||||||
|
`channel_code` varchar(64) NOT NULL COMMENT '渠道标识',
|
||||||
|
`account_id` bigint(20) DEFAULT NULL COMMENT '渠道账户ID',
|
||||||
|
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '审核状态:0-待提交 1-待审核 2-审核成功 3-审核失败 4-撤销',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除状态:0-正常 1-删除',
|
||||||
|
`audit_remark` varchar(200) DEFAULT '' COMMENT '审核备注',
|
||||||
|
`creator` varchar(64) DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_template_channel_account` (`template_id`, `channel_id`),
|
||||||
|
KEY `idx_template_id` (`template_id`),
|
||||||
|
KEY `idx_channel_id` (`channel_id`),
|
||||||
|
KEY `idx_account_id` (`account_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='短信模板渠道绑定表';
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `sms_record`;
|
||||||
|
CREATE TABLE `sms_record` (
|
||||||
|
`id` bigint(20) NOT NULL COMMENT '主键 雪花算法',
|
||||||
|
`task_id` bigint(20) NOT NULL COMMENT '任务编号',
|
||||||
|
`mobile` varchar(20) NOT NULL COMMENT '手机号',
|
||||||
|
`nationcode` varchar(10) NOT NULL DEFAULT '86' COMMENT '国际电话区号如 86',
|
||||||
|
`app_id` varchar(30) DEFAULT NULL COMMENT '调用服务方',
|
||||||
|
`record_type` tinyint(4) DEFAULT '0' COMMENT '0-验证码 1-短信通知 2-推广短信',
|
||||||
|
`template_id` bigint(20) NOT NULL COMMENT '模版编号',
|
||||||
|
`template_param` varchar(50) NOT NULL COMMENT '模版参数',
|
||||||
|
`content` varchar(500) NOT NULL COMMENT '短信内容',
|
||||||
|
`remark` varchar(500) DEFAULT '' COMMENT '备注(比如:发送失败原因)',
|
||||||
|
`attime` varchar(30) DEFAULT '' COMMENT '指定发送时间',
|
||||||
|
`send_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:待发送 / 1:已发送 / 2:发送失败',
|
||||||
|
`international` tinyint(1) NOT NULL DEFAULT '0' COMMENT '短信类型:0-国内 1-国际',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除状态:0-正常 1-删除',
|
||||||
|
`creator` varchar(64) DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_app_id` (`app_id`) USING BTREE,
|
||||||
|
KEY `idx_task_id` (`task_id`) USING BTREE COMMENT '任务编号索引',
|
||||||
|
KEY `idx_attime` (`attime`) USING BTREE COMMENT '延迟短信索引',
|
||||||
|
KEY `idx_create_time` (`create_time`) USING BTREE COMMENT '创建时间索引',
|
||||||
|
KEY `idx_mobile` (`mobile`) USING BTREE
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '短信记录表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sms_record_detail
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sms_record_detail`;
|
||||||
|
CREATE TABLE `sms_record_detail` (
|
||||||
|
`id` bigint(20) NOT NULL COMMENT '主键 雪花算法',
|
||||||
|
`record_id` bigint(20) NOT NULL COMMENT 'sms_record表外键',
|
||||||
|
`task_id` bigint(20) NOT NULL COMMENT '任务编号',
|
||||||
|
`app_id` varchar(30) NOT NULL COMMENT '调用服务方',
|
||||||
|
`mobile` varchar(20) NOT NULL COMMENT '手机号码',
|
||||||
|
`nationcode` varchar(10) NOT NULL DEFAULT '86' COMMENT '国际电话区号',
|
||||||
|
`send_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0:待发送 / 1:已发送 / 2:发送失败',
|
||||||
|
`report_status` tinyint(4) DEFAULT '0' COMMENT '短信报告 0:待回执 / 1:发送成功 / 2:发送失败',
|
||||||
|
`channel_code` varchar(30) NOT NULL COMMENT '渠道编码',
|
||||||
|
`channel_account_id` int(11) NOT NULL COMMENT '渠道账户ID',
|
||||||
|
`msgid` varchar(40) NOT NULL COMMENT '三方短信ID',
|
||||||
|
`error_msg` varchar(300) DEFAULT '' COMMENT '发送失败时,渠道返回的错误信息',
|
||||||
|
`price` decimal(10,6) DEFAULT NULL COMMENT '消费金额',
|
||||||
|
`sender_time` datetime DEFAULT NULL COMMENT '实际发送时间',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除状态:0-正常 1-删除',
|
||||||
|
`creator` varchar(64) DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_record_id` (`record_id`) USING BTREE COMMENT '记录ID索引',
|
||||||
|
KEY `idx_task_id` (`task_id`) USING BTREE COMMENT '任务编号索引',
|
||||||
|
KEY `idx_app_id` (`app_id`) USING BTREE COMMENT '应用ID索引',
|
||||||
|
KEY `idx_msgid` (`msgid`) USING BTREE COMMENT '三方ID索引',
|
||||||
|
KEY `idx_sender_time` (`sender_time`) USING BTREE COMMENT '发送时间索引',
|
||||||
|
KEY `idx_create_time` (`create_time`) USING BTREE COMMENT '创建时间索引',
|
||||||
|
KEY `idx_mobile` (`mobile`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='短信记录详情表';
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `sms_task`;
|
||||||
|
CREATE TABLE `sms_task` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务编号',
|
||||||
|
`app_id` bigint(20) NOT NULL COMMENT '应用编号',
|
||||||
|
`task_name` varchar(100) NOT NULL DEFAULT '' COMMENT '任务名称',
|
||||||
|
`sign_id` bigint(20) NOT NULL COMMENT '签名编号',
|
||||||
|
`sign_name` varchar(20) NOT NULL COMMENT '签名名称',
|
||||||
|
`template_id` bigint(20) NOT NULL COMMENT '模版编号',
|
||||||
|
`template_name` varchar(50) NOT NULL COMMENT '模版名称',
|
||||||
|
`template_content` text NOT NULL COMMENT '模版内容',
|
||||||
|
`template_param` varchar(500) NOT NULL COMMENT '模版参数',
|
||||||
|
`total_count` int(11) NOT NULL DEFAULT '0' COMMENT '手机总数量',
|
||||||
|
`success_count` int(11) NOT NULL DEFAULT '0' COMMENT '发送成功数量',
|
||||||
|
`fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '发送失败数量',
|
||||||
|
`task_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '任务状态:0-待发送 1-发送中 2-已完成 3-已取消',
|
||||||
|
`international` tinyint(1) NOT NULL DEFAULT '0' COMMENT '短信类型:0-国内 1-国际',
|
||||||
|
`load_to_queue` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否加载到队列:0-未加载 1-已加载',
|
||||||
|
`sender_ip` varchar(40) DEFAULT '' COMMENT '发送IP',
|
||||||
|
`attime` varchar(30) DEFAULT '' COMMENT '指定发送时间',
|
||||||
|
`remark` varchar(255) DEFAULT NULL COMMENT '备注信息',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除状态:0-正常 1-删除',
|
||||||
|
`creator` varchar(64) DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`source` tinyint(2) NOT NULL DEFAULT '0' COMMENT '来源:0-网关 1-admin控制台上传',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_app_id` (`app_id`),
|
||||||
|
KEY `idx_create_time` (`create_time`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='短信任务表';
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `sms_task_content`;
|
||||||
|
CREATE TABLE `sms_task_content` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务编号',
|
||||||
|
`task_content` MEDIUMTEXT NOT NULL COMMENT '若无模板参数,则手机号码列表,手机号用英文逗号分隔 ,若模板参数,则手机号码列表,手机号用英文逗号分隔,参数用英文冒号分隔,参数格式为:手机号1^参数1^参数2^参数3,手机号2^参数1^参数2^参数3',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='短信任务内容表';
|
||||||
|
|
||||||
|
-- 租户管理 (租户以及下面的每个应用) --
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6026, '租户管理', '', 1, 21, 0, '/tenant/index', 'fa-solid:binoculars', '', '', 0, b'1', b'1', b'1', '1', '2025-09-15 19:28:25', '1', '2025-09-15 19:28:25', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6027, '租户列表', '', 2, 0, 6026, 'tenant', 'fa:renren', '/tenant/index', 'SmsTenant', 0, b'1', b'1', b'1', '', '2025-09-15 19:28:43', '1', '2025-09-15 19:32:04', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6028, '租户查询', 'sms:tenant:query', 3, 1, 6027, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:43', '', '2025-09-15 19:28:43', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6029, '租户创建', 'sms:tenant:create', 3, 2, 6027, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:43', '', '2025-09-15 19:28:43', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6030, '租户更新', 'sms:tenant:update', 3, 3, 6027, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:43', '', '2025-09-15 19:28:43', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6031, '租户删除', 'sms:tenant:delete', 3, 4, 6027, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:43', '', '2025-09-15 19:28:43', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6032, '租户导出', 'sms:tenant:export', 3, 5, 6027, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:43', '', '2025-09-15 19:28:43', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6033, '应用列表', '', 2, 0, 6026, 'sms-appinfo', 'ep:apple', '/tenant/SmsAppinfo/index', 'SmsAppinfo', 0, b'1', b'1', b'1', '', '2025-09-15 19:28:56', '1', '2025-09-15 19:30:29', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6034, '租户应用查询', 'tenant:sms-appinfo:query', 3, 1, 6033, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:56', '', '2025-09-15 19:28:56', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6035, '租户应用创建', 'tenant:sms-appinfo:create', 3, 2, 6033, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:56', '', '2025-09-15 19:28:56', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6036, '租户应用更新', 'tenant:sms-appinfo:update', 3, 3, 6033, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:56', '', '2025-09-15 19:28:56', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6037, '租户应用删除', 'tenant:sms-appinfo:delete', 3, 4, 6033, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:56', '', '2025-09-15 19:28:56', b'0');
|
||||||
|
INSERT INTO `marketing_admin`.`system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6038, '租户应用导出', 'tenant:sms-appinfo:export', 3, 5, 6033, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-09-15 19:28:56', '', '2025-09-15 19:28:56', b'0');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sms_tenant
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sms_tenant`;
|
||||||
|
CREATE TABLE `sms_tenant` (
|
||||||
|
`id` bigint(20) NOT NULL COMMENT '租户编号' AUTO_INCREMENT ,
|
||||||
|
`tenant_user_name` varchar(100) NOT NULL COMMENT '租户用户名',
|
||||||
|
`password` varchar(100) NOT NULL COMMENT '密码',
|
||||||
|
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
|
||||||
|
`mobile` varchar(20) DEFAULT NULL COMMENT '手机号',
|
||||||
|
`nationcode` varchar(10) DEFAULT NULL COMMENT '手机国家编码',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除状态:0-正常 1-删除',
|
||||||
|
`creator` varchar(64) DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_tenant_user_name` (`tenant_user_name`),
|
||||||
|
KEY `idx_email` (`email`),
|
||||||
|
KEY `idx_mobile` (`mobile`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户表';
|
||||||
|
|
||||||
|
INSERT INTO `marketing_admin`.`sms_tenant` (`id`, `tenant_user_name`, `password`, `email`, `mobile`, `nationcode`, `deleted`, `creator`, `create_time`, `updater`, `update_time`) VALUES (1, 'makemyownlife', '594B6D8465CB0EE37C51A113A4FD07E3', 'zhangyong7120180@163.com', '15011319235', NULL, 0, '1', '2025-09-15 16:40:27', '1', '2025-09-15 16:40:27');
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sms_appinfo
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sms_appinfo`;
|
||||||
|
CREATE TABLE `sms_appinfo` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '应用编号',
|
||||||
|
`tenant_id` bigint(20) NOT NULL DEFAULT '1' COMMENT '租户ID',
|
||||||
|
`app_key` varchar(20) NOT NULL COMMENT '应用密钥Key',
|
||||||
|
`app_name` varchar(40) NOT NULL DEFAULT '' COMMENT '应用名称',
|
||||||
|
`app_secret` varchar(100) NOT NULL COMMENT '应用密钥Secret',
|
||||||
|
`status` tinyint(4) DEFAULT '0' COMMENT '状态:0-有效 / 1-无效',
|
||||||
|
`remark` varchar(255) DEFAULT NULL COMMENT '备注信息',
|
||||||
|
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除状态:0-正常 1-删除',
|
||||||
|
`creator` varchar(64) DEFAULT '' COMMENT '创建人',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`updater` varchar(64) DEFAULT '' COMMENT '更新人',
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='租户应用表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of sms_appinfo
|
||||||
|
-- ----------------------------
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO `sms_appinfo` (`id`, `tenant_id`, `app_key`, `app_name`, `app_secret`, `status`, `remark`, `deleted`, `creator`, `updater`)
|
||||||
|
VALUES (1, 1, 'qQjEiFzn80v8VM4h', 'mydemo', '9c465ece754bd26a9be77f3d0e2606bd', 0, '例子', 0, 'system', 'system');
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
INSERT INTO `marketing_admin`.`QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `JOB_CLASS_NAME`, `IS_DURABLE`, `IS_NONCONCURRENT`, `IS_UPDATE_DATA`, `REQUESTS_RECOVERY`, `JOB_DATA`) VALUES ('schedulerName', 'smsTemplateJob', 'DEFAULT', NULL, 'com.platform.marketing.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000227400104A4F425F48414E444C45525F4E414D4574000E736D7354656D706C6174654A6F627800);
|
||||||
|
|
||||||
|
-- INSERT INTO `marketing_admin`.`QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'smsTemplateJob', 'DEFAULT', 'smsTemplateJob', 'DEFAULT', NULL, 1758081600000, -1, 5, 'WAITING', 'CRON', 1758078283000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000138874000F4A4F425F52455452595F434F554E547371007E000A000000017800);
|
||||||
|
|
||||||
|
-- INSERT INTO `marketing_admin`.`infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (34, '自动向阿里云/腾讯云申请短信模版', 1, 'smsTemplateJob', '', '0 0 * * * ?', 1, 5000, 0, '1', '2025-09-17 11:04:44', '1', '2025-09-17 11:04:44', b'0');
|
||||||
|
|
||||||
|
INSERT INTO `marketing_admin`.`infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (35, '每半个小时执行一次将 task的延迟内容加载到 MQ', 1, 'loadDelayShortMessageToMQJob', '', '* 0/30 * * * ?', 0, 0, 0, '1', '2025-10-12 10:47:32', '1', '2025-10-12 10:47:32', b'0');
|
||||||
|
|
||||||
|
INSERT INTO marketing_admin.QRTZ_JOB_DETAILS
|
||||||
|
(SCHED_NAME, JOB_NAME, JOB_GROUP, DESCRIPTION, JOB_CLASS_NAME, IS_DURABLE, IS_NONCONCURRENT, IS_UPDATE_DATA, REQUESTS_RECOVERY, JOB_DATA)
|
||||||
|
VALUES('schedulerName', 'loadDelayShortMessageToMQJob', 'DEFAULT', NULL, 'com.platform.marketing.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000237400104A4F425F48414E444C45525F4E414D4574001C6C6F616444656C617953686F72744D657373616765546F4D514A6F627800);
|
||||||
|
|
||||||
|
INSERT INTO `marketing_admin`.`QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `JOB_NAME`, `JOB_GROUP`, `DESCRIPTION`, `NEXT_FIRE_TIME`, `PREV_FIRE_TIME`, `PRIORITY`, `TRIGGER_STATE`, `TRIGGER_TYPE`, `START_TIME`, `END_TIME`, `CALENDAR_NAME`, `MISFIRE_INSTR`, `JOB_DATA`) VALUES ('schedulerName', 'loadDelayShortMessageToMQJob', 'DEFAULT', 'loadDelayShortMessageToMQJob', 'DEFAULT', NULL, 1760238000000, -1, 5, 'WAITING', 'CRON', 1760237251000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D7400007400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000074000F4A4F425F52455452595F434F554E5471007E000C7800);
|
||||||
|
|
||||||
|
INSERT INTO `marketing_admin`.`QRTZ_CRON_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `CRON_EXPRESSION`, `TIME_ZONE_ID`) VALUES ('schedulerName', 'loadDelayShortMessageToMQJob', 'DEFAULT', '* 0/30 * * * ?', 'Asia/Shanghai');
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,4 @@
|
||||||
|
config.stopBubbling = true
|
||||||
|
lombok.tostring.callsuper=CALL
|
||||||
|
lombok.equalsandhashcode.callsuper=CALL
|
||||||
|
lombok.accessors.chain=true
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-admin-server</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<name>marketing-admin-server</name>
|
||||||
|
<description>后端 Server 的主项目,通过引入需要 marketing-module-xxx 的依赖,
|
||||||
|
从而实现提供 RESTful API 给 marketing-ui-admin、marketing-ui-user 等前端项目。
|
||||||
|
本质上来说,它就是个空壳(容器)!</description>
|
||||||
|
<url>https://github.com/makemyownlife/marketing-admin</url>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-system</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-infra</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<version>2.7.18</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-protection</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-channel</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-tenant</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-record</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-gateway</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-worker</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>huaweicloud</id>
|
||||||
|
<name>huawei</name>
|
||||||
|
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>aliyunmaven</id>
|
||||||
|
<name>aliyun</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
<?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">
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-admin-all</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>marketing-admin-server</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
后端 Server 的主项目,通过引入需要 marketing-module-xxx 的依赖,
|
||||||
|
从而实现提供 RESTful API 给 marketing-ui-admin、marketing-ui-user 等前端项目。
|
||||||
|
本质上来说,它就是个空壳(容器)!
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<url>https://github.com/makemyownlife/marketing-admin</url>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-system</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-infra</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- spring boot 配置所需依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 服务保障相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-protection</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- ========================== 短信业务 start ========================== -->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-channel</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-tenant</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-record</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-gateway</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-module-worker</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- ========================== 短信业务 end ========================== -->
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>docker</id>
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<excludes>
|
||||||
|
<exclude>application-**</exclude>
|
||||||
|
</excludes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<nonFilteredFileExtensions>
|
||||||
|
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>
|
||||||
|
<nonFilteredFileExtension>png</nonFilteredFileExtension>
|
||||||
|
<nonFilteredFileExtension>jpg</nonFilteredFileExtension>
|
||||||
|
<nonFilteredFileExtension>jpeg</nonFilteredFileExtension>
|
||||||
|
<nonFilteredFileExtension>ico</nonFilteredFileExtension>
|
||||||
|
</nonFilteredFileExtensions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<!-- 设置构建的 jar 包名 -->
|
||||||
|
<finalName>marketing-admin-server</finalName>
|
||||||
|
<plugins>
|
||||||
|
<!-- 打包 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
package com.platform.marketing.server;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.alibaba.fastjson.TypeReference;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
|
import org.apache.http.client.config.RequestConfig;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChuanglanInterMessageDemo
|
||||||
|
*
|
||||||
|
* @author EDY
|
||||||
|
* @createdAt 2023/11/6 14:54
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ChuanglanInterMessageDemo {
|
||||||
|
private RequestConfig requestConfig = RequestConfig.custom()
|
||||||
|
.setConnectTimeout(10000)
|
||||||
|
.setSocketTimeout(10000).build();
|
||||||
|
private CloseableHttpClient httpClient = HttpClients.custom()
|
||||||
|
.setDefaultRequestConfig(requestConfig)
|
||||||
|
.setProxy(new HttpHost("127.0.0.1", 1087)) //代理到白名单IP上
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new ChuanglanInterMessageDemo().sendSms();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendSms() {
|
||||||
|
String password = "1Y2T8c5gWge34f";
|
||||||
|
SmsSendRequest smsRequest = new SmsSendRequest();
|
||||||
|
smsRequest.setAccount("IM9341742");
|
||||||
|
smsRequest.setMobile("006285167000597");
|
||||||
|
smsRequest.setMsg("sms demo test ");
|
||||||
|
smsRequest.setUid("112233445566778899");
|
||||||
|
|
||||||
|
String nonce = String.valueOf(System.currentTimeMillis());
|
||||||
|
TreeMap<String, String> paramMap = JSON.parseObject(JSON.toJSONString(smsRequest), new TypeReference<TreeMap<String, String>>() {
|
||||||
|
});
|
||||||
|
paramMap.put("nonce", nonce);
|
||||||
|
String sign = generateSign(password, paramMap);
|
||||||
|
|
||||||
|
HttpEntity httpEntity = null;
|
||||||
|
CloseableHttpResponse httpResponse = null;
|
||||||
|
try {
|
||||||
|
StringEntity stringEntity = new StringEntity(JSON.toJSONString(smsRequest), ContentType.APPLICATION_JSON);
|
||||||
|
HttpPost httpPost = new HttpPost("https://intapi.tig253.com/send/sms");
|
||||||
|
httpPost.addHeader("Content-Type", "application/json");
|
||||||
|
httpPost.addHeader("nonce", nonce);
|
||||||
|
httpPost.addHeader("sign", sign);
|
||||||
|
httpPost.setEntity(stringEntity);
|
||||||
|
httpResponse = httpClient.execute(httpPost);
|
||||||
|
httpEntity = httpResponse.getEntity();
|
||||||
|
String result = EntityUtils.toString(httpEntity);
|
||||||
|
System.out.println(result);
|
||||||
|
JSONObject rtn = JSON.parseObject(result);
|
||||||
|
String code = rtn.getString("code");
|
||||||
|
if("0".equals(code)) {
|
||||||
|
System.out.println("提交成功");
|
||||||
|
String messageId = rtn.getJSONObject("data").getString("messageId");
|
||||||
|
System.out.println("messageId=" + messageId);
|
||||||
|
} else {
|
||||||
|
System.out.println(String.format("提交失败:code=%s, message=%s", code, rtn.getString("message")));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
EntityUtils.consume(httpEntity);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if(httpResponse != null) {
|
||||||
|
try {
|
||||||
|
httpResponse.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String generateSign(String password, TreeMap<String, String> paramMap) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
|
||||||
|
if (StringUtils.isNotBlank(entry.getValue())) {
|
||||||
|
sb.append(entry.getKey());
|
||||||
|
sb.append(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DigestUtils.md5DigestAsHex((sb.toString() + password).getBytes()).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class SmsSendRequest {
|
||||||
|
private String account;
|
||||||
|
private String mobile;
|
||||||
|
private String msg;
|
||||||
|
private String uid;
|
||||||
|
|
||||||
|
public String getAccount() { return account; }
|
||||||
|
public void setAccount(String account) { this.account = account; }
|
||||||
|
public String getMobile() { return mobile; }
|
||||||
|
public void setMobile(String mobile) { this.mobile = mobile; }
|
||||||
|
public String getMsg() { return msg; }
|
||||||
|
public void setMsg(String msg) { this.msg = msg; }
|
||||||
|
public String getUid() { return uid; }
|
||||||
|
public void setUid(String uid) { this.uid = uid; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.platform.marketing.server;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目的启动类
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${marketing.info.base-package}
|
||||||
|
@EnableScheduling
|
||||||
|
@SpringBootApplication(scanBasePackages = {"${marketing.info.base-package}.server", "${marketing.info.base-package}.module"})
|
||||||
|
public class MarketingAdminApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(MarketingAdminApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.platform.marketing.server.controller;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.pojo.CommonResult;
|
||||||
|
import com.platform.marketing.framework.common.util.servlet.ServletUtils;
|
||||||
|
import com.platform.marketing.framework.idgenerator.core.service.IdGeneratorService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.annotation.security.PermitAll;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认 Controller JUST FOR TEST
|
||||||
|
*
|
||||||
|
* @author zhangyong
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@Slf4j
|
||||||
|
public class DefaultController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IdGeneratorService idGeneratorService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试接口:打印 query、header、body
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = {"/test"})
|
||||||
|
@PermitAll
|
||||||
|
public CommonResult<Boolean> test(HttpServletRequest request) {
|
||||||
|
// 打印查询参数
|
||||||
|
log.info("Query: {}", ServletUtils.getParamMap(request));
|
||||||
|
// 打印请求头
|
||||||
|
log.info("Header: {}", ServletUtils.getHeaderMap(request));
|
||||||
|
// 打印请求体
|
||||||
|
log.info("Body: {}", ServletUtils.getBody(request));
|
||||||
|
// 生成 id
|
||||||
|
Long id = idGeneratorService.createUniqueId("mytable");
|
||||||
|
log.info("ID 生成器创建新序号: {}", id);
|
||||||
|
return CommonResult.success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
dynamic:
|
||||||
|
primary: master
|
||||||
|
datasource:
|
||||||
|
master:
|
||||||
|
url: jdbc:mysql://${SPRING_DATABASE_HOST:localhost}:${SPRING_DATABASE_PORT:3306}/${SPRING_DATABASE_NAME:biz_data_marketing}?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
|
||||||
|
username: ${SPRING_DATABASE_USERNAME:root}
|
||||||
|
password: ${SPRING_DATABASE_PASSWORD:root}
|
||||||
|
slave:
|
||||||
|
lazy: true
|
||||||
|
url: jdbc:mysql://${SPRING_DATABASE_HOST:localhost}:${SPRING_DATABASE_PORT:3306}/${SPRING_DATABASE_NAME:biz_data_marketing}?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
|
||||||
|
username: ${SPRING_DATABASE_USERNAME:root}
|
||||||
|
password: ${SPRING_DATABASE_PASSWORD:root}
|
||||||
|
redis:
|
||||||
|
host: ${SPRING_REDIS_HOST:localhost}
|
||||||
|
port: ${SPRING_REDIS_PORT:6379}
|
||||||
|
database: ${SPRING_REDIS_DATABASE:0}
|
||||||
|
|
||||||
|
logging:
|
||||||
|
file:
|
||||||
|
name: ${user.home}/logs/${spring.application.name}.log
|
||||||
|
level:
|
||||||
|
com.platform.marketing.module.infra.mapper: INFO
|
||||||
|
com.platform.marketing.module.smschannel.mapper: INFO
|
||||||
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
dynamic:
|
||||||
|
primary: master
|
||||||
|
datasource:
|
||||||
|
master:
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/biz_data_marketing?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
|
||||||
|
username: root
|
||||||
|
password: root
|
||||||
|
slave:
|
||||||
|
lazy: true
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/biz_data_marketing?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
|
||||||
|
username: root
|
||||||
|
password: root
|
||||||
|
redis:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 6379
|
||||||
|
database: 0
|
||||||
|
|
||||||
|
logging:
|
||||||
|
file:
|
||||||
|
name: ${user.home}/logs/${spring.application.name}.log
|
||||||
|
level:
|
||||||
|
com.platform.marketing.module.infra.mapper: DEBUG
|
||||||
|
com.platform.marketing.module.smschannel.mapper: DEBUG
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
dynamic:
|
||||||
|
primary: master
|
||||||
|
datasource:
|
||||||
|
master:
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/biz_data_marketing?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
|
||||||
|
username: root
|
||||||
|
password: root
|
||||||
|
slave:
|
||||||
|
lazy: true
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/biz_data_marketing?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
|
||||||
|
username: root
|
||||||
|
password: root
|
||||||
|
redis:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 6379
|
||||||
|
database: 0
|
||||||
|
|
||||||
|
logging:
|
||||||
|
file:
|
||||||
|
name: ${user.home}/logs/${spring.application.name}.log
|
||||||
|
level:
|
||||||
|
com.platform.marketing.module.infra.mapper: INFO
|
||||||
|
com.platform.marketing.module.smschannel.mapper: INFO
|
||||||
|
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
server:
|
||||||
|
port: 8089
|
||||||
|
servlet:
|
||||||
|
encoding:
|
||||||
|
enabled: true
|
||||||
|
charset: UTF-8
|
||||||
|
force: true
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: platform-sms-pro
|
||||||
|
profiles:
|
||||||
|
active: local
|
||||||
|
main:
|
||||||
|
allow-circular-references: true
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 16MB
|
||||||
|
max-request-size: 32MB
|
||||||
|
jackson:
|
||||||
|
serialization:
|
||||||
|
write-dates-as-timestamps: true
|
||||||
|
write-date-timestamps-as-nanoseconds: false
|
||||||
|
write-durations-as-timestamps: true
|
||||||
|
fail-on-empty-beans: false
|
||||||
|
cache:
|
||||||
|
type: REDIS
|
||||||
|
redis:
|
||||||
|
time-to-live: 1h
|
||||||
|
autoconfigure:
|
||||||
|
exclude:
|
||||||
|
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
repositories:
|
||||||
|
enabled: false
|
||||||
|
datasource:
|
||||||
|
druid:
|
||||||
|
web-stat-filter:
|
||||||
|
enabled: true
|
||||||
|
stat-view-servlet:
|
||||||
|
enabled: true
|
||||||
|
allow:
|
||||||
|
url-pattern: /druid/*
|
||||||
|
login-username:
|
||||||
|
login-password:
|
||||||
|
filter:
|
||||||
|
stat:
|
||||||
|
enabled: true
|
||||||
|
log-slow-sql: true
|
||||||
|
slow-sql-millis: 100
|
||||||
|
merge-sql: true
|
||||||
|
wall:
|
||||||
|
config:
|
||||||
|
multi-statement-allow: true
|
||||||
|
dynamic:
|
||||||
|
druid:
|
||||||
|
initial-size: 1
|
||||||
|
min-idle: 1
|
||||||
|
max-active: 20
|
||||||
|
max-wait: 600000
|
||||||
|
time-between-eviction-runs-millis: 60000
|
||||||
|
min-evictable-idle-time-millis: 300000
|
||||||
|
max-evictable-idle-time-millis: 900000
|
||||||
|
validation-query: SELECT 1 FROM DUAL
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: false
|
||||||
|
test-on-return: false
|
||||||
|
quartz:
|
||||||
|
auto-startup: true
|
||||||
|
scheduler-name: schedulerName
|
||||||
|
job-store-type: jdbc
|
||||||
|
wait-for-jobs-to-complete-on-shutdown: true
|
||||||
|
properties:
|
||||||
|
org:
|
||||||
|
quartz:
|
||||||
|
scheduler:
|
||||||
|
instanceName: schedulerName
|
||||||
|
instanceId: AUTO
|
||||||
|
jobStore:
|
||||||
|
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
|
||||||
|
isClustered: true
|
||||||
|
clusterCheckinInterval: 15000
|
||||||
|
misfireThreshold: 60000
|
||||||
|
threadPool:
|
||||||
|
threadCount: 25
|
||||||
|
threadPriority: 5
|
||||||
|
class: org.quartz.simpl.SimpleThreadPool
|
||||||
|
jdbc:
|
||||||
|
initialize-schema: NEVER
|
||||||
|
|
||||||
|
springdoc:
|
||||||
|
api-docs:
|
||||||
|
enabled: true
|
||||||
|
path: /v3/api-docs
|
||||||
|
swagger-ui:
|
||||||
|
enabled: true
|
||||||
|
path: /swagger-ui
|
||||||
|
default-flat-param-object: true
|
||||||
|
knife4j:
|
||||||
|
enable: false
|
||||||
|
setting:
|
||||||
|
language: zh_cn
|
||||||
|
|
||||||
|
mybatis-plus:
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
id-type: NONE
|
||||||
|
logic-delete-value: 1
|
||||||
|
logic-not-delete-value: 0
|
||||||
|
banner: false
|
||||||
|
type-aliases-package: ${marketing.info.base-package}.module.*.mapper.dataobject
|
||||||
|
mybatis-plus-join:
|
||||||
|
banner: false
|
||||||
|
sub-table-logic: true
|
||||||
|
ms-cache: true
|
||||||
|
table-alias: t
|
||||||
|
logic-del-type: on
|
||||||
|
|
||||||
|
marketing:
|
||||||
|
demo: false
|
||||||
|
captcha:
|
||||||
|
enable: false
|
||||||
|
access-log:
|
||||||
|
enable: false
|
||||||
|
info:
|
||||||
|
version: 1.0.0
|
||||||
|
base-package: com.platform.marketing
|
||||||
|
xss:
|
||||||
|
enable: false
|
||||||
|
exclude-urls:
|
||||||
|
- ${spring.boot.admin.context-path}/**
|
||||||
|
- ${management.endpoints.web.base-path}/**
|
||||||
|
security:
|
||||||
|
mock-enable: true
|
||||||
|
permit-all_urls:
|
||||||
|
- /api/mp/open/**
|
||||||
|
- /gateway/**
|
||||||
|
swagger:
|
||||||
|
title: 开发平台
|
||||||
|
description: 开发平台
|
||||||
|
version: ${marketing.info.version}
|
||||||
|
url: ${marketing.web.admin-ui.url}
|
||||||
|
email: keep@163.com
|
||||||
|
license: MIT
|
||||||
|
codegen:
|
||||||
|
base-package: ${marketing.info.base-package}
|
||||||
|
db-schemas: ${spring.datasource.dynamic.datasource.master.name}
|
||||||
|
front-type: 20
|
||||||
|
vo-type: 10
|
||||||
|
delete-batch-enable: true
|
||||||
|
unit-test-enable: false
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,42 @@
|
||||||
|
<configuration scan="true" scanPeriod=" 5 seconds">
|
||||||
|
<jmxConfigurator />
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %p %t %logger{36}:%L - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="ADMIN-ROOT" class="ch.qos.logback.classic.sift.SiftingAppender">
|
||||||
|
<discriminator>
|
||||||
|
<Key>admin</Key>
|
||||||
|
<DefaultValue>admin</DefaultValue>
|
||||||
|
</discriminator>
|
||||||
|
<sift>
|
||||||
|
<appender name="FILE-ADMIN" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<File>../logs/admin.log</File>
|
||||||
|
<rollingPolicy
|
||||||
|
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- rollover daily -->
|
||||||
|
<fileNamePattern>../logs/admin/%d{yyyy-MM-dd}/admin-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
|
||||||
|
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||||
|
<!-- or whenever the file size reaches 100MB -->
|
||||||
|
<maxFileSize>512MB</maxFileSize>
|
||||||
|
</timeBasedFileNamingAndTriggeringPolicy>
|
||||||
|
<maxHistory>60</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %p %t %logger{36}:%L - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
</sift>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<!-- 控制台日志 -->
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
<!-- 文件日志 -->
|
||||||
|
<appender-ref ref="ADMIN-ROOT" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-client-sdk</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<description>marketing-admin基础脚手架</description>
|
||||||
|
<url>https://github.com/makemyownlife/marketing-admin/marketing-client-sdk</url>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>1.2.83</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>1.10</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>1.7.25</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.4</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-collections</groupId>
|
||||||
|
<artifactId>commons-collections</artifactId>
|
||||||
|
<version>3.2.2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>huaweicloud</id>
|
||||||
|
<name>huawei</name>
|
||||||
|
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>aliyunmaven</id>
|
||||||
|
<name>aliyun</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?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>com.platform</groupId>
|
||||||
|
<artifactId>marketing-admin-all</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>marketing-client-sdk</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- fastjson -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>1.2.83</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 当前用于sh256加密 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>1.10</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>1.7.25</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-collections</groupId>
|
||||||
|
<artifactId>commons-collections</artifactId>
|
||||||
|
<version>3.2.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- logback依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>1.2.11</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-core</artifactId>
|
||||||
|
<version>1.2.11</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.platform.marketing.client.sdk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信平台 sdk 配置
|
||||||
|
*/
|
||||||
|
public class SmsConfig {
|
||||||
|
|
||||||
|
private String smsServerUrl;
|
||||||
|
|
||||||
|
private String appKey;
|
||||||
|
|
||||||
|
private String appSecret;
|
||||||
|
|
||||||
|
public String getAppKey() {
|
||||||
|
return appKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppKey(String appKey) {
|
||||||
|
this.appKey = appKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppSecret() {
|
||||||
|
return appSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppSecret(String appSecret) {
|
||||||
|
this.appSecret = appSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSmsServerUrl() {
|
||||||
|
return smsServerUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmsServerUrl(String smsServerUrl) {
|
||||||
|
this.smsServerUrl = smsServerUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
package com.platform.marketing.client.sdk;
|
||||||
|
|
||||||
|
import com.platform.marketing.client.sdk.util.*;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信发送客户端
|
||||||
|
* Created by diven on 2020/1/5.
|
||||||
|
*/
|
||||||
|
public class SmsSenderClient {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(SmsSenderClient.class);
|
||||||
|
|
||||||
|
private final static String SINGLE_SEND_REQUEST_URL = "/gateway/client/sendByTemplateId";
|
||||||
|
|
||||||
|
private final static String BTACH_SEND_REQUEST_URL = "/gateway/client/batchSendByTemplateId";
|
||||||
|
|
||||||
|
private SmsConfig smsConfig;
|
||||||
|
|
||||||
|
private LoadBalancer loadBalancer;
|
||||||
|
|
||||||
|
public SmsSenderClient(SmsConfig smsConfig) {
|
||||||
|
this.smsConfig = smsConfig;
|
||||||
|
this.loadBalancer = new LoadBalancer(smsConfig.getSmsServerUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmsSenderResult sendSmsByTemplateId(String mobile,
|
||||||
|
String templateId,
|
||||||
|
Map<String, String> templateParam) {
|
||||||
|
SmsSenderResult smsSenderResult = sendSmsByTemplateId(mobile, templateId, null, templateParam);
|
||||||
|
return smsSenderResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据模版单发短信 境内手机号或全为境外手机号
|
||||||
|
*
|
||||||
|
* @param mobile 接收短信的手机号码。手机号码格式:
|
||||||
|
* <p>
|
||||||
|
* 国内短信:无任何前缀的手机号码,例如 1590000**** 或者 E.164 标准 +8615011319235。
|
||||||
|
* 国际/港澳台消息:+ 国际区号+ 号码,例如 +852000012****。
|
||||||
|
* @param templateId
|
||||||
|
* @param attime
|
||||||
|
* @param templateParam
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public SmsSenderResult sendSmsByTemplateId(String mobile,
|
||||||
|
String templateId,
|
||||||
|
String attime,
|
||||||
|
Map<String, String> templateParam) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* E.164 是国际电信联盟(ITU)定义的国际电话号码标准格式:
|
||||||
|
* 格式:+[国家代码][地区代码][用户号码]
|
||||||
|
* 最大长度:15位数字(不包括+号)
|
||||||
|
* 特点:
|
||||||
|
* 必须以 + 号开头
|
||||||
|
* 国家代码:1-3位数字
|
||||||
|
* 号码总长度不超过15位
|
||||||
|
* 全球唯一标识一个电话号码
|
||||||
|
*/
|
||||||
|
Pair<String,String> pair = CountryCallingCodes.detectCountryCode(mobile);
|
||||||
|
String nationcode = pair.getLeft();
|
||||||
|
String pureMobile = pair.getRight();
|
||||||
|
String random = SmsSenderUtil.getRandom();
|
||||||
|
String appKey = smsConfig.getAppKey();
|
||||||
|
String time = String.valueOf(SmsSenderUtil.getCurrentTime());
|
||||||
|
// 构造参数
|
||||||
|
Map<String, String> param = new HashMap<String, String>(4);
|
||||||
|
param.put("time", time);
|
||||||
|
param.put("appKey", appKey);
|
||||||
|
param.put("random", random);
|
||||||
|
Map<String, Object> queryParam = new HashMap<String, Object>(4);
|
||||||
|
// 国家编码 中国大陆 86
|
||||||
|
queryParam.put("nationcode", nationcode);
|
||||||
|
queryParam.put("mobile", pureMobile);
|
||||||
|
queryParam.put("templateId", templateId);
|
||||||
|
queryParam.put("templateParam", templateParam);
|
||||||
|
if (!SmsStringUtils.isEmpty(attime)) {
|
||||||
|
// 指定发送时间 (一般用于延迟短信 )
|
||||||
|
queryParam.put("attime", attime);
|
||||||
|
}
|
||||||
|
String q = JSON.toJSONString(queryParam);
|
||||||
|
param.put("q", JSON.toJSONString(queryParam));
|
||||||
|
String sign = SmsSenderUtil.calculateSignature(smsConfig.getAppSecret(), random, time, q);
|
||||||
|
param.put("sign", sign);
|
||||||
|
// 发送请求
|
||||||
|
try {
|
||||||
|
JSONObject jsonResult = loadBalancer.doSendRequest(SINGLE_SEND_REQUEST_URL, param);
|
||||||
|
if (jsonResult != null) {
|
||||||
|
Integer code = jsonResult.getInteger("code");
|
||||||
|
if (code == 0) {
|
||||||
|
return new SmsSenderResult(jsonResult.getString("data"));
|
||||||
|
} else {
|
||||||
|
return new SmsSenderResult(code, jsonResult.getString("msg"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("smsClient sendSingle error:", e);
|
||||||
|
}
|
||||||
|
return new SmsSenderResult(ResponseCode.ERROR.getCode(), "系统异常");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量发送短信
|
||||||
|
* @param phoneNumbers 接收短信的电话号码,以逗号, 分隔 。国内手机号: 15011319235 国际手机号:+852000012****
|
||||||
|
* @param templateId 模版编号
|
||||||
|
* @param attime 延迟时间 Long 时间戳
|
||||||
|
* @param templateParam 模版参数
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public SmsSenderResult sendBatchSmsByTemplateId(String phoneNumbers,
|
||||||
|
String templateId,
|
||||||
|
String attime,
|
||||||
|
Map<String, String> templateParam) {
|
||||||
|
|
||||||
|
String random = SmsSenderUtil.getRandom();
|
||||||
|
String appKey = smsConfig.getAppKey();
|
||||||
|
String time = String.valueOf(SmsSenderUtil.getCurrentTime());
|
||||||
|
// 构造参数
|
||||||
|
Map<String, String> param = new HashMap<String, String>(4);
|
||||||
|
param.put("time", time);
|
||||||
|
param.put("appKey", appKey);
|
||||||
|
param.put("random", random);
|
||||||
|
Map<String, Object> queryParam = new HashMap<String, Object>(4);
|
||||||
|
queryParam.put("phoneNumbers", phoneNumbers);
|
||||||
|
queryParam.put("templateId", templateId);
|
||||||
|
queryParam.put("templateParam", templateParam);
|
||||||
|
if (!SmsStringUtils.isEmpty(attime)) {
|
||||||
|
// 指定发送时间 (一般用于延迟短信 )
|
||||||
|
queryParam.put("attime", attime);
|
||||||
|
}
|
||||||
|
String q = JSON.toJSONString(queryParam);
|
||||||
|
param.put("q", JSON.toJSONString(queryParam));
|
||||||
|
String sign = SmsSenderUtil.calculateSignature(smsConfig.getAppSecret(), random, time, q);
|
||||||
|
param.put("sign", sign);
|
||||||
|
// 发送请求
|
||||||
|
try {
|
||||||
|
JSONObject jsonResult = loadBalancer.doSendRequest(BTACH_SEND_REQUEST_URL, param);
|
||||||
|
if (jsonResult != null) {
|
||||||
|
Integer code = jsonResult.getInteger("code");
|
||||||
|
if (code == 0) {
|
||||||
|
return new SmsSenderResult(jsonResult.getString("data"));
|
||||||
|
} else {
|
||||||
|
return new SmsSenderResult(code, jsonResult.getString("msg"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("smsClient sendSingle error:", e);
|
||||||
|
}
|
||||||
|
return new SmsSenderResult(ResponseCode.ERROR.getCode(), "系统异常");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package com.platform.marketing.client.sdk;
|
||||||
|
|
||||||
|
import com.platform.marketing.client.sdk.util.ResponseCode;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
public class SmsSenderResult {
|
||||||
|
private String msg = StringUtils.EMPTY;
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
private String smsId;
|
||||||
|
|
||||||
|
public SmsSenderResult(String smsId) {
|
||||||
|
this.code = ResponseCode.SUCCESS.getCode();
|
||||||
|
this.smsId = smsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmsSenderResult(String smsId, int code, String msg) {
|
||||||
|
this.code = code;
|
||||||
|
this.smsId = smsId;
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmsSenderResult(int code, String msg) {
|
||||||
|
this.code = code;
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSmsId() {
|
||||||
|
return smsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSmsId(String smsId) {
|
||||||
|
this.smsId = smsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMsg() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMsg(String msg) {
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
return sb.append("smsId:" + smsId).append(" code:" + code + " msg:" + msg).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,360 @@
|
||||||
|
package com.platform.marketing.client.sdk.util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CountryCallingCodes {
|
||||||
|
public static final Map<String, String> COUNTRY_CODES = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 区域1: 北美号码计划
|
||||||
|
COUNTRY_CODES.put("1", "北美号码计划区域 (美国、加拿大及加勒比地区)");
|
||||||
|
|
||||||
|
// 区域2: 非洲
|
||||||
|
COUNTRY_CODES.put("20", "埃及");
|
||||||
|
COUNTRY_CODES.put("211", "南苏丹");
|
||||||
|
COUNTRY_CODES.put("212", "摩洛哥");
|
||||||
|
COUNTRY_CODES.put("213", "阿尔及利亚");
|
||||||
|
COUNTRY_CODES.put("216", "突尼斯");
|
||||||
|
COUNTRY_CODES.put("218", "利比亚");
|
||||||
|
COUNTRY_CODES.put("220", "冈比亚");
|
||||||
|
COUNTRY_CODES.put("221", "塞内加尔");
|
||||||
|
COUNTRY_CODES.put("222", "毛里塔尼亚");
|
||||||
|
COUNTRY_CODES.put("223", "马里");
|
||||||
|
COUNTRY_CODES.put("224", "几内亚");
|
||||||
|
COUNTRY_CODES.put("225", "科特迪瓦");
|
||||||
|
COUNTRY_CODES.put("226", "布基纳法索");
|
||||||
|
COUNTRY_CODES.put("227", "尼日尔");
|
||||||
|
COUNTRY_CODES.put("228", "多哥");
|
||||||
|
COUNTRY_CODES.put("229", "贝宁");
|
||||||
|
COUNTRY_CODES.put("230", "毛里求斯");
|
||||||
|
COUNTRY_CODES.put("231", "利比里亚");
|
||||||
|
COUNTRY_CODES.put("232", "塞拉利昂");
|
||||||
|
COUNTRY_CODES.put("233", "加纳");
|
||||||
|
COUNTRY_CODES.put("234", "尼日利亚");
|
||||||
|
COUNTRY_CODES.put("235", "乍得");
|
||||||
|
COUNTRY_CODES.put("236", "中非共和国");
|
||||||
|
COUNTRY_CODES.put("237", "喀麦隆");
|
||||||
|
COUNTRY_CODES.put("238", "佛得角");
|
||||||
|
COUNTRY_CODES.put("239", "圣多美和普林西比");
|
||||||
|
COUNTRY_CODES.put("240", "赤道几内亚");
|
||||||
|
COUNTRY_CODES.put("241", "加蓬");
|
||||||
|
COUNTRY_CODES.put("242", "刚果共和国");
|
||||||
|
COUNTRY_CODES.put("243", "刚果民主共和国");
|
||||||
|
COUNTRY_CODES.put("244", "安哥拉");
|
||||||
|
COUNTRY_CODES.put("245", "几内亚比绍");
|
||||||
|
COUNTRY_CODES.put("246", "英属印度洋领地");
|
||||||
|
COUNTRY_CODES.put("247", "阿森松岛");
|
||||||
|
COUNTRY_CODES.put("248", "塞舌尔");
|
||||||
|
COUNTRY_CODES.put("249", "苏丹");
|
||||||
|
COUNTRY_CODES.put("250", "卢旺达");
|
||||||
|
COUNTRY_CODES.put("251", "埃塞俄比亚");
|
||||||
|
COUNTRY_CODES.put("252", "索马里");
|
||||||
|
COUNTRY_CODES.put("253", "吉布提");
|
||||||
|
COUNTRY_CODES.put("254", "肯尼亚");
|
||||||
|
COUNTRY_CODES.put("255", "坦桑尼亚");
|
||||||
|
COUNTRY_CODES.put("256", "乌干达");
|
||||||
|
COUNTRY_CODES.put("257", "布隆迪");
|
||||||
|
COUNTRY_CODES.put("258", "莫桑比克");
|
||||||
|
COUNTRY_CODES.put("260", "赞比亚");
|
||||||
|
COUNTRY_CODES.put("261", "马达加斯加");
|
||||||
|
COUNTRY_CODES.put("262", "留尼汪");
|
||||||
|
COUNTRY_CODES.put("263", "津巴布韦");
|
||||||
|
COUNTRY_CODES.put("264", "纳米比亚");
|
||||||
|
COUNTRY_CODES.put("265", "马拉维");
|
||||||
|
COUNTRY_CODES.put("266", "莱索托");
|
||||||
|
COUNTRY_CODES.put("267", "博茨瓦纳");
|
||||||
|
COUNTRY_CODES.put("268", "斯威士兰");
|
||||||
|
COUNTRY_CODES.put("269", "科摩罗");
|
||||||
|
COUNTRY_CODES.put("27", "南非");
|
||||||
|
COUNTRY_CODES.put("290", "圣赫勒拿");
|
||||||
|
COUNTRY_CODES.put("291", "厄立特里亚");
|
||||||
|
COUNTRY_CODES.put("297", "阿鲁巴");
|
||||||
|
COUNTRY_CODES.put("298", "法罗群岛");
|
||||||
|
COUNTRY_CODES.put("299", "格陵兰");
|
||||||
|
|
||||||
|
// 区域3-4: 欧洲
|
||||||
|
COUNTRY_CODES.put("30", "希腊");
|
||||||
|
COUNTRY_CODES.put("31", "荷兰");
|
||||||
|
COUNTRY_CODES.put("32", "比利时");
|
||||||
|
COUNTRY_CODES.put("33", "法国");
|
||||||
|
COUNTRY_CODES.put("34", "西班牙");
|
||||||
|
COUNTRY_CODES.put("350", "直布罗陀");
|
||||||
|
COUNTRY_CODES.put("351", "葡萄牙");
|
||||||
|
COUNTRY_CODES.put("352", "卢森堡");
|
||||||
|
COUNTRY_CODES.put("353", "爱尔兰");
|
||||||
|
COUNTRY_CODES.put("354", "冰岛");
|
||||||
|
COUNTRY_CODES.put("355", "阿尔巴尼亚");
|
||||||
|
COUNTRY_CODES.put("356", "马耳他");
|
||||||
|
COUNTRY_CODES.put("357", "塞浦路斯");
|
||||||
|
COUNTRY_CODES.put("358", "芬兰");
|
||||||
|
COUNTRY_CODES.put("359", "保加利亚");
|
||||||
|
COUNTRY_CODES.put("36", "匈牙利");
|
||||||
|
COUNTRY_CODES.put("370", "立陶宛");
|
||||||
|
COUNTRY_CODES.put("371", "拉脱维亚");
|
||||||
|
COUNTRY_CODES.put("372", "爱沙尼亚");
|
||||||
|
COUNTRY_CODES.put("373", "摩尔多瓦");
|
||||||
|
COUNTRY_CODES.put("374", "亚美尼亚");
|
||||||
|
COUNTRY_CODES.put("375", "白俄罗斯");
|
||||||
|
COUNTRY_CODES.put("376", "安道尔");
|
||||||
|
COUNTRY_CODES.put("377", "摩纳哥");
|
||||||
|
COUNTRY_CODES.put("378", "圣马力诺");
|
||||||
|
COUNTRY_CODES.put("379", "梵蒂冈");
|
||||||
|
COUNTRY_CODES.put("380", "乌克兰");
|
||||||
|
COUNTRY_CODES.put("381", "塞尔维亚");
|
||||||
|
COUNTRY_CODES.put("382", "黑山");
|
||||||
|
COUNTRY_CODES.put("383", "科索沃");
|
||||||
|
COUNTRY_CODES.put("385", "克罗地亚");
|
||||||
|
COUNTRY_CODES.put("386", "斯洛文尼亚");
|
||||||
|
COUNTRY_CODES.put("387", "波黑");
|
||||||
|
COUNTRY_CODES.put("389", "北马其顿");
|
||||||
|
COUNTRY_CODES.put("39", "意大利");
|
||||||
|
COUNTRY_CODES.put("40", "罗马尼亚");
|
||||||
|
COUNTRY_CODES.put("41", "瑞士");
|
||||||
|
COUNTRY_CODES.put("420", "捷克");
|
||||||
|
COUNTRY_CODES.put("421", "斯洛伐克");
|
||||||
|
COUNTRY_CODES.put("423", "列支敦士登");
|
||||||
|
COUNTRY_CODES.put("43", "奥地利");
|
||||||
|
COUNTRY_CODES.put("44", "英国");
|
||||||
|
COUNTRY_CODES.put("45", "丹麦");
|
||||||
|
COUNTRY_CODES.put("46", "瑞典");
|
||||||
|
COUNTRY_CODES.put("47", "挪威");
|
||||||
|
COUNTRY_CODES.put("48", "波兰");
|
||||||
|
COUNTRY_CODES.put("49", "德国");
|
||||||
|
|
||||||
|
// 区域5: 美洲(非北美号码计划)
|
||||||
|
COUNTRY_CODES.put("500", "福克兰群岛");
|
||||||
|
COUNTRY_CODES.put("501", "伯利兹");
|
||||||
|
COUNTRY_CODES.put("502", "危地马拉");
|
||||||
|
COUNTRY_CODES.put("503", "萨尔瓦多");
|
||||||
|
COUNTRY_CODES.put("504", "洪都拉斯");
|
||||||
|
COUNTRY_CODES.put("505", "尼加拉瓜");
|
||||||
|
COUNTRY_CODES.put("506", "哥斯达黎加");
|
||||||
|
COUNTRY_CODES.put("507", "巴拿马");
|
||||||
|
COUNTRY_CODES.put("508", "圣皮埃尔和密克隆");
|
||||||
|
COUNTRY_CODES.put("509", "海地");
|
||||||
|
COUNTRY_CODES.put("51", "秘鲁");
|
||||||
|
COUNTRY_CODES.put("52", "墨西哥");
|
||||||
|
COUNTRY_CODES.put("53", "古巴");
|
||||||
|
COUNTRY_CODES.put("54", "阿根廷");
|
||||||
|
COUNTRY_CODES.put("55", "巴西");
|
||||||
|
COUNTRY_CODES.put("56", "智利");
|
||||||
|
COUNTRY_CODES.put("57", "哥伦比亚");
|
||||||
|
COUNTRY_CODES.put("58", "委内瑞拉");
|
||||||
|
COUNTRY_CODES.put("590", "瓜德罗普");
|
||||||
|
COUNTRY_CODES.put("591", "玻利维亚");
|
||||||
|
COUNTRY_CODES.put("592", "圭亚那");
|
||||||
|
COUNTRY_CODES.put("593", "厄瓜多尔");
|
||||||
|
COUNTRY_CODES.put("594", "法属圭亚那");
|
||||||
|
COUNTRY_CODES.put("595", "巴拉圭");
|
||||||
|
COUNTRY_CODES.put("596", "马提尼克");
|
||||||
|
COUNTRY_CODES.put("597", "苏里南");
|
||||||
|
COUNTRY_CODES.put("598", "乌拉圭");
|
||||||
|
COUNTRY_CODES.put("599", "荷属加勒比");
|
||||||
|
|
||||||
|
// 区域6: 东南亚及大洋洲
|
||||||
|
COUNTRY_CODES.put("60", "马来西亚");
|
||||||
|
COUNTRY_CODES.put("61", "澳大利亚");
|
||||||
|
COUNTRY_CODES.put("62", "印度尼西亚");
|
||||||
|
COUNTRY_CODES.put("63", "菲律宾");
|
||||||
|
COUNTRY_CODES.put("64", "新西兰");
|
||||||
|
COUNTRY_CODES.put("65", "新加坡");
|
||||||
|
COUNTRY_CODES.put("66", "泰国");
|
||||||
|
COUNTRY_CODES.put("670", "东帝汶");
|
||||||
|
COUNTRY_CODES.put("672", "澳大利亚海外领地");
|
||||||
|
COUNTRY_CODES.put("673", "文莱");
|
||||||
|
COUNTRY_CODES.put("674", "瑙鲁");
|
||||||
|
COUNTRY_CODES.put("675", "巴布亚新几内亚");
|
||||||
|
COUNTRY_CODES.put("676", "汤加");
|
||||||
|
COUNTRY_CODES.put("677", "所罗门群岛");
|
||||||
|
COUNTRY_CODES.put("678", "瓦努阿图");
|
||||||
|
COUNTRY_CODES.put("679", "斐济");
|
||||||
|
COUNTRY_CODES.put("680", "帕劳");
|
||||||
|
COUNTRY_CODES.put("681", "瓦利斯和富图纳");
|
||||||
|
COUNTRY_CODES.put("682", "库克群岛");
|
||||||
|
COUNTRY_CODES.put("683", "纽埃");
|
||||||
|
COUNTRY_CODES.put("685", "萨摩亚");
|
||||||
|
COUNTRY_CODES.put("686", "基里巴斯");
|
||||||
|
COUNTRY_CODES.put("687", "新喀里多尼亚");
|
||||||
|
COUNTRY_CODES.put("688", "图瓦卢");
|
||||||
|
COUNTRY_CODES.put("689", "法属波利尼西亚");
|
||||||
|
COUNTRY_CODES.put("690", "托克劳");
|
||||||
|
COUNTRY_CODES.put("691", "密克罗尼西亚联邦");
|
||||||
|
COUNTRY_CODES.put("692", "马绍尔群岛");
|
||||||
|
|
||||||
|
// 区域7: 部分前苏联地区
|
||||||
|
COUNTRY_CODES.put("7", "俄罗斯/哈萨克斯坦");
|
||||||
|
|
||||||
|
// 区域8: 东亚及特殊服务
|
||||||
|
COUNTRY_CODES.put("800", "国际免费电话");
|
||||||
|
COUNTRY_CODES.put("808", "国际共享成本服务");
|
||||||
|
COUNTRY_CODES.put("81", "日本");
|
||||||
|
COUNTRY_CODES.put("82", "韩国");
|
||||||
|
COUNTRY_CODES.put("84", "越南");
|
||||||
|
COUNTRY_CODES.put("850", "朝鲜");
|
||||||
|
COUNTRY_CODES.put("852", "香港");
|
||||||
|
COUNTRY_CODES.put("853", "澳门");
|
||||||
|
COUNTRY_CODES.put("855", "柬埔寨");
|
||||||
|
COUNTRY_CODES.put("856", "老挝");
|
||||||
|
COUNTRY_CODES.put("86", "中国大陆");
|
||||||
|
COUNTRY_CODES.put("870", "国际网络");
|
||||||
|
COUNTRY_CODES.put("878", "环球个人通讯服务");
|
||||||
|
COUNTRY_CODES.put("880", "孟加拉国");
|
||||||
|
COUNTRY_CODES.put("881", "国际移动卫星");
|
||||||
|
COUNTRY_CODES.put("882", "国际网络");
|
||||||
|
COUNTRY_CODES.put("883", "国际网络");
|
||||||
|
COUNTRY_CODES.put("886", "台湾");
|
||||||
|
COUNTRY_CODES.put("888", "电信灾害救济");
|
||||||
|
|
||||||
|
// 区域9: 中东和南亚
|
||||||
|
COUNTRY_CODES.put("90", "土耳其");
|
||||||
|
COUNTRY_CODES.put("91", "印度");
|
||||||
|
COUNTRY_CODES.put("92", "巴基斯坦");
|
||||||
|
COUNTRY_CODES.put("93", "阿富汗");
|
||||||
|
COUNTRY_CODES.put("94", "斯里兰卡");
|
||||||
|
COUNTRY_CODES.put("95", "缅甸");
|
||||||
|
COUNTRY_CODES.put("960", "马尔代夫");
|
||||||
|
COUNTRY_CODES.put("961", "黎巴嫩");
|
||||||
|
COUNTRY_CODES.put("962", "约旦");
|
||||||
|
COUNTRY_CODES.put("963", "叙利亚");
|
||||||
|
COUNTRY_CODES.put("964", "伊拉克");
|
||||||
|
COUNTRY_CODES.put("965", "科威特");
|
||||||
|
COUNTRY_CODES.put("966", "沙特阿拉伯");
|
||||||
|
COUNTRY_CODES.put("967", "也门");
|
||||||
|
COUNTRY_CODES.put("968", "阿曼");
|
||||||
|
COUNTRY_CODES.put("970", "巴勒斯坦");
|
||||||
|
COUNTRY_CODES.put("971", "阿联酋");
|
||||||
|
COUNTRY_CODES.put("972", "以色列");
|
||||||
|
COUNTRY_CODES.put("973", "巴林");
|
||||||
|
COUNTRY_CODES.put("974", "卡塔尔");
|
||||||
|
COUNTRY_CODES.put("975", "不丹");
|
||||||
|
COUNTRY_CODES.put("976", "蒙古");
|
||||||
|
COUNTRY_CODES.put("977", "尼泊尔");
|
||||||
|
COUNTRY_CODES.put("979", "国际高价服务");
|
||||||
|
COUNTRY_CODES.put("98", "伊朗");
|
||||||
|
COUNTRY_CODES.put("992", "塔吉克斯坦");
|
||||||
|
COUNTRY_CODES.put("993", "土库曼斯坦");
|
||||||
|
COUNTRY_CODES.put("994", "阿塞拜疆");
|
||||||
|
COUNTRY_CODES.put("995", "格鲁吉亚");
|
||||||
|
COUNTRY_CODES.put("996", "吉尔吉斯斯坦");
|
||||||
|
COUNTRY_CODES.put("998", "乌兹别克斯坦");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据国家区号获取国家名称
|
||||||
|
*/
|
||||||
|
public static String getCountryName(String countryCode) {
|
||||||
|
return COUNTRY_CODES.getOrDefault(countryCode, "未知国家/地区");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据手机号码识别国家区号
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 根据手机号码识别国家区号和纯手机号
|
||||||
|
* @return Pair<String, String> 左边是国家码,右边是纯手机号
|
||||||
|
*/
|
||||||
|
public static Pair<String, String> detectCountryCode(String phoneNumber) {
|
||||||
|
if (phoneNumber == null || phoneNumber.trim().isEmpty()) {
|
||||||
|
return Pair.of("", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理号码
|
||||||
|
String cleaned = phoneNumber.replaceAll("[^+0-9]", "");
|
||||||
|
|
||||||
|
// 处理国际格式
|
||||||
|
if (cleaned.startsWith("+")) {
|
||||||
|
String withoutPlus = cleaned.substring(1);
|
||||||
|
// 尝试1-3位区号匹配
|
||||||
|
for (int len = 3; len >= 1; len--) {
|
||||||
|
if (withoutPlus.length() >= len) {
|
||||||
|
String potentialCode = withoutPlus.substring(0, len);
|
||||||
|
if (COUNTRY_CODES.containsKey(potentialCode)) {
|
||||||
|
String pureMobile = withoutPlus.substring(len);
|
||||||
|
return Pair.of(potentialCode, pureMobile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果没有匹配到已知区号,返回整个号码作为纯手机号
|
||||||
|
return Pair.of("", withoutPlus);
|
||||||
|
}
|
||||||
|
// 处理0086格式
|
||||||
|
else if (cleaned.startsWith("0086")) {
|
||||||
|
String pureMobile = cleaned.substring(4);
|
||||||
|
return Pair.of("86", pureMobile);
|
||||||
|
}
|
||||||
|
// 处理86前缀
|
||||||
|
else if (cleaned.startsWith("86") && cleaned.length() > 10) {
|
||||||
|
String pureMobile = cleaned.substring(2);
|
||||||
|
return Pair.of("86", pureMobile);
|
||||||
|
}
|
||||||
|
// 处理纯11位中国手机号
|
||||||
|
else if (cleaned.matches("^1[3-9]\\d{9}$")) {
|
||||||
|
return Pair.of("86", cleaned);
|
||||||
|
}
|
||||||
|
// 处理86前缀
|
||||||
|
else if (cleaned.startsWith("62") && cleaned.length() > 10) {
|
||||||
|
String pureMobile = cleaned.substring(2);
|
||||||
|
return Pair.of("62", pureMobile);
|
||||||
|
}
|
||||||
|
// 其他情况
|
||||||
|
else {
|
||||||
|
return Pair.of("", cleaned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有国家区号的区域分类
|
||||||
|
*/
|
||||||
|
public static Map<String, Map<String, String>> getCodesByRegion() {
|
||||||
|
Map<String, Map<String, String>> regions = new HashMap<>();
|
||||||
|
|
||||||
|
Map<String, String> region1 = new HashMap<>(); // 北美
|
||||||
|
Map<String, String> region2 = new HashMap<>(); // 非洲
|
||||||
|
Map<String, String> region34 = new HashMap<>(); // 欧洲
|
||||||
|
Map<String, String> region5 = new HashMap<>(); // 美洲
|
||||||
|
Map<String, String> region6 = new HashMap<>(); // 东南亚大洋洲
|
||||||
|
Map<String, String> region7 = new HashMap<>(); // 前苏联
|
||||||
|
Map<String, String> region8 = new HashMap<>(); // 东亚
|
||||||
|
Map<String, String> region9 = new HashMap<>(); // 中东南亚
|
||||||
|
|
||||||
|
// 这里可以根据区号范围进行分类,简化示例
|
||||||
|
regions.put("区域1: 北美", region1);
|
||||||
|
regions.put("区域2: 非洲", region2);
|
||||||
|
regions.put("区域3-4: 欧洲", region34);
|
||||||
|
regions.put("区域5: 美洲", region5);
|
||||||
|
regions.put("区域6: 东南亚及大洋洲", region6);
|
||||||
|
regions.put("区域7: 前苏联地区", region7);
|
||||||
|
regions.put("区域8: 东亚及特殊服务", region8);
|
||||||
|
regions.put("区域9: 中东和南亚", region9);
|
||||||
|
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试方法
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 测试一些常见号码
|
||||||
|
String[] testNumbers = {
|
||||||
|
"+8615912345678",
|
||||||
|
"+85212345678",
|
||||||
|
"+12125551234",
|
||||||
|
"008615912345678",
|
||||||
|
"15912345678"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String number : testNumbers) {
|
||||||
|
|
||||||
|
Pair<String, String> pair = detectCountryCode(number);
|
||||||
|
String nationCode = pair.getLeft();
|
||||||
|
String pureMobile = pair.getRight();
|
||||||
|
String country = getCountryName(nationCode);
|
||||||
|
System.out.println("nationCode:" + nationCode + "国家:" + country + " pureMobile:" + pureMobile);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("\n总国家/地区数量: " + COUNTRY_CODES.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.platform.marketing.client.sdk.util;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 负载均衡工具类
|
||||||
|
*/
|
||||||
|
public class LoadBalancer {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(LoadBalancer.class);
|
||||||
|
|
||||||
|
private static List<String> CURRENT_SMS_NODES = new ArrayList<String>();
|
||||||
|
|
||||||
|
private static AtomicInteger INDEX = new AtomicInteger(0);
|
||||||
|
|
||||||
|
public LoadBalancer(String smsServerUrl) {
|
||||||
|
String[] arr = StringUtils.split(smsServerUrl, ";");
|
||||||
|
CURRENT_SMS_NODES.addAll(Arrays.asList(arr));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject doSendRequest(String requestPath, Map<String, String> params) {
|
||||||
|
int size = CURRENT_SMS_NODES.size();
|
||||||
|
int tryCount = (size >= 2) ? 2 : 1;
|
||||||
|
JSONObject jsonResult = null;
|
||||||
|
for (int i = 0; i < tryCount; i++) {
|
||||||
|
String nodeUrl = CURRENT_SMS_NODES.get(Math.abs(INDEX.intValue() % size));
|
||||||
|
try {
|
||||||
|
String httpResult = SmsHttpClientUtils.doPost(nodeUrl + requestPath, params, 5000, 5000);
|
||||||
|
jsonResult = JSON.parseObject(httpResult);
|
||||||
|
int code = jsonResult.getIntValue("code");
|
||||||
|
if (code == ResponseCode.SUCCESS.getCode()) {
|
||||||
|
return jsonResult;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("doSendRequest error:", e);
|
||||||
|
} finally {
|
||||||
|
INDEX.incrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.platform.marketing.client.sdk.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理器响应码
|
||||||
|
* Created by diven on 2023/8/13.
|
||||||
|
*/
|
||||||
|
public enum ResponseCode {
|
||||||
|
|
||||||
|
SUCCESS(0, "成功"),
|
||||||
|
|
||||||
|
ERROR(500, "异常");
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
ResponseCode(int code, String value) {
|
||||||
|
this.code = code;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,332 @@
|
||||||
|
package com.platform.marketing.client.sdk.util;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class SmsHttpClientUtils {
|
||||||
|
|
||||||
|
public static final Logger LOG = LoggerFactory.getLogger(SmsHttpClientUtils.class);
|
||||||
|
|
||||||
|
public static final String DEFAULT_CHARSET = "UTF-8";
|
||||||
|
|
||||||
|
private static final String METHOD_POST = "POST";
|
||||||
|
|
||||||
|
private static final String METHOD_GET = "GET";
|
||||||
|
|
||||||
|
private static final int MAX_SIZE = 1024;
|
||||||
|
|
||||||
|
private SmsHttpClientUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String doPost(String url, Map<String, String> params, int connectTimeout, int readTimeout) throws Exception {
|
||||||
|
return doPost(url, params, DEFAULT_CHARSET, connectTimeout, readTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fillHeaders(HttpURLConnection conn, Map<String, String> headers) {
|
||||||
|
if (headers != null) {
|
||||||
|
for (Entry<String, String> entry : headers.entrySet()) {
|
||||||
|
fillHeader(conn, entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fillHeader(HttpURLConnection conn, String key, String value) {
|
||||||
|
conn.setRequestProperty(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteArrayOutputStream getResultOutputStream(HttpURLConnection conn) throws IOException {
|
||||||
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||||
|
InputStream is = conn.getInputStream();
|
||||||
|
try {
|
||||||
|
if (is != null) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len = 0;
|
||||||
|
while ((len = is.read(buffer)) != -1) {
|
||||||
|
outStream.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (is != null) {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String doPost(String url, Map<String, String> params, String charset, int connectTimeout, int readTimeout) throws Exception {
|
||||||
|
String ctype = "application/x-www-form-urlencoded;charset=" + charset;
|
||||||
|
String query = buildQuery(params, charset);
|
||||||
|
byte[] content = {};
|
||||||
|
if (query != null) {
|
||||||
|
try {
|
||||||
|
content = query.getBytes(charset);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return doPost(url, ctype, content, connectTimeout, readTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String doPost(String url, String ctype, byte[] content, int connectTimeout, int readTimeout) throws Exception {
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
String rsp = null;
|
||||||
|
try {
|
||||||
|
conn = getConnection(new URL(url), METHOD_POST, ctype);
|
||||||
|
conn.setConnectTimeout(connectTimeout);
|
||||||
|
conn.setReadTimeout(readTimeout);
|
||||||
|
out = conn.getOutputStream();
|
||||||
|
out.write(content);
|
||||||
|
rsp = getResponseAsString(conn);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("调用 Http地址:" + url + " 访问出现错误 : ", e);
|
||||||
|
throw new Exception("访问远程服务失败", e);
|
||||||
|
} finally {
|
||||||
|
if (out != null) {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conn != null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String doGet(String url, Map<String, String> params) throws Exception {
|
||||||
|
return doGet(url, params, DEFAULT_CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String doGet(String url, Map<String, String> params, String charset) throws Exception {
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
String rsp = null;
|
||||||
|
try {
|
||||||
|
String ctype = "application/x-www-form-urlencoded;charset=" + charset;
|
||||||
|
String query = buildQuery(params, charset);
|
||||||
|
try {
|
||||||
|
conn = getConnection(buildGetUrl(url, query), METHOD_GET, ctype);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("调用 Http地址:" + url + " 访问出现错误 : ", e);
|
||||||
|
throw new Exception(e);
|
||||||
|
}
|
||||||
|
rsp = getResponseAsString(conn);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (conn != null) {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HttpURLConnection getConnection(URL url, String method, String ctype) {
|
||||||
|
try {
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod(method);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setRequestProperty("Accept", "text/xml,text/javascript,text/html,text/plain,*/*;");
|
||||||
|
// conn.setRequestProperty("User-Agent", "remote-java");
|
||||||
|
conn.setRequestProperty("Content-Type", ctype);
|
||||||
|
conn.setRequestProperty("Accept-Language", "zh-CN");
|
||||||
|
return conn;
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("execute open connection error", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URL buildGetUrl(String strUrl, String query) throws IOException {
|
||||||
|
StringBuffer buffer = new StringBuffer(strUrl);
|
||||||
|
URL url = new URL(strUrl);
|
||||||
|
if (SmsStringUtils.isEmpty(query)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SmsStringUtils.isEmpty(url.getQuery())) {
|
||||||
|
if (strUrl.endsWith("?")) {
|
||||||
|
buffer.append(query);
|
||||||
|
} else {
|
||||||
|
buffer.append("?").append(query);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (strUrl.endsWith("&")) {
|
||||||
|
buffer.append(query);
|
||||||
|
} else {
|
||||||
|
buffer.append("&").append(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new URL(buffer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildQuery(Map<String, String> params, String charset) throws Exception {
|
||||||
|
if (params == null || params.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder query = new StringBuilder();
|
||||||
|
Set<Entry<String, String>> entries = params.entrySet();
|
||||||
|
boolean hasParam = false;
|
||||||
|
|
||||||
|
for (Entry<String, String> entry : entries) {
|
||||||
|
String name = entry.getKey();
|
||||||
|
String value = entry.getValue();
|
||||||
|
// 忽略参数名或参数值为空的参数
|
||||||
|
if (SmsStringUtils.areNotEmpty(name, value)) {
|
||||||
|
if (hasParam) {
|
||||||
|
query.append("&");
|
||||||
|
} else {
|
||||||
|
hasParam = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
query.append(name).append("=").append(URLEncoder.encode(value, charset));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String getResponseAsString(HttpURLConnection conn) throws Exception {
|
||||||
|
String charset = getResponseCharset(conn.getContentType());
|
||||||
|
InputStream es = conn.getErrorStream();
|
||||||
|
String msg;
|
||||||
|
if (es == null) {
|
||||||
|
try {
|
||||||
|
return getStreamAsString(conn.getInputStream(), charset);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Exception(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg = getStreamAsString(es, charset);
|
||||||
|
if (SmsStringUtils.isEmpty(msg)) {
|
||||||
|
throw new Exception("返回的内容为空" + msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getStreamAsString(InputStream stream, String charset) throws Exception {
|
||||||
|
try {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charset));
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
|
||||||
|
char[] chars = new char[MAX_SIZE];
|
||||||
|
int count = 0;
|
||||||
|
while ((count = reader.read(chars)) > 0) {
|
||||||
|
writer.write(chars, 0, count);
|
||||||
|
}
|
||||||
|
return writer.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Exception(e);
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getResponseCharset(String ctype) {
|
||||||
|
String charset = DEFAULT_CHARSET;
|
||||||
|
if (!SmsStringUtils.isEmpty(ctype)) {
|
||||||
|
String[] params = ctype.split(";");
|
||||||
|
for (String param : params) {
|
||||||
|
param = param.trim();
|
||||||
|
if (param.startsWith("charset")) {
|
||||||
|
String[] pair = param.split("=", 2);
|
||||||
|
if (pair.length == 2 && !SmsStringUtils.isEmpty(pair[1])) {
|
||||||
|
charset = pair[1].trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decode(String value) {
|
||||||
|
return decode(value, DEFAULT_CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String encode(String value) {
|
||||||
|
return encode(value, DEFAULT_CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String decode(String value, String charset) {
|
||||||
|
String result = null;
|
||||||
|
if (!SmsStringUtils.isEmpty(value)) {
|
||||||
|
try {
|
||||||
|
result = URLDecoder.decode(value, charset);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encode(String value, String charset) {
|
||||||
|
String result = null;
|
||||||
|
if (!SmsStringUtils.isEmpty(value)) {
|
||||||
|
try {
|
||||||
|
result = URLEncoder.encode(value, charset);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Map<String, String> getParamsFromUrl(String url) {
|
||||||
|
Map<String, String> map = null;
|
||||||
|
if (url != null && url.indexOf('?') != -1) {
|
||||||
|
map = splitUrlQuery(url.substring(url.indexOf('?') + 1));
|
||||||
|
}
|
||||||
|
if (map == null) {
|
||||||
|
map = new HashMap<String, String>();
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> splitUrlQuery(String query) {
|
||||||
|
Map<String, String> result = new HashMap<String, String>();
|
||||||
|
|
||||||
|
String[] pairs = query.split("&");
|
||||||
|
if (pairs != null && pairs.length > 0) {
|
||||||
|
for (String pair : pairs) {
|
||||||
|
String[] param = pair.split("=", 2);
|
||||||
|
if (param != null && param.length == 2) {
|
||||||
|
result.put(param[0], param[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.platform.marketing.client.sdk.util;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
|
public class SmsSenderUtil {
|
||||||
|
|
||||||
|
public static long getCurrentTime() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRandom() {
|
||||||
|
//long random = (new Random(SmsSenderUtil.getCurrentTime())).nextLong() % 900000 + 100000;
|
||||||
|
//return String.valueOf(random);
|
||||||
|
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String calculateSignature(String appSecret, String random, String time,
|
||||||
|
String q) {
|
||||||
|
StringBuffer buffer = new StringBuffer("appSecret=")
|
||||||
|
.append(appSecret)
|
||||||
|
.append("&random=")
|
||||||
|
.append(random)
|
||||||
|
.append("&time=")
|
||||||
|
.append(time)
|
||||||
|
.append("&q=")
|
||||||
|
.append(q);
|
||||||
|
return sha256(buffer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String calculateSignature(String appSecret, long random, long time,
|
||||||
|
String[] phoneNumbers) {
|
||||||
|
StringBuffer buffer = new StringBuffer("appSecret=")
|
||||||
|
.append(appSecret)
|
||||||
|
.append("&random=")
|
||||||
|
.append(random)
|
||||||
|
.append("&time=")
|
||||||
|
.append(time)
|
||||||
|
.append("&q=");
|
||||||
|
if (phoneNumbers.length > 0) {
|
||||||
|
buffer.append(phoneNumbers[0]);
|
||||||
|
for (int i = 1; i < phoneNumbers.length; i++) {
|
||||||
|
buffer.append(",");
|
||||||
|
buffer.append(phoneNumbers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sha256(buffer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String calculateSignature(String appSecret, long random, long time,
|
||||||
|
ArrayList<String> phoneNumbers) {
|
||||||
|
return calculateSignature(appSecret, random, time, phoneNumbers.toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String calculateSignature(String appSecret, long random, long time) {
|
||||||
|
StringBuffer buffer = new StringBuffer("appSecret=")
|
||||||
|
.append(appSecret)
|
||||||
|
.append("&random=")
|
||||||
|
.append(random)
|
||||||
|
.append("&time=")
|
||||||
|
.append(time);
|
||||||
|
return sha256(buffer.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sha256(String rawString) {
|
||||||
|
return DigestUtils.sha256Hex(rawString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.platform.marketing.client.sdk.util;
|
||||||
|
|
||||||
|
public class SmsStringUtils {
|
||||||
|
|
||||||
|
public static boolean isEmpty(String str) {
|
||||||
|
return str == null || str.length() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks if a String is whitespace, empty ("") or null.</p>
|
||||||
|
* <p>
|
||||||
|
* <pre>
|
||||||
|
* StringUtils.isBlank(null) = true
|
||||||
|
* StringUtils.isBlank("") = true
|
||||||
|
* StringUtils.isBlank(" ") = true
|
||||||
|
* StringUtils.isBlank("bob") = false
|
||||||
|
* StringUtils.isBlank(" bob ") = false
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param str the String to check, may be null
|
||||||
|
* @return <code>true</code> if the String is null, empty or whitespace
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public static boolean isBlank(String str) {
|
||||||
|
if (str == null || str.length() == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
if ((!Character.isWhitespace(str.charAt(i)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean areNotEmpty(String... values) {
|
||||||
|
boolean result = true;
|
||||||
|
if (values == null || values.length == 0) {
|
||||||
|
result = false;
|
||||||
|
} else {
|
||||||
|
for (String value : values) {
|
||||||
|
result &= !isEmpty(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean equals(String str1, String str2) {
|
||||||
|
return str1 == null ? str2 == null : str1.equals(str2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.platform.marketing.client.sdk.test;
|
||||||
|
|
||||||
|
import com.platform.marketing.client.sdk.SmsConfig;
|
||||||
|
import com.platform.marketing.client.sdk.SmsSenderClient;
|
||||||
|
import com.platform.marketing.client.sdk.SmsSenderResult;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by diven on 2020/9/12.
|
||||||
|
*/
|
||||||
|
public class BatchSmsSendUnitTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SmsConfig smsConfig = new SmsConfig();
|
||||||
|
smsConfig.setAppKey("qQjEiFzn80v8VM4h");
|
||||||
|
smsConfig.setSmsServerUrl("http://14.103.184.174:8089");
|
||||||
|
smsConfig.setAppSecret("9c465ece754bd26a9be77f3d0e2606bd");
|
||||||
|
SmsSenderClient smsSenderClient = new SmsSenderClient(smsConfig);
|
||||||
|
String phoneNumbers = "15011319235,17607197036,15011319235,15011319235,15011319237,15011319237";
|
||||||
|
String templateId = "1";
|
||||||
|
// ${code}为您的登录验证码,请于${time}分钟内填写,如非本人操作,请忽略本短信。
|
||||||
|
Map<String, String> param = new HashMap<String, String>();
|
||||||
|
param.put("code", "1234");
|
||||||
|
param.put("time", "10");
|
||||||
|
// 30 秒之后发送
|
||||||
|
String attime = String.valueOf(System.currentTimeMillis() + 30 * 1000L);
|
||||||
|
SmsSenderResult senderResult = smsSenderClient.sendBatchSmsByTemplateId(phoneNumbers, templateId, attime, param);
|
||||||
|
System.out.println("senderResult:" + JSON.toJSONString(senderResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.platform.marketing.client.sdk.test;
|
||||||
|
|
||||||
|
import com.platform.marketing.client.sdk.SmsConfig;
|
||||||
|
import com.platform.marketing.client.sdk.SmsSenderClient;
|
||||||
|
import com.platform.marketing.client.sdk.SmsSenderResult;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by diven on 2020/9/12.
|
||||||
|
*/
|
||||||
|
public class SingleSmsSendUnitTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SmsConfig smsConfig = new SmsConfig();
|
||||||
|
smsConfig.setAppKey("qQjEiFzn80v8VM4h");
|
||||||
|
smsConfig.setSmsServerUrl("http://14.103.184.174:8089;http://14.103.184.175:8089");
|
||||||
|
smsConfig.setAppSecret("9c465ece754bd26a9be77f3d0e2606bd");
|
||||||
|
SmsSenderClient smsSenderClient = new SmsSenderClient(smsConfig);
|
||||||
|
String mobile = "15011319235";
|
||||||
|
String templateId = "1";
|
||||||
|
// ${code}为您的登录验证码,请于${time}分钟内填写,如非本人操作,请忽略本短信。
|
||||||
|
Map<String, String> param = new HashMap<String, String>();
|
||||||
|
param.put("code", "1234");
|
||||||
|
param.put("time", "10");
|
||||||
|
// 30 秒之后发送
|
||||||
|
String attime = String.valueOf(System.currentTimeMillis() + 30 * 1000L);
|
||||||
|
SmsSenderResult senderResult = smsSenderClient.sendSmsByTemplateId(mobile, templateId, attime, param);
|
||||||
|
System.out.println("senderResult:" + JSON.toJSONString(senderResult));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<configuration scan="true" scanPeriod=" 5 seconds">
|
||||||
|
<jmxConfigurator />
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %p %t %logger{36}:%L - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<!-- 控制台日志 -->
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
|
|
@ -0,0 +1,443 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-dependencies</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>marketing-dependencies</name>
|
||||||
|
<description>基础 bom 文件,管理整个项目的依赖版本</description>
|
||||||
|
<url>https://github.com/makemyownlife/platform-sms-pro</url>
|
||||||
|
<properties>
|
||||||
|
<tika-core.version>2.9.3</tika-core.version>
|
||||||
|
<podam.version>7.2.11.RELEASE</podam.version>
|
||||||
|
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
|
||||||
|
<opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>
|
||||||
|
<mapstruct.version>1.6.3</mapstruct.version>
|
||||||
|
<fastjson.version>1.2.83</fastjson.version>
|
||||||
|
<spring.framework.version>5.3.39</spring.framework.version>
|
||||||
|
<weixin-java.version>4.7.2.B</weixin-java.version>
|
||||||
|
<mybatis.version>3.5.17</mybatis.version>
|
||||||
|
<kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
|
||||||
|
<commons-compress.version>1.27.1</commons-compress.version>
|
||||||
|
<rocketmq-spring.version>2.3.2</rocketmq-spring.version>
|
||||||
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
|
<dynamic-datasource.version>4.3.1</dynamic-datasource.version>
|
||||||
|
<redisson.version>3.41.0</redisson.version>
|
||||||
|
<pf4j-spring.version>0.9.0</pf4j-spring.version>
|
||||||
|
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
|
||||||
|
<vertx.version>4.5.13</vertx.version>
|
||||||
|
<spring.boot.version>2.7.18</spring.boot.version>
|
||||||
|
<springdoc.version>1.8.0</springdoc.version>
|
||||||
|
<lock4j.version>2.2.7</lock4j.version>
|
||||||
|
<commons-io.version>2.17.0</commons-io.version>
|
||||||
|
<jsoup.version>1.18.3</jsoup.version>
|
||||||
|
<mybatis-plus.version>3.5.9</mybatis-plus.version>
|
||||||
|
<knife4j.version>4.5.0</knife4j.version>
|
||||||
|
<lombok.version>1.18.36</lombok.version>
|
||||||
|
<flowable.version>6.8.0</flowable.version>
|
||||||
|
<mockito-inline.version>4.11.0</mockito-inline.version>
|
||||||
|
<justauth.version>1.0.8</justauth.version>
|
||||||
|
<velocity.version>2.4</velocity.version>
|
||||||
|
<spring.security.version>5.8.14</spring.security.version>
|
||||||
|
<easyexcel.version>4.0.3</easyexcel.version>
|
||||||
|
<captcha-plus.version>1.0.10</captcha-plus.version>
|
||||||
|
<bizlog-sdk.version>3.0.6</bizlog-sdk.version>
|
||||||
|
<logback.version>1.2.13</logback.version>
|
||||||
|
<bouncycastle.version>1.78</bouncycastle.version>
|
||||||
|
<mybatis-plus-join.version>1.4.13</mybatis-plus-join.version>
|
||||||
|
<revision>0.0.1-jdk8-SNAPSHOT</revision>
|
||||||
|
<jsch.version>0.1.55</jsch.version>
|
||||||
|
<netty.version>4.1.116.Final</netty.version>
|
||||||
|
<jedis-mock.version>1.1.8</jedis-mock.version>
|
||||||
|
<hutool.version>5.8.35</hutool.version>
|
||||||
|
<guava.version>33.4.0-jre</guava.version>
|
||||||
|
<servlet.versoin>2.5</servlet.versoin>
|
||||||
|
<aws-java-sdk-s3.version>1.12.777</aws-java-sdk-s3.version>
|
||||||
|
<taos.version>3.3.3</taos.version>
|
||||||
|
<commons-net.version>3.11.1</commons-net.version>
|
||||||
|
<jimureport.version>1.7.8</jimureport.version>
|
||||||
|
<spring.boot.webfulx.version>5.3.39</spring.boot.webfulx.version>
|
||||||
|
<druid.version>1.2.24</druid.version>
|
||||||
|
<easy-trans.version>3.0.6</easy-trans.version>
|
||||||
|
<dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
|
||||||
|
</properties>
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-bom</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-framework-bom</artifactId>
|
||||||
|
<version>${spring.framework.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-bom</artifactId>
|
||||||
|
<version>${spring.security.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-dependencies</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.mouzt</groupId>
|
||||||
|
<artifactId>bizlog-sdk</artifactId>
|
||||||
|
<version>${bizlog-sdk.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-biz-ip</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-web</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-security</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
|
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
|
||||||
|
<version>${knife4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-ui</artifactId>
|
||||||
|
<version>${springdoc.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-mybatis</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
|
<version>${druid.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis</artifactId>
|
||||||
|
<version>${mybatis.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-generator</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
|
||||||
|
<version>${dynamic-datasource.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.yulichang</groupId>
|
||||||
|
<artifactId>mybatis-plus-join-boot-starter</artifactId>
|
||||||
|
<version>${mybatis-plus-join.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fhs-opensource</groupId>
|
||||||
|
<artifactId>easy-trans-spring-boot-starter</artifactId>
|
||||||
|
<version>${easy-trans.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-commons</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fhs-opensource</groupId>
|
||||||
|
<artifactId>easy-trans-mybatis-plus-extend</artifactId>
|
||||||
|
<version>${easy-trans.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fhs-opensource</groupId>
|
||||||
|
<artifactId>easy-trans-anno</artifactId>
|
||||||
|
<version>${easy-trans.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-redis</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||||
|
<version>${redisson.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-data-34</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-data-27</artifactId>
|
||||||
|
<version>${redisson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webflux</artifactId>
|
||||||
|
<version>${spring.boot.webfulx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-job</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-mq</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-idgenerator</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.rocketmq</groupId>
|
||||||
|
<artifactId>rocketmq-spring-boot-starter</artifactId>
|
||||||
|
<version>${rocketmq-spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-protection</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||||
|
<version>${lock4j.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-test</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-inline</artifactId>
|
||||||
|
<version>${mockito-inline.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.fppt</groupId>
|
||||||
|
<artifactId>jedis-mock</artifactId>
|
||||||
|
<version>${jedis-mock.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>uk.co.jemos.podam</groupId>
|
||||||
|
<artifactId>podam</artifactId>
|
||||||
|
<version>${podam.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-common</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-excel</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct</artifactId>
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-jdk8</artifactId>
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-processor</artifactId>
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>${hutool.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
|
<version>${bouncycastle.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>easyexcel</artifactId>
|
||||||
|
<version>${easyexcel.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>${commons-io.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-compress</artifactId>
|
||||||
|
<version>${commons-compress.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>${tika-core.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.velocity</groupId>
|
||||||
|
<artifactId>velocity-engine-core</artifactId>
|
||||||
|
<version>${velocity.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>${fastjson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>${guava.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.inject</groupId>
|
||||||
|
<artifactId>guice</artifactId>
|
||||||
|
<version>${guice.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
|
<version>${transmittable-thread-local.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-net</groupId>
|
||||||
|
<artifactId>commons-net</artifactId>
|
||||||
|
<version>${commons-net.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jcraft</groupId>
|
||||||
|
<artifactId>jsch</artifactId>
|
||||||
|
<version>${jsch.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xingyuv</groupId>
|
||||||
|
<artifactId>spring-boot-starter-captcha-plus</artifactId>
|
||||||
|
<version>${captcha-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
<version>${ip2region.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>${jsoup.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.amazonaws</groupId>
|
||||||
|
<artifactId>aws-java-sdk-s3</artifactId>
|
||||||
|
<version>${aws-java-sdk-s3.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xingyuv</groupId>
|
||||||
|
<artifactId>spring-boot-starter-justauth</artifactId>
|
||||||
|
<version>${justauth.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-core</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,601 @@
|
||||||
|
<?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>
|
||||||
|
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-dependencies</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>基础 bom 文件,管理整个项目的依赖版本</description>
|
||||||
|
<url>https://github.com/makemyownlife/platform-sms-pro</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<revision>0.0.1-jdk8-SNAPSHOT</revision>
|
||||||
|
|
||||||
|
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
|
||||||
|
<!-- 统一依赖管理 -->
|
||||||
|
<spring.framework.version>5.3.39</spring.framework.version>
|
||||||
|
<spring.security.version>5.8.14</spring.security.version>
|
||||||
|
<spring.boot.version>2.7.18</spring.boot.version>
|
||||||
|
<spring.boot.webfulx.version>5.3.39</spring.boot.webfulx.version>
|
||||||
|
<!-- Web 相关 -->
|
||||||
|
<springdoc.version>1.8.0</springdoc.version>
|
||||||
|
<knife4j.version>4.5.0</knife4j.version>
|
||||||
|
<servlet.versoin>2.5</servlet.versoin>
|
||||||
|
<!-- DB 相关 -->
|
||||||
|
<druid.version>1.2.24</druid.version>
|
||||||
|
<mybatis.version>3.5.17</mybatis.version>
|
||||||
|
<mybatis-plus.version>3.5.9</mybatis-plus.version>
|
||||||
|
<dynamic-datasource.version>4.3.1</dynamic-datasource.version>
|
||||||
|
<mybatis-plus-join.version>1.4.13</mybatis-plus-join.version>
|
||||||
|
<easy-trans.version>3.0.6</easy-trans.version>
|
||||||
|
<redisson.version>3.41.0</redisson.version>
|
||||||
|
<dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
|
||||||
|
<kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
|
||||||
|
<opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>
|
||||||
|
<taos.version>3.3.3</taos.version>
|
||||||
|
<!-- 消息队列 -->
|
||||||
|
<rocketmq-spring.version>2.3.2</rocketmq-spring.version>
|
||||||
|
<!-- 服务保障相关 -->
|
||||||
|
<lock4j.version>2.2.7</lock4j.version>
|
||||||
|
<!-- Test 测试相关 -->
|
||||||
|
<podam.version>7.2.11.RELEASE</podam.version> <!-- Spring Boot 2.X 最多使用 7.2.11 版本 -->
|
||||||
|
<jedis-mock.version>1.1.8</jedis-mock.version>
|
||||||
|
<mockito-inline.version>4.11.0</mockito-inline.version>
|
||||||
|
<!-- Bpm 工作流相关 -->
|
||||||
|
<flowable.version>6.8.0</flowable.version>
|
||||||
|
<!-- 工具类相关 -->
|
||||||
|
<captcha-plus.version>1.0.10</captcha-plus.version>
|
||||||
|
<jsoup.version>1.18.3</jsoup.version>
|
||||||
|
<lombok.version>1.18.36</lombok.version>
|
||||||
|
<mapstruct.version>1.6.3</mapstruct.version>
|
||||||
|
<hutool.version>5.8.35</hutool.version>
|
||||||
|
<bouncycastle.version>1.78</bouncycastle.version>
|
||||||
|
<easyexcel.version>4.0.3</easyexcel.version>
|
||||||
|
<velocity.version>2.4</velocity.version> <!-- JDK8 不能从 2.4 升级到 2.4.1,会报包不存在!!!! -->
|
||||||
|
<fastjson.version>1.2.83</fastjson.version>
|
||||||
|
<guava.version>33.4.0-jre</guava.version>
|
||||||
|
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
|
||||||
|
<commons-net.version>3.11.1</commons-net.version>
|
||||||
|
<jsch.version>0.1.55</jsch.version>
|
||||||
|
<tika-core.version>2.9.3</tika-core.version> <!-- JDK8 不能从 2.9.3 升级到 3.X,会报 JDK8 不支持 -->
|
||||||
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
|
|
||||||
|
<netty.version>4.1.116.Final</netty.version>
|
||||||
|
|
||||||
|
<pf4j-spring.version>0.9.0</pf4j-spring.version>
|
||||||
|
<vertx.version>4.5.13</vertx.version>
|
||||||
|
<bizlog-sdk.version>3.0.6</bizlog-sdk.version>
|
||||||
|
|
||||||
|
<!-- 三方云服务相关 -->
|
||||||
|
<commons-io.version>2.17.0</commons-io.version>
|
||||||
|
<commons-compress.version>1.27.1</commons-compress.version>
|
||||||
|
<aws-java-sdk-s3.version>1.12.777</aws-java-sdk-s3.version>
|
||||||
|
<justauth.version>1.0.8</justauth.version>
|
||||||
|
<jimureport.version>1.7.8</jimureport.version>
|
||||||
|
<weixin-java.version>4.7.2.B</weixin-java.version>
|
||||||
|
<!-- 专属于 JDK8 安全漏洞升级 -->
|
||||||
|
<logback.version>1.2.13</logback.version> <!-- 无法使用 1.3.X 版本,启动会报错 -->
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- 统一依赖管理 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-bom</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-framework-bom</artifactId> <!-- JDK8 版本独有:保证 Spring Framework 尽量高 -->
|
||||||
|
<version>${spring.framework.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-bom</artifactId> <!-- JDK8 版本独有:保证 Spring Security 尽量高 -->
|
||||||
|
<version>${spring.security.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-dependencies</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 业务组件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.mouzt</groupId>
|
||||||
|
<artifactId>bizlog-sdk</artifactId>
|
||||||
|
<version>${bizlog-sdk.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion> <!-- 排除掉springboot依赖使用项目的 -->
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-biz-ip</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring 核心 -->
|
||||||
|
<dependency>
|
||||||
|
<!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Web 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-web</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-security</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
|
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
|
||||||
|
<version>${knife4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-ui</artifactId>
|
||||||
|
<version>${springdoc.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- DB 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-mybatis</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
|
<version>${druid.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis</artifactId>
|
||||||
|
<version>${mybatis.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->
|
||||||
|
<version>${dynamic-datasource.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.yulichang</groupId>
|
||||||
|
<artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
|
||||||
|
<version>${mybatis-plus-join.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->
|
||||||
|
<artifactId>easy-trans-spring-boot-starter</artifactId>
|
||||||
|
<version>${easy-trans.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-commons</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fhs-opensource</groupId>
|
||||||
|
<artifactId>easy-trans-mybatis-plus-extend</artifactId>
|
||||||
|
<version>${easy-trans.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fhs-opensource</groupId>
|
||||||
|
<artifactId>easy-trans-anno</artifactId>
|
||||||
|
<version>${easy-trans.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-redis</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||||
|
<version>${redisson.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<!-- 使用 redisson-spring-data-27 替代,解决 Tuple NoClassDefFoundError 报错 -->
|
||||||
|
<artifactId>redisson-spring-data-34</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-data-27</artifactId>
|
||||||
|
<version>${redisson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webflux</artifactId>
|
||||||
|
<version>${spring.boot.webfulx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.dameng</groupId>
|
||||||
|
<artifactId>DmJdbcDriver18</artifactId>
|
||||||
|
<version>${dm8.jdbc.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.opengauss</groupId>
|
||||||
|
<artifactId>opengauss-jdbc</artifactId>
|
||||||
|
<version>${opengauss.jdbc.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.com.kingbase</groupId>
|
||||||
|
<artifactId>kingbase8</artifactId>
|
||||||
|
<version>${kingbase.jdbc.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.taosdata.jdbc</groupId>
|
||||||
|
<artifactId>taos-jdbcdriver</artifactId>
|
||||||
|
<version>${taos.version}</version>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Job 定时任务相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-job</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 消息队列相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-mq</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- ID 生成器 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-idgenerator</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.rocketmq</groupId>
|
||||||
|
<artifactId>rocketmq-spring-boot-starter</artifactId>
|
||||||
|
<version>${rocketmq-spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 服务保障相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-protection</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||||
|
<version>${lock4j.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 监控相关 -->
|
||||||
|
|
||||||
|
<!-- Test 测试相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-test</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-inline</artifactId>
|
||||||
|
<version>${mockito-inline.version}</version> <!-- 支持 Mockito 的 final 类与 static 方法的 mock -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>asm</artifactId>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 -->
|
||||||
|
<artifactId>jedis-mock</artifactId>
|
||||||
|
<version>${jedis-mock.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 -->
|
||||||
|
<artifactId>podam</artifactId>
|
||||||
|
<version>${podam.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 工具类相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-common</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-excel</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-jdk8</artifactId>
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-processor</artifactId>
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>${hutool.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
|
<version>${bouncycastle.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>easyexcel</artifactId>
|
||||||
|
<version>${easyexcel.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>${commons-io.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-compress</artifactId>
|
||||||
|
<version>${commons-compress.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId> <!-- 文件类型的识别 -->
|
||||||
|
<version>${tika-core.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.velocity</groupId>
|
||||||
|
<artifactId>velocity-engine-core</artifactId>
|
||||||
|
<version>${velocity.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>${fastjson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>${guava.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.inject</groupId>
|
||||||
|
<artifactId>guice</artifactId>
|
||||||
|
<version>${guice.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId> <!-- 解决 ThreadLocal 父子线程的传值问题 -->
|
||||||
|
<version>${transmittable-thread-local.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-net</groupId>
|
||||||
|
<artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 -->
|
||||||
|
<version>${commons-net.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jcraft</groupId>
|
||||||
|
<artifactId>jsch</artifactId> <!-- 解决 sftp 连接 -->
|
||||||
|
<version>${jsch.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xingyuv</groupId>
|
||||||
|
<artifactId>spring-boot-starter-captcha-plus</artifactId>
|
||||||
|
<version>${captcha-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
<version>${ip2region.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>${jsoup.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 三方云服务相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.amazonaws</groupId>
|
||||||
|
<artifactId>aws-java-sdk-s3</artifactId>
|
||||||
|
<version>${aws-java-sdk-s3.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xingyuv</groupId>
|
||||||
|
<artifactId>spring-boot-starter-justauth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) -->
|
||||||
|
<version>${justauth.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-core</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<!-- 统一 revision 版本 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>flatten-maven-plugin</artifactId>
|
||||||
|
<version>${flatten-maven-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<flattenMode>bom</flattenMode>
|
||||||
|
<updatePomFile>true</updatePomFile>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>flatten</goal>
|
||||||
|
</goals>
|
||||||
|
<id>flatten</id>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>clean</goal>
|
||||||
|
</goals>
|
||||||
|
<id>flatten.clean</id>
|
||||||
|
<phase>clean</phase>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-framework</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<description>该包是技术组件,每个子包,代表一个组件。每个组件包括两部分:
|
||||||
|
1. core 包:是该组件的核心封装
|
||||||
|
2. config 包:是该组件基于 Spring 的配置
|
||||||
|
|
||||||
|
技术组件,也分成两类:
|
||||||
|
1. 框架组件:和我们熟悉的 MyBatis、Redis 等等的拓展
|
||||||
|
2. 业务组件:和业务相关的组件的封装,例如说数据字典、操作日志等等。
|
||||||
|
如果是业务组件,Maven 名字会包含 biz</description>
|
||||||
|
<url>https://github.com/makemyownlife/platform-sms-pro</url>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>huaweicloud</id>
|
||||||
|
<name>huawei</name>
|
||||||
|
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>aliyunmaven</id>
|
||||||
|
<name>aliyun</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-common</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<name>marketing-common</name>
|
||||||
|
<description>定义基础 pojo 类、枚举、工具类等等</description>
|
||||||
|
<url>https://github.com/makemyownlife/platform-sms-pro</url>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
<version>5.3.39</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-expression</artifactId>
|
||||||
|
<version>5.3.39</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-aop</artifactId>
|
||||||
|
<version>5.3.39</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
<artifactId>aspectjweaver</artifactId>
|
||||||
|
<version>1.9.7</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<version>2.7.18</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-web</artifactId>
|
||||||
|
<version>5.3.39</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
<version>4.0.4</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-ui</artifactId>
|
||||||
|
<version>1.8.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.36</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct</artifactId>
|
||||||
|
<version>1.6.3</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-jdk8</artifactId>
|
||||||
|
<version>1.6.3</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-processor</artifactId>
|
||||||
|
<version>1.6.3</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>33.4.0-jre</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.13.5</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
<version>2.13.5</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
<version>2.13.5</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>1.7.36</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.validation</groupId>
|
||||||
|
<artifactId>jakarta.validation-api</artifactId>
|
||||||
|
<version>2.0.2</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>5.8.35</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
|
<version>2.14.5</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fhs-opensource</groupId>
|
||||||
|
<artifactId>easy-trans-anno</artifactId>
|
||||||
|
<version>3.0.6</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate.validator</groupId>
|
||||||
|
<artifactId>hibernate-validator</artifactId>
|
||||||
|
<version>8.0.1.Final</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>huaweicloud</id>
|
||||||
|
<name>huawei</name>
|
||||||
|
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>aliyunmaven</id>
|
||||||
|
<name>aliyun</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
<?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">
|
||||||
|
<parent>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-framework</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>marketing-common</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>定义基础 pojo 类、枚举、工具类等等</description>
|
||||||
|
<url>https://github.com/makemyownlife/platform-sms-pro</url>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring 核心 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-expression</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-aop</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
<artifactId>aspectjweaver</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Web 相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-web</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-ui</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 工具类相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-processor</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.validation</groupId>
|
||||||
|
<artifactId>jakarta.validation-api</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->
|
||||||
|
<artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因,方便 xxx-module-api 包使用 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate.validator</groupId>
|
||||||
|
<artifactId>hibernate-validator</artifactId>
|
||||||
|
<version>8.0.1.Final</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.platform.marketing.framework.common.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可生成 T 数组的接口
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
public interface ArrayValuable<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 数组
|
||||||
|
*/
|
||||||
|
T[] array();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.platform.marketing.framework.common.core;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key Value 的键值对
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class KeyValue<K, V> implements Serializable {
|
||||||
|
|
||||||
|
private K key;
|
||||||
|
private V value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.platform.marketing.framework.common.enums;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import com.platform.marketing.framework.common.core.ArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用状态枚举
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum CommonStatusEnum implements ArrayValuable<Integer> {
|
||||||
|
|
||||||
|
ENABLE(0, "开启"),
|
||||||
|
DISABLE(1, "关闭");
|
||||||
|
|
||||||
|
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态值
|
||||||
|
*/
|
||||||
|
private final Integer status;
|
||||||
|
/**
|
||||||
|
* 状态名
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEnable(Integer status) {
|
||||||
|
return ObjUtil.equal(ENABLE.status, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDisable(Integer status) {
|
||||||
|
return ObjUtil.equal(DISABLE.status, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.platform.marketing.framework.common.enums;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import com.platform.marketing.framework.common.core.ArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间间隔的枚举
|
||||||
|
*
|
||||||
|
* @author dhb52
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum DateIntervalEnum implements ArrayValuable<Integer> {
|
||||||
|
|
||||||
|
DAY(1, "天"),
|
||||||
|
WEEK(2, "周"),
|
||||||
|
MONTH(3, "月"),
|
||||||
|
QUARTER(4, "季度"),
|
||||||
|
YEAR(5, "年")
|
||||||
|
;
|
||||||
|
|
||||||
|
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DateIntervalEnum::getInterval).toArray(Integer[]::new);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private final Integer interval;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateIntervalEnum valueOf(Integer interval) {
|
||||||
|
return ArrayUtil.firstMatch(item -> item.getInterval().equals(interval), DateIntervalEnum.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.platform.marketing.framework.common.enums;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.core.ArrayValuable;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端的枚举
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum TerminalEnum implements ArrayValuable<Integer> {
|
||||||
|
|
||||||
|
UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
|
||||||
|
WECHAT_MINI_PROGRAM(10, "微信小程序"),
|
||||||
|
WECHAT_WAP(11, "微信公众号"),
|
||||||
|
H5(20, "H5 网页"),
|
||||||
|
APP(31, "手机 App"),
|
||||||
|
;
|
||||||
|
|
||||||
|
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端
|
||||||
|
*/
|
||||||
|
private final Integer terminal;
|
||||||
|
/**
|
||||||
|
* 终端名
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.platform.marketing.framework.common.enums;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import com.platform.marketing.framework.common.core.ArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局用户类型枚举
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum UserTypeEnum implements ArrayValuable<Integer> {
|
||||||
|
|
||||||
|
MEMBER(1, "会员"), // 面向 c 端,普通用户 ,暂时占位,后续需要使用。
|
||||||
|
|
||||||
|
ADMIN(2, "管理员"); // 面向 b 端,管理后台
|
||||||
|
|
||||||
|
public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private final Integer value;
|
||||||
|
/**
|
||||||
|
* 类型名
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public static UserTypeEnum valueOf(Integer value) {
|
||||||
|
return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.platform.marketing.framework.common.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
|
||||||
|
*
|
||||||
|
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enums 包下
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public interface WebFilterOrderEnum {
|
||||||
|
|
||||||
|
int CORS_FILTER = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
int TRACE_FILTER = CORS_FILTER + 1;
|
||||||
|
|
||||||
|
int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500;
|
||||||
|
|
||||||
|
// OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
|
||||||
|
|
||||||
|
int TENANT_CONTEXT_FILTER = - 104; // 需要保证在 ApiAccessLogFilter 前面
|
||||||
|
|
||||||
|
int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
|
||||||
|
|
||||||
|
int XSS_FILTER = -102; // 需要保证在 RequestBodyCacheFilter 后面
|
||||||
|
|
||||||
|
// Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类
|
||||||
|
|
||||||
|
int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面
|
||||||
|
|
||||||
|
int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面
|
||||||
|
|
||||||
|
int DEMO_FILTER = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.platform.marketing.framework.common.exception;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
|
import com.platform.marketing.framework.common.exception.enums.ServiceErrorCodeRange;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码对象
|
||||||
|
*
|
||||||
|
* 全局错误码,占用 [0, 999], 参见 {@link GlobalErrorCodeConstants}
|
||||||
|
* 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange}
|
||||||
|
*
|
||||||
|
* TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ErrorCode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码
|
||||||
|
*/
|
||||||
|
private final Integer code;
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
*/
|
||||||
|
private final String msg;
|
||||||
|
|
||||||
|
public ErrorCode(Integer code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.msg = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.platform.marketing.framework.common.exception;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器异常 Exception
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public final class ServerException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局错误码
|
||||||
|
*
|
||||||
|
* @see GlobalErrorCodeConstants
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空构造方法,避免反序列化问题
|
||||||
|
*/
|
||||||
|
public ServerException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerException(ErrorCode errorCode) {
|
||||||
|
this.code = errorCode.getCode();
|
||||||
|
this.message = errorCode.getMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerException(Integer code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerException setCode(Integer code) {
|
||||||
|
this.code = code;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerException setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.platform.marketing.framework.common.exception;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.exception.enums.ServiceErrorCodeRange;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务逻辑异常 Exception
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public final class ServiceException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务错误码
|
||||||
|
*
|
||||||
|
* @see ServiceErrorCodeRange
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空构造方法,避免反序列化问题
|
||||||
|
*/
|
||||||
|
public ServiceException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException(ErrorCode errorCode) {
|
||||||
|
this.code = errorCode.getCode();
|
||||||
|
this.message = errorCode.getMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException(Integer code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException setCode(Integer code) {
|
||||||
|
this.code = code;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.platform.marketing.framework.common.exception.enums;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.exception.ErrorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局错误码枚举
|
||||||
|
* 0-999 系统异常编码保留
|
||||||
|
*
|
||||||
|
* 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
|
||||||
|
* 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的
|
||||||
|
* 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public interface GlobalErrorCodeConstants {
|
||||||
|
|
||||||
|
ErrorCode SUCCESS = new ErrorCode(0, "成功");
|
||||||
|
|
||||||
|
// ========== 客户端错误段 ==========
|
||||||
|
|
||||||
|
ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确");
|
||||||
|
ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录");
|
||||||
|
ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限");
|
||||||
|
ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到");
|
||||||
|
ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
|
||||||
|
ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
|
||||||
|
ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试");
|
||||||
|
|
||||||
|
// ========== 服务端错误段 ==========
|
||||||
|
|
||||||
|
ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
|
||||||
|
ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启");
|
||||||
|
ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项");
|
||||||
|
|
||||||
|
// ========== 自定义错误段 ==========
|
||||||
|
ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
|
||||||
|
ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
|
||||||
|
|
||||||
|
ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.platform.marketing.framework.common.exception.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务异常的错误码区间,解决:解决各模块错误码定义,避免重复,在此只声明不做实际使用
|
||||||
|
*
|
||||||
|
* 一共 10 位,分成四段
|
||||||
|
*
|
||||||
|
* 第一段,1 位,类型
|
||||||
|
* 1 - 业务级别异常
|
||||||
|
* x - 预留
|
||||||
|
* 第二段,3 位,系统类型
|
||||||
|
* 001 - 用户系统
|
||||||
|
* 002 - 商品系统
|
||||||
|
* 003 - 订单系统
|
||||||
|
* 004 - 支付系统
|
||||||
|
* 005 - 优惠劵系统
|
||||||
|
* ... - ...
|
||||||
|
* 第三段,3 位,模块
|
||||||
|
* 不限制规则。
|
||||||
|
* 一般建议,每个系统里面,可能有多个模块,可以再去做分段。以用户系统为例子:
|
||||||
|
* 001 - OAuth2 模块
|
||||||
|
* 002 - User 模块
|
||||||
|
* 003 - MobileCode 模块
|
||||||
|
* 第四段,3 位,错误码
|
||||||
|
* 不限制规则。
|
||||||
|
* 一般建议,每个模块自增。
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class ServiceErrorCodeRange {
|
||||||
|
|
||||||
|
// 模块 infra 错误码区间 [1-001-000-000 ~ 1-002-000-000)
|
||||||
|
// 模块 system 错误码区间 [1-002-000-000 ~ 1-003-000-000)
|
||||||
|
// 模块 report 错误码区间 [1-003-000-000 ~ 1-004-000-000)
|
||||||
|
// 模块 member 错误码区间 [1-004-000-000 ~ 1-005-000-000)
|
||||||
|
// 模块 mp 错误码区间 [1-006-000-000 ~ 1-007-000-000)
|
||||||
|
// 模块 pay 错误码区间 [1-007-000-000 ~ 1-008-000-000)
|
||||||
|
// 模块 bpm 错误码区间 [1-009-000-000 ~ 1-010-000-000)
|
||||||
|
|
||||||
|
// 模块 product 错误码区间 [1-008-000-000 ~ 1-009-000-000)
|
||||||
|
// 模块 trade 错误码区间 [1-011-000-000 ~ 1-012-000-000)
|
||||||
|
// 模块 promotion 错误码区间 [1-013-000-000 ~ 1-014-000-000)
|
||||||
|
|
||||||
|
// 模块 crm 错误码区间 [1-020-000-000 ~ 1-021-000-000)
|
||||||
|
|
||||||
|
// 模块 oneapi 错误码区间 [1-022-000-000 ~ 1-023-000-000)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.platform.marketing.framework.common.exception.util;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.exception.ErrorCode;
|
||||||
|
import com.platform.marketing.framework.common.exception.ServiceException;
|
||||||
|
import com.platform.marketing.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ServiceException} 工具类
|
||||||
|
*
|
||||||
|
* 目的在于,格式化异常信息提示。
|
||||||
|
* 考虑到 String.format 在参数不正确时会报错,因此使用 {} 作为占位符,并使用 {@link #doFormat(int, String, Object...)} 方法来格式化
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ServiceExceptionUtil {
|
||||||
|
|
||||||
|
// ========== 和 ServiceException 的集成 ==========
|
||||||
|
|
||||||
|
public static ServiceException exception(ErrorCode errorCode) {
|
||||||
|
return exception0(errorCode.getCode(), errorCode.getMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceException exception(ErrorCode errorCode, Object... params) {
|
||||||
|
return exception0(errorCode.getCode(), errorCode.getMsg(), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceException exception0(Integer code, String messagePattern, Object... params) {
|
||||||
|
String message = doFormat(code, messagePattern, params);
|
||||||
|
return new ServiceException(code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceException invalidParamException(String messagePattern, Object... params) {
|
||||||
|
return exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), messagePattern, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 格式化方法 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将错误编号对应的消息使用 params 进行格式化。
|
||||||
|
*
|
||||||
|
* @param code 错误编号
|
||||||
|
* @param messagePattern 消息模版
|
||||||
|
* @param params 参数
|
||||||
|
* @return 格式化后的提示
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public static String doFormat(int code, String messagePattern, Object... params) {
|
||||||
|
StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
|
||||||
|
int i = 0;
|
||||||
|
int j;
|
||||||
|
int l;
|
||||||
|
for (l = 0; l < params.length; l++) {
|
||||||
|
j = messagePattern.indexOf("{}", i);
|
||||||
|
if (j == -1) {
|
||||||
|
log.error("[doFormat][参数过多:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
|
||||||
|
if (i == 0) {
|
||||||
|
return messagePattern;
|
||||||
|
} else {
|
||||||
|
sbuf.append(messagePattern.substring(i));
|
||||||
|
return sbuf.toString();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sbuf.append(messagePattern, i, j);
|
||||||
|
sbuf.append(params[l]);
|
||||||
|
i = j + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (messagePattern.indexOf("{}", i) != -1) {
|
||||||
|
log.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
|
||||||
|
}
|
||||||
|
sbuf.append(messagePattern.substring(i));
|
||||||
|
return sbuf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
package com.platform.marketing.framework.common.pojo;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import com.platform.marketing.framework.common.exception.ErrorCode;
|
||||||
|
import com.platform.marketing.framework.common.exception.ServiceException;
|
||||||
|
import com.platform.marketing.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
|
import com.platform.marketing.framework.common.exception.util.ServiceExceptionUtil;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用返回
|
||||||
|
*
|
||||||
|
* @param <T> 数据泛型
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CommonResult<T> implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码
|
||||||
|
*
|
||||||
|
* @see ErrorCode#getCode()
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
/**
|
||||||
|
* 返回数据
|
||||||
|
*/
|
||||||
|
private T data;
|
||||||
|
/**
|
||||||
|
* 错误提示,用户可阅读
|
||||||
|
*
|
||||||
|
* @see ErrorCode#getMsg() ()
|
||||||
|
*/
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将传入的 result 对象,转换成另外一个泛型结果的对象
|
||||||
|
*
|
||||||
|
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
|
||||||
|
*
|
||||||
|
* @param result 传入的 result 对象
|
||||||
|
* @param <T> 返回的泛型
|
||||||
|
* @return 新的 CommonResult 对象
|
||||||
|
*/
|
||||||
|
public static <T> CommonResult<T> error(CommonResult<?> result) {
|
||||||
|
return error(result.getCode(), result.getMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResult<T> error(Integer code, String message) {
|
||||||
|
Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), code, "code 必须是错误的!");
|
||||||
|
CommonResult<T> result = new CommonResult<>();
|
||||||
|
result.code = code;
|
||||||
|
result.msg = message;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResult<T> error(ErrorCode errorCode, Object... params) {
|
||||||
|
Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), errorCode.getCode(), "code 必须是错误的!");
|
||||||
|
CommonResult<T> result = new CommonResult<>();
|
||||||
|
result.code = errorCode.getCode();
|
||||||
|
result.msg = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMsg(), params);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResult<T> error(ErrorCode errorCode) {
|
||||||
|
return error(errorCode.getCode(), errorCode.getMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResult<T> success(T data) {
|
||||||
|
CommonResult<T> result = new CommonResult<>();
|
||||||
|
result.code = GlobalErrorCodeConstants.SUCCESS.getCode();
|
||||||
|
result.data = data;
|
||||||
|
result.msg = "";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSuccess(Integer code) {
|
||||||
|
return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore // 避免 jackson 序列化
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return isSuccess(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore // 避免 jackson 序列化
|
||||||
|
public boolean isError() {
|
||||||
|
return !isSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========= 和 Exception 异常体系集成 =========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常
|
||||||
|
*/
|
||||||
|
public void checkError() throws ServiceException {
|
||||||
|
if (isSuccess()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 业务异常
|
||||||
|
throw new ServiceException(code, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否有异常。如果有,则抛出 {@link ServiceException} 异常
|
||||||
|
* 如果没有,则返回 {@link #data} 数据
|
||||||
|
*/
|
||||||
|
@JsonIgnore // 避免 jackson 序列化
|
||||||
|
public T getCheckedData() {
|
||||||
|
checkError();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResult<T> error(ServiceException serviceException) {
|
||||||
|
return error(serviceException.getCode(), serviceException.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.platform.marketing.framework.common.pojo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
|
import javax.validation.constraints.Max;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Schema(description="分页参数")
|
||||||
|
@Data
|
||||||
|
public class PageParam implements Serializable {
|
||||||
|
|
||||||
|
private static final Integer PAGE_NO = 1;
|
||||||
|
private static final Integer PAGE_SIZE = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每页条数 - 不分页
|
||||||
|
*
|
||||||
|
* 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。
|
||||||
|
*/
|
||||||
|
public static final Integer PAGE_SIZE_NONE = -1;
|
||||||
|
|
||||||
|
@Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1")
|
||||||
|
@NotNull(message = "页码不能为空")
|
||||||
|
@Min(value = 1, message = "页码最小值为 1")
|
||||||
|
private Integer pageNo = PAGE_NO;
|
||||||
|
|
||||||
|
@Schema(description = "每页条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||||
|
@NotNull(message = "每页条数不能为空")
|
||||||
|
@Min(value = 1, message = "每页条数最小值为 1")
|
||||||
|
@Max(value = 100, message = "每页条数最大值为 100")
|
||||||
|
private Integer pageSize = PAGE_SIZE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.platform.marketing.framework.common.pojo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Schema(description = "分页结果")
|
||||||
|
@Data
|
||||||
|
public final class PageResult<T> implements Serializable {
|
||||||
|
|
||||||
|
@Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private List<T> list;
|
||||||
|
|
||||||
|
@Schema(description = "总量", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private Long total;
|
||||||
|
|
||||||
|
public PageResult() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageResult(List<T> list, Long total) {
|
||||||
|
this.list = list;
|
||||||
|
this.total = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageResult(Long total) {
|
||||||
|
this.list = new ArrayList<>();
|
||||||
|
this.total = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> PageResult<T> empty() {
|
||||||
|
return new PageResult<>(0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> PageResult<T> empty(Long total) {
|
||||||
|
return new PageResult<>(total);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.platform.marketing.framework.common.pojo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Schema(description = "可排序的分页参数")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class SortablePageParam extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "排序字段")
|
||||||
|
private List<SortingField> sortingFields;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.platform.marketing.framework.common.pojo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序字段 DTO
|
||||||
|
*
|
||||||
|
* 类名加了 ing 的原因是,避免和 ES SortField 重名。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SortingField implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 顺序 - 升序
|
||||||
|
*/
|
||||||
|
public static final String ORDER_ASC = "asc";
|
||||||
|
/**
|
||||||
|
* 顺序 - 降序
|
||||||
|
*/
|
||||||
|
public static final String ORDER_DESC = "desc";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段
|
||||||
|
*/
|
||||||
|
private String field;
|
||||||
|
/**
|
||||||
|
* 顺序
|
||||||
|
*/
|
||||||
|
private String order;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.platform.marketing.framework.common.util.cache;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class CacheUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建异步刷新的 LoadingCache 对象
|
||||||
|
*
|
||||||
|
* 注意:如果你的缓存和 ThreadLocal 有关系,要么自己处理 ThreadLocal 的传递,要么使用 {@link #buildCache(Duration, CacheLoader)} 方法
|
||||||
|
*
|
||||||
|
* 或者简单理解:
|
||||||
|
* 1、和“人”相关的,使用 {@link #buildCache(Duration, CacheLoader)} 方法
|
||||||
|
* 2、和“全局”、“系统”相关的,使用当前缓存方法
|
||||||
|
*
|
||||||
|
* @param duration 过期时间
|
||||||
|
* @param loader CacheLoader 对象
|
||||||
|
* @return LoadingCache 对象
|
||||||
|
*/
|
||||||
|
public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {
|
||||||
|
return CacheBuilder.newBuilder()
|
||||||
|
// 只阻塞当前数据加载线程,其他线程返回旧值
|
||||||
|
.refreshAfterWrite(duration)
|
||||||
|
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
|
||||||
|
.build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建同步刷新的 LoadingCache 对象
|
||||||
|
*
|
||||||
|
* @param duration 过期时间
|
||||||
|
* @param loader CacheLoader 对象
|
||||||
|
* @return LoadingCache 对象
|
||||||
|
*/
|
||||||
|
public static <K, V> LoadingCache<K, V> buildCache(Duration duration, CacheLoader<K, V> loader) {
|
||||||
|
return CacheBuilder.newBuilder().refreshAfterWrite(duration).build(loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.platform.marketing.framework.common.util.collection;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.collection.IterUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static com.platform.marketing.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class ArrayUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 object 和 newElements 合并成一个数组
|
||||||
|
*
|
||||||
|
* @param object 对象
|
||||||
|
* @param newElements 数组
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 结果数组
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) {
|
||||||
|
if (object == null) {
|
||||||
|
return newElements;
|
||||||
|
}
|
||||||
|
Consumer<T>[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length);
|
||||||
|
result[0] = object;
|
||||||
|
System.arraycopy(newElements, 0, result, 1, newElements.length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, V> V[] toArray(Collection<T> from, Function<T, V> mapper) {
|
||||||
|
return toArray(convertList(from, mapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T[] toArray(Collection<T> from) {
|
||||||
|
if (CollectionUtil.isEmpty(from)) {
|
||||||
|
return (T[]) (new Object[0]);
|
||||||
|
}
|
||||||
|
return ArrayUtil.toArray(from, (Class<T>) IterUtil.getElementType(from.iterator()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T get(T[] array, int index) {
|
||||||
|
if (null == array || index >= array.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return array[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,338 @@
|
||||||
|
package com.platform.marketing.framework.common.util.collection;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import com.platform.marketing.framework.common.pojo.PageResult;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class CollectionUtils {
|
||||||
|
|
||||||
|
public static boolean containsAny(Object source, Object... targets) {
|
||||||
|
return asList(targets).contains(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAnyEmpty(Collection<?>... collections) {
|
||||||
|
return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> boolean anyMatch(Collection<T> from, Predicate<T> predicate) {
|
||||||
|
return from.stream().anyMatch(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return from.stream().filter(predicate).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return distinct(from, keyMapper, (t1, t2) -> t1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, R> List<T> distinct(Collection<T> from, Function<T, R> keyMapper, BinaryOperator<T> cover) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U> List<U> convertList(T[] from, Function<T, U> func) {
|
||||||
|
if (ArrayUtil.isEmpty(from)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return convertList(Arrays.asList(from), func);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U> PageResult<U> convertPage(PageResult<T> from, Function<T, U> func) {
|
||||||
|
if (ArrayUtil.isEmpty(from)) {
|
||||||
|
return new PageResult<>(from.getTotal());
|
||||||
|
}
|
||||||
|
return new PageResult<>(convertList(from.getList(), func), from.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U> List<U> convertListByFlatMap(Collection<T> from,
|
||||||
|
Function<T, ? extends Stream<? extends U>> func) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from,
|
||||||
|
Function<? super T, ? extends U> mapper,
|
||||||
|
Function<U, ? extends Stream<? extends R>> func) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {
|
||||||
|
return map.values()
|
||||||
|
.stream()
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Set<T> convertSet(Collection<T> from) {
|
||||||
|
return convertSet(from, v -> v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K> Map<K, T> convertMapByFilter(Collection<T> from, Predicate<T> filter, Function<T, K> keyFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from,
|
||||||
|
Function<T, ? extends Stream<? extends U>> func) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
return from.stream().filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from,
|
||||||
|
Function<? super T, ? extends U> mapper,
|
||||||
|
Function<U, ? extends Stream<? extends R>> func) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
return from.stream().map(mapper).filter(Objects::nonNull).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return convertMap(from, keyFunc, Function.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc, Supplier<? extends Map<K, T>> supplier) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return supplier.get();
|
||||||
|
}
|
||||||
|
return convertMap(from, keyFunc, Function.identity(), supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, Supplier<? extends Map<K, V>> supplier) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return supplier.get();
|
||||||
|
}
|
||||||
|
return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K, V> Map<K, V> convertMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc, BinaryOperator<V> mergeFunction, Supplier<? extends Map<K, V>> supplier) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K> Map<K, List<T>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K, V> Map<K, List<V>> convertMultiMap(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return from.stream()
|
||||||
|
.collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暂时没想好名字,先以 2 结尾噶
|
||||||
|
public static <T, K, V> Map<K, Set<V>> convertMultiMap2(Collection<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K> Map<K, T> convertImmutableMap(Collection<T> from, Function<T, K> keyFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
ImmutableMap.Builder<K, T> builder = ImmutableMap.builder();
|
||||||
|
from.forEach(item -> builder.put(keyFunc.apply(item), item));
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对比老、新两个列表,找出新增、修改、删除的数据
|
||||||
|
*
|
||||||
|
* @param oldList 老列表
|
||||||
|
* @param newList 新列表
|
||||||
|
* @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同
|
||||||
|
* 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据
|
||||||
|
* @return [新增列表、修改列表、删除列表]
|
||||||
|
*/
|
||||||
|
public static <T> List<List<T>> diffList(Collection<T> oldList, Collection<T> newList,
|
||||||
|
BiFunction<T, T, Boolean> sameFunc) {
|
||||||
|
List<T> createList = new LinkedList<>(newList); // 默认都认为是新增的,后续会进行移除
|
||||||
|
List<T> updateList = new ArrayList<>();
|
||||||
|
List<T> deleteList = new ArrayList<>();
|
||||||
|
|
||||||
|
// 通过以 oldList 为主遍历,找出 updateList 和 deleteList
|
||||||
|
for (T oldObj : oldList) {
|
||||||
|
// 1. 寻找是否有匹配的
|
||||||
|
T foundObj = null;
|
||||||
|
for (Iterator<T> iterator = createList.iterator(); iterator.hasNext(); ) {
|
||||||
|
T newObj = iterator.next();
|
||||||
|
// 1.1 不匹配,则直接跳过
|
||||||
|
if (!sameFunc.apply(oldObj, newObj)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 1.2 匹配,则移除,并结束寻找
|
||||||
|
iterator.remove();
|
||||||
|
foundObj = newObj;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 2. 匹配添加到 updateList;不匹配则添加到 deleteList 中
|
||||||
|
if (foundObj != null) {
|
||||||
|
updateList.add(foundObj);
|
||||||
|
} else {
|
||||||
|
deleteList.add(oldObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return asList(createList, updateList, deleteList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
|
||||||
|
return org.springframework.util.CollectionUtils.containsAny(source, candidates);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getFirst(List<T> from) {
|
||||||
|
return !CollectionUtil.isEmpty(from) ? from.get(0) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T findFirst(Collection<T> from, Predicate<T> predicate) {
|
||||||
|
return findFirst(from, predicate, Function.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, U> U findFirst(Collection<T> from, Predicate<T> predicate, Function<T, U> func) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return from.stream().filter(predicate).findFirst().map(func).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
assert !from.isEmpty(); // 断言,避免告警
|
||||||
|
T t = from.stream().max(Comparator.comparing(valueFunc)).get();
|
||||||
|
return valueFunc.apply(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, V extends Comparable<? super V>> V getMinValue(List<T> from, Function<T, V> valueFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
assert from.size() > 0; // 断言,避免告警
|
||||||
|
T t = from.stream().min(Comparator.comparing(valueFunc)).get();
|
||||||
|
return valueFunc.apply(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, V extends Comparable<? super V>> T getMinObject(List<T> from, Function<T, V> valueFunc) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
assert from.size() > 0; // 断言,避免告警
|
||||||
|
return from.stream().min(Comparator.comparing(valueFunc)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc,
|
||||||
|
BinaryOperator<V> accumulator) {
|
||||||
|
return getSumValue(from, valueFunc, accumulator, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, V extends Comparable<? super V>> V getSumValue(Collection<T> from, Function<T, V> valueFunc,
|
||||||
|
BinaryOperator<V> accumulator, V defaultValue) {
|
||||||
|
if (CollUtil.isEmpty(from)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
assert !from.isEmpty(); // 断言,避免告警
|
||||||
|
return from.stream().map(valueFunc).filter(Objects::nonNull).reduce(accumulator).orElse(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void addIfNotNull(Collection<T> coll, T item) {
|
||||||
|
if (item == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
coll.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Collection<T> singleton(T obj) {
|
||||||
|
return obj == null ? Collections.emptyList() : Collections.singleton(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> newArrayList(List<List<T>> list) {
|
||||||
|
return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.platform.marketing.framework.common.util.collection;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import com.platform.marketing.framework.common.core.KeyValue;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class MapUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从哈希表表中,获得 keys 对应的所有 value 数组
|
||||||
|
*
|
||||||
|
* @param multimap 哈希表
|
||||||
|
* @param keys keys
|
||||||
|
* @return value 数组
|
||||||
|
*/
|
||||||
|
public static <K, V> List<V> getList(Multimap<K, V> multimap, Collection<K> keys) {
|
||||||
|
List<V> result = new ArrayList<>();
|
||||||
|
keys.forEach(k -> {
|
||||||
|
Collection<V> values = multimap.get(k);
|
||||||
|
if (CollectionUtil.isEmpty(values)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result.addAll(values);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从哈希表查找到 key 对应的 value,然后进一步处理
|
||||||
|
* key 为 null 时, 不处理
|
||||||
|
* 注意,如果查找到的 value 为 null 时,不进行处理
|
||||||
|
*
|
||||||
|
* @param map 哈希表
|
||||||
|
* @param key key
|
||||||
|
* @param consumer 进一步处理的逻辑
|
||||||
|
*/
|
||||||
|
public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
|
||||||
|
if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
V value = map.get(key);
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
consumer.accept(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) {
|
||||||
|
Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size());
|
||||||
|
keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue()));
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.platform.marketing.framework.common.util.collection;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class SetUtils {
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <T> Set<T> asSet(T... objs) {
|
||||||
|
return CollUtil.newHashSet(objs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
package com.platform.marketing.framework.common.util.date;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
|
||||||
|
import java.time.*;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class DateUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时区 - 默认
|
||||||
|
*/
|
||||||
|
public static final String TIME_ZONE_DEFAULT = "GMT+8";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 秒转换成毫秒
|
||||||
|
*/
|
||||||
|
public static final long SECOND_MILLIS = 1000;
|
||||||
|
|
||||||
|
public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";
|
||||||
|
|
||||||
|
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 LocalDateTime 转换成 Date
|
||||||
|
*
|
||||||
|
* @param date LocalDateTime
|
||||||
|
* @return LocalDateTime
|
||||||
|
*/
|
||||||
|
public static Date of(LocalDateTime date) {
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 将此日期时间与时区相结合以创建 ZonedDateTime
|
||||||
|
ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
|
||||||
|
// 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
|
||||||
|
Instant instant = zonedDateTime.toInstant();
|
||||||
|
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
|
||||||
|
return Date.from(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Date 转换成 LocalDateTime
|
||||||
|
*
|
||||||
|
* @param date Date
|
||||||
|
* @return LocalDateTime
|
||||||
|
*/
|
||||||
|
public static LocalDateTime of(Date date) {
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 转为时间戳
|
||||||
|
Instant instant = date.toInstant();
|
||||||
|
// UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
|
||||||
|
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date addTime(Duration duration) {
|
||||||
|
return new Date(System.currentTimeMillis() + duration.toMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isExpired(LocalDateTime time) {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
return now.isAfter(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建指定时间
|
||||||
|
*
|
||||||
|
* @param year 年
|
||||||
|
* @param mouth 月
|
||||||
|
* @param day 日
|
||||||
|
* @return 指定时间
|
||||||
|
*/
|
||||||
|
public static Date buildTime(int year, int mouth, int day) {
|
||||||
|
return buildTime(year, mouth, day, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建指定时间
|
||||||
|
*
|
||||||
|
* @param year 年
|
||||||
|
* @param mouth 月
|
||||||
|
* @param day 日
|
||||||
|
* @param hour 小时
|
||||||
|
* @param minute 分钟
|
||||||
|
* @param second 秒
|
||||||
|
* @return 指定时间
|
||||||
|
*/
|
||||||
|
public static Date buildTime(int year, int mouth, int day,
|
||||||
|
int hour, int minute, int second) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.set(Calendar.YEAR, year);
|
||||||
|
calendar.set(Calendar.MONTH, mouth - 1);
|
||||||
|
calendar.set(Calendar.DAY_OF_MONTH, day);
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||||
|
calendar.set(Calendar.MINUTE, minute);
|
||||||
|
calendar.set(Calendar.SECOND, second);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒
|
||||||
|
return calendar.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date max(Date a, Date b) {
|
||||||
|
if (a == null) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (b == null) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return a.compareTo(b) > 0 ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {
|
||||||
|
if (a == null) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (b == null) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return a.isAfter(b) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否今天
|
||||||
|
*
|
||||||
|
* @param date 日期
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
public static boolean isToday(LocalDateTime date) {
|
||||||
|
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否昨天
|
||||||
|
*
|
||||||
|
* @param date 日期
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
public static boolean isYesterday(LocalDateTime date) {
|
||||||
|
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,309 @@
|
||||||
|
package com.platform.marketing.framework.common.util.date;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.platform.marketing.framework.common.enums.DateIntervalEnum;
|
||||||
|
|
||||||
|
import java.time.*;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.time.temporal.TemporalAdjusters;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间工具类,用于 {@link java.time.LocalDateTime}
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class LocalDateTimeUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空的 LocalDateTime 对象,主要用于 DB 唯一索引的默认值
|
||||||
|
*/
|
||||||
|
public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析时间
|
||||||
|
*
|
||||||
|
* 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说,会尽量去解析,直到成功
|
||||||
|
*
|
||||||
|
* @param time 时间
|
||||||
|
* @return 时间字符串
|
||||||
|
*/
|
||||||
|
public static LocalDateTime parse(String time) {
|
||||||
|
try {
|
||||||
|
return LocalDateTimeUtil.parse(time, DatePattern.NORM_DATE_PATTERN);
|
||||||
|
} catch (DateTimeParseException e) {
|
||||||
|
return LocalDateTimeUtil.parse(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime addTime(Duration duration) {
|
||||||
|
return LocalDateTime.now().plus(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime minusTime(Duration duration) {
|
||||||
|
return LocalDateTime.now().minus(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean beforeNow(LocalDateTime date) {
|
||||||
|
return date.isBefore(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean afterNow(LocalDateTime date) {
|
||||||
|
return date.isAfter(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建指定时间
|
||||||
|
*
|
||||||
|
* @param year 年
|
||||||
|
* @param mouth 月
|
||||||
|
* @param day 日
|
||||||
|
* @return 指定时间
|
||||||
|
*/
|
||||||
|
public static LocalDateTime buildTime(int year, int mouth, int day) {
|
||||||
|
return LocalDateTime.of(year, mouth, day, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
|
||||||
|
int year2, int mouth2, int day2) {
|
||||||
|
return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判指定断时间,是否在该时间范围内
|
||||||
|
*
|
||||||
|
* @param startTime 开始时间
|
||||||
|
* @param endTime 结束时间
|
||||||
|
* @param time 指定时间
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) {
|
||||||
|
if (startTime == null || endTime == null || time == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return LocalDateTimeUtil.isIn(parse(time), startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前时间是否在该时间范围内
|
||||||
|
*
|
||||||
|
* @param startTime 开始时间
|
||||||
|
* @param endTime 结束时间
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
|
if (startTime == null || endTime == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前时间是否在该时间范围内
|
||||||
|
*
|
||||||
|
* @param startTime 开始时间
|
||||||
|
* @param endTime 结束时间
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
public static boolean isBetween(String startTime, String endTime) {
|
||||||
|
if (startTime == null || endTime == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LocalDate nowDate = LocalDate.now();
|
||||||
|
return LocalDateTimeUtil.isIn(LocalDateTime.now(),
|
||||||
|
LocalDateTime.of(nowDate, LocalTime.parse(startTime)),
|
||||||
|
LocalDateTime.of(nowDate, LocalTime.parse(endTime)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断时间段是否重叠
|
||||||
|
*
|
||||||
|
* @param startTime1 开始 time1
|
||||||
|
* @param endTime1 结束 time1
|
||||||
|
* @param startTime2 开始 time2
|
||||||
|
* @param endTime2 结束 time2
|
||||||
|
* @return 重叠:true 不重叠:false
|
||||||
|
*/
|
||||||
|
public static boolean isOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
|
||||||
|
LocalDate nowDate = LocalDate.now();
|
||||||
|
return LocalDateTimeUtil.isOverlap(LocalDateTime.of(nowDate, startTime1), LocalDateTime.of(nowDate, endTime1),
|
||||||
|
LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定日期所在的月份的开始时间
|
||||||
|
* 例如:2023-09-30 00:00:00,000
|
||||||
|
*
|
||||||
|
* @param date 日期
|
||||||
|
* @return 月份的开始时间
|
||||||
|
*/
|
||||||
|
public static LocalDateTime beginOfMonth(LocalDateTime date) {
|
||||||
|
return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定日期所在的月份的最后时间
|
||||||
|
* 例如:2023-09-30 23:59:59,999
|
||||||
|
*
|
||||||
|
* @param date 日期
|
||||||
|
* @return 月份的结束时间
|
||||||
|
*/
|
||||||
|
public static LocalDateTime endOfMonth(LocalDateTime date) {
|
||||||
|
return date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定日期所在季度
|
||||||
|
*
|
||||||
|
* @param date 日期
|
||||||
|
* @return 所在季度
|
||||||
|
*/
|
||||||
|
public static int getQuarterOfYear(LocalDateTime date) {
|
||||||
|
return (date.getMonthValue() - 1) / 3 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定日期到现在过了几天,如果指定日期在当前日期之后,获取结果为负
|
||||||
|
*
|
||||||
|
* @param dateTime 日期
|
||||||
|
* @return 相差天数
|
||||||
|
*/
|
||||||
|
public static Long between(LocalDateTime dateTime) {
|
||||||
|
return LocalDateTimeUtil.between(dateTime, LocalDateTime.now(), ChronoUnit.DAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取今天的开始时间
|
||||||
|
*
|
||||||
|
* @return 今天
|
||||||
|
*/
|
||||||
|
public static LocalDateTime getToday() {
|
||||||
|
return LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取昨天的开始时间
|
||||||
|
*
|
||||||
|
* @return 昨天
|
||||||
|
*/
|
||||||
|
public static LocalDateTime getYesterday() {
|
||||||
|
return LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本月的开始时间
|
||||||
|
*
|
||||||
|
* @return 本月
|
||||||
|
*/
|
||||||
|
public static LocalDateTime getMonth() {
|
||||||
|
return beginOfMonth(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本年的开始时间
|
||||||
|
*
|
||||||
|
* @return 本年
|
||||||
|
*/
|
||||||
|
public static LocalDateTime getYear() {
|
||||||
|
return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<LocalDateTime[]> getDateRangeList(LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime,
|
||||||
|
Integer interval) {
|
||||||
|
// 1.1 找到枚举
|
||||||
|
DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
|
||||||
|
Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
|
||||||
|
// 1.2 将时间对齐
|
||||||
|
startTime = LocalDateTimeUtil.beginOfDay(startTime);
|
||||||
|
endTime = LocalDateTimeUtil.endOfDay(endTime);
|
||||||
|
|
||||||
|
// 2. 循环,生成时间范围
|
||||||
|
List<LocalDateTime[]> timeRanges = new ArrayList<>();
|
||||||
|
switch (intervalEnum) {
|
||||||
|
case DAY:
|
||||||
|
while (startTime.isBefore(endTime)) {
|
||||||
|
timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
|
||||||
|
startTime = startTime.plusDays(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WEEK:
|
||||||
|
while (startTime.isBefore(endTime)) {
|
||||||
|
LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1);
|
||||||
|
timeRanges.add(new LocalDateTime[]{startTime, endOfWeek});
|
||||||
|
startTime = endOfWeek.plusNanos(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MONTH:
|
||||||
|
while (startTime.isBefore(endTime)) {
|
||||||
|
LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1);
|
||||||
|
timeRanges.add(new LocalDateTime[]{startTime, endOfMonth});
|
||||||
|
startTime = endOfMonth.plusNanos(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QUARTER:
|
||||||
|
while (startTime.isBefore(endTime)) {
|
||||||
|
int quarterOfYear = getQuarterOfYear(startTime);
|
||||||
|
LocalDateTime quarterEnd = quarterOfYear == 4
|
||||||
|
? startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1)
|
||||||
|
: startTime.withMonth(quarterOfYear * 3 + 1).withDayOfMonth(1).minusNanos(1);
|
||||||
|
timeRanges.add(new LocalDateTime[]{startTime, quarterEnd});
|
||||||
|
startTime = quarterEnd.plusNanos(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case YEAR:
|
||||||
|
while (startTime.isBefore(endTime)) {
|
||||||
|
LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1);
|
||||||
|
timeRanges.add(new LocalDateTime[]{startTime, endOfYear});
|
||||||
|
startTime = endOfYear.plusNanos(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid interval: " + interval);
|
||||||
|
}
|
||||||
|
// 3. 兜底,最后一个时间,需要保持在 endTime 之前
|
||||||
|
LocalDateTime[] lastTimeRange = CollUtil.getLast(timeRanges);
|
||||||
|
if (lastTimeRange != null) {
|
||||||
|
lastTimeRange[1] = endTime;
|
||||||
|
}
|
||||||
|
return timeRanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化时间范围
|
||||||
|
*
|
||||||
|
* @param startTime 开始时间
|
||||||
|
* @param endTime 结束时间
|
||||||
|
* @param interval 时间间隔
|
||||||
|
* @return 时间范围
|
||||||
|
*/
|
||||||
|
public static String formatDateRange(LocalDateTime startTime, LocalDateTime endTime, Integer interval) {
|
||||||
|
// 1. 找到枚举
|
||||||
|
DateIntervalEnum intervalEnum = DateIntervalEnum.valueOf(interval);
|
||||||
|
Assert.notNull(intervalEnum, "interval({}} 找不到对应的枚举", interval);
|
||||||
|
|
||||||
|
// 2. 循环,生成时间范围
|
||||||
|
switch (intervalEnum) {
|
||||||
|
case DAY:
|
||||||
|
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
|
||||||
|
case WEEK:
|
||||||
|
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN)
|
||||||
|
+ StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime));
|
||||||
|
case MONTH:
|
||||||
|
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN);
|
||||||
|
case QUARTER:
|
||||||
|
return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime));
|
||||||
|
case YEAR:
|
||||||
|
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid interval: " + interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
package com.platform.marketing.framework.common.util.http;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.core.map.TableMap;
|
||||||
|
import cn.hutool.core.net.url.UrlBuilder;
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.http.HttpRequest;
|
||||||
|
import cn.hutool.http.HttpResponse;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.util.UriComponents;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class HttpUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码 URL 参数
|
||||||
|
*
|
||||||
|
* @param value 参数
|
||||||
|
* @return 编码后的参数
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static String encodeUtf8(String value) {
|
||||||
|
return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static String replaceUrlQuery(String url, String key, String value) {
|
||||||
|
UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
|
||||||
|
// 先移除
|
||||||
|
TableMap<CharSequence, CharSequence> query = (TableMap<CharSequence, CharSequence>)
|
||||||
|
ReflectUtil.getFieldValue(builder.getQuery(), "query");
|
||||||
|
query.remove(key);
|
||||||
|
// 后添加
|
||||||
|
builder.addQuery(key, value);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String append(String base, Map<String, ?> query, boolean fragment) {
|
||||||
|
return append(base, query, null, fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接 URL
|
||||||
|
*
|
||||||
|
* copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法
|
||||||
|
*
|
||||||
|
* @param base 基础 URL
|
||||||
|
* @param query 查询参数
|
||||||
|
* @param keys query 的 key,对应的原本的 key 的映射。例如说 query 里有个 key 是 xx,实际它的 key 是 extra_xx,则通过 keys 里添加这个映射
|
||||||
|
* @param fragment URL 的 fragment,即拼接到 # 中
|
||||||
|
* @return 拼接后的 URL
|
||||||
|
*/
|
||||||
|
public static String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) {
|
||||||
|
UriComponentsBuilder template = UriComponentsBuilder.newInstance();
|
||||||
|
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base);
|
||||||
|
URI redirectUri;
|
||||||
|
try {
|
||||||
|
// assume it's encoded to start with (if it came in over the wire)
|
||||||
|
redirectUri = builder.build(true).toUri();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ... but allow client registrations to contain hard-coded non-encoded values
|
||||||
|
redirectUri = builder.build().toUri();
|
||||||
|
builder = UriComponentsBuilder.fromUri(redirectUri);
|
||||||
|
}
|
||||||
|
template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost())
|
||||||
|
.userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath());
|
||||||
|
|
||||||
|
if (fragment) {
|
||||||
|
StringBuilder values = new StringBuilder();
|
||||||
|
if (redirectUri.getFragment() != null) {
|
||||||
|
String append = redirectUri.getFragment();
|
||||||
|
values.append(append);
|
||||||
|
}
|
||||||
|
for (String key : query.keySet()) {
|
||||||
|
if (values.length() > 0) {
|
||||||
|
values.append("&");
|
||||||
|
}
|
||||||
|
String name = key;
|
||||||
|
if (keys != null && keys.containsKey(key)) {
|
||||||
|
name = keys.get(key);
|
||||||
|
}
|
||||||
|
values.append(name).append("={").append(key).append("}");
|
||||||
|
}
|
||||||
|
if (values.length() > 0) {
|
||||||
|
template.fragment(values.toString());
|
||||||
|
}
|
||||||
|
UriComponents encoded = template.build().expand(query).encode();
|
||||||
|
builder.fragment(encoded.getFragment());
|
||||||
|
} else {
|
||||||
|
for (String key : query.keySet()) {
|
||||||
|
String name = key;
|
||||||
|
if (keys != null && keys.containsKey(key)) {
|
||||||
|
name = keys.get(key);
|
||||||
|
}
|
||||||
|
template.queryParam(name, "{" + key + "}");
|
||||||
|
}
|
||||||
|
template.fragment(redirectUri.getFragment());
|
||||||
|
UriComponents encoded = template.build().expand(query).encode();
|
||||||
|
builder.query(encoded.getQuery());
|
||||||
|
}
|
||||||
|
return builder.build().toUriString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] obtainBasicAuthorization(HttpServletRequest request) {
|
||||||
|
String clientId;
|
||||||
|
String clientSecret;
|
||||||
|
// 先从 Header 中获取
|
||||||
|
String authorization = request.getHeader("Authorization");
|
||||||
|
authorization = StrUtil.subAfter(authorization, "Basic ", true);
|
||||||
|
if (StringUtils.hasText(authorization)) {
|
||||||
|
authorization = Base64.decodeStr(authorization);
|
||||||
|
clientId = StrUtil.subBefore(authorization, ":", false);
|
||||||
|
clientSecret = StrUtil.subAfter(authorization, ":", false);
|
||||||
|
// 再从 Param 中获取
|
||||||
|
} else {
|
||||||
|
clientId = request.getParameter("client_id");
|
||||||
|
clientSecret = request.getParameter("client_secret");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果两者非空,则返回
|
||||||
|
if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) {
|
||||||
|
return new String[]{clientId, clientSecret};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP post 请求,基于 {@link cn.hutool.http.HttpUtil} 实现
|
||||||
|
*
|
||||||
|
* 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数
|
||||||
|
*
|
||||||
|
* @param url URL
|
||||||
|
* @param headers 请求头
|
||||||
|
* @param requestBody 请求体
|
||||||
|
* @return 请求结果
|
||||||
|
*/
|
||||||
|
public static String post(String url, Map<String, String> headers, String requestBody) {
|
||||||
|
try (HttpResponse response = HttpRequest.post(url)
|
||||||
|
.addHeaders(headers)
|
||||||
|
.body(requestBody)
|
||||||
|
.execute()) {
|
||||||
|
return response.body();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP get 请求,基于 {@link cn.hutool.http.HttpUtil} 实现
|
||||||
|
*
|
||||||
|
* 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数
|
||||||
|
*
|
||||||
|
* @param url URL
|
||||||
|
* @param headers 请求头
|
||||||
|
* @return 请求结果
|
||||||
|
*/
|
||||||
|
public static String get(String url, Map<String, String> headers) {
|
||||||
|
try (HttpResponse response = HttpRequest.get(url)
|
||||||
|
.addHeaders(headers)
|
||||||
|
.execute()) {
|
||||||
|
return response.body();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.platform.marketing.framework.common.util.io;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileTypeUtil;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.io.file.FileNameUtil;
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.digest.DigestUtil;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建临时文件
|
||||||
|
* 该文件会在 JVM 退出时,进行删除
|
||||||
|
*
|
||||||
|
* @param data 文件内容
|
||||||
|
* @return 文件
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static File createTempFile(String data) {
|
||||||
|
File file = createTempFile();
|
||||||
|
// 写入内容
|
||||||
|
FileUtil.writeUtf8String(data, file);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建临时文件
|
||||||
|
* 该文件会在 JVM 退出时,进行删除
|
||||||
|
*
|
||||||
|
* @param data 文件内容
|
||||||
|
* @return 文件
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static File createTempFile(byte[] data) {
|
||||||
|
File file = createTempFile();
|
||||||
|
// 写入内容
|
||||||
|
FileUtil.writeBytes(data, file);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建临时文件,无内容
|
||||||
|
* 该文件会在 JVM 退出时,进行删除
|
||||||
|
*
|
||||||
|
* @return 文件
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static File createTempFile() {
|
||||||
|
// 创建文件,通过 UUID 保证唯一
|
||||||
|
File file = File.createTempFile(IdUtil.simpleUUID(), null);
|
||||||
|
// 标记 JVM 退出时,自动删除
|
||||||
|
file.deleteOnExit();
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成文件路径
|
||||||
|
*
|
||||||
|
* @param content 文件内容
|
||||||
|
* @param originalName 原始文件名
|
||||||
|
* @return path,唯一不可重复
|
||||||
|
*/
|
||||||
|
public static String generatePath(byte[] content, String originalName) {
|
||||||
|
String sha256Hex = DigestUtil.sha256Hex(content);
|
||||||
|
// 情况一:如果存在 name,则优先使用 name 的后缀
|
||||||
|
if (StrUtil.isNotBlank(originalName)) {
|
||||||
|
String extName = FileNameUtil.extName(originalName);
|
||||||
|
return StrUtil.isBlank(extName) ? sha256Hex : sha256Hex + "." + extName;
|
||||||
|
}
|
||||||
|
// 情况二:基于 content 计算
|
||||||
|
return sha256Hex + '.' + FileTypeUtil.getType(new ByteArrayInputStream(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.platform.marketing.framework.common.util.io;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class IoUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从流中读取 UTF8 编码的内容
|
||||||
|
*
|
||||||
|
* @param in 输入流
|
||||||
|
* @param isClose 是否关闭
|
||||||
|
* @return 内容
|
||||||
|
* @throws IORuntimeException IO 异常
|
||||||
|
*/
|
||||||
|
public static String readUtf8(InputStream in, boolean isClose) throws IORuntimeException {
|
||||||
|
return StrUtil.utf8Str(IoUtil.read(in, isClose));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
package com.platform.marketing.framework.common.util.json;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class JsonUtils {
|
||||||
|
|
||||||
|
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
static {
|
||||||
|
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||||
|
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
|
||||||
|
objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化 objectMapper 属性
|
||||||
|
* <p>
|
||||||
|
* 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean
|
||||||
|
*
|
||||||
|
* @param objectMapper ObjectMapper 对象
|
||||||
|
*/
|
||||||
|
public static void init(ObjectMapper objectMapper) {
|
||||||
|
JsonUtils.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static String toJsonString(Object object) {
|
||||||
|
return objectMapper.writeValueAsString(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static byte[] toJsonByte(Object object) {
|
||||||
|
return objectMapper.writeValueAsBytes(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static String toJsonPrettyString(Object object) {
|
||||||
|
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(String text, Class<T> clazz) {
|
||||||
|
if (StrUtil.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(text, clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("json parse err,json:{}", text, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(String text, String path, Class<T> clazz) {
|
||||||
|
if (StrUtil.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
JsonNode treeNode = objectMapper.readTree(text);
|
||||||
|
JsonNode pathNode = treeNode.path(path);
|
||||||
|
return objectMapper.readValue(pathNode.toString(), clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("json parse err,json:{}", text, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(String text, Type type) {
|
||||||
|
if (StrUtil.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructType(type));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("json parse err,json:{}", text, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字符串解析成指定类型的对象
|
||||||
|
* 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下,
|
||||||
|
* 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。
|
||||||
|
*
|
||||||
|
* @param text 字符串
|
||||||
|
* @param clazz 类型
|
||||||
|
* @return 对象
|
||||||
|
*/
|
||||||
|
public static <T> T parseObject2(String text, Class<T> clazz) {
|
||||||
|
if (StrUtil.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return JSONUtil.toBean(text, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
|
||||||
|
if (ArrayUtil.isEmpty(bytes)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(bytes, clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("json parse err,json:{}", bytes, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(text, typeReference);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("json parse err,json:{}", text, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null
|
||||||
|
*
|
||||||
|
* @param text 字符串
|
||||||
|
* @param typeReference 类型引用
|
||||||
|
* @return 指定类型的对象
|
||||||
|
*/
|
||||||
|
public static <T> T parseObjectQuietly(String text, TypeReference<T> typeReference) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(text, typeReference);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> parseArray(String text, Class<T> clazz) {
|
||||||
|
if (StrUtil.isEmpty(text)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("json parse err,json:{}", text, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> parseArray(String text, String path, Class<T> clazz) {
|
||||||
|
if (StrUtil.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
JsonNode treeNode = objectMapper.readTree(text);
|
||||||
|
JsonNode pathNode = treeNode.path(path);
|
||||||
|
return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("json parse err,json:{}", text, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonNode parseTree(String text) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readTree(text);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("json parse err,json:{}", text, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonNode parseTree(byte[] text) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readTree(text);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("json parse err,json:{}", text, e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isJson(String text) {
|
||||||
|
return JSONUtil.isTypeJSON(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字符串是否为 JSON 类型的字符串
|
||||||
|
* @param str 字符串
|
||||||
|
*/
|
||||||
|
public static boolean isJsonObject(String str) {
|
||||||
|
return JSONUtil.isTypeJSONObject(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.platform.marketing.framework.common.util.json.databind;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Long 序列化规则
|
||||||
|
*
|
||||||
|
* 会将超长 long 值转换为 string,解决前端 JavaScript 最大安全整数是 2^53-1 的问题
|
||||||
|
*
|
||||||
|
* @author 星语
|
||||||
|
*/
|
||||||
|
@JacksonStdImpl
|
||||||
|
public class NumberSerializer extends com.fasterxml.jackson.databind.ser.std.NumberSerializer {
|
||||||
|
|
||||||
|
private static final long MAX_SAFE_INTEGER = 9007199254740991L;
|
||||||
|
private static final long MIN_SAFE_INTEGER = -9007199254740991L;
|
||||||
|
|
||||||
|
public static final NumberSerializer INSTANCE = new NumberSerializer(Number.class);
|
||||||
|
|
||||||
|
public NumberSerializer(Class<? extends Number> rawType) {
|
||||||
|
super(rawType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
|
// 超出范围 序列化位字符串
|
||||||
|
if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
|
||||||
|
super.serialize(value, gen, serializers);
|
||||||
|
} else {
|
||||||
|
gen.writeString(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.platform.marketing.framework.common.util.json.databind;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于时间戳的 LocalDateTime 反序列化器
|
||||||
|
*
|
||||||
|
* @author 老五
|
||||||
|
*/
|
||||||
|
public class TimestampLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
|
||||||
|
|
||||||
|
public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||||
|
// 将 Long 时间戳,转换为 LocalDateTime 对象
|
||||||
|
return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.platform.marketing.framework.common.util.json.databind;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于时间戳的 LocalDateTime 序列化器
|
||||||
|
*
|
||||||
|
* @author 老五
|
||||||
|
*/
|
||||||
|
public class TimestampLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
|
||||||
|
|
||||||
|
public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
|
// 将 LocalDateTime 对象,转换为 Long 时间戳
|
||||||
|
gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.platform.marketing.framework.common.util.monitor;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 链路追踪工具类
|
||||||
|
*
|
||||||
|
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class TracerUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私有化构造方法
|
||||||
|
*/
|
||||||
|
private TracerUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得链路追踪编号。
|
||||||
|
* 如果不存在的话为空字符串!!!
|
||||||
|
*
|
||||||
|
* @return 链路追踪编号
|
||||||
|
*/
|
||||||
|
public static String getTraceId() {
|
||||||
|
return UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
package com.platform.marketing.framework.common.util.number;
|
||||||
|
|
||||||
|
import cn.hutool.core.math.Money;
|
||||||
|
import cn.hutool.core.util.NumberUtil;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金额工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class MoneyUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金额的小数位数
|
||||||
|
*/
|
||||||
|
private static final int PRICE_SCALE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 百分比对应的 BigDecimal 对象
|
||||||
|
*/
|
||||||
|
public static final BigDecimal PERCENT_100 = BigDecimal.valueOf(100);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算百分比金额,四舍五入
|
||||||
|
*
|
||||||
|
* @param price 金额
|
||||||
|
* @param rate 百分比,例如说 56.77% 则传入 56.77
|
||||||
|
* @return 百分比金额
|
||||||
|
*/
|
||||||
|
public static Integer calculateRatePrice(Integer price, Double rate) {
|
||||||
|
return calculateRatePrice(price, rate, 0, RoundingMode.HALF_UP).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算百分比金额,向下传入
|
||||||
|
*
|
||||||
|
* @param price 金额
|
||||||
|
* @param rate 百分比,例如说 56.77% 则传入 56.77
|
||||||
|
* @return 百分比金额
|
||||||
|
*/
|
||||||
|
public static Integer calculateRatePriceFloor(Integer price, Double rate) {
|
||||||
|
return calculateRatePrice(price, rate, 0, RoundingMode.FLOOR).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算百分比金额
|
||||||
|
*
|
||||||
|
* @param price 金额(单位分)
|
||||||
|
* @param count 数量
|
||||||
|
* @param percent 折扣(单位分),列如 60.2%,则传入 6020
|
||||||
|
* @return 商品总价
|
||||||
|
*/
|
||||||
|
public static Integer calculator(Integer price, Integer count, Integer percent) {
|
||||||
|
price = price * count;
|
||||||
|
if (percent == null) {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
return MoneyUtils.calculateRatePriceFloor(price, (double) (percent / 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算百分比金额
|
||||||
|
*
|
||||||
|
* @param price 金额
|
||||||
|
* @param rate 百分比,例如说 56.77% 则传入 56.77
|
||||||
|
* @param scale 保留小数位数
|
||||||
|
* @param roundingMode 舍入模式
|
||||||
|
*/
|
||||||
|
public static BigDecimal calculateRatePrice(Number price, Number rate, int scale, RoundingMode roundingMode) {
|
||||||
|
return NumberUtil.toBigDecimal(price).multiply(NumberUtil.toBigDecimal(rate)) // 乘以
|
||||||
|
.divide(BigDecimal.valueOf(100), scale, roundingMode); // 除以 100
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分转元
|
||||||
|
*
|
||||||
|
* @param fen 分
|
||||||
|
* @return 元
|
||||||
|
*/
|
||||||
|
public static BigDecimal fenToYuan(int fen) {
|
||||||
|
return new Money(0, fen).getAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分转元(字符串)
|
||||||
|
*
|
||||||
|
* 例如说 fen 为 1 时,则结果为 0.01
|
||||||
|
*
|
||||||
|
* @param fen 分
|
||||||
|
* @return 元
|
||||||
|
*/
|
||||||
|
public static String fenToYuanStr(int fen) {
|
||||||
|
return new Money(0, fen).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金额相乘,默认进行四舍五入
|
||||||
|
*
|
||||||
|
* 位数:{@link #PRICE_SCALE}
|
||||||
|
*
|
||||||
|
* @param price 金额
|
||||||
|
* @param count 数量
|
||||||
|
* @return 金额相乘结果
|
||||||
|
*/
|
||||||
|
public static BigDecimal priceMultiply(BigDecimal price, BigDecimal count) {
|
||||||
|
if (price == null || count == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return price.multiply(count).setScale(PRICE_SCALE, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金额相乘(百分比),默认进行四舍五入
|
||||||
|
*
|
||||||
|
* 位数:{@link #PRICE_SCALE}
|
||||||
|
*
|
||||||
|
* @param price 金额
|
||||||
|
* @param percent 百分比
|
||||||
|
* @return 金额相乘结果
|
||||||
|
*/
|
||||||
|
public static BigDecimal priceMultiplyPercent(BigDecimal price, BigDecimal percent) {
|
||||||
|
if (price == null || percent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return price.multiply(percent).divide(PERCENT_100, PRICE_SCALE, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.platform.marketing.framework.common.util.number;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.NumberUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class NumberUtils {
|
||||||
|
|
||||||
|
public static Long parseLong(String str) {
|
||||||
|
return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer parseInt(String str) {
|
||||||
|
return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAllNumber(List<String> values) {
|
||||||
|
if (CollUtil.isEmpty(values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (String value : values) {
|
||||||
|
if (!NumberUtil.isNumber(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过经纬度获取地球上两点之间的距离
|
||||||
|
*
|
||||||
|
* 参考 <<a href="https://gitee.com/dromara/hutool/blob/1caabb586b1f95aec66a21d039c5695df5e0f4c1/hutool-core/src/main/java/cn/hutool/core/util/DistanceUtil.java">DistanceUtil</a>> 实现,目前它已经被 hutool 删除
|
||||||
|
*
|
||||||
|
* @param lat1 经度1
|
||||||
|
* @param lng1 纬度1
|
||||||
|
* @param lat2 经度2
|
||||||
|
* @param lng2 纬度2
|
||||||
|
* @return 距离,单位:千米
|
||||||
|
*/
|
||||||
|
public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
|
||||||
|
double radLat1 = lat1 * Math.PI / 180.0;
|
||||||
|
double radLat2 = lat2 * Math.PI / 180.0;
|
||||||
|
double a = radLat1 - radLat2;
|
||||||
|
double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
|
||||||
|
double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
|
||||||
|
+ Math.cos(radLat1) * Math.cos(radLat2)
|
||||||
|
* Math.pow(Math.sin(b / 2), 2)));
|
||||||
|
distance = distance * 6378.137;
|
||||||
|
distance = Math.round(distance * 10000d) / 10000d;
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供精确的乘法运算
|
||||||
|
*
|
||||||
|
* 和 hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是,如果存在 null,则返回 null
|
||||||
|
*
|
||||||
|
* @param values 多个被乘值
|
||||||
|
* @return 积
|
||||||
|
*/
|
||||||
|
public static BigDecimal mul(BigDecimal... values) {
|
||||||
|
for (BigDecimal value : values) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NumberUtil.mul(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.platform.marketing.framework.common.util.object;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import com.platform.marketing.framework.common.pojo.PageResult;
|
||||||
|
import com.platform.marketing.framework.common.util.collection.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean 工具类
|
||||||
|
*
|
||||||
|
* 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
|
||||||
|
* 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class BeanUtils {
|
||||||
|
|
||||||
|
public static <T> T toBean(Object source, Class<T> targetClass) {
|
||||||
|
return BeanUtil.toBean(source, targetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T toBean(Object source, Class<T> targetClass, Consumer<T> peek) {
|
||||||
|
T target = toBean(source, targetClass);
|
||||||
|
if (target != null) {
|
||||||
|
peek.accept(target);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <S, T> List<T> toBean(List<S> source, Class<T> targetType) {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return CollectionUtils.convertList(source, s -> toBean(s, targetType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <S, T> List<T> toBean(List<S> source, Class<T> targetType, Consumer<T> peek) {
|
||||||
|
List<T> list = toBean(source, targetType);
|
||||||
|
if (list != null) {
|
||||||
|
list.forEach(peek);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType) {
|
||||||
|
return toBean(source, targetType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <S, T> PageResult<T> toBean(PageResult<S> source, Class<T> targetType, Consumer<T> peek) {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<T> list = toBean(source.getList(), targetType);
|
||||||
|
if (peek != null) {
|
||||||
|
list.forEach(peek);
|
||||||
|
}
|
||||||
|
return new PageResult<>(list, source.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyProperties(Object source, Object target) {
|
||||||
|
if (source == null || target == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BeanUtil.copyProperties(source, target, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.platform.marketing.framework.common.util.object;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class ObjectUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制对象,并忽略 Id 编号
|
||||||
|
*
|
||||||
|
* @param object 被复制对象
|
||||||
|
* @param consumer 消费者,可以二次编辑被复制对象
|
||||||
|
* @return 复制后的对象
|
||||||
|
*/
|
||||||
|
public static <T> T cloneIgnoreId(T object, Consumer<T> consumer) {
|
||||||
|
T result = ObjectUtil.clone(object);
|
||||||
|
// 忽略 id 编号
|
||||||
|
Field field = ReflectUtil.getField(object.getClass(), "id");
|
||||||
|
if (field != null) {
|
||||||
|
ReflectUtil.setFieldValue(result, field, null);
|
||||||
|
}
|
||||||
|
// 二次编辑
|
||||||
|
if (result != null) {
|
||||||
|
consumer.accept(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Comparable<T>> T max(T obj1, T obj2) {
|
||||||
|
if (obj1 == null) {
|
||||||
|
return obj2;
|
||||||
|
}
|
||||||
|
if (obj2 == null) {
|
||||||
|
return obj1;
|
||||||
|
}
|
||||||
|
return obj1.compareTo(obj2) > 0 ? obj1 : obj2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <T> T defaultIfNull(T... array) {
|
||||||
|
for (T item : array) {
|
||||||
|
if (item != null) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <T> boolean equalsAny(T obj, T... array) {
|
||||||
|
return Arrays.asList(array).contains(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.platform.marketing.framework.common.util.object;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.lang.func.Func1;
|
||||||
|
import cn.hutool.core.lang.func.LambdaUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import com.platform.marketing.framework.common.pojo.PageParam;
|
||||||
|
import com.platform.marketing.framework.common.pojo.SortablePageParam;
|
||||||
|
import com.platform.marketing.framework.common.pojo.SortingField;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link com.platform.marketing.framework.common.pojo.PageParam} 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class PageUtils {
|
||||||
|
|
||||||
|
private static final Object[] ORDER_TYPES = new String[]{SortingField.ORDER_ASC, SortingField.ORDER_DESC};
|
||||||
|
|
||||||
|
public static int getStart(PageParam pageParam) {
|
||||||
|
return (pageParam.getPageNo() - 1) * pageParam.getPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建排序字段(默认倒序)
|
||||||
|
*
|
||||||
|
* @param func 排序字段的 Lambda 表达式
|
||||||
|
* @param <T> 排序字段所属的类型
|
||||||
|
* @return 排序字段
|
||||||
|
*/
|
||||||
|
public static <T> SortingField buildSortingField(Func1<T, ?> func) {
|
||||||
|
return buildSortingField(func, SortingField.ORDER_DESC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建排序字段
|
||||||
|
*
|
||||||
|
* @param func 排序字段的 Lambda 表达式
|
||||||
|
* @param order 排序类型 {@link SortingField#ORDER_ASC} {@link SortingField#ORDER_DESC}
|
||||||
|
* @param <T> 排序字段所属的类型
|
||||||
|
* @return 排序字段
|
||||||
|
*/
|
||||||
|
public static <T> SortingField buildSortingField(Func1<T, ?> func, String order) {
|
||||||
|
Assert.isTrue(ArrayUtil.contains(ORDER_TYPES, order), String.format("字段的排序类型只能是 %s/%s", ORDER_TYPES));
|
||||||
|
|
||||||
|
String fieldName = LambdaUtil.getFieldName(func);
|
||||||
|
return new SortingField(fieldName, order);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建默认的排序字段
|
||||||
|
* 如果排序字段为空,则设置排序字段;否则忽略
|
||||||
|
*
|
||||||
|
* @param sortablePageParam 排序分页查询参数
|
||||||
|
* @param func 排序字段的 Lambda 表达式
|
||||||
|
* @param <T> 排序字段所属的类型
|
||||||
|
*/
|
||||||
|
public static <T> void buildDefaultSortingField(SortablePageParam sortablePageParam, Func1<T, ?> func) {
|
||||||
|
if (sortablePageParam != null && CollUtil.isEmpty(sortablePageParam.getSortingFields())) {
|
||||||
|
sortablePageParam.setSortingFields(singletonList(buildSortingField(func)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
/**
|
||||||
|
* 对于工具类的选择,优先查找 Hutool 中有没对应的方法
|
||||||
|
* 如果没有,则自己封装对应的工具类,以 Utils 结尾,用于区分
|
||||||
|
*
|
||||||
|
* ps:如果担心 Hutool 存在坑的问题,可以阅读 Hutool 的实现源码,以确保可靠性。并且,可以补充相关的单元测试。
|
||||||
|
*/
|
||||||
|
package com.platform.marketing.framework.common.util;
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
package com.platform.marketing.framework.common.util.servlet;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.extra.servlet.ServletUtil;
|
||||||
|
import com.platform.marketing.framework.common.util.json.JsonUtils;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class ServletUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回 JSON 字符串
|
||||||
|
*
|
||||||
|
* @param response 响应
|
||||||
|
* @param object 对象,会序列化成 JSON 字符串
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
|
||||||
|
public static void writeJSON(HttpServletResponse response, Object object) {
|
||||||
|
String content = JsonUtils.toJsonString(object);
|
||||||
|
ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回附件
|
||||||
|
*
|
||||||
|
* @param response 响应
|
||||||
|
* @param filename 文件名
|
||||||
|
* @param content 附件内容
|
||||||
|
*/
|
||||||
|
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
|
||||||
|
// 设置 header 和 contentType
|
||||||
|
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
|
||||||
|
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||||
|
// 输出附件
|
||||||
|
IoUtil.write(response.getOutputStream(), false, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param request 请求
|
||||||
|
* @return ua
|
||||||
|
*/
|
||||||
|
public static String getUserAgent(HttpServletRequest request) {
|
||||||
|
String ua = request.getHeader("User-Agent");
|
||||||
|
return ua != null ? ua : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得请求
|
||||||
|
*
|
||||||
|
* @return HttpServletRequest
|
||||||
|
*/
|
||||||
|
public static HttpServletRequest getRequest() {
|
||||||
|
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||||
|
if (!(requestAttributes instanceof ServletRequestAttributes)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ((ServletRequestAttributes) requestAttributes).getRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getUserAgent() {
|
||||||
|
HttpServletRequest request = getRequest();
|
||||||
|
if (request == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getUserAgent(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getClientIP() {
|
||||||
|
HttpServletRequest request = getRequest();
|
||||||
|
if (request == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ServletUtil.getClientIP(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isJsonRequest(ServletRequest request) {
|
||||||
|
return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getBody(HttpServletRequest request) {
|
||||||
|
// 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取
|
||||||
|
if (isJsonRequest(request)) {
|
||||||
|
return ServletUtil.getBody(request);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getBodyBytes(HttpServletRequest request) {
|
||||||
|
// 只有在 json 请求在读取,因为只有 CacheRequestBodyFilter 才会进行缓存,支持重复读取
|
||||||
|
if (isJsonRequest(request)) {
|
||||||
|
return ServletUtil.getBodyBytes(request);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getClientIP(HttpServletRequest request) {
|
||||||
|
return ServletUtil.getClientIP(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> getParamMap(HttpServletRequest request) {
|
||||||
|
return ServletUtil.getParamMap(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> getHeaderMap(HttpServletRequest request) {
|
||||||
|
return ServletUtil.getHeaderMap(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
package com.platform.marketing.framework.common.util.spring;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.context.expression.BeanFactoryResolver;
|
||||||
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring EL 表达式的工具类
|
||||||
|
*
|
||||||
|
* @author mashu
|
||||||
|
*/
|
||||||
|
public class SpringExpressionUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring EL 表达式解析器
|
||||||
|
*/
|
||||||
|
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||||
|
/**
|
||||||
|
* 参数名发现器
|
||||||
|
*/
|
||||||
|
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||||
|
|
||||||
|
private SpringExpressionUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从切面中,单个解析 EL 表达式的结果
|
||||||
|
*
|
||||||
|
* @param joinPoint 切面点
|
||||||
|
* @param expressionString EL 表达式数组
|
||||||
|
* @return 执行界面
|
||||||
|
*/
|
||||||
|
public static Object parseExpression(JoinPoint joinPoint, String expressionString) {
|
||||||
|
Map<String, Object> result = parseExpressions(joinPoint, Collections.singletonList(expressionString));
|
||||||
|
return result.get(expressionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从切面中,批量解析 EL 表达式的结果
|
||||||
|
*
|
||||||
|
* @param joinPoint 切面点
|
||||||
|
* @param expressionStrings EL 表达式数组
|
||||||
|
* @return 结果,key 为表达式,value 为对应值
|
||||||
|
*/
|
||||||
|
public static Map<String, Object> parseExpressions(JoinPoint joinPoint, List<String> expressionStrings) {
|
||||||
|
// 如果为空,则不进行解析
|
||||||
|
if (CollUtil.isEmpty(expressionStrings)) {
|
||||||
|
return MapUtil.newHashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第一步,构建解析的上下文 EvaluationContext
|
||||||
|
// 通过 joinPoint 获取被注解方法
|
||||||
|
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
Method method = methodSignature.getMethod();
|
||||||
|
// 使用 spring 的 ParameterNameDiscoverer 获取方法形参名数组
|
||||||
|
String[] paramNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
|
||||||
|
// Spring 的表达式上下文对象
|
||||||
|
EvaluationContext context = new StandardEvaluationContext();
|
||||||
|
// 给上下文赋值
|
||||||
|
if (ArrayUtil.isNotEmpty(paramNames)) {
|
||||||
|
Object[] args = joinPoint.getArgs();
|
||||||
|
for (int i = 0; i < paramNames.length; i++) {
|
||||||
|
context.setVariable(paramNames[i], args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二步,逐个参数解析
|
||||||
|
Map<String, Object> result = MapUtil.newHashMap(expressionStrings.size(), true);
|
||||||
|
expressionStrings.forEach(key -> {
|
||||||
|
Object value = EXPRESSION_PARSER.parseExpression(key).getValue(context);
|
||||||
|
result.put(key, value);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Bean 工厂,解析 EL 表达式的结果
|
||||||
|
*
|
||||||
|
* @param expressionString EL 表达式
|
||||||
|
* @return 执行界面
|
||||||
|
*/
|
||||||
|
public static Object parseExpression(String expressionString) {
|
||||||
|
return parseExpression(expressionString, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Bean 工厂,解析 EL 表达式的结果
|
||||||
|
*
|
||||||
|
* @param expressionString EL 表达式
|
||||||
|
* @param variables 变量
|
||||||
|
* @return 执行界面
|
||||||
|
*/
|
||||||
|
public static Object parseExpression(String expressionString, Map<String, Object> variables) {
|
||||||
|
if (StrUtil.isBlank(expressionString)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Expression expression = EXPRESSION_PARSER.parseExpression(expressionString);
|
||||||
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||||
|
context.setBeanResolver(new BeanFactoryResolver(SpringUtil.getApplicationContext()));
|
||||||
|
if (MapUtil.isNotEmpty(variables)) {
|
||||||
|
context.setVariables(variables);
|
||||||
|
}
|
||||||
|
return expression.getValue(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.platform.marketing.framework.common.util.spring;
|
||||||
|
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring 工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class SpringUtils extends SpringUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为生产环境
|
||||||
|
*
|
||||||
|
* @return 是否生产环境
|
||||||
|
*/
|
||||||
|
public static boolean isProd() {
|
||||||
|
String activeProfile = getActiveProfile();
|
||||||
|
return Objects.equals("prod", activeProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.platform.marketing.framework.common.util.string;
|
||||||
|
|
||||||
|
import cn.hutool.core.text.StrPool;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class StrUtils {
|
||||||
|
|
||||||
|
public static String maxLength(CharSequence str, int maxLength) {
|
||||||
|
return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给定字符串是否以任何一个字符串开始
|
||||||
|
* 给定字符串和数组为空都返回 false
|
||||||
|
*
|
||||||
|
* @param str 给定字符串
|
||||||
|
* @param prefixes 需要检测的开始字符串
|
||||||
|
* @since 3.0.6
|
||||||
|
*/
|
||||||
|
public static boolean startWithAny(String str, Collection<String> prefixes) {
|
||||||
|
if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CharSequence suffix : prefixes) {
|
||||||
|
if (StrUtil.startWith(str, suffix, false)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Long> splitToLong(String value, CharSequence separator) {
|
||||||
|
long[] longs = StrUtil.splitToLong(value, separator);
|
||||||
|
return Arrays.stream(longs).boxed().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Long> splitToLongSet(String value) {
|
||||||
|
return splitToLongSet(value, StrPool.COMMA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Long> splitToLongSet(String value, CharSequence separator) {
|
||||||
|
long[] longs = StrUtil.splitToLong(value, separator);
|
||||||
|
return Arrays.stream(longs).boxed().collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> splitToInteger(String value, CharSequence separator) {
|
||||||
|
int[] integers = StrUtil.splitToInt(value, separator);
|
||||||
|
return Arrays.stream(integers).boxed().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除字符串中,包含指定字符串的行
|
||||||
|
*
|
||||||
|
* @param content 字符串
|
||||||
|
* @param sequence 包含的字符串
|
||||||
|
* @return 移除后的字符串
|
||||||
|
*/
|
||||||
|
public static String removeLineContains(String content, String sequence) {
|
||||||
|
if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
return Arrays.stream(content.split("\n"))
|
||||||
|
.filter(line -> !line.contains(sequence))
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.platform.marketing.framework.common.util.thread;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add reset feature for @see java.util.concurrent.CountDownLatch
|
||||||
|
*/
|
||||||
|
public class CountDownLatch2 {
|
||||||
|
private final Sync sync;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@code CountDownLatch2} initialized with the given count.
|
||||||
|
*
|
||||||
|
* @param count the number of times {@link #countDown} must be invoked before threads can pass through {@link
|
||||||
|
* #await}
|
||||||
|
* @throws IllegalArgumentException if {@code count} is negative
|
||||||
|
*/
|
||||||
|
public CountDownLatch2(int count) {
|
||||||
|
if (count < 0)
|
||||||
|
throw new IllegalArgumentException("count < 0");
|
||||||
|
this.sync = new Sync(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes the current thread to wait until the latch has counted down to
|
||||||
|
* zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
|
||||||
|
*
|
||||||
|
* <p>If the current count is zero then this method returns immediately.
|
||||||
|
*
|
||||||
|
* <p>If the current count is greater than zero then the current
|
||||||
|
* thread becomes disabled for thread scheduling purposes and lies
|
||||||
|
* dormant until one of two things happen:
|
||||||
|
* <ul>
|
||||||
|
* <li>The count reaches zero due to invocations of the
|
||||||
|
* {@link #countDown} method; or
|
||||||
|
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
|
||||||
|
* the current thread.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>If the current thread:
|
||||||
|
* <ul>
|
||||||
|
* <li>has its interrupted status set on entry to this method; or
|
||||||
|
* <li>is {@linkplain Thread#interrupt interrupted} while waiting,
|
||||||
|
* </ul>
|
||||||
|
* then {@link InterruptedException} is thrown and the current thread's
|
||||||
|
* interrupted status is cleared.
|
||||||
|
*
|
||||||
|
* @throws InterruptedException if the current thread is interrupted while waiting
|
||||||
|
*/
|
||||||
|
public void await() throws InterruptedException {
|
||||||
|
sync.acquireSharedInterruptibly(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes the current thread to wait until the latch has counted down to
|
||||||
|
* zero, unless the thread is {@linkplain Thread#interrupt interrupted},
|
||||||
|
* or the specified waiting time elapses.
|
||||||
|
*
|
||||||
|
* <p>If the current count is zero then this method returns immediately
|
||||||
|
* with the value {@code true}.
|
||||||
|
*
|
||||||
|
* <p>If the current count is greater than zero then the current
|
||||||
|
* thread becomes disabled for thread scheduling purposes and lies
|
||||||
|
* dormant until one of three things happen:
|
||||||
|
* <ul>
|
||||||
|
* <li>The count reaches zero due to invocations of the
|
||||||
|
* {@link #countDown} method; or
|
||||||
|
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
|
||||||
|
* the current thread; or
|
||||||
|
* <li>The specified waiting time elapses.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>If the count reaches zero then the method returns with the
|
||||||
|
* value {@code true}.
|
||||||
|
*
|
||||||
|
* <p>If the current thread:
|
||||||
|
* <ul>
|
||||||
|
* <li>has its interrupted status set on entry to this method; or
|
||||||
|
* <li>is {@linkplain Thread#interrupt interrupted} while waiting,
|
||||||
|
* </ul>
|
||||||
|
* then {@link InterruptedException} is thrown and the current thread's
|
||||||
|
* interrupted status is cleared.
|
||||||
|
*
|
||||||
|
* <p>If the specified waiting time elapses then the value {@code false}
|
||||||
|
* is returned. If the time is less than or equal to zero, the method
|
||||||
|
* will not wait at all.
|
||||||
|
*
|
||||||
|
* @param timeout the maximum time to wait
|
||||||
|
* @param unit the time unit of the {@code timeout} argument
|
||||||
|
* @return {@code true} if the count reached zero and {@code false} if the waiting time elapsed before the count
|
||||||
|
* reached zero
|
||||||
|
* @throws InterruptedException if the current thread is interrupted while waiting
|
||||||
|
*/
|
||||||
|
public boolean await(long timeout, TimeUnit unit)
|
||||||
|
throws InterruptedException {
|
||||||
|
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements the count of the latch, releasing all waiting threads if
|
||||||
|
* the count reaches zero.
|
||||||
|
*
|
||||||
|
* <p>If the current count is greater than zero then it is decremented.
|
||||||
|
* If the new count is zero then all waiting threads are re-enabled for
|
||||||
|
* thread scheduling purposes.
|
||||||
|
*
|
||||||
|
* <p>If the current count equals zero then nothing happens.
|
||||||
|
*/
|
||||||
|
public void countDown() {
|
||||||
|
sync.releaseShared(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current count.
|
||||||
|
*
|
||||||
|
* <p>This method is typically used for debugging and testing purposes.
|
||||||
|
*
|
||||||
|
* @return the current count
|
||||||
|
*/
|
||||||
|
public long getCount() {
|
||||||
|
return sync.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
sync.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string identifying this latch, as well as its state.
|
||||||
|
* The state, in brackets, includes the String {@code "Count ="}
|
||||||
|
* followed by the current count.
|
||||||
|
*
|
||||||
|
* @return a string identifying this latch, as well as its state
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + "[Count = " + sync.getCount() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronization control For CountDownLatch2.
|
||||||
|
* Uses AQS state to represent count.
|
||||||
|
*/
|
||||||
|
private static final class Sync extends AbstractQueuedSynchronizer {
|
||||||
|
private static final long serialVersionUID = 4982264981922014374L;
|
||||||
|
|
||||||
|
private final int startCount;
|
||||||
|
|
||||||
|
Sync(int count) {
|
||||||
|
this.startCount = count;
|
||||||
|
setState(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCount() {
|
||||||
|
return getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int tryAcquireShared(int acquires) {
|
||||||
|
return (getState() == 0) ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean tryReleaseShared(int releases) {
|
||||||
|
// Decrement count; signal when transition to zero
|
||||||
|
for (; ; ) {
|
||||||
|
int c = getState();
|
||||||
|
if (c == 0)
|
||||||
|
return false;
|
||||||
|
int nextc = c - 1;
|
||||||
|
if (compareAndSetState(c, nextc))
|
||||||
|
return nextc == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void reset() {
|
||||||
|
setState(startCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
package com.platform.marketing.framework.common.util.thread;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public abstract class PlatformServiceThread implements Runnable {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(PlatformServiceThread.class);
|
||||||
|
private static final long JOIN_TIME = 90 * 1000;
|
||||||
|
|
||||||
|
private Thread thread;
|
||||||
|
protected final CountDownLatch2 waitPoint = new CountDownLatch2(1);
|
||||||
|
protected volatile AtomicBoolean hasNotified = new AtomicBoolean(false);
|
||||||
|
protected volatile boolean stopped = false;
|
||||||
|
protected boolean isDaemon = false;
|
||||||
|
|
||||||
|
//Make it able to restart the thread
|
||||||
|
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
public PlatformServiceThread() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getServiceName();
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
log.info("Try to start service thread:{} started:{} lastThread:{}", getServiceName(), started.get(), thread);
|
||||||
|
if (!started.compareAndSet(false, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stopped = false;
|
||||||
|
this.thread = new Thread(this, getServiceName());
|
||||||
|
this.thread.setDaemon(isDaemon);
|
||||||
|
this.thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
this.shutdown(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown(final boolean interrupt) {
|
||||||
|
log.info("Try to shutdown service thread:{} started:{} lastThread:{}", getServiceName(), started.get(), thread);
|
||||||
|
if (!started.compareAndSet(true, false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.stopped = true;
|
||||||
|
log.info("shutdown thread " + this.getServiceName() + " interrupt " + interrupt);
|
||||||
|
|
||||||
|
if (hasNotified.compareAndSet(false, true)) {
|
||||||
|
waitPoint.countDown(); // notify
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (interrupt) {
|
||||||
|
this.thread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
long beginTime = System.currentTimeMillis();
|
||||||
|
if (!this.thread.isDaemon()) {
|
||||||
|
this.thread.join(this.getJointime());
|
||||||
|
}
|
||||||
|
long elapsedTime = System.currentTimeMillis() - beginTime;
|
||||||
|
log.info("join thread " + this.getServiceName() + " elapsed time(ms) " + elapsedTime + " "
|
||||||
|
+ this.getJointime());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error("Interrupted", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getJointime() {
|
||||||
|
return JOIN_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void stop() {
|
||||||
|
this.stop(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void stop(final boolean interrupt) {
|
||||||
|
if (!started.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.stopped = true;
|
||||||
|
log.info("stop thread " + this.getServiceName() + " interrupt " + interrupt);
|
||||||
|
|
||||||
|
if (hasNotified.compareAndSet(false, true)) {
|
||||||
|
waitPoint.countDown(); // notify
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interrupt) {
|
||||||
|
this.thread.interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeStop() {
|
||||||
|
if (!started.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.stopped = true;
|
||||||
|
log.info("makestop thread " + this.getServiceName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void wakeup() {
|
||||||
|
if (hasNotified.compareAndSet(false, true)) {
|
||||||
|
waitPoint.countDown(); // notify
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void waitForRunning(long interval) {
|
||||||
|
if (hasNotified.compareAndSet(true, false)) {
|
||||||
|
this.onWaitEnd();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//entry to wait
|
||||||
|
waitPoint.reset();
|
||||||
|
|
||||||
|
try {
|
||||||
|
waitPoint.await(interval, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error("Interrupted", e);
|
||||||
|
} finally {
|
||||||
|
hasNotified.set(false);
|
||||||
|
this.onWaitEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onWaitEnd() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStopped() {
|
||||||
|
return stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDaemon() {
|
||||||
|
return isDaemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDaemon(boolean daemon) {
|
||||||
|
isDaemon = daemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.platform.marketing.framework.common.util.thread;
|
||||||
|
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
public class ThreadFactoryImpl implements ThreadFactory {
|
||||||
|
private final AtomicLong threadIndex = new AtomicLong(0);
|
||||||
|
private final String threadNamePrefix;
|
||||||
|
private final boolean daemon;
|
||||||
|
|
||||||
|
public ThreadFactoryImpl(final String threadNamePrefix) {
|
||||||
|
this(threadNamePrefix, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThreadFactoryImpl(final String threadNamePrefix, boolean daemon) {
|
||||||
|
this.threadNamePrefix = threadNamePrefix;
|
||||||
|
this.daemon = daemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Thread newThread(Runnable r) {
|
||||||
|
Thread thread = new Thread(r, threadNamePrefix + this.threadIndex.incrementAndGet());
|
||||||
|
thread.setDaemon(daemon);
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.platform.marketing.framework.common.util.validation;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.ConstraintViolationException;
|
||||||
|
import javax.validation.Validation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public class ValidationUtils {
|
||||||
|
|
||||||
|
private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$");
|
||||||
|
|
||||||
|
private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
|
||||||
|
|
||||||
|
private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
|
||||||
|
|
||||||
|
public static boolean isMobile(String mobile) {
|
||||||
|
return StringUtils.hasText(mobile)
|
||||||
|
&& PATTERN_MOBILE.matcher(mobile).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isURL(String url) {
|
||||||
|
return StringUtils.hasText(url)
|
||||||
|
&& PATTERN_URL.matcher(url).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isXmlNCName(String str) {
|
||||||
|
return StringUtils.hasText(str)
|
||||||
|
&& PATTERN_XML_NCNAME.matcher(str).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validate(Object object, Class<?>... groups) {
|
||||||
|
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||||
|
Assert.notNull(validator);
|
||||||
|
validate(validator, object, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validate(Validator validator, Object object, Class<?>... groups) {
|
||||||
|
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
|
||||||
|
if (CollUtil.isNotEmpty(constraintViolations)) {
|
||||||
|
throw new ConstraintViolationException(constraintViolations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.platform.marketing.framework.common.validation;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.core.ArrayValuable;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Target({
|
||||||
|
ElementType.METHOD,
|
||||||
|
ElementType.FIELD,
|
||||||
|
ElementType.ANNOTATION_TYPE,
|
||||||
|
ElementType.CONSTRUCTOR,
|
||||||
|
ElementType.PARAMETER,
|
||||||
|
ElementType.TYPE_USE
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(
|
||||||
|
validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class}
|
||||||
|
)
|
||||||
|
public @interface InEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 实现 ArrayValuable 接口的类
|
||||||
|
*/
|
||||||
|
Class<? extends ArrayValuable<?>> value();
|
||||||
|
|
||||||
|
String message() default "必须在指定范围 {value}";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.platform.marketing.framework.common.validation;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.platform.marketing.framework.common.core.ArrayValuable;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {
|
||||||
|
|
||||||
|
private List<?> values;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(InEnum annotation) {
|
||||||
|
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
|
||||||
|
if (values.length == 0) {
|
||||||
|
this.values = Collections.emptyList();
|
||||||
|
} else {
|
||||||
|
this.values = Arrays.asList(values[0].array());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {
|
||||||
|
if (list == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 校验通过
|
||||||
|
if (CollUtil.containsAll(values, list)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 校验不通过,自定义提示语句
|
||||||
|
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
|
||||||
|
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
|
||||||
|
.replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.platform.marketing.framework.common.validation;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.core.ArrayValuable;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
|
||||||
|
|
||||||
|
private List<?> values;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(InEnum annotation) {
|
||||||
|
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
|
||||||
|
if (values.length == 0) {
|
||||||
|
this.values = Collections.emptyList();
|
||||||
|
} else {
|
||||||
|
this.values = Arrays.asList(values[0].array());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
||||||
|
// 为空时,默认不校验,即认为通过
|
||||||
|
if (value == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 校验通过
|
||||||
|
if (values.contains(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 校验不通过,自定义提示语句
|
||||||
|
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
|
||||||
|
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
|
||||||
|
.replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.platform.marketing.framework.common.validation;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Target({
|
||||||
|
ElementType.METHOD,
|
||||||
|
ElementType.FIELD,
|
||||||
|
ElementType.ANNOTATION_TYPE,
|
||||||
|
ElementType.CONSTRUCTOR,
|
||||||
|
ElementType.PARAMETER,
|
||||||
|
ElementType.TYPE_USE
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(
|
||||||
|
validatedBy = MobileValidator.class
|
||||||
|
)
|
||||||
|
public @interface Mobile {
|
||||||
|
|
||||||
|
String message() default "手机号格式不正确";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.platform.marketing.framework.common.validation;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.platform.marketing.framework.common.util.validation.ValidationUtils;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
|
||||||
|
public class MobileValidator implements ConstraintValidator<Mobile, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(Mobile annotation) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
// 如果手机号为空,默认不校验,即校验通过
|
||||||
|
if (StrUtil.isEmpty(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 校验手机
|
||||||
|
return ValidationUtils.isMobile(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.platform.marketing.framework.common.validation;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Target({
|
||||||
|
ElementType.METHOD,
|
||||||
|
ElementType.FIELD,
|
||||||
|
ElementType.ANNOTATION_TYPE,
|
||||||
|
ElementType.CONSTRUCTOR,
|
||||||
|
ElementType.PARAMETER,
|
||||||
|
ElementType.TYPE_USE
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(
|
||||||
|
validatedBy = TelephoneValidator.class
|
||||||
|
)
|
||||||
|
public @interface Telephone {
|
||||||
|
|
||||||
|
String message() default "电话格式不正确";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.platform.marketing.framework.common.validation;
|
||||||
|
|
||||||
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
|
import cn.hutool.core.util.PhoneUtil;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
|
||||||
|
public class TelephoneValidator implements ConstraintValidator<Telephone, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(Telephone annotation) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
// 如果手机号为空,默认不校验,即校验通过
|
||||||
|
if (CharSequenceUtil.isEmpty(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 校验手机
|
||||||
|
return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* 使用 Hibernate Validator 实现参数校验
|
||||||
|
*/
|
||||||
|
package com.platform.marketing.framework.common.validation;
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-spring-boot-starter-biz-ip</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<name>marketing-spring-boot-starter-biz-ip</name>
|
||||||
|
<description>IP 拓展,支持如下功能:
|
||||||
|
1. IP 功能:查询 IP 对应的城市信息
|
||||||
|
基于 https://gitee.com/lionsoul/ip2region 实现
|
||||||
|
2. 城市功能:查询城市编码对应的城市信息
|
||||||
|
基于 https://github.com/modood/Administrative-divisions-of-China 实现</description>
|
||||||
|
<url>https://github.com/makemyownlife/platform-sms-pro</url>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-common</artifactId>
|
||||||
|
<version>0.0.1-jdk8-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
<version>2.7.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.36</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>1.7.36</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>huaweicloud</id>
|
||||||
|
<name>huawei</name>
|
||||||
|
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>aliyunmaven</id>
|
||||||
|
<name>aliyun</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?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">
|
||||||
|
<parent>
|
||||||
|
<artifactId>marketing-framework</artifactId>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>marketing-spring-boot-starter-biz-ip</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>IP 拓展,支持如下功能:
|
||||||
|
1. IP 功能:查询 IP 对应的城市信息
|
||||||
|
基于 https://gitee.com/lionsoul/ip2region 实现
|
||||||
|
2. 城市功能:查询城市编码对应的城市信息
|
||||||
|
基于 https://github.com/modood/Administrative-divisions-of-China 实现
|
||||||
|
</description>
|
||||||
|
<url>https://github.com/makemyownlife/platform-sms-pro</url>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.platform</groupId>
|
||||||
|
<artifactId>marketing-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- IP地址检索 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.platform.marketing.framework.ip.core;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.ip.core.enums.AreaTypeEnum;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 区域节点,包括国家、省份、城市、地区等信息
|
||||||
|
*
|
||||||
|
* 数据可见 resources/area.csv 文件
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@ToString(exclude = {"parent"}) // 参见 https://gitee.com/marketingcode/marketing-cloud-mini/pulls/2 原因
|
||||||
|
public class Area {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编号 - 全球,即根目录
|
||||||
|
*/
|
||||||
|
public static final Integer ID_GLOBAL = 0;
|
||||||
|
/**
|
||||||
|
* 编号 - 中国
|
||||||
|
*/
|
||||||
|
public static final Integer ID_CHINA = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编号
|
||||||
|
*/
|
||||||
|
private Integer id;
|
||||||
|
/**
|
||||||
|
* 名字
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*
|
||||||
|
* 枚举 {@link AreaTypeEnum}
|
||||||
|
*/
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父节点
|
||||||
|
*/
|
||||||
|
@JsonManagedReference
|
||||||
|
private Area parent;
|
||||||
|
/**
|
||||||
|
* 子节点
|
||||||
|
*/
|
||||||
|
@JsonBackReference
|
||||||
|
private List<Area> children;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.platform.marketing.framework.ip.core.enums;
|
||||||
|
|
||||||
|
import com.platform.marketing.framework.common.core.ArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 区域类型枚举
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum AreaTypeEnum implements ArrayValuable<Integer> {
|
||||||
|
|
||||||
|
COUNTRY(1, "国家"),
|
||||||
|
PROVINCE(2, "省份"),
|
||||||
|
CITY(3, "城市"),
|
||||||
|
DISTRICT(4, "地区"), // 县、镇、区等
|
||||||
|
;
|
||||||
|
|
||||||
|
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AreaTypeEnum::getType).toArray(Integer[]::new);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private final Integer type;
|
||||||
|
/**
|
||||||
|
* 名字
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,214 @@
|
||||||
|
package com.platform.marketing.framework.ip.core.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.resource.ResourceUtil;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.text.csv.CsvRow;
|
||||||
|
import cn.hutool.core.text.csv.CsvUtil;
|
||||||
|
import com.platform.marketing.framework.common.util.object.ObjectUtils;
|
||||||
|
import com.platform.marketing.framework.ip.core.Area;
|
||||||
|
import com.platform.marketing.framework.ip.core.enums.AreaTypeEnum;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static com.platform.marketing.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
import static com.platform.marketing.framework.common.util.collection.CollectionUtils.findFirst;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 区域工具类
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class AreaUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化 SEARCHER
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("InstantiationOfUtilityClass")
|
||||||
|
private final static AreaUtils INSTANCE = new AreaUtils();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Area 内存缓存,提升访问速度
|
||||||
|
*/
|
||||||
|
private static Map<Integer, Area> areas;
|
||||||
|
|
||||||
|
private AreaUtils() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
areas = new HashMap<>();
|
||||||
|
areas.put(Area.ID_GLOBAL, new Area(Area.ID_GLOBAL, "全球", 0,
|
||||||
|
null, new ArrayList<>()));
|
||||||
|
// 从 csv 中加载数据
|
||||||
|
List<CsvRow> rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader("area.csv")).getRows();
|
||||||
|
rows.remove(0); // 删除 header
|
||||||
|
for (CsvRow row : rows) {
|
||||||
|
// 创建 Area 对象
|
||||||
|
Area area = new Area(Integer.valueOf(row.get(0)), row.get(1), Integer.valueOf(row.get(2)),
|
||||||
|
null, new ArrayList<>());
|
||||||
|
// 添加到 areas 中
|
||||||
|
areas.put(area.getId(), area);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建父子关系:因为 Area 中没有 parentId 字段,所以需要重复读取
|
||||||
|
for (CsvRow row : rows) {
|
||||||
|
Area area = areas.get(Integer.valueOf(row.get(0))); // 自己
|
||||||
|
Area parent = areas.get(Integer.valueOf(row.get(3))); // 父
|
||||||
|
Assert.isTrue(area != parent, "{}:父子节点相同", area.getName());
|
||||||
|
area.setParent(parent);
|
||||||
|
parent.getChildren().add(area);
|
||||||
|
}
|
||||||
|
log.info("启动加载 AreaUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定编号对应的区域
|
||||||
|
*
|
||||||
|
* @param id 区域编号
|
||||||
|
* @return 区域
|
||||||
|
*/
|
||||||
|
public static Area getArea(Integer id) {
|
||||||
|
return areas.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定区域对应的编号
|
||||||
|
*
|
||||||
|
* @param pathStr 区域路径,例如说:河南省/石家庄市/新华区
|
||||||
|
* @return 区域
|
||||||
|
*/
|
||||||
|
public static Area parseArea(String pathStr) {
|
||||||
|
String[] paths = pathStr.split("/");
|
||||||
|
Area area = null;
|
||||||
|
for (String path : paths) {
|
||||||
|
if (area == null) {
|
||||||
|
area = findFirst(areas.values(), item -> item.getName().equals(path));
|
||||||
|
} else {
|
||||||
|
area = findFirst(area.getChildren(), item -> item.getName().equals(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有节点的全路径名称如:河南省/石家庄市/新华区
|
||||||
|
*
|
||||||
|
* @param areas 地区树
|
||||||
|
* @return 所有节点的全路径名称
|
||||||
|
*/
|
||||||
|
public static List<String> getAreaNodePathList(List<Area> areas) {
|
||||||
|
List<String> paths = new ArrayList<>();
|
||||||
|
areas.forEach(area -> getAreaNodePathList(area, "", paths));
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建一棵树的所有节点的全路径名称,并将其存储为 "祖先/父级/子级" 的形式
|
||||||
|
*
|
||||||
|
* @param node 父节点
|
||||||
|
* @param path 全路径名称
|
||||||
|
* @param paths 全路径名称列表,省份/城市/地区
|
||||||
|
*/
|
||||||
|
private static void getAreaNodePathList(Area node, String path, List<String> paths) {
|
||||||
|
if (node == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 构建当前节点的路径
|
||||||
|
String currentPath = path.isEmpty() ? node.getName() : path + "/" + node.getName();
|
||||||
|
paths.add(currentPath);
|
||||||
|
// 递归遍历子节点
|
||||||
|
for (Area child : node.getChildren()) {
|
||||||
|
getAreaNodePathList(child, currentPath, paths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化区域
|
||||||
|
*
|
||||||
|
* @param id 区域编号
|
||||||
|
* @return 格式化后的区域
|
||||||
|
*/
|
||||||
|
public static String format(Integer id) {
|
||||||
|
return format(id, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化区域
|
||||||
|
*
|
||||||
|
* 例如说:
|
||||||
|
* 1. id = “静安区”时:上海 上海市 静安区
|
||||||
|
* 2. id = “上海市”时:上海 上海市
|
||||||
|
* 3. id = “上海”时:上海
|
||||||
|
* 4. id = “美国”时:美国
|
||||||
|
* 当区域在中国时,默认不显示中国
|
||||||
|
*
|
||||||
|
* @param id 区域编号
|
||||||
|
* @param separator 分隔符
|
||||||
|
* @return 格式化后的区域
|
||||||
|
*/
|
||||||
|
public static String format(Integer id, String separator) {
|
||||||
|
// 获得区域
|
||||||
|
Area area = areas.get(id);
|
||||||
|
if (area == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < AreaTypeEnum.values().length; i++) { // 避免死循环
|
||||||
|
sb.insert(0, area.getName());
|
||||||
|
// “递归”父节点
|
||||||
|
area = area.getParent();
|
||||||
|
if (area == null
|
||||||
|
|| ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.insert(0, separator);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定类型的区域列表
|
||||||
|
*
|
||||||
|
* @param type 区域类型
|
||||||
|
* @param func 转换函数
|
||||||
|
* @param <T> 结果类型
|
||||||
|
* @return 区域列表
|
||||||
|
*/
|
||||||
|
public static <T> List<T> getByType(AreaTypeEnum type, Function<Area, T> func) {
|
||||||
|
return convertList(areas.values(), func, area -> type.getType().equals(area.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据区域编号、上级区域类型,获取上级区域编号
|
||||||
|
*
|
||||||
|
* @param id 区域编号
|
||||||
|
* @param type 区域类型
|
||||||
|
* @return 上级区域编号
|
||||||
|
*/
|
||||||
|
public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {
|
||||||
|
for (int i = 0; i < Byte.MAX_VALUE; i++) {
|
||||||
|
Area area = AreaUtils.getArea(id);
|
||||||
|
if (area == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 情况一:匹配到,返回它
|
||||||
|
if (type.getType().equals(area.getType())) {
|
||||||
|
return area.getId();
|
||||||
|
}
|
||||||
|
// 情况二:找到根节点,返回空
|
||||||
|
if (area.getParent() == null || area.getParent().getId() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 其它:继续向上查找
|
||||||
|
id = area.getParent().getId();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue