Repository: macrozheng/mall-learning
Branch: master
Commit: cd02c000e57c
Files: 936
Total size: 5.4 MB
Directory structure:
gitextract_txlavdb6/
├── .gitignore
├── README.md
├── docs/
│ ├── .nojekyll
│ ├── README.md
│ ├── _coverpage.md
│ ├── _navbar.md
│ ├── _sidebar.md
│ ├── architect/
│ │ ├── mall_arch_01.md
│ │ ├── mall_arch_02.md
│ │ ├── mall_arch_03.md
│ │ ├── mall_arch_04.md
│ │ ├── mall_arch_05.md
│ │ ├── mall_arch_06.md
│ │ ├── mall_arch_07.md
│ │ ├── mall_arch_08.md
│ │ ├── mall_arch_09.md
│ │ └── mall_arch_10.md
│ ├── cloud/
│ │ ├── admin.md
│ │ ├── bus.md
│ │ ├── config.md
│ │ ├── consul.md
│ │ ├── eureka.md
│ │ ├── feign.md
│ │ ├── gateway.md
│ │ ├── gateway_oauth2.md
│ │ ├── hystrix.md
│ │ ├── hystrix_dashboard.md
│ │ ├── knife4j_cloud.md
│ │ ├── nacos.md
│ │ ├── oauth2.md
│ │ ├── oauth2_custom.md
│ │ ├── oauth2_jwt.md
│ │ ├── oauth2_sso.md
│ │ ├── ribbon.md
│ │ ├── seata.md
│ │ ├── sentinel.md
│ │ ├── sleuth.md
│ │ ├── springcloud.md
│ │ └── zuul.md
│ ├── database/
│ │ ├── mall_database_overview.md
│ │ ├── mall_oms_01.md
│ │ ├── mall_oms_02.md
│ │ ├── mall_oms_03.md
│ │ ├── mall_permission.md
│ │ ├── mall_pms_01.md
│ │ ├── mall_pms_02.md
│ │ ├── mall_sms_01.md
│ │ ├── mall_sms_02.md
│ │ └── mall_sms_03.md
│ ├── deploy/
│ │ ├── mall_deploy_docker.md
│ │ ├── mall_deploy_docker_compose.md
│ │ ├── mall_deploy_jenkins.md
│ │ ├── mall_deploy_web.md
│ │ ├── mall_deploy_windows.md
│ │ ├── mall_swarm_deploy_docker.md
│ │ ├── mall_swarm_deploy_jenkins.md
│ │ ├── mall_swarm_deploy_k8s.md
│ │ └── mall_swarm_deploy_windows.md
│ ├── foreword/
│ │ ├── mall_foreword_01.md
│ │ └── mall_foreword_02.md
│ ├── index.html
│ ├── lib/
│ │ ├── docsify/
│ │ │ └── lib/
│ │ │ ├── plugins/
│ │ │ │ ├── ga.js
│ │ │ │ └── search.js
│ │ │ └── themes/
│ │ │ └── vue.css
│ │ └── prismjs/
│ │ └── components/
│ │ ├── prism-bash.js
│ │ ├── prism-java.js
│ │ ├── prism-sql.js
│ │ └── prism-yaml.js
│ ├── mine/
│ │ ├── mall_learning_path.md
│ │ └── vue_learning.md
│ ├── reference/
│ │ ├── arthas_start.md
│ │ ├── canal_start.md
│ │ ├── datagrip_start.md
│ │ ├── docker.md
│ │ ├── docker_command.md
│ │ ├── docker_compose.md
│ │ ├── docker_file.md
│ │ ├── docker_maven.md
│ │ ├── docker_protect_socket.md
│ │ ├── efk_fluent.md
│ │ ├── elastic_apm_start.md
│ │ ├── elasticsearch_sql_start.md
│ │ ├── elasticsearch_start.md
│ │ ├── elk_security.md
│ │ ├── filebeat_start.md
│ │ ├── flyway_start.md
│ │ ├── gaea.md
│ │ ├── gitlab.md
│ │ ├── gogs_start.md
│ │ ├── harbor_start.md
│ │ ├── hutool.md
│ │ ├── hutool_start.md
│ │ ├── idea.md
│ │ ├── idea_git.md
│ │ ├── idea_plugins.md
│ │ ├── idea_springboot.md
│ │ ├── jenkins.md
│ │ ├── jenkins_vue.md
│ │ ├── jose_jwt_start.md
│ │ ├── knife4j_start.md
│ │ ├── linux.md
│ │ ├── linux_command.md
│ │ ├── linux_firewall.md
│ │ ├── linux_install.md
│ │ ├── lombok_start.md
│ │ ├── mall_elk_advance.md
│ │ ├── maven_docker_fabric8.md
│ │ ├── minio.md
│ │ ├── mongodb_start.md
│ │ ├── my_debug_skill.md
│ │ ├── my_tools.md
│ │ ├── my_web_tools.md
│ │ ├── mybatis_dynamic_sql.md
│ │ ├── mybatis_generator_start.md
│ │ ├── mybatis_plus_start.md
│ │ ├── mysql.md
│ │ ├── mysql_master_slave.md
│ │ ├── mysql_workbench.md
│ │ ├── navicat.md
│ │ ├── navicat_designer.md
│ │ ├── nginx.md
│ │ ├── nginx_https_start.md
│ │ ├── postman.md
│ │ ├── power_job_start.md
│ │ ├── quartz_start.md
│ │ ├── rabbitmq_mqtt_start.md
│ │ ├── rabbitmq_start.md
│ │ ├── redis_cluster.md
│ │ ├── redis_desktop_start.md
│ │ ├── spring_data_redis.md
│ │ ├── springboot_docker_plugin.md
│ │ ├── springboot_start.md
│ │ ├── swagger_postman.md
│ │ ├── swagger_starter.md
│ │ ├── yapi_start.md
│ │ └── zentao.md
│ └── technology/
│ ├── aop_log.md
│ ├── elasticsearch_upgrade.md
│ ├── gateway_cors.md
│ ├── java_stream.md
│ ├── mall_permission_question.md
│ ├── mall_tiny_elk.md
│ ├── minio_use.md
│ ├── mybatis_mapper.md
│ ├── permission_back.md
│ ├── permission_front.md
│ ├── product_search.md
│ ├── product_sku.md
│ ├── rabbitmq_delay.md
│ ├── redis_permission.md
│ ├── springboot_auto_deploy.md
│ ├── springboot_cors.md
│ ├── springboot_validator.md
│ ├── springsecurity_use.md
│ └── swagger_upgrade.md
├── document/
│ ├── json/
│ │ └── accounts.json
│ ├── navicat/
│ │ ├── mall数据库模型.ndm2
│ │ ├── 会员模块数据库模型.ndm2
│ │ ├── 商品模块数据库模型.ndm2
│ │ ├── 权限模块数据库模型.ndm2
│ │ ├── 营销模块数据库模型.ndm2
│ │ └── 订单模块数据库模型.ndm2
│ ├── pos/
│ │ ├── app.pos
│ │ ├── oms.pos
│ │ ├── pms.pos
│ │ ├── sms.pos
│ │ └── ums.pos
│ └── sql/
│ └── mall_tiny.sql
├── mall-tiny/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ ├── api/
│ │ │ │ │ ├── CommonPage.java
│ │ │ │ │ ├── CommonResult.java
│ │ │ │ │ ├── IErrorCode.java
│ │ │ │ │ └── ResultCode.java
│ │ │ │ └── utils/
│ │ │ │ └── JwtTokenUtil.java
│ │ │ ├── component/
│ │ │ │ ├── CancelOrderReceiver.java
│ │ │ │ ├── CancelOrderSender.java
│ │ │ │ ├── JwtAuthenticationTokenFilter.java
│ │ │ │ ├── RestAuthenticationEntryPoint.java
│ │ │ │ └── RestfulAccessDeniedHandler.java
│ │ │ ├── config/
│ │ │ │ ├── GlobalCorsConfig.java
│ │ │ │ ├── IgnoreUrlsConfig.java
│ │ │ │ ├── MallSecurityConfig.java
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── RabbitMqConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── EsProductController.java
│ │ │ │ ├── MemberReadHistoryController.java
│ │ │ │ ├── MinioController.java
│ │ │ │ ├── OmsPortalOrderController.java
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ ├── UmsAdminController.java
│ │ │ │ └── UmsMemberController.java
│ │ │ ├── dao/
│ │ │ │ └── EsProductDao.java
│ │ │ ├── domain/
│ │ │ │ ├── AdminUserDetails.java
│ │ │ │ └── UmsResource.java
│ │ │ ├── dto/
│ │ │ │ ├── BucketPolicyConfigDto.java
│ │ │ │ ├── MinioUploadDto.java
│ │ │ │ ├── OrderParam.java
│ │ │ │ └── QueueEnum.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ ├── nosql/
│ │ │ │ ├── elasticsearch/
│ │ │ │ │ ├── document/
│ │ │ │ │ │ ├── EsProduct.java
│ │ │ │ │ │ └── EsProductAttributeValue.java
│ │ │ │ │ └── repository/
│ │ │ │ │ └── EsProductRepository.java
│ │ │ │ └── mongodb/
│ │ │ │ ├── document/
│ │ │ │ │ └── MemberReadHistory.java
│ │ │ │ └── repository/
│ │ │ │ └── MemberReadHistoryRepository.java
│ │ │ └── service/
│ │ │ ├── EsProductService.java
│ │ │ ├── MemberReadHistoryService.java
│ │ │ ├── OmsPortalOrderService.java
│ │ │ ├── PmsBrandService.java
│ │ │ ├── RedisService.java
│ │ │ ├── UmsAdminService.java
│ │ │ ├── UmsMemberService.java
│ │ │ └── impl/
│ │ │ ├── EsProductServiceImpl.java
│ │ │ ├── MemberReadHistoryServiceImpl.java
│ │ │ ├── OmsPortalOrderServiceImpl.java
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ ├── RedisServiceImpl.java
│ │ │ ├── UmsAdminServiceImpl.java
│ │ │ └── UmsMemberServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── dao/
│ │ │ └── EsProductDao.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-01/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ └── MyBatisConfig.java
│ │ │ ├── controller/
│ │ │ │ └── PmsBrandController.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ └── impl/
│ │ │ └── PmsBrandServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-02/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ └── PmsBrandController.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ └── impl/
│ │ │ └── PmsBrandServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-03/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ └── UmsMemberController.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ ├── RedisService.java
│ │ │ ├── UmsMemberService.java
│ │ │ └── impl/
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ ├── RedisServiceImpl.java
│ │ │ └── UmsMemberServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-04/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ ├── api/
│ │ │ │ │ ├── CommonPage.java
│ │ │ │ │ ├── CommonResult.java
│ │ │ │ │ ├── IErrorCode.java
│ │ │ │ │ └── ResultCode.java
│ │ │ │ └── utils/
│ │ │ │ └── JwtTokenUtil.java
│ │ │ ├── component/
│ │ │ │ ├── JwtAuthenticationTokenFilter.java
│ │ │ │ ├── RestAuthenticationEntryPoint.java
│ │ │ │ └── RestfulAccessDeniedHandler.java
│ │ │ ├── config/
│ │ │ │ ├── IgnoreUrlsConfig.java
│ │ │ │ ├── MallSecurityConfig.java
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ ├── UmsAdminController.java
│ │ │ │ └── UmsMemberController.java
│ │ │ ├── domain/
│ │ │ │ ├── AdminUserDetails.java
│ │ │ │ └── UmsResource.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ ├── RedisService.java
│ │ │ ├── UmsAdminService.java
│ │ │ ├── UmsMemberService.java
│ │ │ └── impl/
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ ├── RedisServiceImpl.java
│ │ │ ├── UmsAdminServiceImpl.java
│ │ │ └── UmsMemberServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-05/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ ├── api/
│ │ │ │ │ ├── CommonPage.java
│ │ │ │ │ ├── CommonResult.java
│ │ │ │ │ ├── IErrorCode.java
│ │ │ │ │ └── ResultCode.java
│ │ │ │ └── utils/
│ │ │ │ └── JwtTokenUtil.java
│ │ │ ├── component/
│ │ │ │ ├── JwtAuthenticationTokenFilter.java
│ │ │ │ ├── RestAuthenticationEntryPoint.java
│ │ │ │ └── RestfulAccessDeniedHandler.java
│ │ │ ├── config/
│ │ │ │ ├── IgnoreUrlsConfig.java
│ │ │ │ ├── MallSecurityConfig.java
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── EsProductController.java
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ ├── UmsAdminController.java
│ │ │ │ └── UmsMemberController.java
│ │ │ ├── dao/
│ │ │ │ └── EsProductDao.java
│ │ │ ├── domain/
│ │ │ │ ├── AdminUserDetails.java
│ │ │ │ └── UmsResource.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ ├── nosql/
│ │ │ │ └── elasticsearch/
│ │ │ │ ├── document/
│ │ │ │ │ ├── EsProduct.java
│ │ │ │ │ └── EsProductAttributeValue.java
│ │ │ │ └── repository/
│ │ │ │ └── EsProductRepository.java
│ │ │ └── service/
│ │ │ ├── EsProductService.java
│ │ │ ├── PmsBrandService.java
│ │ │ ├── RedisService.java
│ │ │ ├── UmsAdminService.java
│ │ │ ├── UmsMemberService.java
│ │ │ └── impl/
│ │ │ ├── EsProductServiceImpl.java
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ ├── RedisServiceImpl.java
│ │ │ ├── UmsAdminServiceImpl.java
│ │ │ └── UmsMemberServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── dao/
│ │ │ └── EsProductDao.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-06/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ ├── api/
│ │ │ │ │ ├── CommonPage.java
│ │ │ │ │ ├── CommonResult.java
│ │ │ │ │ ├── IErrorCode.java
│ │ │ │ │ └── ResultCode.java
│ │ │ │ └── utils/
│ │ │ │ └── JwtTokenUtil.java
│ │ │ ├── component/
│ │ │ │ ├── JwtAuthenticationTokenFilter.java
│ │ │ │ ├── RestAuthenticationEntryPoint.java
│ │ │ │ └── RestfulAccessDeniedHandler.java
│ │ │ ├── config/
│ │ │ │ ├── IgnoreUrlsConfig.java
│ │ │ │ ├── MallSecurityConfig.java
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── EsProductController.java
│ │ │ │ ├── MemberReadHistoryController.java
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ ├── UmsAdminController.java
│ │ │ │ └── UmsMemberController.java
│ │ │ ├── dao/
│ │ │ │ └── EsProductDao.java
│ │ │ ├── domain/
│ │ │ │ ├── AdminUserDetails.java
│ │ │ │ └── UmsResource.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ ├── nosql/
│ │ │ │ ├── elasticsearch/
│ │ │ │ │ ├── document/
│ │ │ │ │ │ ├── EsProduct.java
│ │ │ │ │ │ └── EsProductAttributeValue.java
│ │ │ │ │ └── repository/
│ │ │ │ │ └── EsProductRepository.java
│ │ │ │ └── mongodb/
│ │ │ │ ├── document/
│ │ │ │ │ └── MemberReadHistory.java
│ │ │ │ └── repository/
│ │ │ │ └── MemberReadHistoryRepository.java
│ │ │ └── service/
│ │ │ ├── EsProductService.java
│ │ │ ├── MemberReadHistoryService.java
│ │ │ ├── PmsBrandService.java
│ │ │ ├── RedisService.java
│ │ │ ├── UmsAdminService.java
│ │ │ ├── UmsMemberService.java
│ │ │ └── impl/
│ │ │ ├── EsProductServiceImpl.java
│ │ │ ├── MemberReadHistoryServiceImpl.java
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ ├── RedisServiceImpl.java
│ │ │ ├── UmsAdminServiceImpl.java
│ │ │ └── UmsMemberServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── dao/
│ │ │ └── EsProductDao.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-07/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ ├── api/
│ │ │ │ │ ├── CommonPage.java
│ │ │ │ │ ├── CommonResult.java
│ │ │ │ │ ├── IErrorCode.java
│ │ │ │ │ └── ResultCode.java
│ │ │ │ └── utils/
│ │ │ │ └── JwtTokenUtil.java
│ │ │ ├── component/
│ │ │ │ ├── CancelOrderReceiver.java
│ │ │ │ ├── CancelOrderSender.java
│ │ │ │ ├── JwtAuthenticationTokenFilter.java
│ │ │ │ ├── RestAuthenticationEntryPoint.java
│ │ │ │ └── RestfulAccessDeniedHandler.java
│ │ │ ├── config/
│ │ │ │ ├── IgnoreUrlsConfig.java
│ │ │ │ ├── MallSecurityConfig.java
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── RabbitMqConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── EsProductController.java
│ │ │ │ ├── MemberReadHistoryController.java
│ │ │ │ ├── OmsPortalOrderController.java
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ ├── UmsAdminController.java
│ │ │ │ └── UmsMemberController.java
│ │ │ ├── dao/
│ │ │ │ └── EsProductDao.java
│ │ │ ├── domain/
│ │ │ │ ├── AdminUserDetails.java
│ │ │ │ └── UmsResource.java
│ │ │ ├── dto/
│ │ │ │ ├── OrderParam.java
│ │ │ │ └── QueueEnum.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ ├── nosql/
│ │ │ │ ├── elasticsearch/
│ │ │ │ │ ├── document/
│ │ │ │ │ │ ├── EsProduct.java
│ │ │ │ │ │ └── EsProductAttributeValue.java
│ │ │ │ │ └── repository/
│ │ │ │ │ └── EsProductRepository.java
│ │ │ │ └── mongodb/
│ │ │ │ ├── document/
│ │ │ │ │ └── MemberReadHistory.java
│ │ │ │ └── repository/
│ │ │ │ └── MemberReadHistoryRepository.java
│ │ │ └── service/
│ │ │ ├── EsProductService.java
│ │ │ ├── MemberReadHistoryService.java
│ │ │ ├── OmsPortalOrderService.java
│ │ │ ├── PmsBrandService.java
│ │ │ ├── RedisService.java
│ │ │ ├── UmsAdminService.java
│ │ │ ├── UmsMemberService.java
│ │ │ └── impl/
│ │ │ ├── EsProductServiceImpl.java
│ │ │ ├── MemberReadHistoryServiceImpl.java
│ │ │ ├── OmsPortalOrderServiceImpl.java
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ ├── RedisServiceImpl.java
│ │ │ ├── UmsAdminServiceImpl.java
│ │ │ └── UmsMemberServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── dao/
│ │ │ └── EsProductDao.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-08/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ ├── api/
│ │ │ │ │ ├── CommonPage.java
│ │ │ │ │ ├── CommonResult.java
│ │ │ │ │ ├── IErrorCode.java
│ │ │ │ │ └── ResultCode.java
│ │ │ │ └── utils/
│ │ │ │ └── JwtTokenUtil.java
│ │ │ ├── component/
│ │ │ │ ├── CancelOrderReceiver.java
│ │ │ │ ├── CancelOrderSender.java
│ │ │ │ ├── JwtAuthenticationTokenFilter.java
│ │ │ │ ├── RestAuthenticationEntryPoint.java
│ │ │ │ └── RestfulAccessDeniedHandler.java
│ │ │ ├── config/
│ │ │ │ ├── GlobalCorsConfig.java
│ │ │ │ ├── IgnoreUrlsConfig.java
│ │ │ │ ├── MallSecurityConfig.java
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── RabbitMqConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── EsProductController.java
│ │ │ │ ├── MemberReadHistoryController.java
│ │ │ │ ├── MinioController.java
│ │ │ │ ├── OmsPortalOrderController.java
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ ├── UmsAdminController.java
│ │ │ │ └── UmsMemberController.java
│ │ │ ├── dao/
│ │ │ │ └── EsProductDao.java
│ │ │ ├── domain/
│ │ │ │ ├── AdminUserDetails.java
│ │ │ │ └── UmsResource.java
│ │ │ ├── dto/
│ │ │ │ ├── BucketPolicyConfigDto.java
│ │ │ │ ├── MinioUploadDto.java
│ │ │ │ ├── OrderParam.java
│ │ │ │ └── QueueEnum.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ ├── nosql/
│ │ │ │ ├── elasticsearch/
│ │ │ │ │ ├── document/
│ │ │ │ │ │ ├── EsProduct.java
│ │ │ │ │ │ └── EsProductAttributeValue.java
│ │ │ │ │ └── repository/
│ │ │ │ │ └── EsProductRepository.java
│ │ │ │ └── mongodb/
│ │ │ │ ├── document/
│ │ │ │ │ └── MemberReadHistory.java
│ │ │ │ └── repository/
│ │ │ │ └── MemberReadHistoryRepository.java
│ │ │ └── service/
│ │ │ ├── EsProductService.java
│ │ │ ├── MemberReadHistoryService.java
│ │ │ ├── OmsPortalOrderService.java
│ │ │ ├── PmsBrandService.java
│ │ │ ├── RedisService.java
│ │ │ ├── UmsAdminService.java
│ │ │ ├── UmsMemberService.java
│ │ │ └── impl/
│ │ │ ├── EsProductServiceImpl.java
│ │ │ ├── MemberReadHistoryServiceImpl.java
│ │ │ ├── OmsPortalOrderServiceImpl.java
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ ├── RedisServiceImpl.java
│ │ │ ├── UmsAdminServiceImpl.java
│ │ │ └── UmsMemberServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── dao/
│ │ │ └── EsProductDao.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-alipay/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── AlipayClientConfig.java
│ │ │ │ ├── AlipayConfig.java
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── AlipayController.java
│ │ │ │ ├── AlipayOrderController.java
│ │ │ │ └── PmsBrandController.java
│ │ │ ├── dto/
│ │ │ │ └── AliPayParam.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ ├── AlipayOrderMapper.java
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── AlipayOrder.java
│ │ │ │ ├── AlipayOrderExample.java
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── AlipayOrderService.java
│ │ │ ├── AlipayService.java
│ │ │ ├── PmsBrandService.java
│ │ │ └── impl/
│ │ │ ├── AlipayOrderServiceImpl.java
│ │ │ ├── AlipayServiceImpl.java
│ │ │ └── PmsBrandServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ ├── AlipayOrderMapper.xml
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-boot/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ ├── api/
│ │ │ │ │ ├── CommonPage.java
│ │ │ │ │ ├── CommonResult.java
│ │ │ │ │ ├── IErrorCode.java
│ │ │ │ │ └── ResultCode.java
│ │ │ │ └── utils/
│ │ │ │ └── JwtTokenUtil.java
│ │ │ ├── component/
│ │ │ │ ├── JwtAuthenticationTokenFilter.java
│ │ │ │ ├── RestAuthenticationEntryPoint.java
│ │ │ │ └── RestfulAccessDeniedHandler.java
│ │ │ ├── config/
│ │ │ │ ├── IgnoreUrlsConfig.java
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ └── UmsAdminController.java
│ │ │ ├── domain/
│ │ │ │ └── AdminUserDetails.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ ├── UmsAdminService.java
│ │ │ └── impl/
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ └── UmsAdminServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-docker/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── docker/
│ │ │ ├── Dockerfile
│ │ │ └── docker-compose.yml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ └── PmsBrandController.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ └── impl/
│ │ │ └── PmsBrandServiceImpl.java
│ │ └── resources/
│ │ ├── application-prod.yml
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-generator/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ └── UmsAdminController.java
│ │ │ ├── dao/
│ │ │ │ └── UmsAdminDao.java
│ │ │ ├── domain/
│ │ │ │ ├── AdminRoleDto.java
│ │ │ │ ├── ResourceWithCateDto.java
│ │ │ │ └── RoleStatDto.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ ├── UmsAdminMapper.java
│ │ │ │ │ ├── UmsAdminRoleRelationMapper.java
│ │ │ │ │ ├── UmsResourceCategoryMapper.java
│ │ │ │ │ ├── UmsResourceMapper.java
│ │ │ │ │ └── UmsRoleMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── UmsAdmin.java
│ │ │ │ ├── UmsAdminExample.java
│ │ │ │ ├── UmsAdminRoleRelation.java
│ │ │ │ ├── UmsAdminRoleRelationExample.java
│ │ │ │ ├── UmsResource.java
│ │ │ │ ├── UmsResourceCategory.java
│ │ │ │ ├── UmsResourceCategoryExample.java
│ │ │ │ ├── UmsResourceExample.java
│ │ │ │ ├── UmsRole.java
│ │ │ │ └── UmsRoleExample.java
│ │ │ └── service/
│ │ │ ├── UmsAdminService.java
│ │ │ └── impl/
│ │ │ └── UmsAdminServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ ├── UmsAdminMapper.xml
│ │ │ ├── UmsAdminRoleRelationMapper.xml
│ │ │ ├── UmsResourceCategoryMapper.xml
│ │ │ ├── UmsResourceMapper.xml
│ │ │ └── UmsRoleMapper.xml
│ │ ├── dao/
│ │ │ └── UmsAdminDao.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-hutool/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ └── HutoolController.java
│ │ │ └── domain/
│ │ │ └── PmsBrand.java
│ │ └── resources/
│ │ ├── application.yml
│ │ └── generator.properties
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-jenkins/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── docker/
│ │ │ ├── Dockerfile
│ │ │ ├── docker-compose.yml
│ │ │ └── mall-tiny-jenkins.sh
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ └── PmsBrandController.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ └── impl/
│ │ │ └── PmsBrandServiceImpl.java
│ │ └── resources/
│ │ ├── application-prod.yml
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-lombok/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ └── example/
│ │ │ ├── BuilderExample.java
│ │ │ ├── CleanupExample.java
│ │ │ ├── ConstructorExample.java
│ │ │ ├── DataExample.java
│ │ │ ├── EqualsAndHashCodeExample.java
│ │ │ ├── GetterLazyExample.java
│ │ │ ├── GetterSetterExample.java
│ │ │ ├── LogExample.java
│ │ │ ├── LogSlf4jExample.java
│ │ │ ├── NonNullExample.java
│ │ │ ├── SneakyThrowsExample.java
│ │ │ ├── SynchronizedExample.java
│ │ │ ├── ToStringExample.java
│ │ │ ├── ValExample.java
│ │ │ ├── ValueExample.java
│ │ │ └── WithExample.java
│ │ └── resources/
│ │ └── application.yml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-mybatis/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── dao/
│ │ │ │ ├── UmsAdminDao.java
│ │ │ │ ├── UmsResourceCategoryDao.java
│ │ │ │ └── UmsResourceDao.java
│ │ │ ├── domain/
│ │ │ │ ├── UmsResourceCategoryExt.java
│ │ │ │ └── UmsResourceExt.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ └── Generator.java
│ │ │ ├── model/
│ │ │ │ ├── UmsAdmin.java
│ │ │ │ ├── UmsAdminLoginLog.java
│ │ │ │ ├── UmsAdminRoleRelation.java
│ │ │ │ ├── UmsMenu.java
│ │ │ │ ├── UmsResource.java
│ │ │ │ ├── UmsResourceCategory.java
│ │ │ │ ├── UmsRole.java
│ │ │ │ ├── UmsRoleMenuRelation.java
│ │ │ │ └── UmsRoleResourceRelation.java
│ │ │ └── service/
│ │ │ ├── UmsResourceService.java
│ │ │ └── impl/
│ │ │ └── UmsResourceServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── dao/
│ │ │ ├── UmsAdminDao.xml
│ │ │ ├── UmsResourceCategoryDao.xml
│ │ │ └── UmsResourceDao.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ ├── MallTinyApplicationTests.java
│ └── test/
│ ├── MyBatisAdvanceTest.java
│ ├── MyBatisBaseTest.java
│ └── MyBatisTagTest.java
├── mall-tiny-plus/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── generator/
│ │ │ │ └── MyBatisPlusGenerator.java
│ │ │ └── modules/
│ │ │ └── pms/
│ │ │ ├── controller/
│ │ │ │ └── PmsBrandController.java
│ │ │ ├── mapper/
│ │ │ │ └── PmsBrandMapper.java
│ │ │ ├── model/
│ │ │ │ └── PmsBrand.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ └── impl/
│ │ │ └── PmsBrandServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── generator.properties
│ │ └── mapper/
│ │ └── pms/
│ │ └── PmsBrandMapper.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-rabbit/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ └── RabbitController.java
│ │ │ ├── direct/
│ │ │ │ ├── DirectRabbitConfig.java
│ │ │ │ ├── DirectReceiver.java
│ │ │ │ └── DirectSender.java
│ │ │ ├── fanout/
│ │ │ │ ├── FanoutRabbitConfig.java
│ │ │ │ ├── FanoutReceiver.java
│ │ │ │ └── FanoutSender.java
│ │ │ ├── simple/
│ │ │ │ ├── SimpleRabbitConfig.java
│ │ │ │ ├── SimpleReceiver.java
│ │ │ │ └── SimpleSender.java
│ │ │ ├── topic/
│ │ │ │ ├── TopicRabbitConfig.java
│ │ │ │ ├── TopicReceiver.java
│ │ │ │ └── TopicSender.java
│ │ │ └── work/
│ │ │ ├── WorkRabbitConfig.java
│ │ │ ├── WorkReceiver.java
│ │ │ └── WorkSender.java
│ │ └── resources/
│ │ └── application.yml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-redis/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── GlobalCorsConfig.java
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ └── RedisController.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ ├── RedisService.java
│ │ │ └── impl/
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ └── RedisServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
├── mall-tiny-stream/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ └── api/
│ │ │ │ ├── CommonPage.java
│ │ │ │ ├── CommonResult.java
│ │ │ │ ├── IErrorCode.java
│ │ │ │ └── ResultCode.java
│ │ │ ├── config/
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ └── UmsMenuController.java
│ │ │ ├── dto/
│ │ │ │ └── UmsMenuNode.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ ├── PmsBrandMapper.java
│ │ │ │ │ └── UmsMenuMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ ├── PmsBrandExample.java
│ │ │ │ ├── UmsMenu.java
│ │ │ │ └── UmsMenuExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ ├── UmsMenuService.java
│ │ │ └── impl/
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ └── UmsMenuServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ ├── PmsBrandMapper.xml
│ │ │ └── UmsMenuMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ ├── MallTinyApplicationTests.java
│ └── stream/
│ └── StreamApiTest.java
├── mall-tiny-swagger/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ ├── MallTinyApplication.java
│ │ │ ├── common/
│ │ │ │ ├── api/
│ │ │ │ │ ├── CommonPage.java
│ │ │ │ │ ├── CommonResult.java
│ │ │ │ │ ├── IErrorCode.java
│ │ │ │ │ └── ResultCode.java
│ │ │ │ └── utils/
│ │ │ │ └── JwtTokenUtil.java
│ │ │ ├── component/
│ │ │ │ ├── JwtAuthenticationTokenFilter.java
│ │ │ │ ├── RestAuthenticationEntryPoint.java
│ │ │ │ └── RestfulAccessDeniedHandler.java
│ │ │ ├── config/
│ │ │ │ ├── MyBatisConfig.java
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ └── Swagger2Config.java
│ │ │ ├── controller/
│ │ │ │ ├── PmsBrandController.java
│ │ │ │ └── UmsAdminController.java
│ │ │ ├── domain/
│ │ │ │ └── AdminUserDetails.java
│ │ │ ├── mbg/
│ │ │ │ ├── CommentGenerator.java
│ │ │ │ ├── Generator.java
│ │ │ │ ├── mapper/
│ │ │ │ │ └── PmsBrandMapper.java
│ │ │ │ └── model/
│ │ │ │ ├── PmsBrand.java
│ │ │ │ └── PmsBrandExample.java
│ │ │ └── service/
│ │ │ ├── PmsBrandService.java
│ │ │ ├── UmsAdminService.java
│ │ │ └── impl/
│ │ │ ├── PmsBrandServiceImpl.java
│ │ │ └── UmsAdminServiceImpl.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── com/
│ │ │ └── macro/
│ │ │ └── mall/
│ │ │ └── tiny/
│ │ │ └── mbg/
│ │ │ └── mapper/
│ │ │ └── PmsBrandMapper.xml
│ │ ├── generator.properties
│ │ └── generatorConfig.xml
│ └── test/
│ └── java/
│ └── com/
│ └── macro/
│ └── mall/
│ └── tiny/
│ └── MallTinyApplicationTests.java
└── pom.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Maven #
target/
# IDEA #
.idea/
*.iml
# Eclipse #
.settings/
.classpath
.project
================================================
FILE: README.md
================================================
# mall学习教程
## 简介
mall学习教程,架构、业务、技术要点全方位解析。mall项目(60k+star)是一套电商系统,使用现阶段主流技术实现。涵盖了SpringBoot、MyBatis、Elasticsearch、RabbitMQ、Redis、MongoDB、MySQL等技术,采用Docker容器化部署。
## 教程网站
- 文档教程:[https://www.macrozheng.com](https://www.macrozheng.com)
- 视频教程:[https://www.macrozheng.com/video/](https://www.macrozheng.com/video/)
## 项目地址
### Github
- 后端项目:[https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
- 后台管理系统项目:[https://github.com/macrozheng/mall-admin-web](https://github.com/macrozheng/mall-admin-web)
- 前台商城项目:[https://github.com/macrozheng/mall-app-web](https://github.com/macrozheng/mall-app-web)
- mall学习教程示例代码:[https://github.com/macrozheng/mall-learning](https://github.com/macrozheng/mall-learning)
### Gitee
- 后端项目:[https://gitee.com/macrozheng/mall](https://gitee.com/macrozheng/mall)
- 后台管理系统项目:[https://gitee.com/macrozheng/mall-admin-web](https://gitee.com/macrozheng/mall-admin-web)
- 前台商城项目:[https://gitee.com/macrozheng/mall-app-web](https://gitee.com/macrozheng/mall-app-web)
- mall学习教程示例代码:[https://gitee.com/macrozheng/mall-learning](https://gitee.com/macrozheng/mall-learning)
## 序章
> 对mall项目的架构、业务及学习思路进行介绍。
- [mall项目架构及功能概览](https://www.macrozheng.com/mall/foreword/mall_foreword_01.html)
- [mall项目核心功能演示](https://www.macrozheng.com/mall/foreword/mall_foreword_02.html)
- [mall项目学习所需知识点](https://www.macrozheng.com/mall/foreword/mall_foreword_03.html)
- [mall项目学习思路及课程介绍](https://www.macrozheng.com/mall/foreword/mall_foreword_04.html)
## 快速开始
> 对mall项目的前后端开发环境搭建进行讲解。
- [mall项目后端开发环境搭建](https://www.macrozheng.com/mall/start/mall_deploy_windows.html)
- [mall项目前端开发环境搭建](https://www.macrozheng.com/mall/start/mall_deploy_web.html)
## 架构篇
> 循序渐进带大家搭建一个mall项目在使用的脚手架,学习主流Java技术栈。涵盖SpringBoot、MyBatis、Lombok、Hutool、Swagger、Redis、SpringSecurity、Elasticsearch、MongoDB、RabbitMQ、MinIO等技术。
- [mall项目架构篇介绍](https://www.macrozheng.com/mall/architect/mall_arch_overview.html)
- [mall整合SpringBoot+MyBatis搭建基本骨架](https://www.macrozheng.com/mall/architect/mall_arch_01.html)
- [mall整合Swagger-UI实现在线API文档](https://www.macrozheng.com/mall/architect/mall_arch_02.html)
- [mall整合Redis实现缓存功能](https://www.macrozheng.com/mall/architect/mall_arch_03.html)
- [mall整合SpringSecurity和JWT实现认证和授权(一)](https://www.macrozheng.com/mall/architect/mall_arch_04.html)
- [mall整合SpringSecurity和JWT实现认证和授权(二)](https://www.macrozheng.com/mall/architect/mall_arch_05.html)
- [mall整合SpringTask实现定时任务](https://www.macrozheng.com/mall/architect/mall_arch_06.html)
- [mall整合Elasticsearch实现商品搜索](https://www.macrozheng.com/mall/architect/mall_arch_07.html)
- [mall整合Mongodb实现文档操作](https://www.macrozheng.com/mall/architect/mall_arch_08.html)
- [mall整合RabbitMQ实现延迟消息](https://www.macrozheng.com/mall/architect/mall_arch_09.html)
- [mall整合OSS实现文件上传](https://www.macrozheng.com/mall/architect/mall_arch_10.html)
## 业务篇
> mall项目电商业务与技术实现全方位解析,涵盖权限模块、商品模块、订单模块、营销模块、会员模块的解析。
- [mall项目后台管理系统业务介绍](https://www.macrozheng.com/mall/database/mall_business_overview.html)
- [mall项目前台商城系统业务介绍](https://www.macrozheng.com/mall/database/mall_business_app_overview.html)
- [mall项目开发设计思路](https://www.macrozheng.com/mall/database/mall_dev_design.html)
- [权限模块数据库表解析](https://www.macrozheng.com/mall/database/mall_ums_01.html)
- [商品模块数据库表解析(一)](https://www.macrozheng.com/mall/database/mall_pms_01.html)
- [商品模块数据库表解析(二)](https://www.macrozheng.com/mall/database/mall_pms_02.html)
- [订单模块数据库表解析(一)](https://www.macrozheng.com/mall/database/mall_oms_01.html)
- [订单模块数据库表解析(二)](https://www.macrozheng.com/mall/database/mall_oms_02.html)
- [订单模块数据库表解析(三)](https://www.macrozheng.com/mall/database/mall_oms_03.html)
- [营销模块数据库表解析(一)](https://www.macrozheng.com/mall/database/mall_sms_01.html)
- [营销模块数据库表解析(二)](https://www.macrozheng.com/mall/database/mall_sms_02.html)
- [营销模块数据库表解析(三)](https://www.macrozheng.com/mall/database/mall_sms_03.html)
- [权限管理功能设计与优化](https://www.macrozheng.com/mall/database/mall_permission.html)
## 部署篇
> 实现mall项目的Docker容器化部署和Jenkins自动化部署,同时学习Linux、Docker、Jenkins等技术。
- [mall项目部署篇介绍](https://www.macrozheng.com/mall/deploy/mall_deploy_overview.html)
- [使用虚拟机安装Linux](https://www.macrozheng.com/mall/deploy/linux_install.html)
- [Linux常用命令](https://www.macrozheng.com/mall/deploy/linux_command.html)
- [Linux防火墙Firewall和Iptables的使用](https://www.macrozheng.com/mall/deploy/linux_firewall.html)
- [Docker环境安装及常用命令](https://www.macrozheng.com/mall/deploy/docker_command.html)
- [使用Maven插件为SpringBoot应用构建Docker镜像](https://www.macrozheng.com/mall/deploy/docker_maven.html)
- [使用Dockerfile为SpringBoot应用构建Docker镜像](https://www.macrozheng.com/mall/deploy/docker_file.html)
- [使用Docker Compose部署SpringBoot应用](https://www.macrozheng.com/mall/deploy/docker_compose.html)
- [MySQL常用命令](https://www.macrozheng.com/mall/deploy/mysql.html)
- [mall在Linux环境下的部署(基于Docker容器)](https://www.macrozheng.com/mall/deploy/mall_deploy_docker.html)
- [mall在Linux环境下的部署(基于Docker Compose)](https://www.macrozheng.com/mall/deploy/mall_deploy_docker_compose.html)
- [在Linux上搭建Git服务](https://www.macrozheng.com/mall/deploy/gogs_start.html)
- [使用Jenkins一键打包部署SpringBoot应用](https://www.macrozheng.com/mall/deploy/jenkins.html)
- [使用Jenkins一键打包部署前端应用](https://www.macrozheng.com/mall/deploy/jenkins_vue.html)
- [mall使用Jenkins实现自动化部署](https://www.macrozheng.com/mall/deploy/mall_deploy_jenkins.html)
## 技术要点篇
> mall中一些功能的技术要点解析,这些技术要点和业务结合地比较紧密。
- [MyBatis Generator使用过程中踩过的一个坑](https://www.macrozheng.com/mall/technology/mybatis_mapper.html)
- [SpringBoot应用中使用AOP记录接口访问日志](https://www.macrozheng.com/mall/technology/aop_log.html)
- [前后端分离项目,如何解决跨域问题](https://www.macrozheng.com/mall/technology/springboot_cors.html)
- [Java 8都出那么久了,Stream API了解下?](https://www.macrozheng.com/mall/technology/java_stream.html)
- [仅需四步,整合SpringSecurity+JWT实现登录认证!](https://www.macrozheng.com/mall/technology/springsecurity_use.html)
- [前后端分离项目,如何优雅实现文件存储!](https://www.macrozheng.com/mall/technology/minio_use.html)
- [前后端分离项目,引入Spring Cloud Gateway遇到的一个问题!](https://www.macrozheng.com/mall/technology/gateway_cors.html)
- [手把手教你搞定权限管理,结合Spring Security实现接口的动态权限控制!](https://www.macrozheng.com/mall/technology/permission_back.html)
- [手把手教你搞定权限管理,结合Vue实现菜单的动态权限控制!](https://www.macrozheng.com/mall/technology/permission_front.html)
- [商品SKU功能设计与优化](https://www.macrozheng.com/mall/technology/product_sku.html)
- [SpringBoot中处理校验逻辑的两种方式,真的很机智!](https://www.macrozheng.com/mall/technology/springboot_validator.html)
- [使用Redis+AOP优化权限管理功能,这波操作贼爽!](https://www.macrozheng.com/mall/technology/redis_permission.html)
- [Elasticsearch项目实战,商品搜索功能设计与实现!](https://www.macrozheng.com/mall/technology/product_search.html)
- [RabbitMQ实现延迟消息居然如此简单,整个插件就完事了!](https://www.macrozheng.com/mall/technology/rabbitmq_delay.html)
- [给Swagger升级了新版本,没想到居然有这么多坑!](https://www.macrozheng.com/mall/technology/swagger_upgrade.html)
- [Elasticsearch 升级 7.x 版本后,我感觉掉坑里了!](https://www.macrozheng.com/mall/technology/elasticsearch_upgrade.html)
- [搞定Mall项目中的权限管理功能,弄懂这些问题就妥了!](https://www.macrozheng.com/mall/technology/mall_permission_question.html)
## 参考篇
> mall相关技术的参考教程,每篇都是可以独立学习的教程,学习过程中遇到不懂的知识点可以从这里找找。
- [Hutool中那些常用的工具类和方法 ](https://www.macrozheng.com/mall/reference/hutool_start.html)
- [Nginx的这些妙用,你肯定有不知道的!](https://www.macrozheng.com/mall/reference/nginx.html)
- [Github标星19K+Star,10分钟自建对象存储服务!](https://www.macrozheng.com/mall/reference/minio.html)
- [Spring Data Redis 最佳实践!](https://www.macrozheng.com/mall/reference/spring_data_redis.html)
- [Elasticsearch快速入门,掌握这些刚刚好!](https://www.macrozheng.com/mall/reference/elasticsearch_start.html)
- [MongoDB快速入门,掌握这些刚刚好!](https://www.macrozheng.com/mall/reference/mongodb_start.html)
- [我常用的自动化部署技巧,贼好用,推荐给大家!](https://www.macrozheng.com/mall/reference/springboot_auto_deploy.html)
- [连RabbitMQ的5种核心消息模式都不懂,也敢说自己会用消息队列!](https://www.macrozheng.com/mall/reference/rabbitmq_start.html)
- [SpringBoot应用整合ELK实现日志收集](https://www.macrozheng.com/mall/reference/mall_tiny_elk.html)
- [你居然还去服务器上捞日志,搭个日志收集系统难道不香么!](https://www.macrozheng.com/mall/reference/mall_elk_advance.html)
- [给Swagger换了个新皮肤,瞬间高大上了!](https://www.macrozheng.com/mall/reference/knife4j_start.html)
- [Docker服务开放了这个端口,服务器分分钟变肉机!](https://www.macrozheng.com/mall/reference/docker_protect_socket.html)
- [居然有人想白嫖我的日志,赶紧开启安全保护压压惊!](https://www.macrozheng.com/mall/reference/elk_security.html)
- [Nginx如何支持HTTPS?手把手教贼简单!](https://www.macrozheng.com/mall/reference/nginx_https_start.html)
- [还在手动整合Swagger?Swagger官方Starter是真的香!](https://www.macrozheng.com/mall/reference/swagger_starter.html)
- [肝了一周总结的SpringBoot实战教程,太实用了!](https://www.macrozheng.com/mall/reference/springboot_start.html)
- [解放双手!MyBatis官方代码生成工具给力!](https://www.macrozheng.com/mall/reference/mybatis_generator_start.html)
- [Lombok有啥牛皮的?SpringBoot和IDEA官方都要支持它!](https://www.macrozheng.com/mall/reference/lombok_start.html)
## 公众号
学习不走弯路,关注公众号「**macrozheng**」,回复「**学习路线**」,获取mall项目专属学习路线!
加微信群交流,公众号后台回复「**加群**」即可。

================================================
FILE: docs/.nojekyll
================================================
================================================
FILE: docs/README.md
================================================
# mall学习教程
## 友情提示
> 1. **快速体验项目**:[在线访问地址](http://www.macrozheng.com/admin/index.html)。
> 2. **全套学习教程**:[《mall学习教程》](http://www.macrozheng.com/#/README)。
> 3. **微服务版本**:基于Spring Cloud Hoxton & Alibaba的项目:[mall-swarm](https://github.com/macrozheng/mall-swarm)。
> 4. **专属学习路线**:学习不走弯路,整理了套非常不错的[《mall专属学习路线》](#公众号)。
> 5. **项目交流**:想要加群交流项目的朋友,可以加入[mall项目交流群](#公众号)。
## 简介
mall学习教程,架构、业务、技术要点全方位解析。mall项目(40k+star)是一套电商系统,使用现阶段主流技术实现。涵盖了SpringBoot 2.3.0、MyBatis 3.4.6、Elasticsearch 7.6.2、RabbitMQ 3.7.15、Redis 5.0、MongoDB 4.2.5、Mysql5.7等技术,采用Docker容器化部署。
## 项目地址
- 后台项目:[https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
- 前端项目:[https://github.com/macrozheng/mall-admin-web](https://github.com/macrozheng/mall-admin-web)
- 微服务项目:[https://github.com/macrozheng/mall-swarm](https://github.com/macrozheng/mall-swarm)
## 序章
- [mall架构及功能概览](foreword/mall_foreword_01.md)
- [mall学习所需知识点(推荐资料)](foreword/mall_foreword_02.md)
## 架构篇
> 手把手教你搭建一个mall在使用的项目骨架
- [mall整合SpringBoot+MyBatis搭建基本骨架](architect/mall_arch_01.md)
- [mall整合Swagger-UI实现在线API文档](architect/mall_arch_02.md)
- [mall整合Redis实现缓存功能](architect/mall_arch_03.md)
- [mall整合SpringSecurity和JWT实现认证和授权(一)](architect/mall_arch_04.md)
- [mall整合SpringSecurity和JWT实现认证和授权(二)](architect/mall_arch_05.md)
- [mall整合SpringTask实现定时任务](architect/mall_arch_06.md)
- [mall整合Elasticsearch实现商品搜索](architect/mall_arch_07.md)
- [mall整合Mongodb实现文档操作](architect/mall_arch_08.md)
- [mall整合RabbitMQ实现延迟消息](architect/mall_arch_09.md)
- [mall整合OSS实现文件上传](architect/mall_arch_10.md)
## 业务篇
> 全面解析mall中使用的数据库表结构
- [mall数据库表结构概览](database/mall_database_overview.md)
- [商品模块数据库表解析(一)](database/mall_pms_01.md)
- [商品模块数据库表解析(二)](database/mall_pms_02.md)
- [订单模块数据库表解析(一)](database/mall_oms_01.md)
- [订单模块数据库表解析(二)](database/mall_oms_02.md)
- [订单模块数据库表解析(三)](database/mall_oms_03.md)
- [营销模块数据库表解析(一)](database/mall_sms_01.md)
- [营销模块数据库表解析(二)](database/mall_sms_02.md)
- [营销模块数据库表解析(三)](database/mall_sms_03.md)
- [权限管理功能设计与优化](database/mall_permission.md)
- [商品SKU功能设计与优化](technology/product_sku.md)
## 技术要点篇
> mall中一些功能的技术要点解析
- [MyBatis Generator使用过程中踩过的一个坑](technology/mybatis_mapper.md)
- [SpringBoot应用中使用AOP记录接口访问日志](technology/aop_log.md)
- [SpringBoot应用整合ELK实现日志收集](technology/mall_tiny_elk.md)
- [前后端分离项目,如何解决跨域问题](technology/springboot_cors.md)
- [Java 8都出那么久了,Stream API了解下?](technology/java_stream.md)
- [仅需四步,整合SpringSecurity+JWT实现登录认证!](technology/springsecurity_use.md)
- [前后端分离项目,如何优雅实现文件存储!](technology/minio_use.md)
- [前后端分离项目,引入Spring Cloud Gateway遇到的一个问题!](technology/gateway_cors.md)
- [手把手教你搞定权限管理,结合Spring Security实现接口的动态权限控制!](technology/permission_back.md)
- [手把手教你搞定权限管理,结合Vue实现菜单的动态权限控制!](technology/permission_front.md)
- [SpringBoot中处理校验逻辑的两种方式,真的很机智!](technology/springboot_validator.md)
- [使用Redis+AOP优化权限管理功能,这波操作贼爽!](technology/redis_permission.md)
- [Elasticsearch项目实战,商品搜索功能设计与实现!](technology/product_search.md)
- [RabbitMQ实现延迟消息居然如此简单,整个插件就完事了!](technology/rabbitmq_delay.md)
- [给Swagger升级了新版本,没想到居然有这么多坑!](technology/swagger_upgrade.md)
- [Elasticsearch 升级 7.x 版本后,我感觉掉坑里了!](technology/elasticsearch_upgrade.md)
- [搞定Mall项目中的权限管理功能,弄懂这些问题就妥了!](technology/mall_permission_question.md)
## 部署篇
> mall开发及生产环境的搭建
- [mall在Windows环境下的部署](deploy/mall_deploy_windows.md)
- [mall在Linux环境下的部署(基于Docker容器)](deploy/mall_deploy_docker.md)
- [mall在Linux环境下的部署(基于Docker Compose)](deploy/mall_deploy_docker_compose.md)
- [mall在Linux环境下的自动化部署(基于Jenkins)](deploy/mall_deploy_jenkins.md)
- [mall前端项目的安装与部署](deploy/mall_deploy_web.md)
- [mall-swarm在Windows环境下的部署](deploy/mall_swarm_deploy_windows.md)
- [mall-swarm在Linux环境下的部署(基于Docker容器)](deploy/mall_swarm_deploy_docker.md)
- [微服务架构下的自动化部署,使用Jenkins来实现!](deploy/mall_swarm_deploy_jenkins.md)
- [mall-swarm微服务项目在K8S下的实践!](deploy/mall_swarm_deploy_k8s.md)
- [我常用的自动化部署技巧,贼好用,推荐给大家!](technology/springboot_auto_deploy.md)
## 进阶篇
> 一套涵盖大部分核心组件使用的Spring Cloud教程,包括Spring Cloud Alibaba及分布式事务Seata,基于Spring Cloud Greenwich及SpringBoot 2.1.7
- [Spring Cloud 整体架构概览](cloud/springcloud.md)
- [Spring Cloud Eureka:服务注册与发现](cloud/eureka.md)
- [Spring Cloud Ribbon:负载均衡的服务调用](cloud/ribbon.md)
- [Spring Cloud Hystrix:服务容错保护](cloud/hystrix.md)
- [Hystrix Dashboard:断路器执行监控](cloud/hystrix_dashboard.md)
- [Spring Cloud OpenFeign:基于Ribbon和Hystrix的声明式服务调用](cloud/feign.md)
- [Spring Cloud Zuul:API网关服务](cloud/zuul.md)
- [Spring Cloud Config:外部集中化配置管理](cloud/config.md)
- [Spring Cloud Bus:消息总线](cloud/bus.md)
- [Spring Cloud Sleuth:分布式请求链路跟踪](cloud/sleuth.md)
- [Spring Cloud Consul:服务治理与配置中心](cloud/consul.md)
- [Spring Cloud Gateway:新一代API网关服务](cloud/gateway.md)
- [Spring Boot Admin:微服务应用监控](cloud/admin.md)
- [Spring Cloud Security:Oauth2使用入门](cloud/oauth2.md)
- [Spring Cloud Security:Oauth2结合JWT使用](cloud/oauth2_jwt.md)
- [Spring Cloud Security:Oauth2实现单点登录](cloud/oauth2_sso.md)
- [Spring Cloud Alibaba:Nacos 作为注册中心和配置中心使用](cloud/nacos.md)
- [Spring Cloud Alibaba:Sentinel实现熔断与限流](cloud/sentinel.md)
- [使用Seata彻底解决Spring Cloud中的分布式事务问题](cloud/seata.md)
- [微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!](cloud/gateway_oauth2.md)
- [我扒了半天源码,终于找到了Oauth2自定义处理结果的最佳方案!](cloud/oauth2_custom.md)
- [微服务聚合Swagger文档,这波操作是真的香!](cloud/knife4j_cloud.md)
## 参考篇
> mall相关技术的使用教程
- [开发者必备Mysql命令](reference/mysql.md)
- [还在百度Linux命令?推荐一套我用起来特顺手的命令!](reference/linux_command.md)
- [Linux防火墙Firewall和Iptables的使用](reference/linux_firewall.md)
- [还在百度Docker命令?推荐一套我用起来特顺手的命令!](reference/docker_command.md)
- [使用Maven插件为SpringBoot应用构建Docker镜像](reference/docker_maven.md)
- [使用DockerFile为SpringBoot应用构建Docker镜像](reference/docker_file.md)
- [使用Docker Compose部署SpringBoot应用](reference/docker_compose.md)
- [Hutool中那些常用的工具类和方法 ](reference/hutool.md)
- [Nginx的这些妙用,你肯定有不知道的!](reference/nginx.md)
- [使用Jenkins一键打包部署SpringBoot应用,就是这么6!](reference/jenkins.md)
- [使用Jenkins一键打包部署前端应用,就是这么6!](reference/jenkins_vue.md)
- [Github标星19K+Star,10分钟自建对象存储服务!](reference/minio.md)
- [MySql主从复制,从原理到实践!](reference/mysql_master_slave.md)
- [你还在代码里做读写分离么,试试这个中间件吧!](reference/gaea.md)
- [Spring Data Redis 最佳实践!](reference/spring_data_redis.md)
- [Docker环境下秒建Redis集群,连SpringBoot也整上了!](reference/redis_cluster.md)
- [Elasticsearch快速入门,掌握这些刚刚好!](reference/elasticsearch_start.md)
- [MongoDB快速入门,掌握这些刚刚好!](reference/mongodb_start.md)
- [Github标星34K+Star,这款开源项目助你秒建Git服务!](reference/gogs_start.md)
- [连RabbitMQ的5种核心消息模式都不懂,也敢说自己会用消息队列!](reference/rabbitmq_start.md)
- [你居然还去服务器上捞日志,搭个日志收集系统难道不香么!](reference/mall_elk_advance.md)
- [性能优越的轻量级日志收集工具,微软、亚马逊都在用!](reference/efk_fluent.md)
- [听说你的JWT库用起来特别扭,推荐一款贼好用的!](reference/jose_jwt_start.md)
- [给Swagger换了个新皮肤,瞬间高大上了!](reference/knife4j_start.md)
- [Docker服务开放了这个端口,服务器分分钟变肉机!](reference/docker_protect_socket.md)
- [居然有人想白嫖我的日志,赶紧开启安全保护压压惊!](reference/elk_security.md)
- [面对成百上千台服务器产生的日志,试试这款轻量级日志搬运神器!](reference/filebeat_start.md)
- [还在手动部署SpringBoot应用?试试这个自动化插件!](reference/maven_docker_fabric8.md)
- [不要再重复造轮子了,这款开源工具类库贼好使!](reference/hutool_start.md)
- [还在手写CRUD代码?这款开源框架助你解放双手!](reference/mybatis_plus_start.md)
- [还在手写任务调度代码?试试这款可视化分布式调度框架!](reference/power_job_start.md)
- [微服务应用性能如何?APM监控工具来告诉你!](reference/elastic_apm_start.md)
- [RabbitMQ实现即时通讯居然如此简单!连后端代码都省得写了?](reference/rabbitmq_mqtt_start.md)
- [SpringBoot官方支持任务调度框架,轻量级用起来也挺香!](reference/quartz_start.md)
- [Nginx如何支持HTTPS?手把手教贼简单!](reference/nginx_https_start.md)
- [还在手动整合Swagger?Swagger官方Starter是真的香!](reference/swagger_starter.md)
- [MySQL如何实时同步数据到ES?试试这款阿里开源的神器!](reference/canal_start.md)
- [肝了一周总结的SpringBoot实战教程,太实用了!](reference/springboot_start.md)
- [Elasticsearch官方已支持SQL查询,用起来贼方便!](reference/elasticsearch_sql_start.md)
- [还在使用第三方Docker插件?SpringBoot官方插件真香!](reference/springboot_docker_plugin.md)
- [当Swagger遇上YApi,瞬间高大上了!](reference/yapi_start.md)
- [DockerHub访问慢怎么破?自建个企业级镜像仓库试试!](reference/harbor_start.md)
- [解放双手!MyBatis官方代码生成工具给力!](reference/mybatis_generator_start.md)
- [Lombok有啥牛皮的?SpringBoot和IDEA官方都要支持它!](reference/lombok_start.md)
- [干掉mapper.xml!MyBatis新特性动态SQL真香!](reference/mybatis_dynamic_sql.md)
- [数据库迁移搞炸了!没用这款开源神器的锅?](reference/flyway_start.md)
## 工具篇
> 一些常用开发工具的使用
- [IDEA常用设置及推荐插件](reference/idea.md)
- [Navicat实用功能:数据备份与结构同步](reference/navicat.md)
- [Postman:API接口调试利器](reference/postman.md)
- [10分钟搭建自己的Git仓库](reference/gitlab.md)
- [IDEA中的Git操作,看这一篇就够了!](reference/idea_git.md)
- [虚拟机安装及使用Linux,看这一篇就够了!](reference/linux_install.md)
- [推荐一个项目管理工具,落地基于Scrum的敏捷开发!](reference/zentao.md)
- [IDEA中创建和启动SpringBoot应用的正确姿势](reference/idea_springboot.md)
- [盘点下我用的顺手的那些工具!](reference/my_tools.md)
- [我用起来顺手的数据库设计工具,这次推荐给大家!](reference/navicat_designer.md)
- [我常用的IDEA插件大公开,个个是精品!](reference/idea_plugins.md)
- [IDEA同款数据库管理工具,提示太全了,用起来贼香!](reference/datagrip_start.md)
- [写了100多篇原创文章,我常用的在线工具网站推荐给大家!](reference/my_web_tools.md)
- [线上项目出BUG没法调试?推荐这款阿里开源的诊断神器!](reference/arthas_start.md)
- [被我用烂的DEBUG调试技巧,专治各种搜索不到的问题!](reference/my_debug_skill.md)
- [Github标星 8K+,免费又好用的Redis客户端工具!](reference/redis_desktop_start.md)
- [Swagger界面丑、功能弱怎么破?用Postman增强下就给力了!](reference/swagger_postman.md)
- [干掉Navicat!MySQL官方客户端到底行不行?](reference/mysql_workbench.md)
## 公众号
学习不走弯路,关注公众号「**macrozheng**」,回复「**学习路线**」,获取mall项目专属学习路线!
加微信群交流,公众号后台回复「**加群**」即可。

================================================
FILE: docs/_coverpage.md
================================================

# mall-learning
> mall学习教程,架构、业务、技术要点全方位解析。
mall项目(39k+star)是一套电商系统,使用现阶段主流技术实现。
涵盖了SpringBoot 2.3.0、MyBatis 3.4.6、Elasticsearch 7.6.2、
RabbitMQ 3.7.15、Redis 5.0、MongoDB 4.2.5、Mysql5.7等技术,
采用Docker容器化部署。
[GitHub](https://github.com/macrozheng/mall-learning)
[Get Started](README.md)
================================================
FILE: docs/_navbar.md
================================================
* 演示
* [后台管理](http://www.macrozheng.com/admin/index.html)
* [移动端](http://www.macrozheng.com/app/mainpage.html)
* 项目地址
* [后台项目](https://github.com/macrozheng/mall)
* [前端项目](https://github.com/macrozheng/mall-admin-web)
* [学习教程](https://github.com/macrozheng/mall-learning)
* [项目骨架](https://github.com/macrozheng/mall-tiny)
* SpringCloud
* [SpringCloud版本](https://github.com/macrozheng/mall-swarm)
* [SpringCloud教程](https://github.com/macrozheng/springcloud-learning)
================================================
FILE: docs/_sidebar.md
================================================
* 序章
* [mall架构及功能概览](foreword/mall_foreword_01.md)
* [mall学习所需知识点](foreword/mall_foreword_02.md)
* 架构篇
* [mall整合SpringBoot+MyBatis搭建基本骨架](architect/mall_arch_01.md)
* [mall整合Swagger-UI实现在线API文档](architect/mall_arch_02.md)
* [mall整合Redis实现缓存功能](architect/mall_arch_03.md)
* [mall整合SpringSecurity和JWT实现认证和授权(一)](architect/mall_arch_04.md)
* [mall整合SpringSecurity和JWT实现认证和授权(二)](architect/mall_arch_05.md)
* [mall整合SpringTask实现定时任务](architect/mall_arch_06.md)
* [mall整合Elasticsearch实现商品搜索](architect/mall_arch_07.md)
* [mall整合Mongodb实现文档操作](architect/mall_arch_08.md)
* [mall整合RabbitMQ实现延迟消息](architect/mall_arch_09.md)
* [mall整合OSS实现文件上传](architect/mall_arch_10.md)
* 业务篇
* [mall数据库表结构概览](database/mall_database_overview.md)
* [商品模块数据库表解析(一)](database/mall_pms_01.md)
* [商品模块数据库表解析(二)](database/mall_pms_02.md)
* [订单模块数据库表解析(一)](database/mall_oms_01.md)
* [订单模块数据库表解析(二)](database/mall_oms_02.md)
* [订单模块数据库表解析(三)](database/mall_oms_03.md)
* [营销模块数据库表解析(一)](database/mall_sms_01.md)
* [营销模块数据库表解析(二)](database/mall_sms_02.md)
* [营销模块数据库表解析(三)](database/mall_sms_03.md)
* [权限管理功能设计与优化](database/mall_permission.md)
* [商品SKU功能设计与优化](technology/product_sku.md)
* 技术要点篇
* [MyBatis Generator使用过程中踩过的一个坑](technology/mybatis_mapper.md)
* [SpringBoot应用中使用AOP记录接口访问日志](technology/aop_log.md)
* [SpringBoot应用整合ELK实现日志收集](technology/mall_tiny_elk.md)
* [前后端分离项目,如何解决跨域问题](technology/springboot_cors.md)
* [Java 8都出那么久了,Stream API了解下?](technology/java_stream.md)
* [仅需四步,整合SpringSecurity+JWT实现登录认证!](technology/springsecurity_use.md)
* [前后端分离项目,如何优雅实现文件存储!](technology/minio_use.md)
* [前后端分离项目,引入Spring Cloud Gateway遇到的一个问题!](technology/gateway_cors.md)
* [手把手教你搞定权限管理,结合Spring Security实现接口的动态权限控制!](technology/permission_back.md)
* [手把手教你搞定权限管理,结合Vue实现菜单的动态权限控制!](technology/permission_front.md)
* [SpringBoot中处理校验逻辑的两种方式,真的很机智!](technology/springboot_validator.md)
* [使用Redis+AOP优化权限管理功能,这波操作贼爽!](technology/redis_permission.md)
* [Elasticsearch项目实战,商品搜索功能设计与实现!](technology/product_search.md)
* [RabbitMQ实现延迟消息居然如此简单,整个插件就完事了!](technology/rabbitmq_delay.md)
* [给Swagger升级了新版本,没想到居然有这么多坑!](technology/swagger_upgrade.md)
* [Elasticsearch 升级 7.x 版本后,我感觉掉坑里了!](technology/elasticsearch_upgrade.md)
* [搞定Mall项目中的权限管理功能,弄懂这些问题就妥了!](technology/mall_permission_question.md)
* 部署篇
* [mall在Windows环境下的部署](deploy/mall_deploy_windows.md)
* [mall在Linux环境下的部署(基于Docker容器)](deploy/mall_deploy_docker.md)
* [mall在Linux环境下的部署(基于Docker Compose)](deploy/mall_deploy_docker_compose.md)
* [mall在Linux环境下的自动化部署(基于Jenkins)](deploy/mall_deploy_jenkins.md)
* [mall前端项目的安装与部署](deploy/mall_deploy_web.md)
* [mall-swarm在Windows环境下的部署](deploy/mall_swarm_deploy_windows.md)
* [mall-swarm在Linux环境下的部署(基于Docker容器)](deploy/mall_swarm_deploy_docker.md)
* [微服务架构下的自动化部署,使用Jenkins来实现!](deploy/mall_swarm_deploy_jenkins.md)
* [mall-swarm微服务项目在K8S下的实践!](deploy/mall_swarm_deploy_k8s.md)
* [我常用的自动化部署技巧,贼好用,推荐给大家!](technology/springboot_auto_deploy.md)
* 进阶篇
* [Spring Cloud 整体架构概览](cloud/springcloud.md)
* [Spring Cloud Eureka:服务注册与发现](cloud/eureka.md)
* [Spring Cloud Ribbon:负载均衡的服务调用](cloud/ribbon.md)
* [Spring Cloud Hystrix:服务容错保护](cloud/hystrix.md)
* [Hystrix Dashboard:断路器执行监控](cloud/hystrix_dashboard.md)
* [Spring Cloud OpenFeign:基于Ribbon和Hystrix的声明式服务调用](cloud/feign.md)
* [Spring Cloud Zuul:API网关服务](cloud/zuul.md)
* [Spring Cloud Config:外部集中化配置管理](cloud/config.md)
* [Spring Cloud Bus:消息总线](cloud/bus.md)
* [Spring Cloud Sleuth:分布式请求链路跟踪](cloud/sleuth.md)
* [Spring Cloud Consul:服务治理与配置中心](cloud/consul.md)
* [Spring Cloud Gateway:新一代API网关服务](cloud/gateway.md)
* [Spring Boot Admin:微服务应用监控](cloud/admin.md)
* [Spring Cloud Security:Oauth2使用入门](cloud/oauth2.md)
* [Spring Cloud Security:Oauth2结合JWT使用](cloud/oauth2_jwt.md)
* [Spring Cloud Security:Oauth2实现单点登录](cloud/oauth2_sso.md)
* [Spring Cloud Alibaba:Nacos 作为注册中心和配置中心使用](cloud/nacos.md)
* [Spring Cloud Alibaba:Sentinel实现熔断与限流](cloud/sentinel.md)
* [使用Seata彻底解决Spring Cloud中的分布式事务问题](cloud/seata.md)
* [微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!](cloud/gateway_oauth2.md)
* [我扒了半天源码,终于找到了Oauth2自定义处理结果的最佳方案!](cloud/oauth2_custom.md)
* [微服务聚合Swagger文档,这波操作是真的香!](cloud/knife4j_cloud.md)
* 参考篇
* [开发者必备Mysql命令](reference/mysql.md)
* [还在百度Linux命令?推荐一套我用起来特顺手的命令!](reference/linux_command.md)
* [Linux防火墙Firewall和Iptables的使用](reference/linux_firewall.md)
* [还在百度Docker命令?推荐一套我用起来特顺手的命令!](reference/docker_command.md)
* [使用Maven插件为SpringBoot应用构建Docker镜像](reference/docker_maven.md)
* [使用DockerFile为SpringBoot应用构建Docker镜像](reference/docker_file.md)
* [使用Docker Compose部署SpringBoot应用](reference/docker_compose.md)
* [Hutool中那些常用的工具类和方法 ](reference/hutool.md)
* [Nginx的这些妙用,你肯定有不知道的!](reference/nginx.md)
* [使用Jenkins一键打包部署SpringBoot应用,就是这么6!](reference/jenkins.md)
* [使用Jenkins一键打包部署前端应用,就是这么6!](reference/jenkins_vue.md)
* [Github标星19K+Star,10分钟自建对象存储服务!](reference/minio.md)
* [MySql主从复制,从原理到实践!](reference/mysql_master_slave.md)
* [你还在代码里做读写分离么,试试这个中间件吧!](reference/gaea.md)
* [Spring Data Redis 最佳实践!](reference/spring_data_redis.md)
* [Docker环境下秒建Redis集群,连SpringBoot也整上了!](reference/redis_cluster.md)
* [Elasticsearch快速入门,掌握这些刚刚好!](reference/elasticsearch_start.md)
* [MongoDB快速入门,掌握这些刚刚好!](reference/mongodb_start.md)
* [Github标星34K+Star,这款开源项目助你秒建Git服务!](reference/gogs_start.md)
* [连RabbitMQ的5种核心消息模式都不懂,也敢说自己会用消息队列!](reference/rabbitmq_start.md)
* [你居然还去服务器上捞日志,搭个日志收集系统难道不香么!](reference/mall_elk_advance.md)
* [性能优越的轻量级日志收集工具,微软、亚马逊都在用!](reference/efk_fluent.md)
* [听说你的JWT库用起来特别扭,推荐一款贼好用的!](reference/jose_jwt_start.md)
* [给Swagger换了个新皮肤,瞬间高大上了!](reference/knife4j_start.md)
* [Docker服务开放了这个端口,服务器分分钟变肉机!](reference/docker_protect_socket.md)
* [居然有人想白嫖我的日志,赶紧开启安全保护压压惊!](reference/elk_security.md)
* [面对成百上千台服务器产生的日志,试试这款轻量级日志搬运神器!](reference/filebeat_start.md)
* [还在手动部署SpringBoot应用?试试这个自动化插件!](reference/maven_docker_fabric8.md)
* [不要再重复造轮子了,这款开源工具类库贼好使!](reference/hutool_start.md)
* [还在手写CRUD代码?这款开源框架助你解放双手!](reference/mybatis_plus_start.md)
* [还在手写任务调度代码?试试这款可视化分布式调度框架!](reference/power_job_start.md)
* [微服务应用性能如何?APM监控工具来告诉你!](reference/elastic_apm_start.md)
* [RabbitMQ实现即时通讯居然如此简单!连后端代码都省得写了?](reference/rabbitmq_mqtt_start.md)
* [SpringBoot官方支持任务调度框架,轻量级用起来也挺香!](reference/quartz_start.md)
* [Nginx如何支持HTTPS?手把手教贼简单!](reference/nginx_https_start.md)
* [还在手动整合Swagger?Swagger官方Starter是真的香!](reference/swagger_starter.md)
* [MySQL如何实时同步数据到ES?试试这款阿里开源的神器!](reference/canal_start.md)
* [肝了一周总结的SpringBoot实战教程,太实用了!](reference/springboot_start.md)
* [Elasticsearch官方已支持SQL查询,用起来贼方便!](reference/elasticsearch_sql_start.md)
* [还在使用第三方Docker插件?SpringBoot官方插件真香!](reference/springboot_docker_plugin.md)
* [当Swagger遇上YApi,瞬间高大上了!](reference/yapi_start.md)
* [DockerHub访问慢怎么破?自建个企业级镜像仓库试试!](reference/harbor_start.md)
* [解放双手!MyBatis官方代码生成工具给力!](reference/mybatis_generator_start.md)
* [Lombok有啥牛皮的?SpringBoot和IDEA官方都要支持它!](reference/lombok_start.md)
* [干掉mapper.xml!MyBatis新特性动态SQL真香!](reference/mybatis_dynamic_sql.md)
* [数据库迁移搞炸了!没用这款开源神器的锅?](reference/flyway_start.md)
* 工具篇
* [IDEA常用设置及推荐插件](reference/idea.md)
* [Navicat实用功能:数据备份与结构同步](reference/navicat.md)
* [Postman:API接口调试利器](reference/postman.md)
* [10分钟搭建自己的Git仓库](reference/gitlab.md)
* [IDEA中的Git操作,看这一篇就够了!](reference/idea_git.md)
* [虚拟机安装及使用Linux,看这一篇就够了!](reference/linux_install.md)
* [推荐一个项目管理工具,落地基于Scrum的敏捷开发!](reference/zentao.md)
* [IDEA中创建和启动SpringBoot应用的正确姿势](reference/idea_springboot.md)
* [盘点下我用的顺手的那些工具!](reference/my_tools.md)
* [我用起来顺手的数据库设计工具,这次推荐给大家!](reference/navicat_designer.md)
* [我常用的IDEA插件大公开,个个是精品!](reference/idea_plugins.md)
* [IDEA同款数据库管理工具,提示太全了,用起来贼香!](reference/datagrip_start.md)
* [写了100多篇原创文章,我常用的在线工具网站推荐给大家!](reference/my_web_tools.md)
* [线上项目出BUG没法调试?推荐这款阿里开源的诊断神器!](reference/arthas_start.md)
* [被我用烂的DEBUG调试技巧,专治各种搜索不到的问题!](reference/my_debug_skill.md)
* [Github标星 8K+,免费又好用的Redis客户端工具!](reference/redis_desktop_start.md)
* [Swagger界面丑、功能弱怎么破?用Postman增强下就给力了!](reference/swagger_postman.md)
* [干掉Navicat!MySQL官方客户端到底行不行?](reference/mysql_workbench.md)
================================================
FILE: docs/architect/mall_arch_01.md
================================================
学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线!
# mall整合SpringBoot+MyBatis搭建基本骨架
> 本文主要讲解mall整合SpringBoot+MyBatis搭建基本骨架,以商品品牌为例实现基本的CRUD操作及通过PageHelper实现分页查询。
## mysql数据库环境搭建
- 下载并安装mysql5.7版本,下载地址:https://dev.mysql.com/downloads/installer/
- 设置数据库帐号密码:root root
- 下载并安装客户端连接工具Navicat,下载地址:http://www.formysql.com/xiazai.html
- 创建数据库mall
- 导入mall的数据库脚本,脚本地址:https://github.com/macrozheng/mall-learning/blob/master/document/sql/mall.sql
## 项目使用框架介绍
### SpringBoot
> SpringBoot可以让你快速构建基于Spring的Web应用程序,内置多种Web容器(如Tomcat),通过启动入口程序的main函数即可运行。
### PagerHelper
> MyBatis分页插件,简单的几行代码就能实现分页,在与SpringBoot整合时,只要整合了PagerHelper就自动整合了MyBatis。
```java
PageHelper.startPage(pageNum, pageSize);
//之后进行查询操作将自动进行分页
List brandList = brandMapper.selectByExample(new PmsBrandExample());
//通过构造PageInfo对象获取分页信息,如当前页码,总页数,总条数
PageInfo pageInfo = new PageInfo(list);
```
### Druid
> alibaba开源的数据库连接池,号称Java语言中最好的数据库连接池。
### Mybatis generator
> MyBatis的代码生成器,可以根据数据库生成model、mapper.xml、mapper接口和Example,通常情况下的单表查询不用再手写mapper。
## 项目搭建
### 使用IDEA初始化一个SpringBoot项目

### 添加项目依赖
> 在pom.xml中添加相关依赖。
```xml
org.springframework.boot
spring-boot-starter-parent
2.1.3.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-test
test
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.10
com.alibaba
druid-spring-boot-starter
1.1.10
org.mybatis.generator
mybatis-generator-core
1.3.3
mysql
mysql-connector-java
8.0.15
```
### 修改SpringBoot配置文件
> 在application.yml中添加数据源配置和MyBatis的mapper.xml的路径配置。
```yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
mybatis:
mapper-locations:
- classpath:mapper/*.xml
- classpath*:com/**/mapper/*.xml
```
### 项目结构说明

### Mybatis generator 配置文件
> 配置数据库连接,Mybatis generator生成model、mapper接口及mapper.xml的路径。
```xml
```
### 运行Generator的main函数生成代码
```java
package com.macro.mall.tiny.mbg;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* 用于生产MBG的代码
* Created by macro on 2018/4/26.
*/
public class Generator {
public static void main(String[] args) throws Exception {
//MBG 执行过程中的警告信息
List warnings = new ArrayList();
//当生成的代码重复时,覆盖原代码
boolean overwrite = true;
//读取我们的 MBG 配置文件
InputStream is = Generator.class.getResourceAsStream("/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(is);
is.close();
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
//创建 MBG
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
//执行生成代码
myBatisGenerator.generate(null);
//输出警告信息
for (String warning : warnings) {
System.out.println(warning);
}
}
}
```
### 添加MyBatis的Java配置
> 用于配置需要动态生成的mapper接口的路径
```java
package com.macro.mall.tiny.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis配置类
* Created by macro on 2019/4/8.
*/
@Configuration
@MapperScan("com.macro.mall.tiny.mbg.mapper")
public class MyBatisConfig {
}
```
### 实现Controller中的接口
> 实现PmsBrand表中的添加、修改、删除及分页查询接口。
```java
package com.macro.mall.tiny.controller;
import com.macro.mall.tiny.common.api.CommonPage;
import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.mbg.model.PmsBrand;
import com.macro.mall.tiny.service.PmsBrandService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 品牌管理Controller
* Created by macro on 2019/4/19.
*/
@Controller
@RequestMapping("/brand")
public class PmsBrandController {
@Autowired
private PmsBrandService demoService;
private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);
@RequestMapping(value = "listAll", method = RequestMethod.GET)
@ResponseBody
public CommonResult> getBrandList() {
return CommonResult.success(demoService.listAllBrand());
}
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
CommonResult commonResult;
int count = demoService.createBrand(pmsBrand);
if (count == 1) {
commonResult = CommonResult.success(pmsBrand);
LOGGER.debug("createBrand success:{}", pmsBrand);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("createBrand failed:{}", pmsBrand);
}
return commonResult;
}
@RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
@ResponseBody
public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {
CommonResult commonResult;
int count = demoService.updateBrand(id, pmsBrandDto);
if (count == 1) {
commonResult = CommonResult.success(pmsBrandDto);
LOGGER.debug("updateBrand success:{}", pmsBrandDto);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("updateBrand failed:{}", pmsBrandDto);
}
return commonResult;
}
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult deleteBrand(@PathVariable("id") Long id) {
int count = demoService.deleteBrand(id);
if (count == 1) {
LOGGER.debug("deleteBrand success :id={}", id);
return CommonResult.success(null);
} else {
LOGGER.debug("deleteBrand failed :id={}", id);
return CommonResult.failed("操作失败");
}
}
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public CommonResult> listBrand(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "3") Integer pageSize) {
List brandList = demoService.listBrand(pageNum, pageSize);
return CommonResult.success(CommonPage.restPage(brandList));
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult brand(@PathVariable("id") Long id) {
return CommonResult.success(demoService.getBrand(id));
}
}
```
### 添加Service接口
```java
package com.macro.mall.tiny.service;
import com.macro.mall.tiny.mbg.model.PmsBrand;
import java.util.List;
/**
* PmsBrandService
* Created by macro on 2019/4/19.
*/
public interface PmsBrandService {
List listAllBrand();
int createBrand(PmsBrand brand);
int updateBrand(Long id, PmsBrand brand);
int deleteBrand(Long id);
List listBrand(int pageNum, int pageSize);
PmsBrand getBrand(Long id);
}
```
### 实现Service接口
```java
package com.macro.mall.tiny.service.impl;
import com.github.pagehelper.PageHelper;
import com.macro.mall.tiny.mbg.mapper.PmsBrandMapper;
import com.macro.mall.tiny.mbg.model.PmsBrand;
import com.macro.mall.tiny.mbg.model.PmsBrandExample;
import com.macro.mall.tiny.service.PmsBrandService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* PmsBrandService实现类
* Created by macro on 2019/4/19.
*/
@Service
public class PmsBrandServiceImpl implements PmsBrandService {
@Autowired
private PmsBrandMapper brandMapper;
@Override
public List listAllBrand() {
return brandMapper.selectByExample(new PmsBrandExample());
}
@Override
public int createBrand(PmsBrand brand) {
return brandMapper.insertSelective(brand);
}
@Override
public int updateBrand(Long id, PmsBrand brand) {
brand.setId(id);
return brandMapper.updateByPrimaryKeySelective(brand);
}
@Override
public int deleteBrand(Long id) {
return brandMapper.deleteByPrimaryKey(id);
}
@Override
public List listBrand(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
return brandMapper.selectByExample(new PmsBrandExample());
}
@Override
public PmsBrand getBrand(Long id) {
return brandMapper.selectByPrimaryKey(id);
}
}
```
## 项目源码地址
[https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-01](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-01)
## 公众号

================================================
FILE: docs/architect/mall_arch_02.md
================================================
学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线!
# mall整合Swagger-UI实现在线API文档
> 本文主要讲解mall是如何通过整合Swagger-UI来实现一份相当完善的在线API文档的。
## 项目使用框架介绍
### Swagger-UI
> Swagger-UI是HTML, Javascript, CSS的一个集合,可以动态地根据注解生成在线API文档。
#### 常用注解
- @Api:用于修饰Controller类,生成Controller相关文档信息
- @ApiOperation:用于修饰Controller类中的方法,生成接口方法相关文档信息
- @ApiParam:用于修饰接口中的参数,生成接口参数相关文档信息
- @ApiModelProperty:用于修饰实体类的属性,当实体类是请求参数或返回结果时,直接生成相关文档信息
## 整合Swagger-UI
### 添加项目依赖
> 在pom.xml中新增Swagger-UI相关依赖
```xml
io.springfox
springfox-swagger2
2.7.0
io.springfox
springfox-swagger-ui
2.7.0
```
### 添加Swagger-UI的配置
> 添加Swagger-UI的Java配置文件
注意:Swagger对生成API文档的范围有三种不同的选择
- 生成指定包下面的类的API文档
- 生成有指定注解的类的API文档
- 生成有指定注解的方法的API文档
```java
package com.macro.mall.tiny.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger2API文档的配置
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//为当前包下controller生成API文档
.apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller"))
//为有@Api注解的Controller生成API文档
// .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
//为有@ApiOperation注解的方法生成API文档
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SwaggerUI演示")
.description("mall-tiny")
.contact("macro")
.version("1.0")
.build();
}
}
```
### 给PmsBrandController添加Swagger注解
> 给原有的品牌管理Controller添加上Swagger注解
```java
package com.macro.mall.tiny.controller;
import com.macro.mall.tiny.common.api.CommonPage;
import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.mbg.model.PmsBrand;
import com.macro.mall.tiny.service.PmsBrandService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 品牌管理Controller
* Created by macro on 2019/4/19.
*/
@Api(tags = "PmsBrandController", description = "商品品牌管理")
@Controller
@RequestMapping("/brand")
public class PmsBrandController {
@Autowired
private PmsBrandService brandService;
private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);
@ApiOperation("获取所有品牌列表")
@RequestMapping(value = "listAll", method = RequestMethod.GET)
@ResponseBody
public CommonResult> getBrandList() {
return CommonResult.success(brandService.listAllBrand());
}
@ApiOperation("添加品牌")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
CommonResult commonResult;
int count = brandService.createBrand(pmsBrand);
if (count == 1) {
commonResult = CommonResult.success(pmsBrand);
LOGGER.debug("createBrand success:{}", pmsBrand);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("createBrand failed:{}", pmsBrand);
}
return commonResult;
}
@ApiOperation("更新指定id品牌信息")
@RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
@ResponseBody
public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {
CommonResult commonResult;
int count = brandService.updateBrand(id, pmsBrandDto);
if (count == 1) {
commonResult = CommonResult.success(pmsBrandDto);
LOGGER.debug("updateBrand success:{}", pmsBrandDto);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("updateBrand failed:{}", pmsBrandDto);
}
return commonResult;
}
@ApiOperation("删除指定id的品牌")
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult deleteBrand(@PathVariable("id") Long id) {
int count = brandService.deleteBrand(id);
if (count == 1) {
LOGGER.debug("deleteBrand success :id={}", id);
return CommonResult.success(null);
} else {
LOGGER.debug("deleteBrand failed :id={}", id);
return CommonResult.failed("操作失败");
}
}
@ApiOperation("分页查询品牌列表")
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public CommonResult> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
@ApiParam("页码") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "3")
@ApiParam("每页数量") Integer pageSize) {
List brandList = brandService.listBrand(pageNum, pageSize);
return CommonResult.success(CommonPage.restPage(brandList));
}
@ApiOperation("获取指定id的品牌详情")
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult brand(@PathVariable("id") Long id) {
return CommonResult.success(brandService.getBrand(id));
}
}
```
### 修改MyBatis Generator注释的生成规则
> CommentGenerator为MyBatis Generator的自定义注释生成器,修改addFieldComment方法使其生成Swagger的@ApiModelProperty注解来取代原来的方法注释,添加addJavaFileComment方法,使其能在import中导入@ApiModelProperty,否则需要手动导入该类,在需要生成大量实体类时,是一件非常麻烦的事。
```java
package com.macro.mall.tiny.mbg;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.internal.DefaultCommentGenerator;
import org.mybatis.generator.internal.util.StringUtility;
import java.util.Properties;
/**
* 自定义注释生成器
* Created by macro on 2018/4/26.
*/
public class CommentGenerator extends DefaultCommentGenerator {
private boolean addRemarkComments = false;
private static final String EXAMPLE_SUFFIX="Example";
private static final String API_MODEL_PROPERTY_FULL_CLASS_NAME="io.swagger.annotations.ApiModelProperty";
/**
* 设置用户配置的参数
*/
@Override
public void addConfigurationProperties(Properties properties) {
super.addConfigurationProperties(properties);
this.addRemarkComments = StringUtility.isTrue(properties.getProperty("addRemarkComments"));
}
/**
* 给字段添加注释
*/
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
String remarks = introspectedColumn.getRemarks();
//根据参数和备注信息判断是否添加备注信息
if(addRemarkComments&&StringUtility.stringHasValue(remarks)){
// addFieldJavaDoc(field, remarks);
//数据库中特殊字符需要转义
if(remarks.contains("\"")){
remarks = remarks.replace("\"","'");
}
//给model的字段添加swagger注解
field.addJavaDocLine("@ApiModelProperty(value = \""+remarks+"\")");
}
}
/**
* 给model的字段添加注释
*/
private void addFieldJavaDoc(Field field, String remarks) {
//文档注释开始
field.addJavaDocLine("/**");
//获取数据库字段的备注信息
String[] remarkLines = remarks.split(System.getProperty("line.separator"));
for(String remarkLine:remarkLines){
field.addJavaDocLine(" * "+remarkLine);
}
addJavadocTag(field, false);
field.addJavaDocLine(" */");
}
@Override
public void addJavaFileComment(CompilationUnit compilationUnit) {
super.addJavaFileComment(compilationUnit);
//只在model中添加swagger注解类的导入
if(!compilationUnit.isJavaInterface()&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){
compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME));
}
}
}
```
### 运行代码生成器重新生成mbg包中的代码
> 运行com.macro.mall.tiny.mbg.Generator的main方法,重新生成mbg中的代码,可以看到PmsBrand类中已经自动根据数据库注释添加了@ApiModelProperty注解

### 运行项目,查看结果
#### 访问Swagger-UI接口文档地址
接口地址:http://localhost:8080/swagger-ui.html

#### 对请求参数已经添加说明

#### 对返回结果已经添加说明

### 直接在在线文档上面进行接口测试


## 项目源码地址
[https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-02](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-02)
## 公众号

================================================
FILE: docs/architect/mall_arch_03.md
================================================
学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线!
# mall整合Redis实现缓存功能
> 本文主要讲解mall整合Redis的过程,以短信验证码的存储验证为例。
## Redis的安装和启动
> Redis是用C语言开发的一个高性能键值对数据库,可用于数据缓存,主要用于处理大量数据的高访问负载。
- 下载Redis,下载地址:https://github.com/MicrosoftArchive/redis/releases

- 下载完后解压到指定目录

- 在当前地址栏输入cmd后,执行redis的启动命令:redis-server.exe redis.windows.conf

## 整合Redis
### 添加项目依赖
> 在pom.xml中新增Redis相关依赖
```xml
org.springframework.boot
spring-boot-starter-data-redis
```
### 修改SpringBoot配置文件
> 在application.yml中添加Redis的配置及Redis中自定义key的配置。
#### 在spring节点下添加Redis的配置
```yml
redis:
host: localhost # Redis服务器地址
database: 0 # Redis数据库索引(默认为0)
port: 6379 # Redis服务器连接端口
password: # Redis服务器连接密码(默认为空)
jedis:
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 8 # 连接池中的最大空闲连接
min-idle: 0 # 连接池中的最小空闲连接
timeout: 3000ms # 连接超时时间(毫秒)
```
#### 在根节点下添加Redis自定义key的配置
```yml
# 自定义redis key
redis:
key:
prefix:
authCode: "portal:authCode:"
expire:
authCode: 120 # 验证码超期时间
```
### 添加RedisService接口用于定义一些常用Redis操作
```java
package com.macro.mall.tiny.service;
/**
* redis操作Service,
* 对象和数组都以json形式进行存储
* Created by macro on 2018/8/7.
*/
public interface RedisService {
/**
* 存储数据
*/
void set(String key, String value);
/**
* 获取数据
*/
String get(String key);
/**
* 设置超期时间
*/
boolean expire(String key, long expire);
/**
* 删除数据
*/
void remove(String key);
/**
* 自增操作
* @param delta 自增步长
*/
Long increment(String key, long delta);
}
```
### 注入StringRedisTemplate,实现RedisService接口
```java
package com.macro.mall.tiny.service.impl;
import com.macro.mall.tiny.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* redis操作Service的实现类
* Created by macro on 2018/8/7.
*/
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public void set(String key, String value) {
stringRedisTemplate.opsForValue().set(key, value);
}
@Override
public String get(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
@Override
public boolean expire(String key, long expire) {
return stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
@Override
public void remove(String key) {
stringRedisTemplate.delete(key);
}
@Override
public Long increment(String key, long delta) {
return stringRedisTemplate.opsForValue().increment(key,delta);
}
}
```
### 添加UmsMemberController
> 添加根据电话号码获取验证码的接口和校验验证码的接口
```java
package com.macro.mall.tiny.controller;
import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.service.UmsMemberService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 会员登录注册管理Controller
* Created by macro on 2018/8/3.
*/
@Controller
@Api(tags = "UmsMemberController", description = "会员登录注册管理")
@RequestMapping("/sso")
public class UmsMemberController {
@Autowired
private UmsMemberService memberService;
@ApiOperation("获取验证码")
@RequestMapping(value = "/getAuthCode", method = RequestMethod.GET)
@ResponseBody
public CommonResult getAuthCode(@RequestParam String telephone) {
return memberService.generateAuthCode(telephone);
}
@ApiOperation("判断验证码是否正确")
@RequestMapping(value = "/verifyAuthCode", method = RequestMethod.POST)
@ResponseBody
public CommonResult updatePassword(@RequestParam String telephone,
@RequestParam String authCode) {
return memberService.verifyAuthCode(telephone,authCode);
}
}
```
### 添加UmsMemberService接口
```java
package com.macro.mall.tiny.service;
import com.macro.mall.tiny.common.api.CommonResult;
/**
* 会员管理Service
* Created by macro on 2018/8/3.
*/
public interface UmsMemberService {
/**
* 生成验证码
*/
CommonResult generateAuthCode(String telephone);
/**
* 判断验证码和手机号码是否匹配
*/
CommonResult verifyAuthCode(String telephone, String authCode);
}
```
### 添加UmsMemberService接口的实现类UmsMemberServiceImpl
> 生成验证码时,将自定义的Redis键值加上手机号生成一个Redis的key,以验证码为value存入到Redis中,并设置过期时间为自己配置的时间(这里为120s)。校验验证码时根据手机号码来获取Redis里面存储的验证码,并与传入的验证码进行比对。
```java
package com.macro.mall.tiny.service.impl;
import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.service.RedisService;
import com.macro.mall.tiny.service.UmsMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Random;
/**
* 会员管理Service实现类
* Created by macro on 2018/8/3.
*/
@Service
public class UmsMemberServiceImpl implements UmsMemberService {
@Autowired
private RedisService redisService;
@Value("${redis.key.prefix.authCode}")
private String REDIS_KEY_PREFIX_AUTH_CODE;
@Value("${redis.key.expire.authCode}")
private Long AUTH_CODE_EXPIRE_SECONDS;
@Override
public CommonResult generateAuthCode(String telephone) {
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 6; i++) {
sb.append(random.nextInt(10));
}
//验证码绑定手机号并存储到redis
redisService.set(REDIS_KEY_PREFIX_AUTH_CODE + telephone, sb.toString());
redisService.expire(REDIS_KEY_PREFIX_AUTH_CODE + telephone, AUTH_CODE_EXPIRE_SECONDS);
return CommonResult.success(sb.toString(), "获取验证码成功");
}
//对输入的验证码进行校验
@Override
public CommonResult verifyAuthCode(String telephone, String authCode) {
if (StringUtils.isEmpty(authCode)) {
return CommonResult.failed("请输入验证码");
}
String realAuthCode = redisService.get(REDIS_KEY_PREFIX_AUTH_CODE + telephone);
boolean result = authCode.equals(realAuthCode);
if (result) {
return CommonResult.success(null, "验证码校验成功");
} else {
return CommonResult.failed("验证码不正确");
}
}
}
```
### 运行项目
> 访问Swagger的API文档地址http://localhost:8080/swagger-ui.html ,对接口进行测试。

## 项目源码地址
[https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-03](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-03)
## 公众号

================================================
FILE: docs/architect/mall_arch_04.md
================================================
学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线!
# mall整合SpringSecurity和JWT实现认证和授权(一)
> 本文主要讲解mall通过整合SpringSecurity和JWT实现后台用户的登录和授权功能,同时改造Swagger-UI的配置使其可以自动记住登录令牌进行发送。
## 项目使用框架介绍
### SpringSecurity
> SpringSecurity是一个强大的可高度定制的认证和授权框架,对于Spring应用来说它是一套Web安全标准。SpringSecurity注重于为Java应用提供认证和授权功能,像所有的Spring项目一样,它对自定义需求具有强大的扩展性。
### JWT
> JWT是JSON WEB TOKEN的缩写,它是基于 RFC 7519 标准定义的一种可以安全传输的的JSON对象,由于使用了数字签名,所以是可信任和安全的。
#### JWT的组成
- JWT token的格式:header.payload.signature
- header中用于存放签名的生成算法
```json
{"alg": "HS512"}
```
- payload中用于存放用户名、token的生成时间和过期时间
```json
{"sub":"admin","created":1489079981393,"exp":1489684781}
```
- signature为以header和payload生成的签名,一旦header和payload被篡改,验证将失败
```java
//secret为加密算法的密钥
String signature = HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
```
#### JWT实例
这是一个JWT的字符串
```
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImNyZWF0ZWQiOjE1NTY3NzkxMjUzMDksImV4cCI6MTU1NzM4MzkyNX0.d-iki0193X0bBOETf2UN3r3PotNIEAV7mzIxxeI5IxFyzzkOZxS0PGfF_SK6wxCv2K8S0cZjMkv6b5bCqc0VBw
```
可以在该网站上获得解析结果:https://jwt.io/

#### JWT实现认证和授权的原理
- 用户调用登录接口,登录成功后获取到JWT的token;
- 之后用户每次调用接口都在http的header中添加一个叫Authorization的头,值为JWT的token;
- 后台程序通过对Authorization头中信息的解码及数字签名校验来获取其中的用户信息,从而实现认证和授权。
### Hutool
> Hutool是一个丰富的Java开源工具包,它帮助我们简化每一行代码,减少每一个方法,mall项目采用了此工具包。
## 项目使用表说明
- `ums_admin`:后台用户表
- `ums_role`:后台用户角色表
- `ums_permission`:后台用户权限表
- `ums_admin_role_relation`:后台用户和角色关系表,用户与角色是多对多关系
- `ums_role_permission_relation`:后台用户角色和权限关系表,角色与权限是多对多关系
- `ums_admin_permission_relation`:后台用户和权限关系表(除角色中定义的权限以外的加减权限),加权限是指用户比角色多出的权限,减权限是指用户比角色少的权限
## 整合SpringSecurity及JWT
### 在pom.xml中添加项目依赖
```xml
org.springframework.boot
spring-boot-starter-security
cn.hutool
hutool-all
4.5.7
io.jsonwebtoken
jjwt
0.9.0
```
### 添加JWT token的工具类
> 用于生成和解析JWT token的工具类
相关方法说明:
- generateToken(UserDetails userDetails) :用于根据登录用户信息生成token
- getUserNameFromToken(String token):从token中获取登录用户的信息
- validateToken(String token, UserDetails userDetails):判断token是否还有效
```java
package com.macro.mall.tiny.common.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JwtToken生成的工具类
* Created by macro on 2018/4/26.
*/
@Component
public class JwtTokenUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
/**
* 根据负责生成JWT的token
*/
private String generateToken(Map claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/**
* 从token中获取JWT中的负载
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.info("JWT格式验证失败:{}",token);
}
return claims;
}
/**
* 生成token的过期时间
*/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/**
* 从token中获取登录用户名
*/
public String getUserNameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 验证token是否还有效
*
* @param token 客户端传入的token
* @param userDetails 从数据库中查询出来的用户信息
*/
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/**
* 判断token是否已经失效
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
/**
* 从token中获取过期时间
*/
private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
/**
* 根据用户信息生成token
*/
public String generateToken(UserDetails userDetails) {
Map claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
/**
* 判断token是否可以被刷新
*/
public boolean canRefresh(String token) {
return !isTokenExpired(token);
}
/**
* 刷新token
*/
public String refreshToken(String token) {
Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
}
```
### 添加SpringSecurity的配置类
```java
package com.macro.mall.tiny.config;
import com.macro.mall.tiny.component.JwtAuthenticationTokenFilter;
import com.macro.mall.tiny.component.RestAuthenticationEntryPoint;
import com.macro.mall.tiny.component.RestfulAccessDeniedHandler;
import com.macro.mall.tiny.dto.AdminUserDetails;
import com.macro.mall.tiny.mbg.model.UmsAdmin;
import com.macro.mall.tiny.mbg.model.UmsPermission;
import com.macro.mall.tiny.service.UmsAdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.util.List;
/**
* SpringSecurity的配置
* Created by macro on 2018/4/26.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UmsAdminService adminService;
@Autowired
private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf
.disable()
.sessionManagement()// 基于token,所以不需要session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, // 允许对于网站静态资源的无授权访问
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/swagger-resources/**",
"/v2/api-docs/**"
)
.permitAll()
.antMatchers("/admin/login", "/admin/register")// 对登录注册要允许匿名访问
.permitAll()
.antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求
.permitAll()
// .antMatchers("/**")//测试时全部运行访问
// .permitAll()
.anyRequest()// 除上面外的所有请求全部需要鉴权认证
.authenticated();
// 禁用缓存
httpSecurity.headers().cacheControl();
// 添加JWT filter
httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//添加自定义未授权和未登录结果返回
httpSecurity.exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandler)
.authenticationEntryPoint(restAuthenticationEntryPoint);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
//获取登录用户信息
return username -> {
UmsAdmin admin = adminService.getAdminByUsername(username);
if (admin != null) {
List permissionList = adminService.getPermissionList(admin.getId());
return new AdminUserDetails(admin,permissionList);
}
throw new UsernameNotFoundException("用户名或密码错误");
};
}
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
return new JwtAuthenticationTokenFilter();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
```
#### 相关依赖及方法说明
- configure(HttpSecurity httpSecurity):用于配置需要拦截的url路径、jwt过滤器及出异常后的处理器;
- configure(AuthenticationManagerBuilder auth):用于配置UserDetailsService及PasswordEncoder;
- RestfulAccessDeniedHandler:当用户没有访问权限时的处理器,用于返回JSON格式的处理结果;
- RestAuthenticationEntryPoint:当未登录或token失效时,返回JSON格式的结果;
- UserDetailsService:SpringSecurity定义的核心接口,用于根据用户名获取用户信息,需要自行实现;
- UserDetails:SpringSecurity定义用于封装用户信息的类(主要是用户信息和权限),需要自行实现;
- PasswordEncoder:SpringSecurity定义的用于对密码进行编码及比对的接口,目前使用的是BCryptPasswordEncoder;
- JwtAuthenticationTokenFilter:在用户名和密码校验前添加的过滤器,如果有jwt的token,会自行根据token信息进行登录。
### 添加RestfulAccessDeniedHandler
```java
package com.macro.mall.tiny.component;
import cn.hutool.json.JSONUtil;
import com.macro.mall.tiny.common.api.CommonResult;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 当访问接口没有权限时,自定义的返回结果
* Created by macro on 2018/4/26.
*/
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler{
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException e) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
response.getWriter().flush();
}
}
```
### 添加RestAuthenticationEntryPoint
```java
package com.macro.mall.tiny.component;
import cn.hutool.json.JSONUtil;
import com.macro.mall.tiny.common.api.CommonResult;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 当未登录或者token失效访问接口时,自定义的返回结果
* Created by macro on 2018/5/14.
*/
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println(JSONUtil.parse(CommonResult.unauthorized(authException.getMessage())));
response.getWriter().flush();
}
}
```
### 添加AdminUserDetails
```java
package com.macro.mall.tiny.dto;
import com.macro.mall.tiny.mbg.model.UmsAdmin;
import com.macro.mall.tiny.mbg.model.UmsPermission;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* SpringSecurity需要的用户详情
* Created by macro on 2018/4/26.
*/
public class AdminUserDetails implements UserDetails {
private UmsAdmin umsAdmin;
private List permissionList;
public AdminUserDetails(UmsAdmin umsAdmin, List permissionList) {
this.umsAdmin = umsAdmin;
this.permissionList = permissionList;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
//返回当前用户的权限
return permissionList.stream()
.filter(permission -> permission.getValue()!=null)
.map(permission ->new SimpleGrantedAuthority(permission.getValue()))
.collect(Collectors.toList());
}
@Override
public String getPassword() {
return umsAdmin.getPassword();
}
@Override
public String getUsername() {
return umsAdmin.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return umsAdmin.getStatus().equals(1);
}
}
```
### 添加JwtAuthenticationTokenFilter
> 在用户名和密码校验前添加的过滤器,如果请求中有jwt的token且有效,会取出token中的用户名,然后调用SpringSecurity的API进行登录操作。
```java
package com.macro.mall.tiny.component;
import com.macro.mall.tiny.common.utils.JwtTokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* JWT登录授权过滤器
* Created by macro on 2018/4/26.
*/
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(this.tokenHeader);
if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "
String username = jwtTokenUtil.getUserNameFromToken(authToken);
LOGGER.info("checking username:{}", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
LOGGER.info("authenticated user:{}", username);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}
```
## 项目源码地址
[https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-04](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-04)
## 公众号

================================================
FILE: docs/architect/mall_arch_05.md
================================================
学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线!
# mall整合SpringSecurity和JWT实现认证和授权(二)
> 接上一篇,controller和service层的代码实现及登录授权流程演示。
## 登录注册功能实现
### 添加UmsAdminController类
> 实现了后台用户登录、注册及获取权限的接口
```java
package com.macro.mall.tiny.controller;
import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.dto.UmsAdminLoginParam;
import com.macro.mall.tiny.mbg.model.UmsAdmin;
import com.macro.mall.tiny.mbg.model.UmsPermission;
import com.macro.mall.tiny.service.UmsAdminService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 后台用户管理
* Created by macro on 2018/4/26.
*/
@Controller
@Api(tags = "UmsAdminController", description = "后台用户管理")
@RequestMapping("/admin")
public class UmsAdminController {
@Autowired
private UmsAdminService adminService;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@ApiOperation(value = "用户注册")
@RequestMapping(value = "/register", method = RequestMethod.POST)
@ResponseBody
public CommonResult register(@RequestBody UmsAdmin umsAdminParam, BindingResult result) {
UmsAdmin umsAdmin = adminService.register(umsAdminParam);
if (umsAdmin == null) {
CommonResult.failed();
}
return CommonResult.success(umsAdmin);
}
@ApiOperation(value = "登录以后返回token")
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public CommonResult login(@RequestBody UmsAdminLoginParam umsAdminLoginParam, BindingResult result) {
String token = adminService.login(umsAdminLoginParam.getUsername(), umsAdminLoginParam.getPassword());
if (token == null) {
return CommonResult.validateFailed("用户名或密码错误");
}
Map tokenMap = new HashMap<>();
tokenMap.put("token", token);
tokenMap.put("tokenHead", tokenHead);
return CommonResult.success(tokenMap);
}
@ApiOperation("获取用户所有权限(包括+-权限)")
@RequestMapping(value = "/permission/{adminId}", method = RequestMethod.GET)
@ResponseBody
public CommonResult> getPermissionList(@PathVariable Long adminId) {
List permissionList = adminService.getPermissionList(adminId);
return CommonResult.success(permissionList);
}
}
```
### 添加UmsAdminService接口
```java
package com.macro.mall.tiny.service;
import com.macro.mall.tiny.mbg.model.UmsAdmin;
import com.macro.mall.tiny.mbg.model.UmsPermission;
import java.util.List;
/**
* 后台管理员Service
* Created by macro on 2018/4/26.
*/
public interface UmsAdminService {
/**
* 根据用户名获取后台管理员
*/
UmsAdmin getAdminByUsername(String username);
/**
* 注册功能
*/
UmsAdmin register(UmsAdmin umsAdminParam);
/**
* 登录功能
* @param username 用户名
* @param password 密码
* @return 生成的JWT的token
*/
String login(String username, String password);
/**
* 获取用户所有权限(包括角色权限和+-权限)
*/
List getPermissionList(Long adminId);
}
```
### 添加UmsAdminServiceImpl类
```java
package com.macro.mall.tiny.service.impl;
import com.macro.mall.tiny.common.utils.JwtTokenUtil;
import com.macro.mall.tiny.dao.UmsAdminRoleRelationDao;
import com.macro.mall.tiny.dto.UmsAdminLoginParam;
import com.macro.mall.tiny.mbg.mapper.UmsAdminMapper;
import com.macro.mall.tiny.mbg.model.UmsAdmin;
import com.macro.mall.tiny.mbg.model.UmsAdminExample;
import com.macro.mall.tiny.mbg.model.UmsPermission;
import com.macro.mall.tiny.service.UmsAdminService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
/**
* UmsAdminService实现类
* Created by macro on 2018/4/26.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
private static final Logger LOGGER = LoggerFactory.getLogger(UmsAdminServiceImpl.class);
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private PasswordEncoder passwordEncoder;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Autowired
private UmsAdminMapper adminMapper;
@Autowired
private UmsAdminRoleRelationDao adminRoleRelationDao;
@Override
public UmsAdmin getAdminByUsername(String username) {
UmsAdminExample example = new UmsAdminExample();
example.createCriteria().andUsernameEqualTo(username);
List adminList = adminMapper.selectByExample(example);
if (adminList != null && adminList.size() > 0) {
return adminList.get(0);
}
return null;
}
@Override
public UmsAdmin register(UmsAdmin umsAdminParam) {
UmsAdmin umsAdmin = new UmsAdmin();
BeanUtils.copyProperties(umsAdminParam, umsAdmin);
umsAdmin.setCreateTime(new Date());
umsAdmin.setStatus(1);
//查询是否有相同用户名的用户
UmsAdminExample example = new UmsAdminExample();
example.createCriteria().andUsernameEqualTo(umsAdmin.getUsername());
List umsAdminList = adminMapper.selectByExample(example);
if (umsAdminList.size() > 0) {
return null;
}
//将密码进行加密操作
String encodePassword = passwordEncoder.encode(umsAdmin.getPassword());
umsAdmin.setPassword(encodePassword);
adminMapper.insert(umsAdmin);
return umsAdmin;
}
@Override
public String login(String username, String password) {
String token = null;
try {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException("密码不正确");
}
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
token = jwtTokenUtil.generateToken(userDetails);
} catch (AuthenticationException e) {
LOGGER.warn("登录异常:{}", e.getMessage());
}
return token;
}
@Override
public List getPermissionList(Long adminId) {
return adminRoleRelationDao.getPermissionList(adminId);
}
}
```
### 修改Swagger的配置
> 通过修改配置实现调用接口自带Authorization头,这样就可以访问需要登录的接口了。
```java
package com.macro.mall.tiny.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger2API文档的配置
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//为当前包下controller生成API文档
.apis(RequestHandlerSelectors.basePackage("com.macro.mall.tiny.controller"))
.paths(PathSelectors.any())
.build()
//添加登录认证
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SwaggerUI演示")
.description("mall-tiny")
.contact("macro")
.version("1.0")
.build();
}
private List securitySchemes() {
//设置请求头信息
List result = new ArrayList<>();
ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
result.add(apiKey);
return result;
}
private List securityContexts() {
//设置需要登录认证的路径
List result = new ArrayList<>();
result.add(getContextByPath("/brand/.*"));
return result;
}
private SecurityContext getContextByPath(String pathRegex){
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(pathRegex))
.build();
}
private List defaultAuth() {
List result = new ArrayList<>();
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
result.add(new SecurityReference("Authorization", authorizationScopes));
return result;
}
}
```
### 给PmsBrandController接口中的方法添加访问权限
- 给查询接口添加`pms:brand:read`权限
- 给修改接口添加`pms:brand:update`权限
- 给删除接口添加`pms:brand:delete`权限
- 给添加接口添加`pms:brand:create`权限
例子:
```java
@PreAuthorize("hasAuthority('pms:brand:read')")
public CommonResult> getBrandList() {
return CommonResult.success(brandService.listAllBrand());
}
```
## 认证与授权流程演示
### 运行项目,访问API
Swagger api地址:http://localhost:8080/swagger-ui.html

### 未登录前访问接口


### 登录后访问接口
- 进行登录操作:登录帐号test 123456


- 点击Authorize按钮,在弹框中输入登录接口中获取到的token信息


- 登录后访问获取权限列表接口,发现已经可以正常访问


### 访问需要权限的接口
> 由于test帐号并没有设置任何权限,所以他无法访问具有`pms:brand:read`权限的获取品牌列表接口。


### 改用其他有权限的帐号登录
> 改用admin 123456登录后访问,点击Authorize按钮打开弹框,点击logout登出后再重新输入新token。
`注意`:如果admin帐号密码不对的话,公众号后台回复`体验`来获取。


## 项目源码地址
[https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-04](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-04)
## 公众号

================================================
FILE: docs/architect/mall_arch_06.md
================================================
学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线!
# mall整合SpringTask实现定时任务
> 本文主要讲解mall整合SpringTask的过程,以批量修改超时订单为例。
## 项目使用框架介绍
### SpringTask
> SpringTask是Spring自主研发的轻量级定时任务工具,相比于Quartz更加简单方便,且不需要引入其他依赖即可使用。
### Cron表达式
> Cron表达式是一个字符串,包括6~7个时间元素,在SpringTask中可以用于指定任务的执行时间。
#### Cron的语法格式
Seconds Minutes Hours DayofMonth Month DayofWeek
#### Cron格式中每个时间元素的说明
时间元素 | 可出现的字符 | 有效数值范围
----|----|----
Seconds | , - * / | 0-59
Minutes | , - * / | 0-59
Hours | , - * / | 0-23
DayofMonth | , - * / ? L W | 0-31
Month | , - * / | 1-12
DayofWeek | , - * / ? L # | 1-7或SUN-SAT
#### Cron格式中特殊字符说明
字符 | 作用 | 举例
----|----|----
, | 列出枚举值 | 在Minutes域使用5,10,表示在5分和10分各触发一次
\- | 表示触发范围 | 在Minutes域使用5-10,表示从5分到10分钟每分钟触发一次
\* | 匹配任意值 | 在Minutes域使用*, 表示每分钟都会触发一次
/ | 起始时间开始触发,每隔固定时间触发一次 | 在Minutes域使用5/10,表示5分时触发一次,每10分钟再触发一次
? | 在DayofMonth和DayofWeek中,用于匹配任意值 | 在DayofMonth域使用?,表示每天都触发一次
\# | 在DayofMonth中,确定第几个星期几 | 1#3表示第三个星期日
L | 表示最后 | 在DayofWeek中使用5L,表示在最后一个星期四触发
W | 表示有效工作日(周一到周五) | 在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日4日触发一次
## 业务场景说明
- 用户对某商品进行下单操作;
- 系统需要根据用户购买的商品信息生成订单并锁定商品的库存;
- 系统设置了60分钟用户不付款就会取消订单;
- 开启一个定时任务,每隔10分钟检查下,如果有超时还未付款的订单,就取消订单并取消锁定的商品库存。
## 整合SpringTask
> 由于SpringTask已经存在于Spring框架中,所以无需添加依赖。
### 添加SpringTask的配置
> 只需要在配置类中添加一个@EnableScheduling注解即可开启SpringTask的定时任务能力。
```java
package com.macro.mall.tiny.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 定时任务配置
* Created by macro on 2019/4/8.
*/
@Configuration
@EnableScheduling
public class SpringTaskConfig {
}
```
### 添加OrderTimeOutCancelTask来执行定时任务
```java
package com.macro.mall.tiny.component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* Created by macro on 2018/8/24.
* 订单超时取消并解锁库存的定时器
*/
@Component
public class OrderTimeOutCancelTask {
private Logger LOGGER = LoggerFactory.getLogger(OrderTimeOutCancelTask.class);
/**
* cron表达式:Seconds Minutes Hours DayofMonth Month DayofWeek [Year]
* 每10分钟扫描一次,扫描设定超时时间之前下的订单,如果没支付则取消该订单
*/
@Scheduled(cron = "0 0/10 * ? * ?")
private void cancelTimeOutOrder() {
// TODO: 2019/5/3 此处应调用取消订单的方法,具体查看mall项目源码
LOGGER.info("取消订单,并根据sku编号释放锁定库存");
}
}
```
## 项目源码地址
[https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-05](https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-05)
## 公众号

================================================
FILE: docs/architect/mall_arch_07.md
================================================
学习不走弯路,[关注公众号](#公众号) 回复「学习路线」,获取mall项目专属学习路线!
# mall整合Elasticsearch实现商品搜索
> 本文主要讲解mall整合Elasticsearch的过程,以实现商品信息在Elasticsearch中的导入、查询、修改、删除为例。
## 项目使用框架介绍
### Elasticsearch
> Elasticsearch 是一个分布式、可扩展、实时的搜索与数据分析引擎。 它能从项目一开始就赋予你的数据以搜索、分析和探索的能力,可用于实现全文搜索和实时数据统计。
#### Elasticsearch的安装和使用
1. 下载Elasticsearch6.2.2的zip包,并解压到指定目录,下载地址:[https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2](https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2)

2. 安装中文分词插件,在elasticsearch-6.2.2\bin目录下执行以下命令:elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.2/elasticsearch-analysis-ik-6.2.2.zip

3. 运行bin目录下的elasticsearch.bat启动Elasticsearch

4. 下载Kibana,作为访问Elasticsearch的客户端,请下载6.2.2版本的zip包,并解压到指定目录,下载地址:[https://artifacts.elastic.co/downloads/kibana/kibana-6.2.2-windows-x86_64.zip](https://artifacts.elastic.co/downloads/kibana/kibana-6.2.2-windows-x86_64.zip)

5. 运行bin目录下的kibana.bat,启动Kibana的用户界面

6. 访问[http://localhost:5601](http://localhost:5601) 即可打开Kibana的用户界面

### Spring Data Elasticsearch
> Spring Data Elasticsearch是Spring提供的一种以Spring Data风格来操作数据存储的方式,它可以避免编写大量的样板代码。
#### 常用注解
##### @Document
```java
//标示映射到Elasticsearch文档上的领域对象
public @interface Document {
//索引库名次,mysql中数据库的概念
String indexName();
//文档类型,mysql中表的概念
String type() default "";
//默认分片数
short shards() default 5;
//默认副本数量
short replicas() default 1;
}
```
##### @Id
```java
//表示是文档的id,文档可以认为是mysql中表行的概念
public @interface Id {
}
```
##### @Field
```java
public @interface Field {
//文档中字段的类型
FieldType type() default FieldType.Auto;
//是否建立倒排索引
boolean index() default true;
//是否进行存储
boolean store() default false;
//分词器名次
String analyzer() default "";
}
```
```java
//为文档自动指定元数据类型
public enum FieldType {
Text,//会进行分词并建了索引的字符类型
Integer,
Long,
Date,
Float,
Double,
Boolean,
Object,
Auto,//自动判断字段类型
Nested,//嵌套对象类型
Ip,
Attachment,
Keyword//不会进行分词建立索引的类型
}
```
#### Sping Data方式的数据操作
##### 继承ElasticsearchRepository接口可以获得常用的数据操作方法

##### 可以使用衍生查询
>在接口中直接指定查询方法名称便可查询,无需进行实现,如商品表中有商品名称、标题和关键字,直接定义以下查询,就可以对这三个字段进行全文搜索。
```java
/**
* 搜索查询
*
* @param name 商品名称
* @param subTitle 商品标题
* @param keywords 商品关键字
* @param page 分页信息
* @return
*/
Page findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);
```
> 在idea中直接会提示对应字段

##### 使用@Query注解可以用Elasticsearch的DSL语句进行查询
```java
@Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}")
Page findByName(String name,Pageable pageable);
```
## 项目使用表说明
- `pms_product`:商品信息表
- `pms_product_attribute`:商品属性参数表
- `pms_product_attribute_value`:存储产品参数值的表
## 整合Elasticsearch实现商品搜索
### 在pom.xml中添加相关依赖
```xml
org.springframework.boot
spring-boot-starter-data-elasticsearch
```
### 修改SpringBoot配置文件
> 修改application.yml文件,在spring节点下添加Elasticsearch相关配置。
```yml
data:
elasticsearch:
repositories:
enabled: true
cluster-nodes: 127.0.0.1:9300 # es的连接地址及端口号
cluster-name: elasticsearch # es集群的名称
```
### 添加商品文档对象EsProduct
> 不需要中文分词的字段设置成@Field(type = FieldType.Keyword)类型,需要中文分词的设置成@Field(analyzer = "ik_max_word",type = FieldType.Text)类型。
```java
package com.macro.mall.tiny.nosql.elasticsearch.document;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* 搜索中的商品信息
* Created by macro on 2018/6/19.
*/
@Document(indexName = "pms", type = "product",shards = 1,replicas = 0)
public class EsProduct implements Serializable {
private static final long serialVersionUID = -1L;
@Id
private Long id;
@Field(type = FieldType.Keyword)
private String productSn;
private Long brandId;
@Field(type = FieldType.Keyword)
private String brandName;
private Long productCategoryId;
@Field(type = FieldType.Keyword)
private String productCategoryName;
private String pic;
@Field(analyzer = "ik_max_word",type = FieldType.Text)
private String name;
@Field(analyzer = "ik_max_word",type = FieldType.Text)
private String subTitle;
@Field(analyzer = "ik_max_word",type = FieldType.Text)
private String keywords;
private BigDecimal price;
private Integer sale;
private Integer newStatus;
private Integer recommandStatus;
private Integer stock;
private Integer promotionType;
private Integer sort;
@Field(type =FieldType.Nested)
private List attrValueList;
//省略了所有getter和setter方法
}
```
### 添加EsProductRepository接口用于操作Elasticsearch
> 继承ElasticsearchRepository接口,这样就拥有了一些基本的Elasticsearch数据操作方法,同时定义了一个衍生查询方法。
```java
package com.macro.mall.tiny.nosql.elasticsearch.repository;
import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* 商品ES操作类
* Created by macro on 2018/6/19.
*/
public interface EsProductRepository extends ElasticsearchRepository {
/**
* 搜索查询
*
* @param name 商品名称
* @param subTitle 商品标题
* @param keywords 商品关键字
* @param page 分页信息
* @return
*/
Page findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);
}
```
### 添加EsProductService接口
```java
package com.macro.mall.tiny.service;
import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct;
import org.springframework.data.domain.Page;
import java.util.List;
/**
* 商品搜索管理Service
* Created by macro on 2018/6/19.
*/
public interface EsProductService {
/**
* 从数据库中导入所有商品到ES
*/
int importAll();
/**
* 根据id删除商品
*/
void delete(Long id);
/**
* 根据id创建商品
*/
EsProduct create(Long id);
/**
* 批量删除商品
*/
void delete(List ids);
/**
* 根据关键字搜索名称或者副标题
*/
Page search(String keyword, Integer pageNum, Integer pageSize);
}
```
### 添加EsProductService接口的实现类EsProductServiceImpl
```java
package com.macro.mall.tiny.service.impl;
import com.macro.mall.tiny.dao.EsProductDao;
import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct;
import com.macro.mall.tiny.nosql.elasticsearch.repository.EsProductRepository;
import com.macro.mall.tiny.service.EsProductService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 商品搜索管理Service实现类
* Created by macro on 2018/6/19.
*/
@Service
public class EsProductServiceImpl implements EsProductService {
private static final Logger LOGGER = LoggerFactory.getLogger(EsProductServiceImpl.class);
@Autowired
private EsProductDao productDao;
@Autowired
private EsProductRepository productRepository;
@Override
public int importAll() {
List esProductList = productDao.getAllEsProductList(null);
Iterable esProductIterable = productRepository.saveAll(esProductList);
Iterator iterator = esProductIterable.iterator();
int result = 0;
while (iterator.hasNext()) {
result++;
iterator.next();
}
return result;
}
@Override
public void delete(Long id) {
productRepository.deleteById(id);
}
@Override
public EsProduct create(Long id) {
EsProduct result = null;
List esProductList = productDao.getAllEsProductList(id);
if (esProductList.size() > 0) {
EsProduct esProduct = esProductList.get(0);
result = productRepository.save(esProduct);
}
return result;
}
@Override
public void delete(List ids) {
if (!CollectionUtils.isEmpty(ids)) {
List esProductList = new ArrayList<>();
for (Long id : ids) {
EsProduct esProduct = new EsProduct();
esProduct.setId(id);
esProductList.add(esProduct);
}
productRepository.deleteAll(esProductList);
}
}
@Override
public Page search(String keyword, Integer pageNum, Integer pageSize) {
Pageable pageable = PageRequest.of(pageNum, pageSize);
return productRepository.findByNameOrSubTitleOrKeywords(keyword, keyword, keyword, pageable);
}
}
```
### 添加EsProductController定义接口
```java
package com.macro.mall.tiny.controller;
import com.macro.mall.tiny.common.api.CommonPage;
import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct;
import com.macro.mall.tiny.service.EsProductService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 搜索商品管理Controller
* Created by macro on 2018/6/19.
*/
@Controller
@Api(tags = "EsProductController", description = "搜索商品管理")
@RequestMapping("/esProduct")
public class EsProductController {
@Autowired
private EsProductService esProductService;
@ApiOperation(value = "导入所有数据库中商品到ES")
@RequestMapping(value = "/importAll", method = RequestMethod.POST)
@ResponseBody
public CommonResult importAllList() {
int count = esProductService.importAll();
return CommonResult.success(count);
}
@ApiOperation(value = "根据id删除商品")
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult